forked from github/multus-cni
259 lines
6.8 KiB
Go
259 lines
6.8 KiB
Go
package kubeletclient
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
. "github.com/onsi/ginkgo"
|
|
. "github.com/onsi/gomega"
|
|
|
|
"golang.org/x/sys/unix"
|
|
"google.golang.org/grpc"
|
|
v1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
k8sTypes "k8s.io/apimachinery/pkg/types"
|
|
|
|
mtypes "gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/types"
|
|
podresourcesapi "k8s.io/kubelet/pkg/apis/podresources/v1"
|
|
)
|
|
|
|
var (
|
|
socketDir string
|
|
socketName string
|
|
fakeServer *fakeResourceServer
|
|
)
|
|
|
|
type fakeResourceServer struct {
|
|
server *grpc.Server
|
|
}
|
|
|
|
//TODO: This is stub code for test, but we may need to change for the testing we use this API in the future...
|
|
func (m *fakeResourceServer) GetAllocatableResources(ctx context.Context, req *podresourcesapi.AllocatableResourcesRequest) (*podresourcesapi.AllocatableResourcesResponse, error) {
|
|
return &podresourcesapi.AllocatableResourcesResponse{}, nil
|
|
}
|
|
|
|
func (m *fakeResourceServer) List(ctx context.Context, req *podresourcesapi.ListPodResourcesRequest) (*podresourcesapi.ListPodResourcesResponse, error) {
|
|
podName := "pod-name"
|
|
podNamespace := "pod-namespace"
|
|
containerName := "container-name"
|
|
|
|
devs := []*podresourcesapi.ContainerDevices{
|
|
{
|
|
ResourceName: "resource",
|
|
DeviceIds: []string{"dev0", "dev1"},
|
|
},
|
|
}
|
|
|
|
resp := &podresourcesapi.ListPodResourcesResponse{
|
|
PodResources: []*podresourcesapi.PodResources{
|
|
{
|
|
Name: podName,
|
|
Namespace: podNamespace,
|
|
Containers: []*podresourcesapi.ContainerResources{
|
|
{
|
|
Name: containerName,
|
|
Devices: devs,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
func TestKubeletclient(t *testing.T) {
|
|
RegisterFailHandler(Fail)
|
|
RunSpecs(t, "Kubeletclient Suite")
|
|
}
|
|
|
|
var testKubeletSocket string
|
|
|
|
// CreateListener creates a listener on the specified endpoint.
|
|
// based from k8s.io/kubernetes/pkg/kubelet/util
|
|
func CreateListener(addr string) (net.Listener, error) {
|
|
// Unlink to cleanup the previous socket file.
|
|
err := unix.Unlink(addr)
|
|
if err != nil && !os.IsNotExist(err) {
|
|
return nil, fmt.Errorf("failed to unlink socket file %q: %v", addr, err)
|
|
}
|
|
|
|
if err := os.MkdirAll(filepath.Dir(addr), 0750); err != nil {
|
|
return nil, fmt.Errorf("error creating socket directory %q: %v", filepath.Dir(addr), err)
|
|
}
|
|
|
|
// Create the socket on a tempfile and move it to the destination socket to handle improper cleanup
|
|
file, err := ioutil.TempFile(filepath.Dir(addr), "")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create temporary file: %v", err)
|
|
}
|
|
|
|
if err := os.Remove(file.Name()); err != nil {
|
|
return nil, fmt.Errorf("failed to remove temporary file: %v", err)
|
|
}
|
|
|
|
l, err := net.Listen(unixProtocol, file.Name())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err = os.Rename(file.Name(), addr); err != nil {
|
|
return nil, fmt.Errorf("failed to move temporary file to addr %q: %v", addr, err)
|
|
}
|
|
|
|
return l, nil
|
|
}
|
|
|
|
func setUp() error {
|
|
tempSocketDir, err := ioutil.TempDir("", "kubelet-resource-client")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
testingPodResourcesPath := filepath.Join(tempSocketDir, defaultPodResourcesPath)
|
|
|
|
if err := os.MkdirAll(testingPodResourcesPath, os.ModeDir); err != nil {
|
|
return err
|
|
}
|
|
|
|
socketDir = testingPodResourcesPath
|
|
socketName = filepath.Join(socketDir, "kubelet.sock")
|
|
testKubeletSocket = socketName
|
|
|
|
fakeServer = &fakeResourceServer{server: grpc.NewServer()}
|
|
podresourcesapi.RegisterPodResourcesListerServer(fakeServer.server, fakeServer)
|
|
lis, err := CreateListener(socketName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
go fakeServer.server.Serve(lis)
|
|
return nil
|
|
}
|
|
|
|
func tearDown(path string) error {
|
|
if fakeServer != nil {
|
|
fakeServer.server.Stop()
|
|
}
|
|
err := os.RemoveAll(path)
|
|
return err
|
|
}
|
|
|
|
var _ = BeforeSuite(func() {
|
|
err := setUp()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
})
|
|
|
|
var _ = AfterSuite(func() {
|
|
err := tearDown(socketDir)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
})
|
|
|
|
var _ = Describe("Kubelet resource endpoint data read operations", func() {
|
|
|
|
Context("GetResourceClient()", func() {
|
|
It("should return no error", func() {
|
|
_, err := GetResourceClient(testKubeletSocket)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
})
|
|
|
|
It("should fail with missing file", func() {
|
|
_, err := GetResourceClient("sampleSocketString")
|
|
Expect(err).To(HaveOccurred())
|
|
})
|
|
})
|
|
|
|
Context("GetPodResourceMap() with valid pod name and namespace", func() {
|
|
It("should return no error", func() {
|
|
podUID := k8sTypes.UID("970a395d-bb3b-11e8-89df-408d5c537d23")
|
|
fakePod := &v1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "pod-name",
|
|
Namespace: "pod-namespace",
|
|
UID: podUID,
|
|
},
|
|
Spec: v1.PodSpec{
|
|
Containers: []v1.Container{
|
|
{
|
|
Name: "container-name",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
client, err := getKubeletClient(testKubeletSocket)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
outputRMap := map[string]*mtypes.ResourceInfo{
|
|
"resource": {DeviceIDs: []string{"dev0", "dev1"}},
|
|
}
|
|
resourceMap, err := client.GetPodResourceMap(fakePod)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(resourceMap).ShouldNot(BeNil())
|
|
Expect(resourceMap).To(Equal(outputRMap))
|
|
})
|
|
|
|
It("should return an error with garbage socket value", func() {
|
|
_, err := getKubeletClient("/badfilepath!?//")
|
|
Expect(err).To(HaveOccurred())
|
|
})
|
|
})
|
|
|
|
Context("GetPodResourceMap() with empty podname", func() {
|
|
It("should return error", func() {
|
|
podUID := k8sTypes.UID("970a395d-bb3b-11e8-89df-408d5c537d23")
|
|
fakePod := &v1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "",
|
|
Namespace: "pod-namespace",
|
|
UID: podUID,
|
|
},
|
|
}
|
|
client, err := getKubeletClient(testKubeletSocket)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
_, err = client.GetPodResourceMap(fakePod)
|
|
Expect(err).To(HaveOccurred())
|
|
})
|
|
})
|
|
|
|
Context("GetPodResourceMap() with empty namespace", func() {
|
|
It("should return error", func() {
|
|
podUID := k8sTypes.UID("970a395d-bb3b-11e8-89df-408d5c537d23")
|
|
fakePod := &v1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "pod-name",
|
|
Namespace: "",
|
|
UID: podUID,
|
|
},
|
|
}
|
|
client, err := getKubeletClient(testKubeletSocket)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
_, err = client.GetPodResourceMap(fakePod)
|
|
Expect(err).To(HaveOccurred())
|
|
})
|
|
})
|
|
|
|
Context("GetPodResourceMap() with non-existent podname and namespace", func() {
|
|
It("should return no error", func() {
|
|
podUID := k8sTypes.UID("970a395d-bb3b-11e8-89df-408d5c537d23")
|
|
fakePod := &v1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "whateverpod",
|
|
Namespace: "whatevernamespace",
|
|
UID: podUID,
|
|
},
|
|
}
|
|
|
|
client, err := getKubeletClient(testKubeletSocket)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
emptyRMap := map[string]*mtypes.ResourceInfo{}
|
|
resourceMap, err := client.GetPodResourceMap(fakePod)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(resourceMap).ShouldNot(BeNil())
|
|
Expect(resourceMap).To(Equal(emptyRMap))
|
|
})
|
|
})
|
|
})
|