From 79f8d1b1c5d5e35fbebfac34b15c4c817e8a5728 Mon Sep 17 00:00:00 2001 From: yliao Date: Sun, 3 Aug 2025 01:46:20 +0000 Subject: [PATCH] fixed bug such that implicit extended resource name can always be used, no matter the explicit extendedResourceName field in device class is set or not. --- pkg/kubelet/cm/dra/manager.go | 4 +- .../dynamicresources/dynamicresources.go | 5 +- .../dynamicresources/dynamicresources_test.go | 232 ++++++++++++++++-- .../plugins/dynamicresources/extended/util.go | 7 +- .../dynamicresources/extended/util_test.go | 9 + .../framework/plugins/noderesources/fit.go | 2 +- .../plugins/noderesources/fit_test.go | 60 +++++ pkg/scheduler/testing/wrappers.go | 15 ++ pkg/scheduler/util/utils.go | 8 + test/e2e/dra/dra.go | 63 +++-- 10 files changed, 353 insertions(+), 52 deletions(-) diff --git a/pkg/kubelet/cm/dra/manager.go b/pkg/kubelet/cm/dra/manager.go index 70457c42c4a..99360cdadcb 100644 --- a/pkg/kubelet/cm/dra/manager.go +++ b/pkg/kubelet/cm/dra/manager.go @@ -39,7 +39,6 @@ import ( drahealthv1alpha1 "k8s.io/kubelet/pkg/apis/dra-health/v1alpha1" drapb "k8s.io/kubelet/pkg/apis/dra/v1" - v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper" kubefeatures "k8s.io/kubernetes/pkg/features" draplugin "k8s.io/kubernetes/pkg/kubelet/cm/dra/plugin" "k8s.io/kubernetes/pkg/kubelet/cm/dra/state" @@ -48,6 +47,7 @@ import ( kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" kubeletmetrics "k8s.io/kubernetes/pkg/kubelet/metrics" "k8s.io/kubernetes/pkg/kubelet/pluginmanager/cache" + schedutil "k8s.io/kubernetes/pkg/scheduler/util" ) // draManagerStateFileName is the file name where dra manager stores its state @@ -526,7 +526,7 @@ func (m *Manager) GetResources(pod *v1.Pod, container *v1.Container) (*Container // We only care about the resources requested by the pod continue } - if v1helper.IsExtendedResourceName(rName) { + if schedutil.IsDRAExtendedResourceName(rName) { requestName := "" for _, rm := range pod.Status.ExtendedResourceClaimStatus.RequestMappings { if rm.ContainerName == container.Name && rm.ResourceName == rName.String() { diff --git a/pkg/scheduler/framework/plugins/dynamicresources/dynamicresources.go b/pkg/scheduler/framework/plugins/dynamicresources/dynamicresources.go index cec0e1b6848..0473d19e7cf 100644 --- a/pkg/scheduler/framework/plugins/dynamicresources/dynamicresources.go +++ b/pkg/scheduler/framework/plugins/dynamicresources/dynamicresources.go @@ -47,7 +47,6 @@ import ( "k8s.io/dynamic-resource-allocation/structured" "k8s.io/klog/v2" fwk "k8s.io/kube-scheduler/framework" - v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper" "k8s.io/kubernetes/pkg/scheduler/apis/config" "k8s.io/kubernetes/pkg/scheduler/apis/config/validation" "k8s.io/kubernetes/pkg/scheduler/framework" @@ -435,7 +434,7 @@ func hasDeviceClassMappedExtendedResource(reqs v1.ResourceList, deviceClassMappi // We only care about the resources requested by the pod we are trying to schedule. continue } - if v1helper.IsExtendedResourceName(rName) { + if schedutil.IsDRAExtendedResourceName(rName) { _, ok := deviceClassMapping[rName] if ok { return true @@ -766,7 +765,7 @@ func (pl *DynamicResources) filterExtendedResources(state *stateData, pod *v1.Po extendedResources := make(map[v1.ResourceName]int64) hasExtendedResource := false for rName, rQuant := range state.draExtendedResource.podScalarResources { - if !v1helper.IsExtendedResourceName(rName) { + if !schedutil.IsDRAExtendedResourceName(rName) { continue } // Skip in case request quantity is zero diff --git a/pkg/scheduler/framework/plugins/dynamicresources/dynamicresources_test.go b/pkg/scheduler/framework/plugins/dynamicresources/dynamicresources_test.go index 42d54610d30..82ccc5088e7 100644 --- a/pkg/scheduler/framework/plugins/dynamicresources/dynamicresources_test.go +++ b/pkg/scheduler/framework/plugins/dynamicresources/dynamicresources_test.go @@ -60,21 +60,22 @@ import ( var ( podKind = v1.SchemeGroupVersion.WithKind("Pod") - nodeName = "worker" - node2Name = "worker-2" - node3Name = "worker-3" - driver = "some-driver" - driver2 = "some-driver-2" - podName = "my-pod" - podUID = "1234" - resourceName = "my-resource" - resourceName2 = resourceName + "-2" - claimName = podName + "-" + resourceName - claimName2 = podName + "-" + resourceName2 - className = "my-resource-class" - namespace = "default" - attrName = resourceapi.QualifiedName("healthy") // device attribute only available on non-default node - extendedResourceName = "example.com/gpu" + nodeName = "worker" + node2Name = "worker-2" + node3Name = "worker-3" + driver = "some-driver" + driver2 = "some-driver-2" + podName = "my-pod" + podUID = "1234" + resourceName = "my-resource" + resourceName2 = resourceName + "-2" + claimName = podName + "-" + resourceName + claimName2 = podName + "-" + resourceName2 + className = "my-resource-class" + namespace = "default" + attrName = resourceapi.QualifiedName("healthy") // device attribute only available on non-default node + extendedResourceName = "example.com/gpu" + implicitExtendedResourceName = "deviceclass.resource.kubernetes.io/my-resource-class" deviceClass = &resourceapi.DeviceClass{ ObjectMeta: metav1.ObjectMeta{ @@ -124,6 +125,22 @@ var ( v1.ResourceName(extendedResourceName): "1", }). Obj() + podWithImplicitExtendedResourceName = st.MakePod().Name(podName).Namespace(namespace). + UID(podUID). + Req(map[v1.ResourceName]string{ + v1.ResourceName(implicitExtendedResourceName): "1", + v1.ResourceName(extendedResourceName): "2", + }). + Obj() + podWithImplicitExtendedResourceNameTwoContainers = st.MakePod().Name(podName).Namespace(namespace). + UID(podUID). + Req(map[v1.ResourceName]string{ + v1.ResourceName(implicitExtendedResourceName): "1", + }). + Req(map[v1.ResourceName]string{ + v1.ResourceName(extendedResourceName): "2", + }). + Obj() // Node with "instance-1" device and no device attributes. workerNode = &st.MakeNode().Name(nodeName).Label("kubernetes.io/hostname", nodeName).Node @@ -218,6 +235,60 @@ var ( return st.MakeNodeSelector().In("metadata.name", []string{nodeName}, st.NodeSelectorTypeMatchFields).Obj() }(), } + implicitExtendedResourceAllocationResult = &resourceapi.AllocationResult{ + Devices: resourceapi.DeviceAllocationResult{ + Results: []resourceapi.DeviceRequestAllocationResult{ + { + Driver: driver, + Pool: nodeName, + Device: "instance-1", + Request: "container-0-request-0", + }, + { + Driver: driver, + Pool: nodeName, + Device: "instance-2", + Request: "container-0-request-1", + }, + { + Driver: driver, + Pool: nodeName, + Device: "instance-3", + Request: "container-0-request-1", + }, + }, + }, + NodeSelector: func() *v1.NodeSelector { + return st.MakeNodeSelector().In("metadata.name", []string{nodeName}, st.NodeSelectorTypeMatchFields).Obj() + }(), + } + implicitExtendedResourceAllocationResultTwoContainers = &resourceapi.AllocationResult{ + Devices: resourceapi.DeviceAllocationResult{ + Results: []resourceapi.DeviceRequestAllocationResult{ + { + Driver: driver, + Pool: nodeName, + Device: "instance-1", + Request: "container-0-request-0", + }, + { + Driver: driver, + Pool: nodeName, + Device: "instance-2", + Request: "container-1-request-0", + }, + { + Driver: driver, + Pool: nodeName, + Device: "instance-3", + Request: "container-1-request-0", + }, + }, + }, + NodeSelector: func() *v1.NodeSelector { + return st.MakeNodeSelector().In("metadata.name", []string{nodeName}, st.NodeSelectorTypeMatchFields).Obj() + }(), + } extendedResourceAllocationResultNode2 = &resourceapi.AllocationResult{ Devices: resourceapi.DeviceAllocationResult{ Results: []resourceapi.DeviceRequestAllocationResult{{ @@ -311,7 +382,78 @@ var ( RequestWithName("container-0-request-0", className). Allocation(extendedResourceAllocationResult). Obj() - + implicitExtendedResourceClaim = st.MakeResourceClaim(). + Name("my-pod-extended-resources-0"). + GenerateName("my-pod-extended-resources-"). + Namespace(namespace). + Annotations(map[string]string{"resource.kubernetes.io/extended-resource-claim": "true"}). + OwnerRef( + metav1.OwnerReference{ + APIVersion: "v1", + Kind: "Pod", + Name: podName, + UID: types.UID(podUID), + Controller: ptr.To(true), + BlockOwnerDeletion: ptr.To(true), + }). + RequestWithName("container-0-request-0", className). + RequestWithNameCount("container-0-request-1", className, 2). + Allocation(implicitExtendedResourceAllocationResult). + Obj() + implicitExtendedResourceClaimNoName = st.MakeResourceClaim(). + Name(specialClaimInMemName). + GenerateName("my-pod-extended-resources-"). + Namespace(namespace). + Annotations(map[string]string{"resource.kubernetes.io/extended-resource-claim": "true"}). + OwnerRef( + metav1.OwnerReference{ + APIVersion: "v1", + Kind: "Pod", + Name: podName, + UID: types.UID(podUID), + Controller: ptr.To(true), + BlockOwnerDeletion: ptr.To(true), + }). + RequestWithName("container-0-request-0", className). + RequestWithNameCount("container-0-request-1", className, 2). + Allocation(implicitExtendedResourceAllocationResult). + Obj() + implicitExtendedResourceClaimTwoContainers = st.MakeResourceClaim(). + Name("my-pod-extended-resources-0"). + GenerateName("my-pod-extended-resources-"). + Namespace(namespace). + Annotations(map[string]string{"resource.kubernetes.io/extended-resource-claim": "true"}). + OwnerRef( + metav1.OwnerReference{ + APIVersion: "v1", + Kind: "Pod", + Name: podName, + UID: types.UID(podUID), + Controller: ptr.To(true), + BlockOwnerDeletion: ptr.To(true), + }). + RequestWithName("container-0-request-0", className). + RequestWithNameCount("container-1-request-0", className, 2). + Allocation(implicitExtendedResourceAllocationResultTwoContainers). + Obj() + implicitExtendedResourceClaimNoNameTwoContainers = st.MakeResourceClaim(). + Name(specialClaimInMemName). + GenerateName("my-pod-extended-resources-"). + Namespace(namespace). + Annotations(map[string]string{"resource.kubernetes.io/extended-resource-claim": "true"}). + OwnerRef( + metav1.OwnerReference{ + APIVersion: "v1", + Kind: "Pod", + Name: podName, + UID: types.UID(podUID), + Controller: ptr.To(true), + BlockOwnerDeletion: ptr.To(true), + }). + RequestWithName("container-0-request-0", className). + RequestWithNameCount("container-1-request-0", className, 2). + Allocation(implicitExtendedResourceAllocationResultTwoContainers). + Obj() extendedResourceClaimNode2 = st.MakeResourceClaim(). Name("my-pod-extended-resources-0"). GenerateName("my-pod-extended-resources-"). @@ -1311,6 +1453,42 @@ func TestPlugin(t *testing.T) { }, }, }, + "implicit-extended-resource-name-with-resources": { + enableDRAExtendedResource: true, + pod: podWithImplicitExtendedResourceName, + classes: []*resourceapi.DeviceClass{deviceClassWithExtendResourceName}, + objs: []apiruntime.Object{largeWorkerNodeSlice, podWithImplicitExtendedResourceName}, + want: want{ + reserve: result{ + inFlightClaim: implicitExtendedResourceClaimNoName, + }, + prebind: result{ + assumedClaim: reserve(implicitExtendedResourceClaim, podWithImplicitExtendedResourceName), + added: []metav1.Object{reserve(implicitExtendedResourceClaim, podWithImplicitExtendedResourceName)}, + }, + postbind: result{ + assumedClaim: reserve(implicitExtendedResourceClaim, podWithImplicitExtendedResourceName), + }, + }, + }, + "implicit-extended-resource-name-two-containers-with-resources": { + enableDRAExtendedResource: true, + pod: podWithImplicitExtendedResourceNameTwoContainers, + classes: []*resourceapi.DeviceClass{deviceClassWithExtendResourceName}, + objs: []apiruntime.Object{largeWorkerNodeSlice, podWithImplicitExtendedResourceNameTwoContainers}, + want: want{ + reserve: result{ + inFlightClaim: implicitExtendedResourceClaimNoNameTwoContainers, + }, + prebind: result{ + assumedClaim: reserve(implicitExtendedResourceClaimTwoContainers, podWithImplicitExtendedResourceNameTwoContainers), + added: []metav1.Object{reserve(implicitExtendedResourceClaimTwoContainers, podWithImplicitExtendedResourceNameTwoContainers)}, + }, + postbind: result{ + assumedClaim: reserve(implicitExtendedResourceClaimTwoContainers, podWithImplicitExtendedResourceNameTwoContainers), + }, + }, + }, "extended-resource-name-with-resources-fail-patch": { enableDRAExtendedResource: true, failPatch: true, @@ -1938,6 +2116,8 @@ func (tc *testContext) verify(t *testing.T, expected result, initialObjects []me ignoreFieldsInResourceClaims := []cmp.Option{ cmpopts.IgnoreFields(metav1.ObjectMeta{}, "UID", "ResourceVersion"), cmpopts.IgnoreFields(resourceapi.AllocationResult{}, "AllocationTimestamp"), + // It does not matter which specific device is allocated for the testing purpose. + cmpopts.IgnoreFields(resourceapi.DeviceRequestAllocationResult{}, "Device"), } if diff := cmp.Diff(wantObjects, objects, ignoreFieldsInResourceClaims...); diff != "" { t.Errorf("Stored objects are different (- expected, + actual):\n%s", diff) @@ -1995,12 +2175,27 @@ func (tc *testContext) verify(t *testing.T, expected result, initialObjects []me } } +// sortClaim sorts given claim's Requests in spec, and Results in status +func sortClaim(claim *resourceapi.ResourceClaim) { + requests := claim.Spec.Devices.Requests + sort.Slice(requests, func(i, j int) bool { + return requests[i].Name < requests[j].Name + }) + if claim.Status.Allocation != nil { + results := claim.Status.Allocation.Devices.Results + sort.Slice(results, func(i, j int) bool { + return results[i].Request < results[j].Request + }) + } +} + func (tc *testContext) listAll(t *testing.T) (objects []metav1.Object) { t.Helper() claims, err := tc.client.ResourceV1().ResourceClaims("").List(tc.ctx, metav1.ListOptions{}) require.NoError(t, err, "list claims") for _, claim := range claims.Items { claim := claim + sortClaim(&claim) objects = append(objects, &claim) } sortObjects(objects) @@ -2012,6 +2207,7 @@ func (tc *testContext) listAssumedClaims() ([]metav1.Object, []metav1.Object) { var sameClaims []metav1.Object for _, obj := range tc.draManager.resourceClaimTracker.cache.List(nil) { claim := obj.(*resourceapi.ResourceClaim) + sortClaim(claim) obj, _ := tc.draManager.resourceClaimTracker.cache.Get(claim.Namespace + "/" + claim.Name) apiObj, _ := tc.draManager.resourceClaimTracker.cache.GetAPIObj(claim.Namespace + "/" + claim.Name) if obj != apiObj { @@ -2028,7 +2224,9 @@ func (tc *testContext) listAssumedClaims() ([]metav1.Object, []metav1.Object) { func (tc *testContext) listInFlightClaims() []metav1.Object { var inFlightClaims []metav1.Object tc.draManager.resourceClaimTracker.inFlightAllocations.Range(func(key, value any) bool { - inFlightClaims = append(inFlightClaims, value.(*resourceapi.ResourceClaim)) + claim := value.(*resourceapi.ResourceClaim) + sortClaim(claim) + inFlightClaims = append(inFlightClaims, claim) return true }) sortObjects(inFlightClaims) diff --git a/pkg/scheduler/framework/plugins/dynamicresources/extended/util.go b/pkg/scheduler/framework/plugins/dynamicresources/extended/util.go index b9fe05cefb0..9921626602d 100644 --- a/pkg/scheduler/framework/plugins/dynamicresources/extended/util.go +++ b/pkg/scheduler/framework/plugins/dynamicresources/extended/util.go @@ -18,7 +18,7 @@ package extended import ( v1 "k8s.io/api/core/v1" - "k8s.io/api/resource/v1beta1" + resourceapi "k8s.io/api/resource/v1" fwk "k8s.io/kube-scheduler/framework" ) @@ -29,11 +29,10 @@ func DeviceClassMapping(draManager fwk.SharedDRAManager) (map[v1.ResourceName]st return nil, err } for _, c := range classes { - if c.Spec.ExtendedResourceName == nil { - extendedResources[v1.ResourceName(v1beta1.ResourceDeviceClassPrefix+c.Name)] = c.Name - } else { + if c.Spec.ExtendedResourceName != nil { extendedResources[v1.ResourceName(*c.Spec.ExtendedResourceName)] = c.Name } + extendedResources[v1.ResourceName(resourceapi.ResourceDeviceClassPrefix+c.Name)] = c.Name } return extendedResources, nil } diff --git a/pkg/scheduler/framework/plugins/dynamicresources/extended/util_test.go b/pkg/scheduler/framework/plugins/dynamicresources/extended/util_test.go index d4d789b0845..e10a99f61c7 100644 --- a/pkg/scheduler/framework/plugins/dynamicresources/extended/util_test.go +++ b/pkg/scheduler/framework/plugins/dynamicresources/extended/util_test.go @@ -122,6 +122,15 @@ func TestDeviceClassMapping(t *testing.T) { if c != "class2" { t.Errorf("result does not match device class name %s", "class2") } + for _, c := range []string{"class1", "class2", "class3"} { + n, ok := rm[v1.ResourceName("deviceclass.resource.kubernetes.io/"+c)] + if !ok { + t.Errorf("result does not contain implicit extended resource for device class %s", c) + } + if n != c { + t.Errorf("result %s does not match device class name %s", n, c) + } + } } func TestNoDeviceClassMapping(t *testing.T) { diff --git a/pkg/scheduler/framework/plugins/noderesources/fit.go b/pkg/scheduler/framework/plugins/noderesources/fit.go index ab46f3a2175..2abaac92232 100644 --- a/pkg/scheduler/framework/plugins/noderesources/fit.go +++ b/pkg/scheduler/framework/plugins/noderesources/fit.go @@ -241,7 +241,7 @@ func withDeviceClass(result *preFilterState, draManager fwk.SharedDRAManager) *f continue } - if v1helper.IsExtendedResourceName(rName) { + if schedutil.IsDRAExtendedResourceName(rName) { hasExtendedResource = true break } diff --git a/pkg/scheduler/framework/plugins/noderesources/fit_test.go b/pkg/scheduler/framework/plugins/noderesources/fit_test.go index a7f5c28bba4..9c819f52fb2 100644 --- a/pkg/scheduler/framework/plugins/noderesources/fit_test.go +++ b/pkg/scheduler/framework/plugins/noderesources/fit_test.go @@ -1908,3 +1908,63 @@ func TestHaveAnyRequestedResourcesIncreased(t *testing.T) { }) } } +func TestWithDeviceClass(t *testing.T) { + logger, ctx := ktesting.NewTestContext(t) + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + client := fake.NewClientset(deviceClassWithExtendResourceName) + informerFactory := informers.NewSharedInformerFactory(client, 0) + draManager := dynamicresources.NewDRAManager(ctx, assumecache.NewAssumeCache(logger, informerFactory.Resource().V1().ResourceClaims().Informer(), "resource claim", "", nil), nil, informerFactory) + informerFactory.Start(ctx.Done()) + t.Cleanup(func() { + // Now we can wait for all goroutines to stop. + informerFactory.Shutdown() + }) + informerFactory.WaitForCacheSync(ctx.Done()) + + testCases := map[string]struct { + state *preFilterState + expectedResourceToDeviceClass map[v1.ResourceName]string + }{ + + "regular extended resource name": { + state: &preFilterState{ + Resource: framework.Resource{ + ScalarResources: map[v1.ResourceName]int64{extendedResourceA: 1}, + }, + }, + expectedResourceToDeviceClass: map[v1.ResourceName]string{ + v1.ResourceName("deviceclass.resource.kubernetes.io/device-class-name"): deviceClassName, + v1.ResourceName("extended.resource.dra.io/something"): deviceClassName, + }, + }, + "implicit extended resource name": { + state: &preFilterState{ + Resource: framework.Resource{ + ScalarResources: map[v1.ResourceName]int64{v1.ResourceName("deviceclass.resource.kubernetes.io/" + deviceClassName): 1}, + }, + }, + expectedResourceToDeviceClass: map[v1.ResourceName]string{ + v1.ResourceName("deviceclass.resource.kubernetes.io/device-class-name"): deviceClassName, + v1.ResourceName("extended.resource.dra.io/something"): deviceClassName, + }, + }, + "no extended resource name": { + state: &preFilterState{ + Resource: framework.Resource{ + MilliCPU: 1000, + }, + }, + expectedResourceToDeviceClass: nil, + }, + } + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + withDeviceClass(tc.state, draManager) + if diff := cmp.Diff(tc.state.resourceToDeviceClass, tc.expectedResourceToDeviceClass); diff != "" { + t.Error("resourceToDeviceClass doesn't match (-expected +actual):\n", diff) + } + }) + } +} diff --git a/pkg/scheduler/testing/wrappers.go b/pkg/scheduler/testing/wrappers.go index 09c7b4f3ac3..5a67d6a507a 100644 --- a/pkg/scheduler/testing/wrappers.go +++ b/pkg/scheduler/testing/wrappers.go @@ -1173,6 +1173,21 @@ func (wrapper *ResourceClaimWrapper) RequestWithName(name, deviceClassName strin return wrapper } +// RequestWithNameCount adds one device request for the given device class with given request name and count. +func (wrapper *ResourceClaimWrapper) RequestWithNameCount(name, deviceClassName string, count int64) *ResourceClaimWrapper { + wrapper.Spec.Devices.Requests = append(wrapper.Spec.Devices.Requests, + resourceapi.DeviceRequest{ + Name: name, + Exactly: &resourceapi.ExactDeviceRequest{ + // Cannot rely on defaulting here, this is used in unit tests. + AllocationMode: resourceapi.DeviceAllocationModeExactCount, + Count: count, + DeviceClassName: deviceClassName, + }, + }) + return wrapper +} + // RequestWithPrioritizedList adds one device request with one subrequest // per provided deviceClassName. func (wrapper *ResourceClaimWrapper) RequestWithPrioritizedList(deviceClassNames ...string) *ResourceClaimWrapper { diff --git a/pkg/scheduler/util/utils.go b/pkg/scheduler/util/utils.go index acaaaabb4d7..06ba395030d 100644 --- a/pkg/scheduler/util/utils.go +++ b/pkg/scheduler/util/utils.go @@ -20,9 +20,11 @@ import ( "context" "encoding/json" "fmt" + "strings" "time" v1 "k8s.io/api/core/v1" + resourceapi "k8s.io/api/resource/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -142,6 +144,12 @@ func IsScalarResourceName(name v1.ResourceName) bool { v1helper.IsPrefixedNativeResource(name) || v1helper.IsAttachableVolumeResourceName(name) } +// IsDRAExtendedResourceName returns true when name is an extended resource name, or an implicit extended resource name +// derived from device class name with the format of deviceclass.resource.kubernetes.io/ +func IsDRAExtendedResourceName(name v1.ResourceName) bool { + return v1helper.IsExtendedResourceName(name) || strings.HasPrefix(string(name), resourceapi.ResourceDeviceClassPrefix) +} + // As converts two objects to the given type. // Both objects must be of the same type. If not, an error is returned. // nil objects are allowed and will be converted to nil. diff --git a/test/e2e/dra/dra.go b/test/e2e/dra/dra.go index b2bf23dc4e2..6d780d7f45f 100644 --- a/test/e2e/dra/dra.go +++ b/test/e2e/dra/dra.go @@ -1909,51 +1909,64 @@ var _ = framework.SIGDescribe("node")(framework.WithLabel("DRA"), func() { }) }) + extendedResourceTest := func(ctx context.Context, b *drautils.Builder, f *framework.Framework, resourceNames []string, containerEnv []string) { + pod := b.Pod() + res := v1.ResourceList{} + for _, resourceName := range resourceNames { + res[v1.ResourceName(resourceName)] = resource.MustParse("1") + } + pod.Spec.Containers[0].Resources.Requests = res + pod.Spec.Containers[0].Resources.Limits = res + + b.Create(ctx, pod) + err := e2epod.WaitForPodRunningInNamespace(ctx, f.ClientSet, pod) + framework.ExpectNoError(err, "start pod") + drautils.TestContainerEnv(ctx, f, pod, pod.Spec.Containers[0].Name, false, containerEnv...) + } + framework.Context(f.WithFeatureGate(features.DRAExtendedResource), func() { nodes := drautils.NewNodes(f, 1, 1) driver := drautils.NewDriver(f, nodes, drautils.NetworkResources(10, false)) b := drautils.NewBuilder(f, driver) b.UseExtendedResourceName = true - ginkgo.It("must run a pod with extended resource with one container one resource", func(ctx context.Context) { - pod := b.Pod() - res := v1.ResourceList{} - res[v1.ResourceName(b.ExtendedResourceName(0))] = resource.MustParse("1") - pod.Spec.Containers[0].Resources.Requests = res - pod.Spec.Containers[0].Resources.Limits = res - - b.Create(ctx, pod) - err := e2epod.WaitForPodRunningInNamespace(ctx, f.ClientSet, pod) - framework.ExpectNoError(err, "start pod") - containerEnv := []string{ + ginkgo.It("must run a pod with both implicit and explicit extended resource with one container two resources", func(ctx context.Context) { + extendedResourceTest(ctx, b, f, []string{ + // implicit extended resource name + "deviceclass.resource.kubernetes.io/" + b.ClassName(), + // b.ExtendedResourceName(0) is added to the deivce class with name: b.ClassName()+"0" + b.ExtendedResourceName(0), + }, []string{ "container_0_request_0", "true", - } - drautils.TestContainerEnv(ctx, f, pod, pod.Spec.Containers[0].Name, false, containerEnv...) + "container_0_request_1", "true", + }) + }) + + ginkgo.It("must run a pod with extended resource with one container one resource", func(ctx context.Context) { + extendedResourceTest(ctx, b, f, []string{ + b.ExtendedResourceName(0), + }, []string{ + "container_0_request_0", "true", + }) }) ginkgo.It("must run a pod with extended resource with one container three resources", func(ctx context.Context) { var objects []klog.KMetadata - pod := b.Pod() - res := v1.ResourceList{} for i := range 3 { - res[v1.ResourceName(b.ExtendedResourceName(i))] = resource.MustParse("1") if i > 0 { objects = append(objects, b.Class(i)) } } - pod.Spec.Containers[0].Resources.Requests = res - pod.Spec.Containers[0].Resources.Limits = res - objects = append(objects, pod) - b.Create(ctx, objects...) - err := e2epod.WaitForPodRunningInNamespace(ctx, f.ClientSet, pod) - framework.ExpectNoError(err, "start pod") - containerEnv := []string{ + extendedResourceTest(ctx, b, f, []string{ + b.ExtendedResourceName(0), + b.ExtendedResourceName(1), + b.ExtendedResourceName(2), + }, []string{ "container_0_request_0", "true", "container_0_request_1", "true", "container_0_request_2", "true", - } - drautils.TestContainerEnv(ctx, f, pod, pod.Spec.Containers[0].Name, false, containerEnv...) + }) }) ginkgo.It("must run a pod with extended resource with three containers one resource each", func(ctx context.Context) { var objects []klog.KMetadata