mirror of
https://github.com/intel/intel-device-plugins-for-kubernetes.git
synced 2025-06-03 03:59:37 +00:00

When kubelet notifies the plugin about its restart by removing the plugin's socket we do reconnect to kubelet, but we don't send the current list of monitored devices to kubelet. As result kubelet is not aware of discovered devices if it restarts. Always send the current list of monitored devices to kubelet upon ListAndWatch() request.
268 lines
6.9 KiB
Go
268 lines
6.9 KiB
Go
// Copyright 2017 Intel Corporation. All Rights Reserved.
|
|
//
|
|
// 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 main
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"golang.org/x/net/context"
|
|
"google.golang.org/grpc/metadata"
|
|
|
|
pluginapi "k8s.io/kubernetes/pkg/kubelet/apis/deviceplugin/v1beta1"
|
|
|
|
"github.com/intel/intel-device-plugins-for-kubernetes/cmd/fpga_plugin/devicecache"
|
|
"github.com/intel/intel-device-plugins-for-kubernetes/internal/deviceplugin"
|
|
)
|
|
|
|
// Minimal implementation of pluginapi.DevicePlugin_ListAndWatchServer
|
|
type listAndWatchServerStub struct {
|
|
testDM *deviceManager
|
|
generateErr int
|
|
sendCounter int
|
|
cdata chan []*pluginapi.Device
|
|
}
|
|
|
|
func (s *listAndWatchServerStub) Send(resp *pluginapi.ListAndWatchResponse) error {
|
|
s.sendCounter = s.sendCounter + 1
|
|
if s.generateErr == s.sendCounter {
|
|
fmt.Println("listAndWatchServerStub::Send returns error")
|
|
return fmt.Errorf("Fake error")
|
|
}
|
|
|
|
fmt.Println("listAndWatchServerStub::Send", resp.Devices)
|
|
s.cdata <- resp.Devices
|
|
return nil
|
|
}
|
|
|
|
func (s *listAndWatchServerStub) Context() context.Context {
|
|
return nil
|
|
}
|
|
|
|
func (s *listAndWatchServerStub) RecvMsg(m interface{}) error {
|
|
return nil
|
|
}
|
|
|
|
func (s *listAndWatchServerStub) SendMsg(m interface{}) error {
|
|
return nil
|
|
}
|
|
|
|
func (s *listAndWatchServerStub) SendHeader(m metadata.MD) error {
|
|
return nil
|
|
}
|
|
|
|
func (s *listAndWatchServerStub) SetHeader(m metadata.MD) error {
|
|
return nil
|
|
}
|
|
|
|
func (s *listAndWatchServerStub) SetTrailer(m metadata.MD) {
|
|
}
|
|
|
|
func TestGetDevicePluginOptions(t *testing.T) {
|
|
dm := &deviceManager{}
|
|
dm.GetDevicePluginOptions(nil, nil)
|
|
}
|
|
|
|
func TestPreStartContainer(t *testing.T) {
|
|
dm := &deviceManager{}
|
|
dm.PreStartContainer(nil, nil)
|
|
}
|
|
|
|
func TestListAndWatch(t *testing.T) {
|
|
tcases := []struct {
|
|
name string
|
|
updates []map[string]deviceplugin.DeviceInfo
|
|
errorOnCall int
|
|
}{
|
|
{
|
|
name: "No updates and close",
|
|
},
|
|
{
|
|
name: "No updates and close, but expect streaming error",
|
|
errorOnCall: 1,
|
|
},
|
|
{
|
|
name: "Send 1 update",
|
|
updates: []map[string]deviceplugin.DeviceInfo{
|
|
{
|
|
"fake_id": {
|
|
State: pluginapi.Healthy,
|
|
Nodes: []string{"/dev/intel-fpga-port.0"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Send 1 update, but expect streaming error",
|
|
updates: []map[string]deviceplugin.DeviceInfo{
|
|
{
|
|
"fake_id": {
|
|
State: pluginapi.Healthy,
|
|
Nodes: []string{"/dev/intel-fpga-port.0"},
|
|
},
|
|
},
|
|
},
|
|
errorOnCall: 2,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tcases {
|
|
devCh := make(chan map[string]deviceplugin.DeviceInfo, len(tt.updates))
|
|
testDM := newDeviceManager("intel.com/fpgatest-fpgaID", "fpgaID", devicecache.AfMode, devCh)
|
|
|
|
server := &listAndWatchServerStub{
|
|
testDM: testDM,
|
|
generateErr: tt.errorOnCall,
|
|
cdata: make(chan []*pluginapi.Device, len(tt.updates)+1),
|
|
}
|
|
|
|
// push device infos to DM's channel
|
|
for _, update := range tt.updates {
|
|
devCh <- update
|
|
}
|
|
close(devCh)
|
|
|
|
err := testDM.ListAndWatch(&pluginapi.Empty{}, server)
|
|
if err != nil && tt.errorOnCall == 0 {
|
|
t.Errorf("Test case '%s': got unexpected error %v", tt.name, err)
|
|
}
|
|
if err == nil && tt.errorOnCall != 0 {
|
|
t.Errorf("Test case '%s': expected an error, but got nothing", tt.name)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAllocate(t *testing.T) {
|
|
testDM := newDeviceManager("", "", devicecache.RegionMode, nil)
|
|
if testDM == nil {
|
|
t.Fatal("Failed to create a deviceManager")
|
|
}
|
|
|
|
rqt := &pluginapi.AllocateRequest{
|
|
ContainerRequests: []*pluginapi.ContainerAllocateRequest{
|
|
{
|
|
DevicesIDs: []string{"dev1"},
|
|
},
|
|
},
|
|
}
|
|
|
|
testDM.devices["dev1"] = deviceplugin.DeviceInfo{
|
|
State: pluginapi.Healthy,
|
|
Nodes: []string{"/dev/dev1"},
|
|
}
|
|
resp, err := testDM.Allocate(nil, rqt)
|
|
if err != nil {
|
|
t.Fatalf("Failed to allocate healthy device: %v", err)
|
|
}
|
|
|
|
if len(resp.ContainerResponses[0].Devices) != 1 {
|
|
t.Fatal("Allocated wrong number of devices")
|
|
}
|
|
|
|
if len(resp.ContainerResponses[0].Annotations) != 1 {
|
|
t.Fatal("Set wrong number of annotations")
|
|
}
|
|
|
|
annotation, ok := resp.ContainerResponses[0].Annotations[annotationName]
|
|
if ok == false {
|
|
t.Fatalf("%s annotation is not set", annotationName)
|
|
}
|
|
|
|
expectedAnnotationValue := fmt.Sprintf("%s-%s", resourceNamePrefix, devicecache.RegionMode)
|
|
if annotation != expectedAnnotationValue {
|
|
t.Fatalf("Set wrong %s annotation value %s, should be %s", resourceNamePrefix, annotation, expectedAnnotationValue)
|
|
}
|
|
}
|
|
|
|
func startDeviceManagerStub(dm *deviceManager, pluginPrefix string) {
|
|
}
|
|
|
|
func TestHandleUpdate(t *testing.T) {
|
|
tcases := []struct {
|
|
name string
|
|
dms map[string]*deviceManager
|
|
updateInfo devicecache.UpdateInfo
|
|
expectedDMs int
|
|
}{
|
|
{
|
|
name: "Empty update",
|
|
updateInfo: devicecache.UpdateInfo{},
|
|
expectedDMs: 0,
|
|
},
|
|
{
|
|
name: "Add device manager",
|
|
updateInfo: devicecache.UpdateInfo{
|
|
Added: map[string]map[string]deviceplugin.DeviceInfo{
|
|
"ce48969398f05f33946d560708be108a": {
|
|
"intel-fpga-fme.0": {
|
|
State: pluginapi.Healthy,
|
|
Nodes: []string{"/dev/intel-fpga-port.0", "/dev/intel-fpga-fme.0"},
|
|
},
|
|
"intel-fpga-fme.1": {
|
|
State: pluginapi.Healthy,
|
|
Nodes: []string{"/dev/intel-fpga-port.1", "/dev/intel-fpga-fme.1"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expectedDMs: 1,
|
|
},
|
|
{
|
|
name: "Update existing device manager",
|
|
dms: map[string]*deviceManager{
|
|
"ce48969398f05f33946d560708be108a": {
|
|
ch: make(chan map[string]deviceplugin.DeviceInfo, 1),
|
|
},
|
|
},
|
|
updateInfo: devicecache.UpdateInfo{
|
|
Updated: map[string]map[string]deviceplugin.DeviceInfo{
|
|
"ce48969398f05f33946d560708be108a": {
|
|
"intel-fpga-fme.1": {
|
|
State: pluginapi.Healthy,
|
|
Nodes: []string{"/dev/intel-fpga-port.1", "/dev/intel-fpga-fme.1"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expectedDMs: 1,
|
|
},
|
|
{
|
|
name: "Remove device manager",
|
|
dms: map[string]*deviceManager{
|
|
"ce48969398f05f33946d560708be108a": {
|
|
ch: make(chan map[string]deviceplugin.DeviceInfo, 1),
|
|
},
|
|
},
|
|
updateInfo: devicecache.UpdateInfo{
|
|
Removed: map[string]map[string]deviceplugin.DeviceInfo{
|
|
"ce48969398f05f33946d560708be108a": {},
|
|
},
|
|
},
|
|
expectedDMs: 0,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tcases {
|
|
if tt.dms == nil {
|
|
tt.dms = make(map[string]*deviceManager)
|
|
}
|
|
handleUpdate(tt.dms, tt.updateInfo, startDeviceManagerStub, devicecache.AfMode)
|
|
if len(tt.dms) != tt.expectedDMs {
|
|
t.Errorf("Test case '%s': expected %d runnig device managers, but got %d",
|
|
tt.name, tt.expectedDMs, len(tt.dms))
|
|
}
|
|
}
|
|
}
|