Replace godep with dep
This commit is contained in:
parent
1e7489927c
commit
bf5616c65b
14883 changed files with 3937406 additions and 361781 deletions
56
vendor/k8s.io/kubernetes/pkg/kubelet/deviceplugin/BUILD
generated
vendored
Normal file
56
vendor/k8s.io/kubernetes/pkg/kubelet/deviceplugin/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"device_plugin_stub.go",
|
||||
"endpoint.go",
|
||||
"manager.go",
|
||||
"types.go",
|
||||
"utils.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api/v1/helper:go_default_library",
|
||||
"//pkg/kubelet/apis/deviceplugin/v1alpha1:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/golang.org/x/net/context:go_default_library",
|
||||
"//vendor/google.golang.org/grpc:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"endpoint_test.go",
|
||||
"manager_test.go",
|
||||
"utils_test.go",
|
||||
],
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//pkg/kubelet/apis/deviceplugin/v1alpha1:go_default_library",
|
||||
"//vendor/github.com/stretchr/testify/require:go_default_library",
|
||||
],
|
||||
)
|
||||
158
vendor/k8s.io/kubernetes/pkg/kubelet/deviceplugin/device_plugin_stub.go
generated
vendored
Normal file
158
vendor/k8s.io/kubernetes/pkg/kubelet/deviceplugin/device_plugin_stub.go
generated
vendored
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package deviceplugin
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
pluginapi "k8s.io/kubernetes/pkg/kubelet/apis/deviceplugin/v1alpha1"
|
||||
)
|
||||
|
||||
// Stub implementation for DevicePlugin.
|
||||
type Stub struct {
|
||||
devs []*pluginapi.Device
|
||||
socket string
|
||||
|
||||
stop chan interface{}
|
||||
update chan []*pluginapi.Device
|
||||
|
||||
server *grpc.Server
|
||||
}
|
||||
|
||||
// NewDevicePluginStub returns an initialized DevicePlugin Stub.
|
||||
func NewDevicePluginStub(devs []*pluginapi.Device, socket string) *Stub {
|
||||
return &Stub{
|
||||
devs: devs,
|
||||
socket: socket,
|
||||
|
||||
stop: make(chan interface{}),
|
||||
update: make(chan []*pluginapi.Device),
|
||||
}
|
||||
}
|
||||
|
||||
// Start starts the gRPC server of the device plugin
|
||||
func (m *Stub) Start() error {
|
||||
err := m.cleanup()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sock, err := net.Listen("unix", m.socket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.server = grpc.NewServer([]grpc.ServerOption{}...)
|
||||
pluginapi.RegisterDevicePluginServer(m.server, m)
|
||||
|
||||
go m.server.Serve(sock)
|
||||
// Wait till grpc server is ready.
|
||||
for i := 0; i < 10; i++ {
|
||||
services := m.server.GetServiceInfo()
|
||||
if len(services) > 1 {
|
||||
break
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
log.Println("Starting to serve on", m.socket)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop stops the gRPC server
|
||||
func (m *Stub) Stop() error {
|
||||
m.server.Stop()
|
||||
close(m.stop)
|
||||
|
||||
return m.cleanup()
|
||||
}
|
||||
|
||||
// Register registers the device plugin for the given resourceName with Kubelet.
|
||||
func (m *Stub) Register(kubeletEndpoint, resourceName string) error {
|
||||
conn, err := grpc.Dial(kubeletEndpoint, grpc.WithInsecure(),
|
||||
grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) {
|
||||
return net.DialTimeout("unix", addr, timeout)
|
||||
}))
|
||||
defer conn.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client := pluginapi.NewRegistrationClient(conn)
|
||||
reqt := &pluginapi.RegisterRequest{
|
||||
Version: pluginapi.Version,
|
||||
Endpoint: path.Base(m.socket),
|
||||
ResourceName: resourceName,
|
||||
}
|
||||
|
||||
_, err = client.Register(context.Background(), reqt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListAndWatch lists devices and update that list according to the Update call
|
||||
func (m *Stub) ListAndWatch(e *pluginapi.Empty, s pluginapi.DevicePlugin_ListAndWatchServer) error {
|
||||
log.Println("ListAndWatch")
|
||||
var devs []*pluginapi.Device
|
||||
|
||||
for _, d := range m.devs {
|
||||
devs = append(devs, &pluginapi.Device{
|
||||
ID: d.ID,
|
||||
Health: pluginapi.Healthy,
|
||||
})
|
||||
}
|
||||
|
||||
s.Send(&pluginapi.ListAndWatchResponse{Devices: devs})
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-m.stop:
|
||||
return nil
|
||||
case updated := <-m.update:
|
||||
s.Send(&pluginapi.ListAndWatchResponse{Devices: updated})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update allows the device plugin to send new devices through ListAndWatch
|
||||
func (m *Stub) Update(devs []*pluginapi.Device) {
|
||||
m.update <- devs
|
||||
}
|
||||
|
||||
// Allocate does a mock allocation
|
||||
func (m *Stub) Allocate(ctx context.Context, r *pluginapi.AllocateRequest) (*pluginapi.AllocateResponse, error) {
|
||||
log.Printf("Allocate, %+v", r)
|
||||
|
||||
var response pluginapi.AllocateResponse
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
func (m *Stub) cleanup() error {
|
||||
if err := os.Remove(m.socket); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
214
vendor/k8s.io/kubernetes/pkg/kubelet/deviceplugin/endpoint.go
generated
vendored
Normal file
214
vendor/k8s.io/kubernetes/pkg/kubelet/deviceplugin/endpoint.go
generated
vendored
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package deviceplugin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
pluginapi "k8s.io/kubernetes/pkg/kubelet/apis/deviceplugin/v1alpha1"
|
||||
)
|
||||
|
||||
// endpoint maps to a single registered device plugin. It is responsible
|
||||
// for managing gRPC communications with the device plugin and caching
|
||||
// device states reported by the device plugin.
|
||||
type endpoint struct {
|
||||
client pluginapi.DevicePluginClient
|
||||
|
||||
socketPath string
|
||||
resourceName string
|
||||
|
||||
devices map[string]*pluginapi.Device
|
||||
mutex sync.Mutex
|
||||
|
||||
callback MonitorCallback
|
||||
|
||||
cancel context.CancelFunc
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// newEndpoint creates a new endpoint for the given resourceName.
|
||||
func newEndpoint(socketPath, resourceName string, callback MonitorCallback) (*endpoint, error) {
|
||||
client, err := dial(socketPath)
|
||||
if err != nil {
|
||||
glog.Errorf("Can't create new endpoint with path %s err %v", socketPath, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, stop := context.WithCancel(context.Background())
|
||||
|
||||
return &endpoint{
|
||||
client: client,
|
||||
|
||||
socketPath: socketPath,
|
||||
resourceName: resourceName,
|
||||
|
||||
devices: nil,
|
||||
callback: callback,
|
||||
|
||||
cancel: stop,
|
||||
ctx: ctx,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (e *endpoint) getDevices() []*pluginapi.Device {
|
||||
e.mutex.Lock()
|
||||
defer e.mutex.Unlock()
|
||||
return copyDevices(e.devices)
|
||||
}
|
||||
|
||||
// list initializes ListAndWatch gRPC call for the device plugin and gets the
|
||||
// initial list of the devices. Returns ListAndWatch gRPC stream on success.
|
||||
func (e *endpoint) list() (pluginapi.DevicePlugin_ListAndWatchClient, error) {
|
||||
glog.V(3).Infof("Starting List")
|
||||
stream, err := e.client.ListAndWatch(e.ctx, &pluginapi.Empty{})
|
||||
if err != nil {
|
||||
glog.Errorf(errListAndWatch, e.resourceName, err)
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
devs, err := stream.Recv()
|
||||
if err != nil {
|
||||
glog.Errorf(errListAndWatch, e.resourceName, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
devices := make(map[string]*pluginapi.Device)
|
||||
var added, updated, deleted []*pluginapi.Device
|
||||
for _, d := range devs.Devices {
|
||||
devices[d.ID] = d
|
||||
added = append(added, cloneDevice(d))
|
||||
}
|
||||
|
||||
e.mutex.Lock()
|
||||
e.devices = devices
|
||||
e.mutex.Unlock()
|
||||
|
||||
e.callback(e.resourceName, added, updated, deleted)
|
||||
|
||||
return stream, nil
|
||||
}
|
||||
|
||||
// listAndWatch blocks on receiving ListAndWatch gRPC stream updates. Each ListAndWatch
|
||||
// stream update contains a new list of device states. listAndWatch compares the new
|
||||
// device states with its cached states to get list of new, updated, and deleted devices.
|
||||
// It then issues a callback to pass this information to the device_plugin_handler which
|
||||
// will adjust the resource available information accordingly.
|
||||
func (e *endpoint) listAndWatch(stream pluginapi.DevicePlugin_ListAndWatchClient) {
|
||||
glog.V(3).Infof("Starting ListAndWatch")
|
||||
|
||||
devices := make(map[string]*pluginapi.Device)
|
||||
|
||||
e.mutex.Lock()
|
||||
for _, d := range e.devices {
|
||||
devices[d.ID] = cloneDevice(d)
|
||||
}
|
||||
e.mutex.Unlock()
|
||||
|
||||
for {
|
||||
response, err := stream.Recv()
|
||||
if err != nil {
|
||||
glog.Errorf(errListAndWatch, e.resourceName, err)
|
||||
return
|
||||
}
|
||||
|
||||
devs := response.Devices
|
||||
glog.V(2).Infof("State pushed for device plugin %s", e.resourceName)
|
||||
|
||||
newDevs := make(map[string]*pluginapi.Device)
|
||||
var added, updated []*pluginapi.Device
|
||||
|
||||
for _, d := range devs {
|
||||
dOld, ok := devices[d.ID]
|
||||
newDevs[d.ID] = d
|
||||
|
||||
if !ok {
|
||||
glog.V(2).Infof("New device for Endpoint %s: %v", e.resourceName, d)
|
||||
|
||||
devices[d.ID] = d
|
||||
added = append(added, cloneDevice(d))
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if d.Health == dOld.Health {
|
||||
continue
|
||||
}
|
||||
|
||||
if d.Health == pluginapi.Unhealthy {
|
||||
glog.Errorf("Device %s is now Unhealthy", d.ID)
|
||||
} else if d.Health == pluginapi.Healthy {
|
||||
glog.V(2).Infof("Device %s is now Healthy", d.ID)
|
||||
}
|
||||
|
||||
devices[d.ID] = d
|
||||
updated = append(updated, cloneDevice(d))
|
||||
}
|
||||
|
||||
var deleted []*pluginapi.Device
|
||||
for id, d := range devices {
|
||||
if _, ok := newDevs[id]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
glog.Errorf("Device %s was deleted", d.ID)
|
||||
|
||||
deleted = append(deleted, cloneDevice(d))
|
||||
delete(devices, id)
|
||||
}
|
||||
|
||||
e.mutex.Lock()
|
||||
e.devices = devices
|
||||
e.mutex.Unlock()
|
||||
|
||||
e.callback(e.resourceName, added, updated, deleted)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// allocate issues Allocate gRPC call to the device plugin.
|
||||
func (e *endpoint) allocate(devs []string) (*pluginapi.AllocateResponse, error) {
|
||||
return e.client.Allocate(context.Background(), &pluginapi.AllocateRequest{
|
||||
DevicesIDs: devs,
|
||||
})
|
||||
}
|
||||
|
||||
func (e *endpoint) stop() {
|
||||
e.cancel()
|
||||
}
|
||||
|
||||
// dial establishes the gRPC communication with the registered device plugin.
|
||||
func dial(unixSocketPath string) (pluginapi.DevicePluginClient, error) {
|
||||
c, err := grpc.Dial(unixSocketPath, grpc.WithInsecure(),
|
||||
grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) {
|
||||
return net.DialTimeout("unix", addr, timeout)
|
||||
}),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(errFailedToDialDevicePlugin+" %v", err)
|
||||
}
|
||||
|
||||
return pluginapi.NewDevicePluginClient(c), nil
|
||||
}
|
||||
132
vendor/k8s.io/kubernetes/pkg/kubelet/deviceplugin/endpoint_test.go
generated
vendored
Normal file
132
vendor/k8s.io/kubernetes/pkg/kubelet/deviceplugin/endpoint_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package deviceplugin
|
||||
|
||||
import (
|
||||
"path"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
pluginapi "k8s.io/kubernetes/pkg/kubelet/apis/deviceplugin/v1alpha1"
|
||||
)
|
||||
|
||||
var (
|
||||
esocketName = "mock.sock"
|
||||
)
|
||||
|
||||
func TestNewEndpoint(t *testing.T) {
|
||||
socket := path.Join("/tmp", esocketName)
|
||||
|
||||
devs := []*pluginapi.Device{
|
||||
{ID: "ADeviceId", Health: pluginapi.Healthy},
|
||||
}
|
||||
|
||||
p, e := esetup(t, devs, socket, "mock", func(n string, a, u, r []*pluginapi.Device) {})
|
||||
defer ecleanup(t, p, e)
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
socket := path.Join("/tmp", esocketName)
|
||||
|
||||
devs := []*pluginapi.Device{
|
||||
{ID: "ADeviceId", Health: pluginapi.Healthy},
|
||||
}
|
||||
|
||||
p, e := esetup(t, devs, socket, "mock", func(n string, a, u, r []*pluginapi.Device) {})
|
||||
defer ecleanup(t, p, e)
|
||||
|
||||
_, err := e.list()
|
||||
require.NoError(t, err)
|
||||
|
||||
e.mutex.Lock()
|
||||
defer e.mutex.Unlock()
|
||||
|
||||
require.Len(t, e.devices, 1)
|
||||
|
||||
d, ok := e.devices[devs[0].ID]
|
||||
require.True(t, ok)
|
||||
|
||||
require.Equal(t, d.ID, devs[0].ID)
|
||||
require.Equal(t, d.Health, devs[0].Health)
|
||||
}
|
||||
|
||||
func TestListAndWatch(t *testing.T) {
|
||||
socket := path.Join("/tmp", esocketName)
|
||||
|
||||
devs := []*pluginapi.Device{
|
||||
{ID: "ADeviceId", Health: pluginapi.Healthy},
|
||||
{ID: "AnotherDeviceId", Health: pluginapi.Healthy},
|
||||
}
|
||||
|
||||
updated := []*pluginapi.Device{
|
||||
{ID: "ADeviceId", Health: pluginapi.Unhealthy},
|
||||
{ID: "AThirdDeviceId", Health: pluginapi.Healthy},
|
||||
}
|
||||
|
||||
p, e := esetup(t, devs, socket, "mock", func(n string, a, u, r []*pluginapi.Device) {
|
||||
require.Len(t, a, 1)
|
||||
require.Len(t, u, 1)
|
||||
require.Len(t, r, 1)
|
||||
|
||||
require.Equal(t, a[0].ID, updated[1].ID)
|
||||
|
||||
require.Equal(t, u[0].ID, updated[0].ID)
|
||||
require.Equal(t, u[0].Health, updated[0].Health)
|
||||
|
||||
require.Equal(t, r[0].ID, devs[1].ID)
|
||||
})
|
||||
defer ecleanup(t, p, e)
|
||||
|
||||
s, err := e.list()
|
||||
require.NoError(t, err)
|
||||
|
||||
go e.listAndWatch(s)
|
||||
p.Update(updated)
|
||||
time.Sleep(time.Second)
|
||||
|
||||
e.mutex.Lock()
|
||||
defer e.mutex.Unlock()
|
||||
|
||||
require.Len(t, e.devices, 2)
|
||||
for _, dref := range updated {
|
||||
d, ok := e.devices[dref.ID]
|
||||
|
||||
require.True(t, ok)
|
||||
require.Equal(t, d.ID, dref.ID)
|
||||
require.Equal(t, d.Health, dref.Health)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func esetup(t *testing.T, devs []*pluginapi.Device, socket, resourceName string, callback MonitorCallback) (*Stub, *endpoint) {
|
||||
p := NewDevicePluginStub(devs, socket)
|
||||
|
||||
err := p.Start()
|
||||
require.NoError(t, err)
|
||||
|
||||
e, err := newEndpoint(socket, "mock", func(n string, a, u, r []*pluginapi.Device) {})
|
||||
require.NoError(t, err)
|
||||
|
||||
return p, e
|
||||
}
|
||||
|
||||
func ecleanup(t *testing.T, p *Stub, e *endpoint) {
|
||||
p.Stop()
|
||||
e.stop()
|
||||
}
|
||||
239
vendor/k8s.io/kubernetes/pkg/kubelet/deviceplugin/manager.go
generated
vendored
Normal file
239
vendor/k8s.io/kubernetes/pkg/kubelet/deviceplugin/manager.go
generated
vendored
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package deviceplugin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
pluginapi "k8s.io/kubernetes/pkg/kubelet/apis/deviceplugin/v1alpha1"
|
||||
)
|
||||
|
||||
// ManagerImpl is the structure in charge of managing Device Plugins.
|
||||
type ManagerImpl struct {
|
||||
socketname string
|
||||
socketdir string
|
||||
|
||||
endpoints map[string]*endpoint // Key is ResourceName
|
||||
mutex sync.Mutex
|
||||
|
||||
callback MonitorCallback
|
||||
|
||||
server *grpc.Server
|
||||
}
|
||||
|
||||
// NewManagerImpl creates a new manager on the socket `socketPath`.
|
||||
// f is the callback that is called when a device becomes unhealthy.
|
||||
// socketPath is present for testing purposes in production this is pluginapi.KubeletSocket
|
||||
func NewManagerImpl(socketPath string, f MonitorCallback) (*ManagerImpl, error) {
|
||||
glog.V(2).Infof("Creating Device Plugin manager at %s", socketPath)
|
||||
|
||||
if socketPath == "" || !filepath.IsAbs(socketPath) {
|
||||
return nil, fmt.Errorf(errBadSocket+" %v", socketPath)
|
||||
}
|
||||
|
||||
dir, file := filepath.Split(socketPath)
|
||||
return &ManagerImpl{
|
||||
endpoints: make(map[string]*endpoint),
|
||||
|
||||
socketname: file,
|
||||
socketdir: dir,
|
||||
callback: f,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *ManagerImpl) removeContents(dir string) error {
|
||||
d, err := os.Open(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer d.Close()
|
||||
names, err := d.Readdirnames(-1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, name := range names {
|
||||
filePath := filepath.Join(dir, name)
|
||||
if filePath == m.CheckpointFile() {
|
||||
continue
|
||||
}
|
||||
stat, err := os.Stat(filePath)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to stat file %v: %v", filePath, err)
|
||||
continue
|
||||
}
|
||||
if stat.IsDir() {
|
||||
continue
|
||||
}
|
||||
err = os.RemoveAll(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckpointFile returns device plugin checkpoint file path.
|
||||
func (m *ManagerImpl) CheckpointFile() string {
|
||||
return filepath.Join(m.socketdir, "kubelet_internal_checkpoint")
|
||||
}
|
||||
|
||||
// Start starts the Device Plugin Manager
|
||||
func (m *ManagerImpl) Start() error {
|
||||
glog.V(2).Infof("Starting Device Plugin manager")
|
||||
|
||||
socketPath := filepath.Join(m.socketdir, m.socketname)
|
||||
os.MkdirAll(m.socketdir, 0755)
|
||||
|
||||
// Removes all stale sockets in m.socketdir. Device plugins can monitor
|
||||
// this and use it as a signal to re-register with the new Kubelet.
|
||||
if err := m.removeContents(m.socketdir); err != nil {
|
||||
glog.Errorf("Fail to clean up stale contents under %s: %+v", m.socketdir, err)
|
||||
}
|
||||
|
||||
if err := os.Remove(socketPath); err != nil && !os.IsNotExist(err) {
|
||||
glog.Errorf(errRemoveSocket+" %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
s, err := net.Listen("unix", socketPath)
|
||||
if err != nil {
|
||||
glog.Errorf(errListenSocket+" %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
m.server = grpc.NewServer([]grpc.ServerOption{}...)
|
||||
|
||||
pluginapi.RegisterRegistrationServer(m.server, m)
|
||||
go m.server.Serve(s)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Devices is the map of devices that are known by the Device
|
||||
// Plugin manager with the kind of the devices as key
|
||||
func (m *ManagerImpl) Devices() map[string][]*pluginapi.Device {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
devs := make(map[string][]*pluginapi.Device)
|
||||
for k, e := range m.endpoints {
|
||||
glog.V(3).Infof("Endpoint: %+v: %+v", k, e)
|
||||
devs[k] = e.getDevices()
|
||||
}
|
||||
|
||||
return devs
|
||||
}
|
||||
|
||||
// Allocate is the call that you can use to allocate a set of devices
|
||||
// from the registered device plugins.
|
||||
func (m *ManagerImpl) Allocate(resourceName string, devs []string) (*pluginapi.AllocateResponse, error) {
|
||||
|
||||
if len(devs) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
glog.V(3).Infof("Recieved allocation request for devices %v for device plugin %s",
|
||||
devs, resourceName)
|
||||
m.mutex.Lock()
|
||||
e, ok := m.endpoints[resourceName]
|
||||
m.mutex.Unlock()
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Unknown Device Plugin %s", resourceName)
|
||||
}
|
||||
|
||||
return e.allocate(devs)
|
||||
}
|
||||
|
||||
// Register registers a device plugin.
|
||||
func (m *ManagerImpl) Register(ctx context.Context,
|
||||
r *pluginapi.RegisterRequest) (*pluginapi.Empty, error) {
|
||||
glog.V(2).Infof("Got request for Device Plugin %s", r.ResourceName)
|
||||
if r.Version != pluginapi.Version {
|
||||
return &pluginapi.Empty{}, fmt.Errorf(errUnsuportedVersion)
|
||||
}
|
||||
|
||||
if err := IsResourceNameValid(r.ResourceName); err != nil {
|
||||
return &pluginapi.Empty{}, err
|
||||
}
|
||||
|
||||
// TODO: for now, always accepts newest device plugin. Later may consider to
|
||||
// add some policies here, e.g., verify whether an old device plugin with the
|
||||
// same resource name is still alive to determine whether we want to accept
|
||||
// the new registration.
|
||||
go m.addEndpoint(r)
|
||||
|
||||
return &pluginapi.Empty{}, nil
|
||||
}
|
||||
|
||||
// Stop is the function that can stop the gRPC server.
|
||||
func (m *ManagerImpl) Stop() error {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
for _, e := range m.endpoints {
|
||||
e.stop()
|
||||
}
|
||||
|
||||
m.server.Stop()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ManagerImpl) addEndpoint(r *pluginapi.RegisterRequest) {
|
||||
socketPath := filepath.Join(m.socketdir, r.Endpoint)
|
||||
e, err := newEndpoint(socketPath, r.ResourceName, m.callback)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to dial device plugin with request %v: %v", r, err)
|
||||
return
|
||||
}
|
||||
stream, err := e.list()
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to List devices for plugin %v: %v", r.ResourceName, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Associates the newly created endpoint with the corresponding resource name.
|
||||
// Stops existing endpoint if there is any.
|
||||
m.mutex.Lock()
|
||||
old, ok := m.endpoints[r.ResourceName]
|
||||
m.endpoints[r.ResourceName] = e
|
||||
m.mutex.Unlock()
|
||||
glog.V(2).Infof("Registered endpoint %v", e)
|
||||
if ok && old != nil {
|
||||
old.stop()
|
||||
}
|
||||
|
||||
go func() {
|
||||
e.listAndWatch(stream)
|
||||
|
||||
m.mutex.Lock()
|
||||
if old, ok := m.endpoints[r.ResourceName]; ok && old == e {
|
||||
glog.V(2).Infof("Delete resource for endpoint %v", e)
|
||||
delete(m.endpoints, r.ResourceName)
|
||||
// Issues callback to delete all of devices.
|
||||
e.callback(e.resourceName, []*pluginapi.Device{}, []*pluginapi.Device{}, e.getDevices())
|
||||
}
|
||||
glog.V(2).Infof("Unregistered endpoint %v", e)
|
||||
m.mutex.Unlock()
|
||||
}()
|
||||
}
|
||||
109
vendor/k8s.io/kubernetes/pkg/kubelet/deviceplugin/manager_test.go
generated
vendored
Normal file
109
vendor/k8s.io/kubernetes/pkg/kubelet/deviceplugin/manager_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package deviceplugin
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
pluginapi "k8s.io/kubernetes/pkg/kubelet/apis/deviceplugin/v1alpha1"
|
||||
)
|
||||
|
||||
const (
|
||||
socketName = "/tmp/device_plugin/server.sock"
|
||||
pluginSocketName = "/tmp/device_plugin/device-plugin.sock"
|
||||
testResourceName = "fake-domain/resource"
|
||||
)
|
||||
|
||||
func TestNewManagerImpl(t *testing.T) {
|
||||
_, err := NewManagerImpl("", func(n string, a, u, r []*pluginapi.Device) {})
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = NewManagerImpl(socketName, func(n string, a, u, r []*pluginapi.Device) {})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestNewManagerImplStart(t *testing.T) {
|
||||
m, p := setup(t, []*pluginapi.Device{}, func(n string, a, u, r []*pluginapi.Device) {})
|
||||
cleanup(t, m, p)
|
||||
}
|
||||
|
||||
// Tests that the device plugin manager correctly handles registration and re-registration by
|
||||
// making sure that after registration, devices are correctly updated and if a re-registration
|
||||
// happens, we will NOT delete devices.
|
||||
func TestDevicePluginReRegistration(t *testing.T) {
|
||||
devs := []*pluginapi.Device{
|
||||
{ID: "Dev1", Health: pluginapi.Healthy},
|
||||
{ID: "Dev2", Health: pluginapi.Healthy},
|
||||
}
|
||||
|
||||
callbackCount := 0
|
||||
callbackChan := make(chan int)
|
||||
var stopping int32
|
||||
stopping = 0
|
||||
callback := func(n string, a, u, r []*pluginapi.Device) {
|
||||
// Should be called twice, one for each plugin registration, till we are stopping.
|
||||
if callbackCount > 1 && atomic.LoadInt32(&stopping) <= 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
callbackCount++
|
||||
callbackChan <- callbackCount
|
||||
}
|
||||
m, p1 := setup(t, devs, callback)
|
||||
p1.Register(socketName, testResourceName)
|
||||
// Wait for the first callback to be issued.
|
||||
<-callbackChan
|
||||
devices := m.Devices()
|
||||
require.Equal(t, 2, len(devices[testResourceName]), "Devices are not updated.")
|
||||
|
||||
p2 := NewDevicePluginStub(devs, pluginSocketName+".new")
|
||||
err := p2.Start()
|
||||
require.NoError(t, err)
|
||||
p2.Register(socketName, testResourceName)
|
||||
// Wait for the second callback to be issued.
|
||||
<-callbackChan
|
||||
|
||||
devices2 := m.Devices()
|
||||
require.Equal(t, 2, len(devices2[testResourceName]), "Devices shouldn't change.")
|
||||
// Wait long enough to catch unexpected callbacks.
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
atomic.StoreInt32(&stopping, 1)
|
||||
cleanup(t, m, p1)
|
||||
p2.Stop()
|
||||
}
|
||||
|
||||
func setup(t *testing.T, devs []*pluginapi.Device, callback MonitorCallback) (Manager, *Stub) {
|
||||
m, err := NewManagerImpl(socketName, callback)
|
||||
require.NoError(t, err)
|
||||
err = m.Start()
|
||||
require.NoError(t, err)
|
||||
|
||||
p := NewDevicePluginStub(devs, pluginSocketName)
|
||||
err = p.Start()
|
||||
require.NoError(t, err)
|
||||
|
||||
return m, p
|
||||
}
|
||||
|
||||
func cleanup(t *testing.T, m Manager, p *Stub) {
|
||||
p.Stop()
|
||||
m.Stop()
|
||||
}
|
||||
74
vendor/k8s.io/kubernetes/pkg/kubelet/deviceplugin/types.go
generated
vendored
Normal file
74
vendor/k8s.io/kubernetes/pkg/kubelet/deviceplugin/types.go
generated
vendored
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package deviceplugin
|
||||
|
||||
import (
|
||||
pluginapi "k8s.io/kubernetes/pkg/kubelet/apis/deviceplugin/v1alpha1"
|
||||
)
|
||||
|
||||
// MonitorCallback is the function called when a device's health state changes,
|
||||
// or new devices are reported, or old devices are deleted.
|
||||
// Updated contains the most recent state of the Device.
|
||||
type MonitorCallback func(resourceName string, added, updated, deleted []*pluginapi.Device)
|
||||
|
||||
// Manager manages all the Device Plugins running on a node.
|
||||
type Manager interface {
|
||||
// Start starts the gRPC Registration service.
|
||||
Start() error
|
||||
|
||||
// Devices is the map of devices that have registered themselves
|
||||
// against the manager.
|
||||
// The map key is the ResourceName of the device plugins.
|
||||
Devices() map[string][]*pluginapi.Device
|
||||
|
||||
// Allocate takes resourceName and list of device Ids, and calls the
|
||||
// gRPC Allocate on the device plugin matching the resourceName.
|
||||
Allocate(string, []string) (*pluginapi.AllocateResponse, error)
|
||||
|
||||
// Stop stops the manager.
|
||||
Stop() error
|
||||
|
||||
// Returns checkpoint file path.
|
||||
CheckpointFile() string
|
||||
}
|
||||
|
||||
// TODO: evaluate whether we need these error definitions.
|
||||
const (
|
||||
// errFailedToDialDevicePlugin is the error raised when the device plugin could not be
|
||||
// reached on the registered socket
|
||||
errFailedToDialDevicePlugin = "failed to dial device plugin:"
|
||||
// errUnsuportedVersion is the error raised when the device plugin uses an API version not
|
||||
// supported by the Kubelet registry
|
||||
errUnsuportedVersion = "unsupported API version by the Kubelet registry"
|
||||
// errDevicePluginAlreadyExists is the error raised when a device plugin with the
|
||||
// same Resource Name tries to register itself
|
||||
errDevicePluginAlreadyExists = "another device plugin already registered this Resource Name"
|
||||
// errInvalidResourceName is the error raised when a device plugin is registering
|
||||
// itself with an invalid ResourceName
|
||||
errInvalidResourceName = "the ResourceName is invalid"
|
||||
// errEmptyResourceName is the error raised when the resource name field is empty
|
||||
errEmptyResourceName = "invalid Empty ResourceName"
|
||||
|
||||
// errBadSocket is the error raised when the registry socket path is not absolute
|
||||
errBadSocket = "bad socketPath, must be an absolute path:"
|
||||
// errRemoveSocket is the error raised when the registry could not remove the existing socket
|
||||
errRemoveSocket = "failed to remove socket while starting device plugin registry, with error"
|
||||
// errListenSocket is the error raised when the registry could not listen on the socket
|
||||
errListenSocket = "failed to listen to socket while starting device plugin registry, with error"
|
||||
// errListAndWatch is the error raised when ListAndWatch ended unsuccessfully
|
||||
errListAndWatch = "listAndWatch ended unexpectedly for device plugin %s with error %v"
|
||||
)
|
||||
61
vendor/k8s.io/kubernetes/pkg/kubelet/deviceplugin/utils.go
generated
vendored
Normal file
61
vendor/k8s.io/kubernetes/pkg/kubelet/deviceplugin/utils.go
generated
vendored
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package deviceplugin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
v1helper "k8s.io/kubernetes/pkg/api/v1/helper"
|
||||
pluginapi "k8s.io/kubernetes/pkg/kubelet/apis/deviceplugin/v1alpha1"
|
||||
)
|
||||
|
||||
func cloneDevice(d *pluginapi.Device) *pluginapi.Device {
|
||||
return &pluginapi.Device{
|
||||
ID: d.ID,
|
||||
Health: d.Health,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func copyDevices(devs map[string]*pluginapi.Device) []*pluginapi.Device {
|
||||
var clones []*pluginapi.Device
|
||||
|
||||
for _, d := range devs {
|
||||
clones = append(clones, cloneDevice(d))
|
||||
}
|
||||
|
||||
return clones
|
||||
}
|
||||
|
||||
// IsResourceNameValid returns an error if the resource is invalid or is not an
|
||||
// extended resource name.
|
||||
func IsResourceNameValid(resourceName string) error {
|
||||
if resourceName == "" {
|
||||
return fmt.Errorf(errEmptyResourceName)
|
||||
}
|
||||
if !IsDeviceName(v1.ResourceName(resourceName)) {
|
||||
return fmt.Errorf(errInvalidResourceName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsDeviceName returns whether the ResourceName points to an extended resource
|
||||
// name exported by a device plugin.
|
||||
func IsDeviceName(k v1.ResourceName) bool {
|
||||
return v1helper.IsExtendedResourceName(k)
|
||||
}
|
||||
53
vendor/k8s.io/kubernetes/pkg/kubelet/deviceplugin/utils_test.go
generated
vendored
Normal file
53
vendor/k8s.io/kubernetes/pkg/kubelet/deviceplugin/utils_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package deviceplugin
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
pluginapi "k8s.io/kubernetes/pkg/kubelet/apis/deviceplugin/v1alpha1"
|
||||
)
|
||||
|
||||
func TestCloneDevice(t *testing.T) {
|
||||
d := cloneDevice(&pluginapi.Device{ID: "ADeviceId", Health: pluginapi.Healthy})
|
||||
|
||||
require.Equal(t, d.ID, "ADeviceId")
|
||||
require.Equal(t, d.Health, pluginapi.Healthy)
|
||||
}
|
||||
|
||||
func TestCopyDevices(t *testing.T) {
|
||||
d := map[string]*pluginapi.Device{
|
||||
"ADeviceId": {ID: "ADeviceId", Health: pluginapi.Healthy},
|
||||
}
|
||||
|
||||
devs := copyDevices(d)
|
||||
require.Len(t, devs, 1)
|
||||
}
|
||||
|
||||
func TestIsResourceName(t *testing.T) {
|
||||
require.NotNil(t, IsResourceNameValid(""))
|
||||
require.NotNil(t, IsResourceNameValid("cpu"))
|
||||
require.NotNil(t, IsResourceNameValid("name1"))
|
||||
require.NotNil(t, IsResourceNameValid("alpha.kubernetes.io/name1"))
|
||||
require.NotNil(t, IsResourceNameValid("beta.kubernetes.io/name1"))
|
||||
require.NotNil(t, IsResourceNameValid("kubernetes.io/name1"))
|
||||
require.Nil(t, IsResourceNameValid("domain1.io/name1"))
|
||||
require.Nil(t, IsResourceNameValid("alpha.domain1.io/name1"))
|
||||
require.Nil(t, IsResourceNameValid("beta.domain1.io/name1"))
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue