
incompatible changes: - Add GetDevicePluginOptions rpc call. This is needed when we switch from Registration service to probe-based plugin watcher. - Change AllocateRequest and AllocateResponse to allow device requests from multiple containers in a pod. Currently only made mechanical change on the devicemanager and test code to cope with the API but still issues an Allocate call per container. We can modify the devicemanager in 1.11 to issue a single Allocate call per pod. The change will also facilitate incremental API change to communicate pod level information through Allocate rpc if there is such future need.
185 lines
4.8 KiB
Go
185 lines
4.8 KiB
Go
/*
|
|
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 devicemanager
|
|
|
|
import (
|
|
"log"
|
|
"net"
|
|
"os"
|
|
"path"
|
|
"time"
|
|
|
|
"golang.org/x/net/context"
|
|
"google.golang.org/grpc"
|
|
|
|
pluginapi "k8s.io/kubernetes/pkg/kubelet/apis/deviceplugin/v1beta1"
|
|
)
|
|
|
|
// Stub implementation for DevicePlugin.
|
|
type Stub struct {
|
|
devs []*pluginapi.Device
|
|
socket string
|
|
|
|
stop chan interface{}
|
|
update chan []*pluginapi.Device
|
|
|
|
server *grpc.Server
|
|
|
|
// allocFunc is used for handling allocation request
|
|
allocFunc stubAllocFunc
|
|
}
|
|
|
|
// stubAllocFunc is the function called when receive an allocation request from Kubelet
|
|
type stubAllocFunc func(r *pluginapi.AllocateRequest, devs map[string]pluginapi.Device) (*pluginapi.AllocateResponse, error)
|
|
|
|
func defaultAllocFunc(r *pluginapi.AllocateRequest, devs map[string]pluginapi.Device) (*pluginapi.AllocateResponse, error) {
|
|
var response pluginapi.AllocateResponse
|
|
|
|
return &response, nil
|
|
}
|
|
|
|
// 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),
|
|
|
|
allocFunc: defaultAllocFunc,
|
|
}
|
|
}
|
|
|
|
// SetAllocFunc sets allocFunc of the device plugin
|
|
func (m *Stub) SetAllocFunc(f stubAllocFunc) {
|
|
m.allocFunc = f
|
|
}
|
|
|
|
// 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)
|
|
_, conn, err := dial(m.socket)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
conn.Close()
|
|
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, preStartContainerFlag bool) error {
|
|
conn, err := grpc.Dial(kubeletEndpoint, grpc.WithInsecure(), grpc.WithBlock(),
|
|
grpc.WithTimeout(10*time.Second),
|
|
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,
|
|
Options: &pluginapi.DevicePluginOptions{PreStartRequired: preStartContainerFlag},
|
|
}
|
|
|
|
_, err = client.Register(context.Background(), reqt)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetDevicePluginOptions returns DevicePluginOptions settings for the device plugin.
|
|
func (m *Stub) GetDevicePluginOptions(ctx context.Context, e *pluginapi.Empty) (*pluginapi.DevicePluginOptions, error) {
|
|
return &pluginapi.DevicePluginOptions{}, nil
|
|
}
|
|
|
|
// PreStartContainer resets the devices received
|
|
func (m *Stub) PreStartContainer(ctx context.Context, r *pluginapi.PreStartContainerRequest) (*pluginapi.PreStartContainerResponse, error) {
|
|
log.Printf("PreStartContainer, %+v", r)
|
|
return &pluginapi.PreStartContainerResponse{}, 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")
|
|
|
|
s.Send(&pluginapi.ListAndWatchResponse{Devices: m.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)
|
|
|
|
devs := make(map[string]pluginapi.Device)
|
|
|
|
for _, dev := range m.devs {
|
|
devs[dev.ID] = *dev
|
|
}
|
|
|
|
return m.allocFunc(r, devs)
|
|
}
|
|
|
|
func (m *Stub) cleanup() error {
|
|
if err := os.Remove(m.socket); err != nil && !os.IsNotExist(err) {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|