Files
multus-cni/pkg/draclient/draclient_test.go
Fred Rolland 0913b1c3a1 draclient: use field selectors and lightweight device index for ResourceSlice cache
Replace full ResourceSlice object cache with a flat deviceInfoCacheKey→deviceInfo
index. Populate lazily per driver using combined spec.nodeName+spec.driver field
selectors derived from pod.Spec.NodeName, reducing API response size to a single
node's slices for one driver. Device lookup becomes O(1). Add ctx propagation to
GetPodResourceMap interface.

Signed-off-by: Fred Rolland <frolland@nvidia.com>
2026-06-24 22:31:17 +03:00

1980 lines
69 KiB
Go

// Copyright (c) 2025 Multus 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 draclient
// disable dot-imports only for testing
//revive:disable:dot-imports
import (
"context"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
v1 "k8s.io/api/core/v1"
resourcev1api "k8s.io/api/resource/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
k8sTypes "k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes/fake"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/types"
)
var _ = Describe("DRA Client operations", func() {
Describe("NewClient", func() {
It("should create a new DRA client successfully", func() {
fakeClient := fake.NewClientset()
client := NewClient(fakeClient.ResourceV1())
Expect(client).NotTo(BeNil())
})
})
Describe("GetPodResourceMap", func() {
var (
fakeClient *fake.Clientset
draClient ClientInterface
)
BeforeEach(func() {
fakeClient = fake.NewClientset()
draClient = NewClient(fakeClient.ResourceV1())
})
Context("when pod has no resource claims", func() {
It("should return empty resource map without error", func() {
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: "default",
UID: k8sTypes.UID("test-uid"),
},
Status: v1.PodStatus{
ResourceClaimStatuses: []v1.PodResourceClaimStatus{},
},
}
resourceMap := make(map[string]*types.ResourceInfo)
err := draClient.GetPodResourceMap(context.TODO(), pod, resourceMap)
Expect(err).NotTo(HaveOccurred())
Expect(resourceMap).To(BeEmpty())
})
})
Context("when resource claim exists with valid device allocation", func() {
It("should successfully populate resource map with device IDs", func() {
claimName := "test-claim"
deviceName := "device-1"
driverName := "test-driver.example.com"
poolName := "test-pool"
requestName := "gpu"
deviceID := "pci:0000:00:01.0"
mapKey := "intel.com/gpu-vf"
// Create ResourceSlice with device
deviceIDValue := deviceID
mapKeyValue := mapKey
resourceSlice := &resourcev1api.ResourceSlice{
ObjectMeta: metav1.ObjectMeta{
Name: "test-resource-slice",
},
Spec: resourcev1api.ResourceSliceSpec{
Driver: driverName,
Pool: resourcev1api.ResourcePool{
Name: poolName,
ResourceSliceCount: 1,
},
Devices: []resourcev1api.Device{
{
Name: deviceName,
Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
multusDeviceIDAttr: {
StringValue: &deviceIDValue,
},
multusResourceNameAttr: {
StringValue: &mapKeyValue,
},
},
},
},
},
}
// Create ResourceClaim with allocation
resourceClaim := &resourcev1api.ResourceClaim{
ObjectMeta: metav1.ObjectMeta{
Name: claimName,
Namespace: "default",
},
Status: resourcev1api.ResourceClaimStatus{
Allocation: &resourcev1api.AllocationResult{
Devices: resourcev1api.DeviceAllocationResult{
Results: []resourcev1api.DeviceRequestAllocationResult{
{
Request: requestName,
Driver: driverName,
Pool: poolName,
Device: deviceName,
},
},
},
},
},
}
// Create pod with resource claim status.
// Spec.NodeName is set so ensureDriverCachePopulated takes the
// node-scoped selector branch (spec.nodeName + spec.driver).
claimNamePtr := claimName
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: "default",
UID: k8sTypes.UID("test-uid"),
},
Spec: v1.PodSpec{
NodeName: "test-node",
},
Status: v1.PodStatus{
ResourceClaimStatuses: []v1.PodResourceClaimStatus{
{
Name: claimName, // spec ref name (pod.spec.resourceClaims[].name)
ResourceClaimName: &claimNamePtr,
},
},
},
}
// Add objects to fake client
_, err := fakeClient.ResourceV1().ResourceClaims("default").Create(context.TODO(), resourceClaim, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
_, err = fakeClient.ResourceV1().ResourceSlices().Create(context.TODO(), resourceSlice, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
// Execute
resourceMap := make(map[string]*types.ResourceInfo)
err = draClient.GetPodResourceMap(context.TODO(), pod, resourceMap)
Expect(err).NotTo(HaveOccurred())
Expect(resourceMap).To(HaveKey(mapKey))
Expect(resourceMap[mapKey].DeviceIDs).To(Equal([]string{deviceID}))
})
It("should not include devices from a different node when the pod has a nodeName set", func() {
// This test verifies node-scoping: when nodeName is non-empty the code
// uses a spec.nodeName/spec.driver field selector. The fake API server
// does not enforce server-side field selectors, but we can still confirm
// that only the device actually allocated to the pod's claim appears in
// the result — devices from a slice that is on a different node but
// shares the same driver are a no-op because the claim does not reference
// their device/pool.
claimName := "node-scoped-claim"
driverName := "test-driver.example.com"
poolName := "test-pool"
deviceName := "device-1"
deviceID := "pci:0000:00:01.0"
mapKey := "intel.com/sriov-vf"
deviceIDValue := deviceID
mapKeyValue := mapKey
podNodeName := "test-node"
otherNodeNameVal := "other-node"
// Slice for the pod's node.
sliceOnPodNode := &resourcev1api.ResourceSlice{
ObjectMeta: metav1.ObjectMeta{Name: "slice-test-node"},
Spec: resourcev1api.ResourceSliceSpec{
Driver: driverName,
NodeName: &podNodeName,
Pool: resourcev1api.ResourcePool{Name: poolName, ResourceSliceCount: 1},
Devices: []resourcev1api.Device{
{Name: deviceName, Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
multusDeviceIDAttr: {StringValue: &deviceIDValue},
multusResourceNameAttr: {StringValue: &mapKeyValue},
}},
},
},
}
otherDeviceID := "pci:0000:00:99.0"
otherDeviceIDValue := otherDeviceID
otherMapKeyValue := mapKey
// Slice for a different node — should not produce entries for this pod's claim.
sliceOnOtherNode := &resourcev1api.ResourceSlice{
ObjectMeta: metav1.ObjectMeta{Name: "slice-other-node"},
Spec: resourcev1api.ResourceSliceSpec{
Driver: driverName,
NodeName: &otherNodeNameVal,
Pool: resourcev1api.ResourcePool{Name: "other-pool", ResourceSliceCount: 1},
Devices: []resourcev1api.Device{
{Name: "other-device", Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
multusDeviceIDAttr: {StringValue: &otherDeviceIDValue},
multusResourceNameAttr: {StringValue: &otherMapKeyValue},
}},
},
},
}
claimNamePtr := claimName
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "scoped-pod", Namespace: "default"},
Spec: v1.PodSpec{NodeName: "test-node"},
Status: v1.PodStatus{
ResourceClaimStatuses: []v1.PodResourceClaimStatus{
{Name: claimName, ResourceClaimName: &claimNamePtr},
},
},
}
resourceClaim := &resourcev1api.ResourceClaim{
ObjectMeta: metav1.ObjectMeta{Name: claimName, Namespace: "default"},
Status: resourcev1api.ResourceClaimStatus{Allocation: &resourcev1api.AllocationResult{
Devices: resourcev1api.DeviceAllocationResult{Results: []resourcev1api.DeviceRequestAllocationResult{
{Request: "sriov", Driver: driverName, Pool: poolName, Device: deviceName},
}},
}},
}
_, err := fakeClient.ResourceV1().ResourceClaims("default").Create(context.TODO(), resourceClaim, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
_, err = fakeClient.ResourceV1().ResourceSlices().Create(context.TODO(), sliceOnPodNode, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
_, err = fakeClient.ResourceV1().ResourceSlices().Create(context.TODO(), sliceOnOtherNode, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
resourceMap := make(map[string]*types.ResourceInfo)
err = draClient.GetPodResourceMap(context.TODO(), pod, resourceMap)
Expect(err).NotTo(HaveOccurred())
// Only the device allocated by the claim (on "test-node") must appear.
Expect(resourceMap).To(HaveKey(mapKey))
Expect(resourceMap[mapKey].DeviceIDs).To(ConsistOf(deviceID))
// The other-node device ID must not be present.
Expect(resourceMap[mapKey].DeviceIDs).NotTo(ContainElement(otherDeviceID))
})
})
Context("when multiple devices are allocated to the same claim/request", func() {
It("should append all device IDs to the resource map", func() {
claimName := "multi-device-claim"
driverName := "test-driver.example.com"
poolName := "test-pool"
requestName := "gpu"
device1Name := "device-1"
device2Name := "device-2"
deviceID1 := "pci:0000:00:01.0"
deviceID2 := "pci:0000:00:02.0"
mapKey := "example.com/multi-gpu"
// Create ResourceSlice with multiple devices (same resourceName → one NAD, multiple device IDs)
deviceID1Value := deviceID1
deviceID2Value := deviceID2
mapKeyValue := mapKey
resourceSlice := &resourcev1api.ResourceSlice{
ObjectMeta: metav1.ObjectMeta{
Name: "test-resource-slice",
},
Spec: resourcev1api.ResourceSliceSpec{
Driver: driverName,
Pool: resourcev1api.ResourcePool{
Name: poolName,
ResourceSliceCount: 1,
},
Devices: []resourcev1api.Device{
{
Name: device1Name,
Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
multusDeviceIDAttr: {
StringValue: &deviceID1Value,
},
multusResourceNameAttr: {StringValue: &mapKeyValue},
},
},
{
Name: device2Name,
Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
multusDeviceIDAttr: {
StringValue: &deviceID2Value,
},
multusResourceNameAttr: {StringValue: &mapKeyValue},
},
},
},
},
}
// Create ResourceClaim with multiple device allocations
resourceClaim := &resourcev1api.ResourceClaim{
ObjectMeta: metav1.ObjectMeta{
Name: claimName,
Namespace: "default",
},
Status: resourcev1api.ResourceClaimStatus{
Allocation: &resourcev1api.AllocationResult{
Devices: resourcev1api.DeviceAllocationResult{
Results: []resourcev1api.DeviceRequestAllocationResult{
{
Request: requestName,
Driver: driverName,
Pool: poolName,
Device: device1Name,
},
{
Request: requestName,
Driver: driverName,
Pool: poolName,
Device: device2Name,
},
},
},
},
},
}
claimNamePtr := claimName
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: "default",
UID: k8sTypes.UID("test-uid"),
},
Status: v1.PodStatus{
ResourceClaimStatuses: []v1.PodResourceClaimStatus{
{
Name: claimName,
ResourceClaimName: &claimNamePtr,
},
},
},
}
// Add objects to fake client
_, err := fakeClient.ResourceV1().ResourceClaims("default").Create(context.TODO(), resourceClaim, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
_, err = fakeClient.ResourceV1().ResourceSlices().Create(context.TODO(), resourceSlice, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
// Execute
resourceMap := make(map[string]*types.ResourceInfo)
err = draClient.GetPodResourceMap(context.TODO(), pod, resourceMap)
Expect(err).NotTo(HaveOccurred())
Expect(resourceMap).To(HaveKey(mapKey))
Expect(resourceMap[mapKey].DeviceIDs).To(ConsistOf(deviceID1, deviceID2))
})
})
Context("when one claim allocates multiple devices with distinct resourceName attributes", func() {
It("should populate separate resource map entries per network", func() {
claimName := "dual-net-claim"
driverName := "test-driver.example.com"
poolName := "test-pool"
device1Name := "dev-net-a"
device2Name := "dev-net-b"
deviceID1 := "pci:0000:00:01.0"
deviceID2 := "pci:0000:00:02.0"
keyA := "vendor.com/net-a"
keyB := "vendor.com/net-b"
d1, d2 := deviceID1, deviceID2
kA, kB := keyA, keyB
resourceSlice := &resourcev1api.ResourceSlice{
ObjectMeta: metav1.ObjectMeta{Name: "slice-dual"},
Spec: resourcev1api.ResourceSliceSpec{
Driver: driverName,
Pool: resourcev1api.ResourcePool{Name: poolName, ResourceSliceCount: 1},
Devices: []resourcev1api.Device{
{
Name: device1Name,
Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
multusDeviceIDAttr: {StringValue: &d1},
multusResourceNameAttr: {StringValue: &kA},
},
},
{
Name: device2Name,
Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
multusDeviceIDAttr: {StringValue: &d2},
multusResourceNameAttr: {StringValue: &kB},
},
},
},
},
}
resourceClaim := &resourcev1api.ResourceClaim{
ObjectMeta: metav1.ObjectMeta{Name: claimName, Namespace: "default"},
Status: resourcev1api.ResourceClaimStatus{
Allocation: &resourcev1api.AllocationResult{
Devices: resourcev1api.DeviceAllocationResult{
Results: []resourcev1api.DeviceRequestAllocationResult{
{Request: "req-a", Driver: driverName, Pool: poolName, Device: device1Name},
{Request: "req-b", Driver: driverName, Pool: poolName, Device: device2Name},
},
},
},
},
}
claimNamePtr := claimName
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod-dual", Namespace: "default", UID: k8sTypes.UID("uid-dual")},
Status: v1.PodStatus{
ResourceClaimStatuses: []v1.PodResourceClaimStatus{
{Name: claimName, ResourceClaimName: &claimNamePtr},
},
},
}
_, err := fakeClient.ResourceV1().ResourceClaims("default").Create(context.TODO(), resourceClaim, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
_, err = fakeClient.ResourceV1().ResourceSlices().Create(context.TODO(), resourceSlice, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
resourceMap := make(map[string]*types.ResourceInfo)
err = draClient.GetPodResourceMap(context.TODO(), pod, resourceMap)
Expect(err).NotTo(HaveOccurred())
Expect(resourceMap).To(HaveKey(keyA))
Expect(resourceMap[keyA].DeviceIDs).To(Equal([]string{deviceID1}))
Expect(resourceMap).To(HaveKey(keyB))
Expect(resourceMap[keyB].DeviceIDs).To(Equal([]string{deviceID2}))
})
})
Context("when pod has ExtendedResourceClaimStatus (extended resource feature gate)", func() {
It("should populate resource map using extended resource name as key", func() {
claimName := "ext-resource-dual-port-extended-resources-4sd7g"
device1Name := "0000-08-00-3"
device2Name := "0000-08-02-2"
driverName := "sriovnetwork.k8snetworkplumbingwg.io"
poolName := "c-234-183-40-044"
deviceID1 := "0000:08:00.3"
deviceID2 := "0000:08:02.2"
deviceID1Value := deviceID1
deviceID2Value := deviceID2
rnPort1 := "example.com/sriov-port1"
rnPort2 := "example.com/sriov-port2"
resourceSlice := &resourcev1api.ResourceSlice{
ObjectMeta: metav1.ObjectMeta{
Name: "test-resource-slice",
},
Spec: resourcev1api.ResourceSliceSpec{
Driver: driverName,
Pool: resourcev1api.ResourcePool{
Name: poolName,
ResourceSliceCount: 1,
},
Devices: []resourcev1api.Device{
{
Name: device1Name,
Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
multusDeviceIDAttr: {StringValue: &deviceID1Value},
multusResourceNameAttr: {StringValue: &rnPort1},
},
},
{
Name: device2Name,
Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
multusDeviceIDAttr: {StringValue: &deviceID2Value},
multusResourceNameAttr: {StringValue: &rnPort2},
},
},
},
},
}
resourceClaim := &resourcev1api.ResourceClaim{
ObjectMeta: metav1.ObjectMeta{
Name: claimName,
Namespace: "default",
},
Status: resourcev1api.ResourceClaimStatus{
Allocation: &resourcev1api.AllocationResult{
Devices: resourcev1api.DeviceAllocationResult{
Results: []resourcev1api.DeviceRequestAllocationResult{
{
Request: "container-0-request-0",
Driver: driverName,
Pool: poolName,
Device: device1Name,
},
{
Request: "container-0-request-1",
Driver: driverName,
Pool: poolName,
Device: device2Name,
},
},
},
},
},
}
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "ext-resource-dual-port",
Namespace: "default",
UID: k8sTypes.UID("dc0b90ad-0ca2-4d83-bac9-61e053a86850"),
},
Status: v1.PodStatus{
ResourceClaimStatuses: nil,
ExtendedResourceClaimStatus: &v1.PodExtendedResourceClaimStatus{
ResourceClaimName: claimName,
RequestMappings: []v1.ContainerExtendedResourceRequest{
{
ContainerName: "test-container",
ResourceName: "example.com/sriov-port1",
RequestName: "container-0-request-0",
},
{
ContainerName: "test-container",
ResourceName: "example.com/sriov-port2",
RequestName: "container-0-request-1",
},
},
},
},
}
_, err := fakeClient.ResourceV1().ResourceClaims("default").Create(context.TODO(), resourceClaim, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
_, err = fakeClient.ResourceV1().ResourceSlices().Create(context.TODO(), resourceSlice, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
resourceMap := make(map[string]*types.ResourceInfo)
err = draClient.GetPodResourceMap(context.TODO(), pod, resourceMap)
Expect(err).NotTo(HaveOccurred())
Expect(resourceMap).To(HaveKey("example.com/sriov-port1"))
Expect(resourceMap["example.com/sriov-port1"].DeviceIDs).To(Equal([]string{deviceID1}))
Expect(resourceMap).To(HaveKey("example.com/sriov-port2"))
Expect(resourceMap["example.com/sriov-port2"].DeviceIDs).To(Equal([]string{deviceID2}))
})
It("should populate resource map with multiple devices when request has count > 1", func() {
claimName := "multus-dual-port-extended-resources-ftknr"
device1Name := "0000-08-00-3"
device2Name := "0000-08-00-4"
device3Name := "0000-08-02-4"
driverName := "sriovnetwork.k8snetworkplumbingwg.io"
poolName := "c-237-169-100-104"
deviceID1 := "0000:08:00.3"
deviceID2 := "0000:08:00.4"
deviceID3 := "0000:08:02.4"
rnP1 := "nvidia.com/sriov-port1"
rnP2 := "nvidia.com/sriov-port2"
resourceSlice := &resourcev1api.ResourceSlice{
ObjectMeta: metav1.ObjectMeta{
Name: "test-resource-slice",
},
Spec: resourcev1api.ResourceSliceSpec{
Driver: driverName,
Pool: resourcev1api.ResourcePool{
Name: poolName,
ResourceSliceCount: 1,
},
Devices: []resourcev1api.Device{
{Name: device1Name, Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
multusDeviceIDAttr: {StringValue: &deviceID1}, multusResourceNameAttr: {StringValue: &rnP1},
}},
{Name: device2Name, Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
multusDeviceIDAttr: {StringValue: &deviceID2}, multusResourceNameAttr: {StringValue: &rnP1},
}},
{Name: device3Name, Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
multusDeviceIDAttr: {StringValue: &deviceID3}, multusResourceNameAttr: {StringValue: &rnP2},
}},
},
},
}
resourceClaim := &resourcev1api.ResourceClaim{
ObjectMeta: metav1.ObjectMeta{
Name: claimName,
Namespace: "default",
},
Status: resourcev1api.ResourceClaimStatus{
Allocation: &resourcev1api.AllocationResult{
Devices: resourcev1api.DeviceAllocationResult{
Results: []resourcev1api.DeviceRequestAllocationResult{
{Request: "container-0-request-0", Driver: driverName, Pool: poolName, Device: device1Name},
{Request: "container-0-request-0", Driver: driverName, Pool: poolName, Device: device2Name},
{Request: "container-0-request-1", Driver: driverName, Pool: poolName, Device: device3Name},
},
},
},
},
}
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "multus-dual-port",
Namespace: "default",
UID: k8sTypes.UID("895bdfd9-a532-4127-97b5-2a0aa5cc91a7"),
},
Status: v1.PodStatus{
ExtendedResourceClaimStatus: &v1.PodExtendedResourceClaimStatus{
ResourceClaimName: claimName,
RequestMappings: []v1.ContainerExtendedResourceRequest{
{ContainerName: "test-container", ResourceName: "nvidia.com/sriov-port1", RequestName: "container-0-request-0"},
{ContainerName: "test-container", ResourceName: "nvidia.com/sriov-port2", RequestName: "container-0-request-1"},
},
},
},
}
_, err := fakeClient.ResourceV1().ResourceClaims("default").Create(context.TODO(), resourceClaim, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
_, err = fakeClient.ResourceV1().ResourceSlices().Create(context.TODO(), resourceSlice, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
resourceMap := make(map[string]*types.ResourceInfo)
err = draClient.GetPodResourceMap(context.TODO(), pod, resourceMap)
Expect(err).NotTo(HaveOccurred())
Expect(resourceMap).To(HaveKey("nvidia.com/sriov-port1"))
Expect(resourceMap["nvidia.com/sriov-port1"].DeviceIDs).To(ConsistOf(deviceID1, deviceID2))
Expect(resourceMap).To(HaveKey("nvidia.com/sriov-port2"))
Expect(resourceMap["nvidia.com/sriov-port2"].DeviceIDs).To(Equal([]string{deviceID3}))
})
It("should return an error when device resourceName attribute does not match extended mapping", func() {
claimName := "ext-mismatch-claim"
deviceName := "0000-08-00-3"
driverName := "sriovnetwork.k8snetworkplumbingwg.io"
poolName := "pool-1"
deviceID := "0000:08:00.3"
wrongRN := "wrong.example.com/port"
deviceIDVal := deviceID
resourceSlice := &resourcev1api.ResourceSlice{
ObjectMeta: metav1.ObjectMeta{Name: "slice-mismatch"},
Spec: resourcev1api.ResourceSliceSpec{
Driver: driverName,
Pool: resourcev1api.ResourcePool{Name: poolName, ResourceSliceCount: 1},
Devices: []resourcev1api.Device{
{
Name: deviceName,
Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
multusDeviceIDAttr: {StringValue: &deviceIDVal},
multusResourceNameAttr: {StringValue: &wrongRN},
},
},
},
},
}
resourceClaim := &resourcev1api.ResourceClaim{
ObjectMeta: metav1.ObjectMeta{Name: claimName, Namespace: "default"},
Status: resourcev1api.ResourceClaimStatus{
Allocation: &resourcev1api.AllocationResult{
Devices: resourcev1api.DeviceAllocationResult{
Results: []resourcev1api.DeviceRequestAllocationResult{
{Request: "req-0", Driver: driverName, Pool: poolName, Device: deviceName},
},
},
},
},
}
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod-mismatch", Namespace: "default", UID: k8sTypes.UID("uid-mismatch")},
Status: v1.PodStatus{
ExtendedResourceClaimStatus: &v1.PodExtendedResourceClaimStatus{
ResourceClaimName: claimName,
RequestMappings: []v1.ContainerExtendedResourceRequest{
{ContainerName: "c", ResourceName: "expected.example.com/port", RequestName: "req-0"},
},
},
},
}
_, err := fakeClient.ResourceV1().ResourceClaims("default").Create(context.TODO(), resourceClaim, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
_, err = fakeClient.ResourceV1().ResourceSlices().Create(context.TODO(), resourceSlice, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
resourceMap := make(map[string]*types.ResourceInfo)
err = draClient.GetPodResourceMap(context.TODO(), pod, resourceMap)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring(multusResourceNameAttr))
Expect(err.Error()).To(ContainSubstring("expected.example.com/port"))
})
})
Context("when resource claim does not exist", func() {
It("should return an error", func() {
claimName := "non-existent-claim"
claimNamePtr := claimName
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: "default",
UID: k8sTypes.UID("test-uid"),
},
Status: v1.PodStatus{
ResourceClaimStatuses: []v1.PodResourceClaimStatus{
{
Name: claimName,
ResourceClaimName: &claimNamePtr,
},
},
},
}
resourceMap := make(map[string]*types.ResourceInfo)
err := draClient.GetPodResourceMap(context.TODO(), pod, resourceMap)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("not found"))
})
})
Context("when resource slice does not exist", func() {
It("should skip the claim and return empty resource map without error", func() {
claimName := "test-claim"
deviceName := "device-1"
driverName := "test-driver.example.com"
poolName := "test-pool"
requestName := "gpu"
// Create ResourceClaim but no ResourceSlice
resourceClaim := &resourcev1api.ResourceClaim{
ObjectMeta: metav1.ObjectMeta{
Name: claimName,
Namespace: "default",
},
Status: resourcev1api.ResourceClaimStatus{
Allocation: &resourcev1api.AllocationResult{
Devices: resourcev1api.DeviceAllocationResult{
Results: []resourcev1api.DeviceRequestAllocationResult{
{
Request: requestName,
Driver: driverName,
Pool: poolName,
Device: deviceName,
},
},
},
},
},
}
claimNamePtr := claimName
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: "default",
UID: k8sTypes.UID("test-uid"),
},
Status: v1.PodStatus{
ResourceClaimStatuses: []v1.PodResourceClaimStatus{
{
Name: claimName,
ResourceClaimName: &claimNamePtr,
},
},
},
}
// Add only resource claim
_, err := fakeClient.ResourceV1().ResourceClaims("default").Create(context.TODO(), resourceClaim, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
// Execute — no slice means List returns empty; device misses cache → errDeviceNotInAnySlice
// → skipped silently (non-CNI claim graceful handling). No error, empty map.
resourceMap := make(map[string]*types.ResourceInfo)
err = draClient.GetPodResourceMap(context.TODO(), pod, resourceMap)
Expect(err).NotTo(HaveOccurred())
Expect(resourceMap).To(BeEmpty())
})
})
Context("when device does not have deviceID attribute", func() {
It("should warn and skip the claim without failing", func() {
claimName := "test-claim"
deviceName := "device-1"
driverName := "test-driver.example.com"
poolName := "test-pool"
requestName := "gpu"
// Create ResourceSlice with device but NO deviceID attribute
resourceSlice := &resourcev1api.ResourceSlice{
ObjectMeta: metav1.ObjectMeta{
Name: "test-resource-slice",
},
Spec: resourcev1api.ResourceSliceSpec{
Driver: driverName,
Pool: resourcev1api.ResourcePool{
Name: poolName,
ResourceSliceCount: 1,
},
Devices: []resourcev1api.Device{
{
Name: deviceName,
Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
// Missing multusDeviceIDAttr
"some-other-attribute": {
StringValue: func() *string { s := "value"; return &s }(),
},
},
},
},
},
}
// Create ResourceClaim
resourceClaim := &resourcev1api.ResourceClaim{
ObjectMeta: metav1.ObjectMeta{
Name: claimName,
Namespace: "default",
},
Status: resourcev1api.ResourceClaimStatus{
Allocation: &resourcev1api.AllocationResult{
Devices: resourcev1api.DeviceAllocationResult{
Results: []resourcev1api.DeviceRequestAllocationResult{
{
Request: requestName,
Driver: driverName,
Pool: poolName,
Device: deviceName,
},
},
},
},
},
}
claimNamePtr := claimName
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: "default",
UID: k8sTypes.UID("test-uid"),
},
Status: v1.PodStatus{
ResourceClaimStatuses: []v1.PodResourceClaimStatus{
{
Name: claimName,
ResourceClaimName: &claimNamePtr,
},
},
},
}
// Add objects
_, err := fakeClient.ResourceV1().ResourceClaims("default").Create(context.TODO(), resourceClaim, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
_, err = fakeClient.ResourceV1().ResourceSlices().Create(context.TODO(), resourceSlice, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
// Execute
resourceMap := make(map[string]*types.ResourceInfo)
err = draClient.GetPodResourceMap(context.TODO(), pod, resourceMap)
Expect(err).NotTo(HaveOccurred())
Expect(resourceMap).To(BeEmpty())
})
})
Context("when device has deviceID but missing resourceName attribute", func() {
It("should warn and skip without failing when nothing maps to a resource name", func() {
claimName := "test-claim"
deviceName := "device-1"
driverName := "test-driver.example.com"
poolName := "test-pool"
requestName := "gpu"
deviceID := "pci:0000:00:01.0"
deviceIDValue := deviceID
resourceSlice := &resourcev1api.ResourceSlice{
ObjectMeta: metav1.ObjectMeta{Name: "test-resource-slice"},
Spec: resourcev1api.ResourceSliceSpec{
Driver: driverName,
Pool: resourcev1api.ResourcePool{Name: poolName, ResourceSliceCount: 1},
Devices: []resourcev1api.Device{
{
Name: deviceName,
Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
multusDeviceIDAttr: {StringValue: &deviceIDValue},
},
},
},
},
}
resourceClaim := &resourcev1api.ResourceClaim{
ObjectMeta: metav1.ObjectMeta{Name: claimName, Namespace: "default"},
Status: resourcev1api.ResourceClaimStatus{
Allocation: &resourcev1api.AllocationResult{
Devices: resourcev1api.DeviceAllocationResult{
Results: []resourcev1api.DeviceRequestAllocationResult{
{Request: requestName, Driver: driverName, Pool: poolName, Device: deviceName},
},
},
},
},
}
claimNamePtr := claimName
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "test-pod", Namespace: "default", UID: k8sTypes.UID("test-uid")},
Status: v1.PodStatus{
ResourceClaimStatuses: []v1.PodResourceClaimStatus{
{Name: claimName, ResourceClaimName: &claimNamePtr},
},
},
}
_, err := fakeClient.ResourceV1().ResourceClaims("default").Create(context.TODO(), resourceClaim, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
_, err = fakeClient.ResourceV1().ResourceSlices().Create(context.TODO(), resourceSlice, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
resourceMap := make(map[string]*types.ResourceInfo)
err = draClient.GetPodResourceMap(context.TODO(), pod, resourceMap)
Expect(err).NotTo(HaveOccurred())
Expect(resourceMap).To(BeEmpty())
})
})
Context("when device name in allocation does not match any device in slice", func() {
It("should warn and skip the claim without failing", func() {
claimName := "test-claim"
deviceName := "device-1"
wrongDeviceName := "wrong-device"
driverName := "test-driver.example.com"
poolName := "test-pool"
requestName := "gpu"
deviceID := "pci:0000:00:01.0"
// Create ResourceSlice with device
deviceIDValue := deviceID
resourceSlice := &resourcev1api.ResourceSlice{
ObjectMeta: metav1.ObjectMeta{
Name: "test-resource-slice",
},
Spec: resourcev1api.ResourceSliceSpec{
Driver: driverName,
Pool: resourcev1api.ResourcePool{
Name: poolName,
ResourceSliceCount: 1,
},
Devices: []resourcev1api.Device{
{
Name: deviceName,
Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
multusDeviceIDAttr: {
StringValue: &deviceIDValue,
},
},
},
},
},
}
// Create ResourceClaim with WRONG device name
resourceClaim := &resourcev1api.ResourceClaim{
ObjectMeta: metav1.ObjectMeta{
Name: claimName,
Namespace: "default",
},
Status: resourcev1api.ResourceClaimStatus{
Allocation: &resourcev1api.AllocationResult{
Devices: resourcev1api.DeviceAllocationResult{
Results: []resourcev1api.DeviceRequestAllocationResult{
{
Request: requestName,
Driver: driverName,
Pool: poolName,
Device: wrongDeviceName,
},
},
},
},
},
}
claimNamePtr := claimName
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: "default",
UID: k8sTypes.UID("test-uid"),
},
Status: v1.PodStatus{
ResourceClaimStatuses: []v1.PodResourceClaimStatus{
{
Name: claimName,
ResourceClaimName: &claimNamePtr,
},
},
},
}
// Add objects
_, err := fakeClient.ResourceV1().ResourceClaims("default").Create(context.TODO(), resourceClaim, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
_, err = fakeClient.ResourceV1().ResourceSlices().Create(context.TODO(), resourceSlice, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
// Execute
resourceMap := make(map[string]*types.ResourceInfo)
err = draClient.GetPodResourceMap(context.TODO(), pod, resourceMap)
Expect(err).NotTo(HaveOccurred())
Expect(resourceMap).To(BeEmpty())
})
It("should preserve existing kubelet map entries when the claim maps nothing", func() {
claimName := "test-claim"
deviceName := "device-1"
wrongDeviceName := "wrong-device"
driverName := "test-driver.example.com"
poolName := "test-pool"
requestName := "gpu"
deviceID := "pci:0000:00:01.0"
legacyKey := "example.com/legacy-vf"
legacyPCI := "0000:8d:00.4"
deviceIDValue := deviceID
resourceSlice := &resourcev1api.ResourceSlice{
ObjectMeta: metav1.ObjectMeta{Name: "test-resource-slice"},
Spec: resourcev1api.ResourceSliceSpec{
Driver: driverName,
Pool: resourcev1api.ResourcePool{Name: poolName, ResourceSliceCount: 1},
Devices: []resourcev1api.Device{
{
Name: deviceName,
Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
multusDeviceIDAttr: {StringValue: &deviceIDValue},
},
},
},
},
}
resourceClaim := &resourcev1api.ResourceClaim{
ObjectMeta: metav1.ObjectMeta{Name: claimName, Namespace: "default"},
Status: resourcev1api.ResourceClaimStatus{
Allocation: &resourcev1api.AllocationResult{
Devices: resourcev1api.DeviceAllocationResult{
Results: []resourcev1api.DeviceRequestAllocationResult{
{Request: requestName, Driver: driverName, Pool: poolName, Device: wrongDeviceName},
},
},
},
},
}
claimNamePtr := claimName
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "hybrid-pod", Namespace: "default", UID: k8sTypes.UID("uid-hybrid")},
Status: v1.PodStatus{
ResourceClaimStatuses: []v1.PodResourceClaimStatus{
{Name: claimName, ResourceClaimName: &claimNamePtr},
},
},
}
_, err := fakeClient.ResourceV1().ResourceClaims("default").Create(context.TODO(), resourceClaim, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
_, err = fakeClient.ResourceV1().ResourceSlices().Create(context.TODO(), resourceSlice, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
resourceMap := map[string]*types.ResourceInfo{
legacyKey: {DeviceIDs: []string{legacyPCI}},
}
err = draClient.GetPodResourceMap(context.TODO(), pod, resourceMap)
Expect(err).NotTo(HaveOccurred())
Expect(resourceMap[legacyKey].DeviceIDs).To(Equal([]string{legacyPCI}))
})
It("should succeed when one allocation result is missing from slices but another resolves", func() {
claimName := "multi-claim"
driverSRIOV := "sriovnetwork.k8snetworkplumbingwg.io"
driverGPU := "gpu.nvidia.com"
poolName := "orch-dev-a100-002"
deviceVF := "0000-8d-00-4"
deviceGPU := "gpu-4"
deviceIDVF := "pci:0000:8d:00.4"
mapKey := "nvidia.com/port2"
deviceIDVFVal := deviceIDVF
mapKeyVal := mapKey
// SR-IOV slice has the VF; empty GPU slice exists so List() finds gpu.nvidia.com/pool (real clusters
// always publish a slice per driver/pool). Allocation still references gpu-4, which is not listed here.
resourceSlice := &resourcev1api.ResourceSlice{
ObjectMeta: metav1.ObjectMeta{Name: "sriov-slice"},
Spec: resourcev1api.ResourceSliceSpec{
Driver: driverSRIOV,
Pool: resourcev1api.ResourcePool{Name: poolName, ResourceSliceCount: 1},
Devices: []resourcev1api.Device{
{
Name: deviceVF,
Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
multusDeviceIDAttr: {StringValue: &deviceIDVFVal},
multusResourceNameAttr: {StringValue: &mapKeyVal},
},
},
},
},
}
gpuSlice := &resourcev1api.ResourceSlice{
ObjectMeta: metav1.ObjectMeta{Name: "gpu-slice"},
Spec: resourcev1api.ResourceSliceSpec{
Driver: driverGPU,
Pool: resourcev1api.ResourcePool{Name: poolName, ResourceSliceCount: 1},
Devices: []resourcev1api.Device{},
},
}
resourceClaim := &resourcev1api.ResourceClaim{
ObjectMeta: metav1.ObjectMeta{Name: claimName, Namespace: "default"},
Status: resourcev1api.ResourceClaimStatus{
Allocation: &resourcev1api.AllocationResult{
Devices: resourcev1api.DeviceAllocationResult{
Results: []resourcev1api.DeviceRequestAllocationResult{
{Request: "vf", Driver: driverSRIOV, Pool: poolName, Device: deviceVF},
{Request: "gpu", Driver: driverGPU, Pool: poolName, Device: deviceGPU},
},
},
},
},
}
claimNamePtr := claimName
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "mixed-pod", Namespace: "default", UID: k8sTypes.UID("uid-mixed")},
Status: v1.PodStatus{
ResourceClaimStatuses: []v1.PodResourceClaimStatus{
{Name: claimName, ResourceClaimName: &claimNamePtr},
},
},
}
_, err := fakeClient.ResourceV1().ResourceClaims("default").Create(context.TODO(), resourceClaim, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
_, err = fakeClient.ResourceV1().ResourceSlices().Create(context.TODO(), resourceSlice, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
_, err = fakeClient.ResourceV1().ResourceSlices().Create(context.TODO(), gpuSlice, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
resourceMap := make(map[string]*types.ResourceInfo)
err = draClient.GetPodResourceMap(context.TODO(), pod, resourceMap)
Expect(err).NotTo(HaveOccurred())
Expect(resourceMap[mapKey].DeviceIDs).To(Equal([]string{deviceIDVF}))
})
})
Context("when caching is working correctly", func() {
It("should cache resource claims and slices", func() {
claimName := "test-claim"
deviceName := "device-1"
driverName := "test-driver.example.com"
poolName := "test-pool"
requestName := "gpu"
deviceID := "pci:0000:00:01.0"
mapKey := "cache-test.example.com/res"
// Create ResourceSlice with device
deviceIDValue := deviceID
mapKeyValue := mapKey
resourceSlice := &resourcev1api.ResourceSlice{
ObjectMeta: metav1.ObjectMeta{
Name: "test-resource-slice",
},
Spec: resourcev1api.ResourceSliceSpec{
Driver: driverName,
Pool: resourcev1api.ResourcePool{
Name: poolName,
ResourceSliceCount: 1,
},
Devices: []resourcev1api.Device{
{
Name: deviceName,
Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
multusDeviceIDAttr: {
StringValue: &deviceIDValue,
},
multusResourceNameAttr: {StringValue: &mapKeyValue},
},
},
},
},
}
// Create ResourceClaim
resourceClaim := &resourcev1api.ResourceClaim{
ObjectMeta: metav1.ObjectMeta{
Name: claimName,
Namespace: "default",
},
Status: resourcev1api.ResourceClaimStatus{
Allocation: &resourcev1api.AllocationResult{
Devices: resourcev1api.DeviceAllocationResult{
Results: []resourcev1api.DeviceRequestAllocationResult{
{
Request: requestName,
Driver: driverName,
Pool: poolName,
Device: deviceName,
},
},
},
},
},
}
claimNamePtr := claimName
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: "default",
UID: k8sTypes.UID("test-uid"),
},
Status: v1.PodStatus{
ResourceClaimStatuses: []v1.PodResourceClaimStatus{
{
Name: claimName,
ResourceClaimName: &claimNamePtr,
},
},
},
}
// Add objects to fake client
_, err := fakeClient.ResourceV1().ResourceClaims("default").Create(context.TODO(), resourceClaim, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
_, err = fakeClient.ResourceV1().ResourceSlices().Create(context.TODO(), resourceSlice, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
// First call - should populate cache
resourceMap := make(map[string]*types.ResourceInfo)
err = draClient.GetPodResourceMap(context.TODO(), pod, resourceMap)
Expect(err).NotTo(HaveOccurred())
expectedKey := mapKey
Expect(resourceMap).To(HaveKey(expectedKey))
Expect(resourceMap[expectedKey].DeviceIDs).To(Equal([]string{deviceID}))
// Delete the objects from the API server
err = fakeClient.ResourceV1().ResourceClaims("default").Delete(context.TODO(), claimName, metav1.DeleteOptions{})
Expect(err).NotTo(HaveOccurred())
err = fakeClient.ResourceV1().ResourceSlices().Delete(context.TODO(), resourceSlice.Name, metav1.DeleteOptions{})
Expect(err).NotTo(HaveOccurred())
// Second call - should use cache and succeed even though objects are deleted
resourceMap2 := make(map[string]*types.ResourceInfo)
err = draClient.GetPodResourceMap(context.TODO(), pod, resourceMap2)
Expect(err).NotTo(HaveOccurred())
Expect(resourceMap2).To(HaveKey(expectedKey))
Expect(resourceMap2[expectedKey].DeviceIDs).To(Equal([]string{deviceID}))
})
})
Context("when two claims share the same driver", func() {
It("should populate the driver cache only once and resolve both devices", func() {
driverName := "sriovnetwork.k8snetworkplumbingwg.io"
poolName := "test-pool"
device1Name := "dev-a"
device2Name := "dev-b"
deviceID1 := "pci:0000:01:00.0"
deviceID2 := "pci:0000:02:00.0"
mapKey1 := "sriov.example.com/net-a"
mapKey2 := "sriov.example.com/net-b"
claim1Name := "claim-a"
claim2Name := "claim-b"
dv1, dv2, mk1, mk2 := deviceID1, deviceID2, mapKey1, mapKey2
resourceSlice := &resourcev1api.ResourceSlice{
ObjectMeta: metav1.ObjectMeta{Name: "sriov-slice"},
Spec: resourcev1api.ResourceSliceSpec{
Driver: driverName,
Pool: resourcev1api.ResourcePool{Name: poolName, ResourceSliceCount: 1},
Devices: []resourcev1api.Device{
{Name: device1Name, Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
multusDeviceIDAttr: {StringValue: &dv1}, multusResourceNameAttr: {StringValue: &mk1},
}},
{Name: device2Name, Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
multusDeviceIDAttr: {StringValue: &dv2}, multusResourceNameAttr: {StringValue: &mk2},
}},
},
},
}
c1ptr, c2ptr := claim1Name, claim2Name
resourceClaim1 := &resourcev1api.ResourceClaim{
ObjectMeta: metav1.ObjectMeta{Name: claim1Name, Namespace: "default"},
Status: resourcev1api.ResourceClaimStatus{Allocation: &resourcev1api.AllocationResult{
Devices: resourcev1api.DeviceAllocationResult{Results: []resourcev1api.DeviceRequestAllocationResult{
{Request: "req-a", Driver: driverName, Pool: poolName, Device: device1Name},
}},
}},
}
resourceClaim2 := &resourcev1api.ResourceClaim{
ObjectMeta: metav1.ObjectMeta{Name: claim2Name, Namespace: "default"},
Status: resourcev1api.ResourceClaimStatus{Allocation: &resourcev1api.AllocationResult{
Devices: resourcev1api.DeviceAllocationResult{Results: []resourcev1api.DeviceRequestAllocationResult{
{Request: "req-b", Driver: driverName, Pool: poolName, Device: device2Name},
}},
}},
}
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "two-claim-pod", Namespace: "default", UID: k8sTypes.UID("uid-two")},
Status: v1.PodStatus{
ResourceClaimStatuses: []v1.PodResourceClaimStatus{
{Name: claim1Name, ResourceClaimName: &c1ptr},
{Name: claim2Name, ResourceClaimName: &c2ptr},
},
},
}
_, err := fakeClient.ResourceV1().ResourceClaims("default").Create(context.TODO(), resourceClaim1, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
_, err = fakeClient.ResourceV1().ResourceClaims("default").Create(context.TODO(), resourceClaim2, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
_, err = fakeClient.ResourceV1().ResourceSlices().Create(context.TODO(), resourceSlice, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
resourceMap := make(map[string]*types.ResourceInfo)
err = draClient.GetPodResourceMap(context.TODO(), pod, resourceMap)
Expect(err).NotTo(HaveOccurred())
Expect(resourceMap).To(HaveKey(mapKey1))
Expect(resourceMap[mapKey1].DeviceIDs).To(Equal([]string{deviceID1}))
Expect(resourceMap).To(HaveKey(mapKey2))
Expect(resourceMap[mapKey2].DeviceIDs).To(Equal([]string{deviceID2}))
// Verify the driver cache was populated after the first call: delete the slice from
// the API server and call again — if cache is working, it must still resolve correctly.
err = fakeClient.ResourceV1().ResourceSlices().Delete(context.TODO(), resourceSlice.Name, metav1.DeleteOptions{})
Expect(err).NotTo(HaveOccurred())
resourceMap2 := make(map[string]*types.ResourceInfo)
err = draClient.GetPodResourceMap(context.TODO(), pod, resourceMap2)
Expect(err).NotTo(HaveOccurred())
Expect(resourceMap2[mapKey1].DeviceIDs).To(Equal([]string{deviceID1}))
Expect(resourceMap2[mapKey2].DeviceIDs).To(Equal([]string{deviceID2}))
})
})
Context("when ResourceClaim names collide across namespaces", func() {
It("should cache claims separately per namespace", func() {
claimName := "same-claim-name"
driverName := "test-driver.example.com"
poolName := "test-pool"
requestName := "gpu"
mapKey := "cross-ns.example.com/res"
nsA, nsB := "team-a", "team-b"
deviceA, deviceB := "dev-a", "dev-b"
pciA, pciB := "pci:0000:01:00.0", "pci:0000:02:00.0"
mapKeyVal := mapKey
pciAVal, pciBVal := pciA, pciB
resourceSlice := &resourcev1api.ResourceSlice{
ObjectMeta: metav1.ObjectMeta{Name: "shared-slice"},
Spec: resourcev1api.ResourceSliceSpec{
Driver: driverName,
Pool: resourcev1api.ResourcePool{Name: poolName, ResourceSliceCount: 1},
Devices: []resourcev1api.Device{
{
Name: deviceA,
Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
multusDeviceIDAttr: {StringValue: &pciAVal},
multusResourceNameAttr: {StringValue: &mapKeyVal},
},
},
{
Name: deviceB,
Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
multusDeviceIDAttr: {StringValue: &pciBVal},
multusResourceNameAttr: {StringValue: &mapKeyVal},
},
},
},
},
}
claimA := &resourcev1api.ResourceClaim{
ObjectMeta: metav1.ObjectMeta{Name: claimName, Namespace: nsA},
Status: resourcev1api.ResourceClaimStatus{
Allocation: &resourcev1api.AllocationResult{
Devices: resourcev1api.DeviceAllocationResult{
Results: []resourcev1api.DeviceRequestAllocationResult{
{Request: requestName, Driver: driverName, Pool: poolName, Device: deviceA},
},
},
},
},
}
claimB := &resourcev1api.ResourceClaim{
ObjectMeta: metav1.ObjectMeta{Name: claimName, Namespace: nsB},
Status: resourcev1api.ResourceClaimStatus{
Allocation: &resourcev1api.AllocationResult{
Devices: resourcev1api.DeviceAllocationResult{
Results: []resourcev1api.DeviceRequestAllocationResult{
{Request: requestName, Driver: driverName, Pool: poolName, Device: deviceB},
},
},
},
},
}
_, err := fakeClient.ResourceV1().ResourceClaims(nsA).Create(context.TODO(), claimA, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
_, err = fakeClient.ResourceV1().ResourceClaims(nsB).Create(context.TODO(), claimB, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
_, err = fakeClient.ResourceV1().ResourceSlices().Create(context.TODO(), resourceSlice, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
claimPtr := claimName
podA := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod-a", Namespace: nsA, UID: k8sTypes.UID("uid-a")},
Status: v1.PodStatus{
ResourceClaimStatuses: []v1.PodResourceClaimStatus{
{Name: claimName, ResourceClaimName: &claimPtr},
},
},
}
podB := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod-b", Namespace: nsB, UID: k8sTypes.UID("uid-b")},
Status: v1.PodStatus{
ResourceClaimStatuses: []v1.PodResourceClaimStatus{
{Name: claimName, ResourceClaimName: &claimPtr},
},
},
}
m1 := make(map[string]*types.ResourceInfo)
err = draClient.GetPodResourceMap(context.TODO(), podA, m1)
Expect(err).NotTo(HaveOccurred())
Expect(m1[mapKey].DeviceIDs).To(Equal([]string{pciA}))
m2 := make(map[string]*types.ResourceInfo)
err = draClient.GetPodResourceMap(context.TODO(), podB, m2)
Expect(err).NotTo(HaveOccurred())
Expect(m2[mapKey].DeviceIDs).To(Equal([]string{pciB}))
})
})
Context("when pod has multiple different claims", func() {
It("should populate resource map with all claims sequentially", func() {
// Note: Due to fake client limitations with field selectors,
// we test each claim separately to avoid conflicts
claim1Name := "claim-1"
device1Name := "device-1"
driver1Name := "driver1.example.com"
pool1Name := "pool-1"
request1Name := "gpu"
deviceID1 := "pci:0000:00:01.0"
mapKey1 := "driver1.example.com/gpu-net"
// First claim setup
deviceID1Value := deviceID1
mapKey1Val := mapKey1
resourceSlice1 := &resourcev1api.ResourceSlice{
ObjectMeta: metav1.ObjectMeta{
Name: "test-resource-slice-1",
},
Spec: resourcev1api.ResourceSliceSpec{
Driver: driver1Name,
Pool: resourcev1api.ResourcePool{
Name: pool1Name,
ResourceSliceCount: 1,
},
Devices: []resourcev1api.Device{
{
Name: device1Name,
Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
multusDeviceIDAttr: {
StringValue: &deviceID1Value,
},
multusResourceNameAttr: {StringValue: &mapKey1Val},
},
},
},
},
}
resourceClaim1 := &resourcev1api.ResourceClaim{
ObjectMeta: metav1.ObjectMeta{
Name: claim1Name,
Namespace: "default",
},
Status: resourcev1api.ResourceClaimStatus{
Allocation: &resourcev1api.AllocationResult{
Devices: resourcev1api.DeviceAllocationResult{
Results: []resourcev1api.DeviceRequestAllocationResult{
{
Request: request1Name,
Driver: driver1Name,
Pool: pool1Name,
Device: device1Name,
},
},
},
},
},
}
claim1NamePtr := claim1Name
pod1 := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod-1",
Namespace: "default",
UID: k8sTypes.UID("test-uid-1"),
},
Status: v1.PodStatus{
ResourceClaimStatuses: []v1.PodResourceClaimStatus{
{
Name: claim1Name,
ResourceClaimName: &claim1NamePtr,
},
},
},
}
// Add first claim objects
_, err := fakeClient.ResourceV1().ResourceClaims("default").Create(context.TODO(), resourceClaim1, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
_, err = fakeClient.ResourceV1().ResourceSlices().Create(context.TODO(), resourceSlice1, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
// Test first claim
resourceMap := make(map[string]*types.ResourceInfo)
err = draClient.GetPodResourceMap(context.TODO(), pod1, resourceMap)
Expect(err).NotTo(HaveOccurred())
Expect(resourceMap).To(HaveKey(mapKey1))
Expect(resourceMap[mapKey1].DeviceIDs).To(Equal([]string{deviceID1}))
// Now test second claim with a fresh client to avoid field selector issues
claim2Name := "claim-2"
device2Name := "device-2"
driver2Name := "driver2.example.com"
pool2Name := "pool-2"
request2Name := "nic"
deviceID2 := "pci:0000:00:02.0"
mapKey2 := "driver2.example.com/nic-net"
fakeClient2 := fake.NewClientset()
draClient2 := NewClient(fakeClient2.ResourceV1())
deviceID2Value := deviceID2
mapKey2Val := mapKey2
resourceSlice2 := &resourcev1api.ResourceSlice{
ObjectMeta: metav1.ObjectMeta{
Name: "test-resource-slice-2",
},
Spec: resourcev1api.ResourceSliceSpec{
Driver: driver2Name,
Pool: resourcev1api.ResourcePool{
Name: pool2Name,
ResourceSliceCount: 1,
},
Devices: []resourcev1api.Device{
{
Name: device2Name,
Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
multusDeviceIDAttr: {
StringValue: &deviceID2Value,
},
multusResourceNameAttr: {StringValue: &mapKey2Val},
},
},
},
},
}
resourceClaim2 := &resourcev1api.ResourceClaim{
ObjectMeta: metav1.ObjectMeta{
Name: claim2Name,
Namespace: "default",
},
Status: resourcev1api.ResourceClaimStatus{
Allocation: &resourcev1api.AllocationResult{
Devices: resourcev1api.DeviceAllocationResult{
Results: []resourcev1api.DeviceRequestAllocationResult{
{
Request: request2Name,
Driver: driver2Name,
Pool: pool2Name,
Device: device2Name,
},
},
},
},
},
}
claim2NamePtr := claim2Name
pod2 := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod-2",
Namespace: "default",
UID: k8sTypes.UID("test-uid-2"),
},
Status: v1.PodStatus{
ResourceClaimStatuses: []v1.PodResourceClaimStatus{
{
Name: claim2Name,
ResourceClaimName: &claim2NamePtr,
},
},
},
}
// Add second claim objects
_, err = fakeClient2.ResourceV1().ResourceClaims("default").Create(context.TODO(), resourceClaim2, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
_, err = fakeClient2.ResourceV1().ResourceSlices().Create(context.TODO(), resourceSlice2, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
// Test second claim
resourceMap2 := make(map[string]*types.ResourceInfo)
err = draClient2.GetPodResourceMap(context.TODO(), pod2, resourceMap2)
Expect(err).NotTo(HaveOccurred())
Expect(resourceMap2).To(HaveKey(mapKey2))
Expect(resourceMap2[mapKey2].DeviceIDs).To(Equal([]string{deviceID2}))
})
})
Context("when resource map already has an entry for the claim/request", func() {
It("should append device IDs to existing entry", func() {
claimName := "test-claim"
deviceName := "device-1"
driverName := "test-driver.example.com"
poolName := "test-pool"
requestName := "gpu"
deviceID := "pci:0000:00:01.0"
existingDeviceID := "pci:0000:00:00.0"
mapKey := "append-test.example.com/res"
// Create ResourceSlice with device
deviceIDValue := deviceID
mapKeyValue := mapKey
resourceSlice := &resourcev1api.ResourceSlice{
ObjectMeta: metav1.ObjectMeta{
Name: "test-resource-slice",
},
Spec: resourcev1api.ResourceSliceSpec{
Driver: driverName,
Pool: resourcev1api.ResourcePool{
Name: poolName,
ResourceSliceCount: 1,
},
Devices: []resourcev1api.Device{
{
Name: deviceName,
Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
multusDeviceIDAttr: {
StringValue: &deviceIDValue,
},
multusResourceNameAttr: {StringValue: &mapKeyValue},
},
},
},
},
}
// Create ResourceClaim
resourceClaim := &resourcev1api.ResourceClaim{
ObjectMeta: metav1.ObjectMeta{
Name: claimName,
Namespace: "default",
},
Status: resourcev1api.ResourceClaimStatus{
Allocation: &resourcev1api.AllocationResult{
Devices: resourcev1api.DeviceAllocationResult{
Results: []resourcev1api.DeviceRequestAllocationResult{
{
Request: requestName,
Driver: driverName,
Pool: poolName,
Device: deviceName,
},
},
},
},
},
}
claimNamePtr := claimName
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: "default",
UID: k8sTypes.UID("test-uid"),
},
Status: v1.PodStatus{
ResourceClaimStatuses: []v1.PodResourceClaimStatus{
{
Name: claimName,
ResourceClaimName: &claimNamePtr,
},
},
},
}
// Add objects
_, err := fakeClient.ResourceV1().ResourceClaims("default").Create(context.TODO(), resourceClaim, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
_, err = fakeClient.ResourceV1().ResourceSlices().Create(context.TODO(), resourceSlice, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
// Pre-populate resourceMap with existing entry
resourceMap := make(map[string]*types.ResourceInfo)
expectedKey := mapKey
resourceMap[expectedKey] = &types.ResourceInfo{
DeviceIDs: []string{existingDeviceID},
}
// Execute
err = draClient.GetPodResourceMap(context.TODO(), pod, resourceMap)
Expect(err).NotTo(HaveOccurred())
// Verify device ID was appended
Expect(resourceMap).To(HaveKey(expectedKey))
Expect(resourceMap[expectedKey].DeviceIDs).To(Equal([]string{existingDeviceID, deviceID}))
})
})
Context("when multiple resource slices exist for the same driver/pool", func() {
It("should find the allocated device by searching all slices", func() {
claimName := "test-claim"
deviceName := "device-1"
driverName := "test-driver.example.com"
poolName := "test-pool"
requestName := "gpu"
deviceID := "pci:0000:00:01.0"
mapKey := "multi-slice.example.com/res"
// DRA allows multiple ResourceSlices per driver/pool (e.g. per NUMA). Allocation references device-1
// which lives only in the first slice; second slice holds another device.
deviceIDValue := deviceID
mapKeyValue := mapKey
resourceSlice1 := &resourcev1api.ResourceSlice{
ObjectMeta: metav1.ObjectMeta{
Name: "test-resource-slice-1",
},
Spec: resourcev1api.ResourceSliceSpec{
Driver: driverName,
Pool: resourcev1api.ResourcePool{
Name: poolName,
ResourceSliceCount: 1,
},
Devices: []resourcev1api.Device{
{
Name: deviceName,
Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
multusDeviceIDAttr: {
StringValue: &deviceIDValue,
},
multusResourceNameAttr: {StringValue: &mapKeyValue},
},
},
},
},
}
resourceSlice2 := &resourcev1api.ResourceSlice{
ObjectMeta: metav1.ObjectMeta{
Name: "test-resource-slice-2",
},
Spec: resourcev1api.ResourceSliceSpec{
Driver: driverName,
Pool: resourcev1api.ResourcePool{
Name: poolName,
ResourceSliceCount: 1,
},
Devices: []resourcev1api.Device{
{
Name: "device-2",
Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
multusDeviceIDAttr: {
StringValue: &deviceIDValue,
},
multusResourceNameAttr: {StringValue: &mapKeyValue},
},
},
},
},
}
// Create ResourceClaim
resourceClaim := &resourcev1api.ResourceClaim{
ObjectMeta: metav1.ObjectMeta{
Name: claimName,
Namespace: "default",
},
Status: resourcev1api.ResourceClaimStatus{
Allocation: &resourcev1api.AllocationResult{
Devices: resourcev1api.DeviceAllocationResult{
Results: []resourcev1api.DeviceRequestAllocationResult{
{
Request: requestName,
Driver: driverName,
Pool: poolName,
Device: deviceName,
},
},
},
},
},
}
claimNamePtr := claimName
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: "default",
UID: k8sTypes.UID("test-uid"),
},
Status: v1.PodStatus{
ResourceClaimStatuses: []v1.PodResourceClaimStatus{
{
Name: claimName,
ResourceClaimName: &claimNamePtr,
},
},
},
}
_, err := fakeClient.ResourceV1().ResourceClaims("default").Create(context.TODO(), resourceClaim, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
var objects []runtime.Object
objects = append(objects, resourceSlice1, resourceSlice2)
fakeClient2 := fake.NewClientset(objects...)
draClient2 := NewClient(fakeClient2.ResourceV1())
_, err = fakeClient2.ResourceV1().ResourceClaims("default").Create(context.TODO(), resourceClaim, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
resourceMap := make(map[string]*types.ResourceInfo)
err = draClient2.GetPodResourceMap(context.TODO(), pod, resourceMap)
Expect(err).NotTo(HaveOccurred())
Expect(resourceMap).To(HaveKey(mapKey))
Expect(resourceMap[mapKey].DeviceIDs).To(Equal([]string{deviceID}))
})
})
Context("when pod has both regular ResourceClaimStatuses and ExtendedResourceClaimStatus", func() {
It("should populate resource map from both paths independently", func() {
driverName := "sriovnetwork.k8snetworkplumbingwg.io"
poolName := "test-pool"
// Regular claim: one VF mapped via ResourceClaimStatuses
regularClaimName := "regular-claim"
regularDeviceName := "vf-0"
regularDeviceID := "pci:0000:01:00.0"
regularMapKey := "sriov.example.com/vf-net"
// Extended claim: two ports mapped via ExtendedResourceClaimStatus
extClaimName := "extended-claim"
extDevice1Name := "vf-1"
extDevice2Name := "vf-2"
extDeviceID1 := "pci:0000:02:00.0"
extDeviceID2 := "pci:0000:03:00.0"
extMapKey1 := "sriov.example.com/port-a"
extMapKey2 := "sriov.example.com/port-b"
rdID, rmk := regularDeviceID, regularMapKey
ed1, ed2 := extDeviceID1, extDeviceID2
emk1, emk2 := extMapKey1, extMapKey2
// One ResourceSlice covers all devices for the driver
resourceSlice := &resourcev1api.ResourceSlice{
ObjectMeta: metav1.ObjectMeta{Name: "sriov-slice"},
Spec: resourcev1api.ResourceSliceSpec{
Driver: driverName,
Pool: resourcev1api.ResourcePool{Name: poolName, ResourceSliceCount: 1},
Devices: []resourcev1api.Device{
{Name: regularDeviceName, Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
multusDeviceIDAttr: {StringValue: &rdID}, multusResourceNameAttr: {StringValue: &rmk},
}},
{Name: extDevice1Name, Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
multusDeviceIDAttr: {StringValue: &ed1}, multusResourceNameAttr: {StringValue: &emk1},
}},
{Name: extDevice2Name, Attributes: map[resourcev1api.QualifiedName]resourcev1api.DeviceAttribute{
multusDeviceIDAttr: {StringValue: &ed2}, multusResourceNameAttr: {StringValue: &emk2},
}},
},
},
}
regularClaim := &resourcev1api.ResourceClaim{
ObjectMeta: metav1.ObjectMeta{Name: regularClaimName, Namespace: "default"},
Status: resourcev1api.ResourceClaimStatus{Allocation: &resourcev1api.AllocationResult{
Devices: resourcev1api.DeviceAllocationResult{Results: []resourcev1api.DeviceRequestAllocationResult{
{Request: "vf", Driver: driverName, Pool: poolName, Device: regularDeviceName},
}},
}},
}
extClaim := &resourcev1api.ResourceClaim{
ObjectMeta: metav1.ObjectMeta{Name: extClaimName, Namespace: "default"},
Status: resourcev1api.ResourceClaimStatus{Allocation: &resourcev1api.AllocationResult{
Devices: resourcev1api.DeviceAllocationResult{Results: []resourcev1api.DeviceRequestAllocationResult{
{Request: "req-port-a", Driver: driverName, Pool: poolName, Device: extDevice1Name},
{Request: "req-port-b", Driver: driverName, Pool: poolName, Device: extDevice2Name},
}},
}},
}
regularClaimPtr := regularClaimName
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "combo-pod", Namespace: "default", UID: k8sTypes.UID("uid-combo")},
Status: v1.PodStatus{
ResourceClaimStatuses: []v1.PodResourceClaimStatus{
{Name: regularClaimName, ResourceClaimName: &regularClaimPtr},
},
ExtendedResourceClaimStatus: &v1.PodExtendedResourceClaimStatus{
ResourceClaimName: extClaimName,
RequestMappings: []v1.ContainerExtendedResourceRequest{
{ContainerName: "c", ResourceName: extMapKey1, RequestName: "req-port-a"},
{ContainerName: "c", ResourceName: extMapKey2, RequestName: "req-port-b"},
},
},
},
}
_, err := fakeClient.ResourceV1().ResourceSlices().Create(context.TODO(), resourceSlice, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
_, err = fakeClient.ResourceV1().ResourceClaims("default").Create(context.TODO(), regularClaim, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
_, err = fakeClient.ResourceV1().ResourceClaims("default").Create(context.TODO(), extClaim, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())
resourceMap := make(map[string]*types.ResourceInfo)
err = draClient.GetPodResourceMap(context.TODO(), pod, resourceMap)
Expect(err).NotTo(HaveOccurred())
// Regular claim path
Expect(resourceMap).To(HaveKey(regularMapKey))
Expect(resourceMap[regularMapKey].DeviceIDs).To(Equal([]string{regularDeviceID}))
// Extended resource claim path
Expect(resourceMap).To(HaveKey(extMapKey1))
Expect(resourceMap[extMapKey1].DeviceIDs).To(Equal([]string{extDeviceID1}))
Expect(resourceMap).To(HaveKey(extMapKey2))
Expect(resourceMap[extMapKey2].DeviceIDs).To(Equal([]string{extDeviceID2}))
Expect(resourceMap).To(HaveLen(3))
})
})
})
})