diff --git a/checkpoint/checkpoint.go b/checkpoint/checkpoint.go index be7dd7385..98a6b221f 100644 --- a/checkpoint/checkpoint.go +++ b/checkpoint/checkpoint.go @@ -21,6 +21,7 @@ import ( "github.com/intel/multus-cni/logging" "github.com/intel/multus-cni/types" + v1 "k8s.io/api/core/v1" ) const ( @@ -45,22 +46,18 @@ type Data struct { Checksum uint64 } -type Checkpoint interface { - // GetComputeDeviceMap returns an instance of a map of ResourceInfo for a PodID - GetComputeDeviceMap(string) (map[string]*types.ResourceInfo, error) -} type checkpoint struct { fileName string podEntires []PodDevicesEntry } // GetCheckpoint returns an instance of Checkpoint -func GetCheckpoint() (Checkpoint, error) { +func GetCheckpoint() (types.ResourceClient, error) { logging.Debugf("GetCheckpoint(): invoked") return getCheckpoint(checkPointfile) } -func getCheckpoint(filePath string) (Checkpoint, error) { +func getCheckpoint(filePath string) (types.ResourceClient, error) { cp := &checkpoint{fileName: filePath} err := cp.getPodEntries() if err != nil { @@ -89,12 +86,12 @@ func (cp *checkpoint) getPodEntries() error { } // GetComputeDeviceMap returns an instance of a map of ResourceInfo -func (cp *checkpoint) GetComputeDeviceMap(podID string) (map[string]*types.ResourceInfo, error) { - +func (cp *checkpoint) GetPodResourceMap(pod *v1.Pod) (map[string]*types.ResourceInfo, error) { + podID := string(pod.UID) resourceMap := make(map[string]*types.ResourceInfo) if podID == "" { - return nil, logging.Errorf("GetComputeDeviceMap(): invalid Pod cannot be empty") + return nil, logging.Errorf("GetPodResourceMap(): invalid Pod cannot be empty") } for _, pod := range cp.podEntires { diff --git a/checkpoint/checkpoint_test.go b/checkpoint/checkpoint_test.go index 7834a382e..c1e7578a5 100644 --- a/checkpoint/checkpoint_test.go +++ b/checkpoint/checkpoint_test.go @@ -10,6 +10,9 @@ import ( "testing" "github.com/intel/multus-cni/types" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + k8sTypes "k8s.io/apimachinery/pkg/types" ) const ( @@ -74,7 +77,7 @@ var _ = BeforeSuite(func() { var _ = Describe("Kubelet checkpoint data read operations", func() { Context("Using /tmp/kubelet_internal_checkpoint file", func() { var ( - cp Checkpoint + cp types.ResourceClient err error resourceMap map[string]*types.ResourceInfo resourceInfo *types.ResourceInfo @@ -87,7 +90,15 @@ var _ = Describe("Kubelet checkpoint data read operations", func() { }) It("should return a ResourceMap instance", func() { - rmap, err := cp.GetComputeDeviceMap("970a395d-bb3b-11e8-89df-408d5c537d23") + podUID := k8sTypes.UID("970a395d-bb3b-11e8-89df-408d5c537d23") + fakePod := &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "fakePod", + Namespace: "podNamespace", + UID: podUID, + }, + } + rmap, err := cp.GetPodResourceMap(fakePod) Expect(err).NotTo(HaveOccurred()) Expect(rmap).NotTo(BeEmpty()) resourceMap = rmap diff --git a/glide.lock b/glide.lock index 20cbe0cc0..4ae361858 100644 --- a/glide.lock +++ b/glide.lock @@ -1,13 +1,11 @@ -hash: 0c4ea2a342364d2ff3b43242730cb3b1db3b7e8456f6cf43da3c51dbb67e18da -updated: 2018-07-27T03:29:02.093332104+01:00 +hash: 4288149814e91e63396f7874b87151aca74047110f0d1f43027b60dd6014988f +updated: 2019-01-09T10:19:29.688011713Z imports: - name: github.com/containernetworking/cni version: 07c1a6da47b7fbf8b357f4949ecce2113e598491 subpackages: - libcni - pkg/invoke - - pkg/ip - - pkg/ipam - pkg/skel - pkg/types - pkg/types/020 @@ -16,6 +14,8 @@ imports: - name: github.com/containernetworking/plugins version: 2b8b1ac0af4568e928d96ccc5f47b075416eeabd subpackages: + - pkg/ip + - pkg/ipam - pkg/ns - pkg/testutils - name: github.com/ghodss/yaml @@ -23,7 +23,9 @@ imports: - name: github.com/gogo/protobuf version: c0656edd0d9eab7c66d1eb0c568f9039345796f7 subpackages: + - gogoproto - proto + - protoc-gen-gogo/descriptor - sortkeys - name: github.com/golang/glog version: 44145f04b68cf362d9c4df2182967c2275eaefed @@ -53,6 +55,8 @@ imports: version: 6633656539c1639d9d78127b7d47c622b5d7b6dc - name: github.com/json-iterator/go version: f2b4162afba35581b6d4a50d3b8f34e33c144682 +- name: github.com/Microsoft/go-winio + version: 78439966b38d69bf38227fbf57ac8a6fee70f69a - name: github.com/modern-go/concurrent version: bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94 - name: github.com/modern-go/reflect2 @@ -61,6 +65,7 @@ imports: version: 7f8ab55aaf3b86885aa55b762e803744d1674700 subpackages: - config + - extensions/table - internal/codelocation - internal/containernode - internal/failer @@ -91,15 +96,15 @@ imports: - name: github.com/peterbourgon/diskv version: 5f041e8faa004a95c88a202771f4cc3e991971e6 - name: github.com/pkg/errors - version: 816c9085562cd7ee03e7f8188a1cfd942858cded + version: ffb6e22f01932bf7ac35e0bad9be11f01d1c8685 - name: github.com/spf13/pflag version: 583c0c0531f06d5278b7d917446061adc344b5cd - name: github.com/vishvananda/netlink - version: 6e453822d85ef5721799774b654d4d02fed62afb + version: b2de5d10e38ecce8607e6b438b6d174f389a004e subpackages: - nl - name: github.com/vishvananda/netns - version: 54f0e4339ce73702a0607f49922aaa1e749b418d + version: be1fbeda19366dea804f00efff2dd73a1642fdcc - name: golang.org/x/crypto version: 49796115aa4b964c318aad4f3084fdb41e9aa067 subpackages: @@ -111,7 +116,9 @@ imports: - http2 - http2/hpack - idna + - internal/timeseries - lex/httplex + - trace - name: golang.org/x/sys version: 95c6576299259db960f6c5b9b69ea52422860fce subpackages: @@ -128,6 +135,40 @@ imports: version: f51c12702a4d776e4c1fa9b0fabab841babae631 subpackages: - rate +- name: google.golang.org/genproto + version: 09f6ed296fc66555a25fe4ce95173148778dfa85 + subpackages: + - googleapis/rpc/status +- name: google.golang.org/grpc + version: 168a6198bcb0ef175f7dacec0b8691fc141dc9b8 + subpackages: + - balancer + - balancer/base + - balancer/roundrobin + - codes + - connectivity + - credentials + - encoding + - encoding/proto + - grpclb/grpc_lb_v1/messages + - grpclog + - health + - health/grpc_health_v1 + - internal + - internal/backoff + - internal/channelz + - internal/grpcrand + - keepalive + - metadata + - naming + - peer + - resolver + - resolver/dns + - resolver/passthrough + - stats + - status + - tap + - transport - name: gopkg.in/inf.v0 version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 - name: gopkg.in/yaml.v2 @@ -135,6 +176,7 @@ imports: - name: k8s.io/api version: 2d6f90ab1293a1fb871cf149423ebb72aa7423aa subpackages: + - admission/v1beta1 - admissionregistration/v1alpha1 - admissionregistration/v1beta1 - apps/v1 @@ -258,4 +300,12 @@ imports: - util/homedir - util/integer - util/retry +- name: k8s.io/klog + version: 8139d8cb77af419532b33dfa7dd09fbc5f1d344f +- name: k8s.io/kubernetes + version: ddf47ac13c1a9483ea035a79cd7c10005ff21a6d + subpackages: + - pkg/kubelet/apis/podresources + - pkg/kubelet/apis/podresources/v1alpha1 + - pkg/kubelet/util testImports: [] diff --git a/glide.yaml b/glide.yaml index 8fbf50306..0c2265dae 100644 --- a/glide.yaml +++ b/glide.yaml @@ -32,5 +32,11 @@ import: - kubernetes - tools/clientcmd - util/retry +- package: k8s.io/kubernetes + version: v1.13.0 + subpackages: + - pkg/kubelet/apis/podresources + - pkg/kubelet/apis/podresources/v1alpha1 + - pkg/kubelet/util - package: github.com/vishvananda/netns - package: github.com/pkg/errors diff --git a/k8sclient/k8sclient.go b/k8sclient/k8sclient.go index 69d3218bb..dd2489509 100644 --- a/k8sclient/k8sclient.go +++ b/k8sclient/k8sclient.go @@ -31,7 +31,7 @@ import ( "github.com/containernetworking/cni/libcni" "github.com/containernetworking/cni/pkg/skel" cnitypes "github.com/containernetworking/cni/pkg/types" - "github.com/intel/multus-cni/checkpoint" + "github.com/intel/multus-cni/kubeletclient" "github.com/intel/multus-cni/logging" "github.com/intel/multus-cni/types" ) @@ -336,7 +336,7 @@ func cniConfigFromNetworkResource(customResource *types.NetworkAttachmentDefinit return config, nil } -func getKubernetesDelegate(client KubeClient, net *types.NetworkSelectionElement, confdir string, podID string, resourceMap map[string]*types.ResourceInfo) (*types.DelegateNetConf, map[string]*types.ResourceInfo, error) { +func getKubernetesDelegate(client KubeClient, net *types.NetworkSelectionElement, confdir string, pod *v1.Pod, resourceMap map[string]*types.ResourceInfo) (*types.DelegateNetConf, map[string]*types.ResourceInfo, error) { logging.Debugf("getKubernetesDelegate: %v, %v, %s", client, net, confdir) rawPath := fmt.Sprintf("/apis/k8s.cni.cncf.io/v1/namespaces/%s/network-attachment-definitions/%s", net.Namespace, net.Name) @@ -353,18 +353,18 @@ func getKubernetesDelegate(client KubeClient, net *types.NetworkSelectionElement // Get resourceName annotation from NetworkAttachmentDefinition deviceID := "" resourceName, ok := customResource.Metadata.Annotations[resourceNameAnnot] - if ok && podID != "" { + if ok && pod.Name != "" && pod.Namespace != "" { // ResourceName annotation is found; try to get device info from resourceMap logging.Debugf("getKubernetesDelegate: found resourceName annotation : %s", resourceName) if resourceMap == nil { - checkpoint, err := checkpoint.GetCheckpoint() + ck, err := kubeletclient.GetResourceClient() if err != nil { - return nil, resourceMap, logging.Errorf("getKubernetesDelegate: failed to get a checkpoint instance: %v", err) + return nil, resourceMap, logging.Errorf("getKubernetesDelegate: failed to get a ResourceClient instance: %v", err) } - resourceMap, err = checkpoint.GetComputeDeviceMap(podID) + resourceMap, err = ck.GetPodResourceMap(pod) if err != nil { - return nil, resourceMap, logging.Errorf("getKubernetesDelegate: failed to get resourceMap from kubelet checkpoint file: %v", err) + return nil, resourceMap, logging.Errorf("getKubernetesDelegate: failed to get resourceMap from ResourceClient: %v", err) } logging.Debugf("getKubernetesDelegate(): resourceMap instance: %+v", resourceMap) } @@ -373,7 +373,7 @@ func getKubernetesDelegate(client KubeClient, net *types.NetworkSelectionElement if ok { if idCount := len(entry.DeviceIDs); idCount > 0 && idCount > entry.Index { deviceID = entry.DeviceIDs[entry.Index] - logging.Debugf("getKubernetesDelegate: podID: %s deviceID: %s", podID, deviceID) + logging.Debugf("getKubernetesDelegate: podName: %s deviceID: %s", pod.Name, deviceID) entry.Index++ // increment Index for next delegate } } @@ -536,7 +536,6 @@ func GetNetworkDelegates(k8sclient KubeClient, pod *v1.Pod, networks []*types.Ne var delegates []*types.DelegateNetConf defaultNamespace := pod.ObjectMeta.Namespace - podID := pod.UID for _, net := range networks { // The pods namespace (stored as defaultNamespace, does not equal the annotation's target namespace in net.Namespace) @@ -547,7 +546,7 @@ func GetNetworkDelegates(k8sclient KubeClient, pod *v1.Pod, networks []*types.Ne } } - delegate, updatedResourceMap, err := getKubernetesDelegate(k8sclient, net, confdir, string(podID), resourceMap) + delegate, updatedResourceMap, err := getKubernetesDelegate(k8sclient, net, confdir, pod, resourceMap) if err != nil { return nil, logging.Errorf("GetPodNetwork: failed getting the delegate: %v", err) } @@ -690,7 +689,7 @@ func tryLoadK8sPodDefaultNetwork(kubeClient KubeClient, pod *v1.Pod, conf *types return nil, logging.Errorf("tryLoadK8sPodDefaultNetwork: more than one default network is specified: %s", netAnnot) } - delegate, _, err := getKubernetesDelegate(kubeClient, networks[0], conf.ConfDir, "", nil) + delegate, _, err := getKubernetesDelegate(kubeClient, networks[0], conf.ConfDir, pod, nil) if err != nil { return nil, logging.Errorf("tryLoadK8sPodDefaultNetwork: failed getting the delegate: %v", err) } diff --git a/kubeletclient/kubeletclient.go b/kubeletclient/kubeletclient.go new file mode 100644 index 000000000..4e1df566b --- /dev/null +++ b/kubeletclient/kubeletclient.go @@ -0,0 +1,113 @@ +package kubeletclient + +import ( + "os" + "path/filepath" + "time" + + "github.com/intel/multus-cni/checkpoint" + "github.com/intel/multus-cni/logging" + "github.com/intel/multus-cni/types" + "golang.org/x/net/context" + v1 "k8s.io/api/core/v1" + "k8s.io/kubernetes/pkg/kubelet/apis/podresources" + podresourcesapi "k8s.io/kubernetes/pkg/kubelet/apis/podresources/v1alpha1" + "k8s.io/kubernetes/pkg/kubelet/util" +) + +const ( + defaultKubeletSocketFile = "kubelet.sock" + defaultPodResourcesMaxSize = 1024 * 1024 * 16 // 16 Mb +) + +var ( + kubeletSocket string + defaultPodResourcesPath = "/var/lib/kubelet/pod-resources" +) + +// GetResourceClient returns an instance of ResourceClient interface initialized with Pod resource information +func GetResourceClient() (types.ResourceClient, error) { + // If Kubelet resource API endpoint exist use that by default + // Or else fallback with checkpoint file + if hasKubeletAPIEndpoint() { + logging.Printf(logging.VerboseLevel, "GetResourceClient(): using Kubelet resource API endpoint") + return getKubeletClient() + } else { + logging.Printf(logging.VerboseLevel, "GetResourceClient(): using Kubelet device plugin checkpoint") + return checkpoint.GetCheckpoint() + } +} + +func getKubeletClient() (types.ResourceClient, error) { + newClient := &kubeletClient{} + if kubeletSocket == "" { + kubeletSocket = util.LocalEndpoint(defaultPodResourcesPath, podresources.Socket) + } + + client, conn, err := podresources.GetClient(kubeletSocket, 10*time.Second, defaultPodResourcesMaxSize) + if err != nil { + return nil, logging.Errorf("GetResourceClient(): error getting grpc client: %v\n", err) + } + defer conn.Close() + + if err := newClient.getPodResources(client); err != nil { + return nil, logging.Errorf("GetResourceClient(): error getting resource client: %v\n", err) + } + + return newClient, nil +} + +type kubeletClient struct { + resources []*podresourcesapi.PodResources +} + +func (rc *kubeletClient) getPodResources(client podresourcesapi.PodResourcesListerClient) error { + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + resp, err := client.List(ctx, &podresourcesapi.ListPodResourcesRequest{}) + if err != nil { + return logging.Errorf("getPodResources(): %v.Get(_) = _, %v", client, err) + } + + rc.resources = resp.PodResources + return nil +} + +// GetPodResourceMap returns an instance of a map of Pod ResourceInfo given a (Pod name, namespace) tuple +func (rc *kubeletClient) GetPodResourceMap(pod *v1.Pod) (map[string]*types.ResourceInfo, error) { + resourceMap := make(map[string]*types.ResourceInfo) + + name := pod.Name + ns := pod.Namespace + + if name == "" || ns == "" { + return nil, logging.Errorf("GetPodResourcesMap(): Pod name or namespace cannot be empty") + } + + for _, pr := range rc.resources { + if pr.Name == name && pr.Namespace == ns { + for _, cnt := range pr.Containers { + for _, dev := range cnt.Devices { + if rInfo, ok := resourceMap[dev.ResourceName]; ok { + rInfo.DeviceIDs = append(rInfo.DeviceIDs, dev.DeviceIds...) + } else { + resourceMap[dev.ResourceName] = &types.ResourceInfo{DeviceIDs: dev.DeviceIds} + } + } + } + } + } + return resourceMap, nil +} + +func hasKubeletAPIEndpoint() bool { + // Check for kubelet resource API socket file + kubeletAPISocket := filepath.Join(defaultPodResourcesPath, defaultKubeletSocketFile) + if _, err := os.Stat(kubeletAPISocket); err != nil { + logging.Verbosef("hasKubeletAPIEndpoint(): error looking up kubelet resource api socket file: %q", err) + return false + } + return true +} diff --git a/kubeletclient/kubeletclient_test.go b/kubeletclient/kubeletclient_test.go new file mode 100644 index 000000000..2802e57a1 --- /dev/null +++ b/kubeletclient/kubeletclient_test.go @@ -0,0 +1,209 @@ +package kubeletclient + +import ( + "context" + "io/ioutil" + "os" + "path/filepath" + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "google.golang.org/grpc" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + k8sTypes "k8s.io/apimachinery/pkg/types" + "k8s.io/kubernetes/pkg/kubelet/util" + + mtypes "github.com/intel/multus-cni/types" + podresourcesapi "k8s.io/kubernetes/pkg/kubelet/apis/podresources/v1alpha1" +) + +var ( + socketDir string + socketName string + fakeServer *fakeResourceServer +) + +type fakeResourceServer struct { + server *grpc.Server +} + +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") +} + +func setUp() error { + tempSocketDir, err := ioutil.TempDir("", "kubelet-resource-client") + if err != nil { + return err + } + defaultPodResourcesPath = filepath.Join(tempSocketDir, defaultPodResourcesPath) + + if err := os.MkdirAll(defaultPodResourcesPath, os.ModeDir); err != nil { + return err + } + + socketDir = defaultPodResourcesPath + socketName = filepath.Join(socketDir, "kubelet.sock") + + fakeServer = &fakeResourceServer{server: grpc.NewServer()} + podresourcesapi.RegisterPodResourcesListerServer(fakeServer.server, fakeServer) + lis, err := util.CreateListener(socketName) + if err != nil { + return nil + } + go fakeServer.server.Serve(lis) + return nil +} + +func tearDown(path string) error { + if fakeServer != nil { + fakeServer.server.Stop() + } + if err := os.RemoveAll(path); err != nil { + return err + } + return nil +} + +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() { + kubeletSocket = socketName + _, err := GetResourceClient() + Expect(err).NotTo(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", + }, + }, + }, + } + kubeletSocket = socketName + client, err := getKubeletClient() + Expect(err).NotTo(HaveOccurred()) + + outputRMap := map[string]*mtypes.ResourceInfo{ + "resource": &mtypes.ResourceInfo{DeviceIDs: []string{"dev0", "dev1"}}, + } + resourceMap, err := client.GetPodResourceMap(fakePod) + Expect(err).NotTo(HaveOccurred()) + Expect(resourceMap).ShouldNot(BeNil()) + Expect(resourceMap).To(Equal(outputRMap)) + }) + }) + + 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, + }, + } + kubeletSocket = socketName + client, err := getKubeletClient() + 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, + }, + } + kubeletSocket = socketName + client, err := getKubeletClient() + 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, + }, + } + + kubeletSocket = socketName + client, err := getKubeletClient() + 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)) + }) + }) +}) diff --git a/types/types.go b/types/types.go index 79e89c43c..398055655 100644 --- a/types/types.go +++ b/types/types.go @@ -20,6 +20,7 @@ import ( "github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types/current" + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -50,9 +51,9 @@ type NetConf struct { // Option to isolate the usage of CR's to the namespace in which a pod resides. NamespaceIsolation bool `json:"namespaceIsolation"` // Option to set system namespaces (to avoid to add defaultNetworks) - SystemNamespaces []string `json:"systemNamespaces"` + SystemNamespaces []string `json:"systemNamespaces"` // Option to set the namespace that multus-cni uses (clusterNetwork/defaultNetworks) - MultusNamespace string `json:"multusNamespace"` + MultusNamespace string `json:"multusNamespace"` } type RuntimeConfig struct { @@ -149,3 +150,9 @@ type ResourceInfo struct { Index int DeviceIDs []string } + +// ResourceClient provides a kubelet Pod resource handle +type ResourceClient interface { + // GetPodResourceMap returns an instance of a map of Pod ResourceInfo given a (Pod name, namespace) tuple + GetPodResourceMap(*v1.Pod) (map[string]*ResourceInfo, error) +}