diff --git a/pkg/kubelet/lifecycle/predicate_test.go b/pkg/kubelet/lifecycle/predicate_test.go index 477b1debb2f..0337d9b7806 100644 --- a/pkg/kubelet/lifecycle/predicate_test.go +++ b/pkg/kubelet/lifecycle/predicate_test.go @@ -29,6 +29,7 @@ import ( utilfeature "k8s.io/apiserver/pkg/util/feature" featuregatetesting "k8s.io/component-base/featuregate/testing" v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper" + "k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/kubelet/types" schedulerframework "k8s.io/kubernetes/pkg/scheduler/framework" "k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodeaffinity" @@ -39,49 +40,51 @@ import ( ) var ( - quantity = *resource.NewQuantity(1, resource.DecimalSI) + quantity = *resource.NewQuantity(1, resource.DecimalSI) + extendedResource = v1.ResourceName("foo.com/bar") ) func TestRemoveMissingExtendedResources(t *testing.T) { for _, test := range []struct { - desc string - pod *v1.Pod - node *v1.Node + desc string + pod *v1.Pod + node *v1.Node + enableDRAExtendedResource bool expectedPod *v1.Pod }{ { desc: "requests in Limits should be ignored", pod: makeTestPod( - v1.ResourceList{}, // Requests - v1.ResourceList{"foo.com/bar": quantity}, // Limits + v1.ResourceList{}, // Requests + v1.ResourceList{extendedResource: quantity}, // Limits ), node: makeTestNode( v1.ResourceList{"foo.com/baz": quantity}, // Allocatable ), expectedPod: makeTestPod( - v1.ResourceList{}, // Requests - v1.ResourceList{"foo.com/bar": quantity}, // Limits + v1.ResourceList{}, // Requests + v1.ResourceList{extendedResource: quantity}, // Limits ), }, { desc: "requests for resources available in node should not be removed", pod: makeTestPod( - v1.ResourceList{"foo.com/bar": quantity}, // Requests - v1.ResourceList{}, // Limits + v1.ResourceList{extendedResource: quantity}, // Requests + v1.ResourceList{}, // Limits ), node: makeTestNode( - v1.ResourceList{"foo.com/bar": quantity}, // Allocatable + v1.ResourceList{extendedResource: quantity}, // Allocatable ), expectedPod: makeTestPod( - v1.ResourceList{"foo.com/bar": quantity}, // Requests - v1.ResourceList{}), // Limits + v1.ResourceList{extendedResource: quantity}, // Requests + v1.ResourceList{}), // Limits }, { desc: "requests for resources unavailable in node should be removed", pod: makeTestPod( - v1.ResourceList{"foo.com/bar": quantity}, // Requests - v1.ResourceList{}, // Limits + v1.ResourceList{extendedResource: quantity}, // Requests + v1.ResourceList{}, // Limits ), node: makeTestNode( v1.ResourceList{"foo.com/baz": quantity}, // Allocatable @@ -91,13 +94,62 @@ func TestRemoveMissingExtendedResources(t *testing.T) { v1.ResourceList{}, // Limits ), }, + { + desc: "requests for extended resources with Allocatable 0 should not be removed", + pod: makeTestPod( + v1.ResourceList{extendedResource: quantity}, // Requests + v1.ResourceList{extendedResource: quantity}, // Limits + ), + node: makeTestNode( + v1.ResourceList{extendedResource: *resource.NewQuantity(0, resource.DecimalSI)}, // Allocatable = 0 + ), + expectedPod: makeTestPod( + v1.ResourceList{extendedResource: quantity}, // Requests + v1.ResourceList{extendedResource: quantity}, // Limits + ), + }, + { + desc: "requests for extended resources that are not available in node should be removed", + pod: makeTestPod( + v1.ResourceList{extendedResource: quantity}, // Requests + v1.ResourceList{extendedResource: quantity}, // Limits + ), + node: makeTestNode( + v1.ResourceList{}, // Allocatable is absent for foo.com/bar + ), + expectedPod: makeTestPod( + v1.ResourceList{}, // Requests are filtered + v1.ResourceList{extendedResource: quantity}, // Limits + ), + }, + { + desc: "request for extended resource backed by DRA should be removed if Allocatable is 0", + pod: makeTestPodWithDRAResource(extendedResource, quantity), + node: makeTestNode( + v1.ResourceList{extendedResource: *resource.NewQuantity(0, resource.DecimalSI)}, // Allocatable = 0 + ), + enableDRAExtendedResource: true, + expectedPod: emptyRequests(makeTestPodWithDRAResource(extendedResource, quantity)), + }, + { + desc: "request for extended resource backed by DRA should be removed if Allocatable is absent", + pod: makeTestPodWithDRAResource(extendedResource, quantity), + node: makeTestNode( + v1.ResourceList{}, // Allocatable is absent for foo.com/bar + ), + enableDRAExtendedResource: true, + expectedPod: emptyRequests(makeTestPodWithDRAResource(extendedResource, quantity)), + }, } { - nodeInfo := schedulerframework.NewNodeInfo() - nodeInfo.SetNode(test.node) - pod := removeMissingExtendedResources(test.pod, nodeInfo) - if diff := cmp.Diff(test.expectedPod, pod); diff != "" { - t.Errorf("unexpected pod (-want, +got):\n%s", diff) - } + t.Run(test.desc, func(t *testing.T) { + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DRAExtendedResource, test.enableDRAExtendedResource) + nodeInfo := schedulerframework.NewNodeInfo() + nodeInfo.SetNode(test.node) + pod := removeMissingExtendedResources(test.pod, nodeInfo) + if diff := cmp.Diff(test.expectedPod, pod); diff != "" { + t.Errorf("unexpected pod (-want, +got):\n%s", diff) + } + }) } } @@ -124,6 +176,44 @@ func makeTestPod(requests, limits v1.ResourceList) *v1.Pod { } } +func makeTestPodWithDRAResource(resourceName v1.ResourceName, quantity resource.Quantity) *v1.Pod { + pod := &v1.Pod{ + Spec: v1.PodSpec{ + Containers: []v1.Container{ + { + Name: "test-container", + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{resourceName: quantity}, + Limits: v1.ResourceList{resourceName: quantity}, + }, + }, + }, + }, + } + pod.Status = v1.PodStatus{ + ExtendedResourceClaimStatus: &v1.PodExtendedResourceClaimStatus{ + RequestMappings: []v1.ContainerExtendedResourceRequest{ + { + ResourceName: string(resourceName), + ContainerName: pod.Spec.Containers[0].Name, + }, + }, + }, + } + return pod +} + +func emptyRequests(pod *v1.Pod) *v1.Pod { + newPod := pod.DeepCopy() + for i := range newPod.Spec.Containers { + newPod.Spec.Containers[i].Resources.Requests = v1.ResourceList{} + } + for i := range newPod.Spec.InitContainers { + newPod.Spec.InitContainers[i].Resources.Requests = v1.ResourceList{} + } + return newPod +} + func makeTestNode(allocatable v1.ResourceList) *v1.Node { return &v1.Node{ Status: v1.NodeStatus{ diff --git a/pkg/scheduler/eventhandlers_test.go b/pkg/scheduler/eventhandlers_test.go index 2684074b6ec..2c713e8bfdf 100644 --- a/pkg/scheduler/eventhandlers_test.go +++ b/pkg/scheduler/eventhandlers_test.go @@ -697,13 +697,16 @@ func TestAdmissionCheck(t *testing.T) { nodenameError := AdmissionResult{Name: nodename.Name, Reason: nodename.ErrReason} nodeportsError := AdmissionResult{Name: nodeports.Name, Reason: nodeports.ErrReason} podOverheadError := AdmissionResult{InsufficientResource: &noderesources.InsufficientResource{ResourceName: v1.ResourceCPU, Reason: "Insufficient cpu", Requested: 2000, Used: 7000, Capacity: 8000}} + extendedResourceError := AdmissionResult{InsufficientResource: &noderesources.InsufficientResource{ResourceName: "foo.com/bar", Reason: "Insufficient foo.com/bar", Requested: 1, Unresolvable: true}} cpu := map[v1.ResourceName]string{v1.ResourceCPU: "8"} + extendedResource := map[v1.ResourceName]string{"foo.com/bar": "1"} tests := []struct { - name string - node *v1.Node - existingPods []*v1.Pod - pod *v1.Pod - wantAdmissionResults [][]AdmissionResult + name string + node *v1.Node + existingPods []*v1.Pod + pod *v1.Pod + wantAdmissionResults [][]AdmissionResult + enableDRAExtendedResource bool }{ { name: "check nodeAffinity and nodeports, nodeAffinity need fail quickly if includeAllFailures is false", @@ -732,9 +735,23 @@ func TestAdmissionCheck(t *testing.T) { }, wantAdmissionResults: [][]AdmissionResult{{nodenameError, nodeportsError}, {nodenameError}}, }, + { + name: "check extended resource handling when node Allocatable doesn't have the resource", + node: st.MakeNode().Name("fake-node").Obj(), + pod: st.MakePod().Name("pod1").Req(extendedResource).Obj(), + wantAdmissionResults: [][]AdmissionResult{{extendedResourceError}, {extendedResourceError}}, + }, + { + name: "check extended resource handling when node Allocatable doesn't have the resource and DRAExtendedResource is enabled", + node: st.MakeNode().Name("fake-node").Obj(), + pod: st.MakePod().Name("pod1").Req(extendedResource).Obj(), + wantAdmissionResults: [][]AdmissionResult{{extendedResourceError}, {extendedResourceError}}, + enableDRAExtendedResource: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DRAExtendedResource, tt.enableDRAExtendedResource) nodeInfo := framework.NewNodeInfo(tt.existingPods...) nodeInfo.SetNode(tt.node)