diff --git a/pkg/scheduler/framework/plugins/defaultpreemption/BUILD b/pkg/scheduler/framework/plugins/defaultpreemption/BUILD index fd0417dbf36..e740192d8a2 100644 --- a/pkg/scheduler/framework/plugins/defaultpreemption/BUILD +++ b/pkg/scheduler/framework/plugins/defaultpreemption/BUILD @@ -61,19 +61,16 @@ go_test( "//pkg/scheduler/internal/cache:go_default_library", "//pkg/scheduler/internal/queue:go_default_library", "//pkg/scheduler/testing:go_default_library", - "//pkg/scheduler/util:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/api/policy/v1beta1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//staging/src/k8s.io/client-go/informers:go_default_library", "//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library", "//staging/src/k8s.io/client-go/testing:go_default_library", "//staging/src/k8s.io/client-go/tools/events:go_default_library", "//staging/src/k8s.io/kube-scheduler/extender/v1:go_default_library", + "//vendor/github.com/google/go-cmp/cmp:go_default_library", ], ) diff --git a/pkg/scheduler/framework/plugins/defaultpreemption/default_preemption_test.go b/pkg/scheduler/framework/plugins/defaultpreemption/default_preemption_test.go index b189f69c045..bfd8d8c034a 100644 --- a/pkg/scheduler/framework/plugins/defaultpreemption/default_preemption_test.go +++ b/pkg/scheduler/framework/plugins/defaultpreemption/default_preemption_test.go @@ -19,20 +19,18 @@ package defaultpreemption import ( "context" "fmt" - "math" - "strconv" + "reflect" + "sort" "strings" "testing" "time" + "github.com/google/go-cmp/cmp" v1 "k8s.io/api/core/v1" policy "k8s.io/api/policy/v1beta1" - "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/sets" - "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/informers" clientsetfake "k8s.io/client-go/kubernetes/fake" clienttesting "k8s.io/client-go/testing" @@ -53,167 +51,40 @@ import ( "k8s.io/kubernetes/pkg/scheduler/framework/plugins/volumezone" frameworkruntime "k8s.io/kubernetes/pkg/scheduler/framework/runtime" framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1" + "k8s.io/kubernetes/pkg/scheduler/internal/cache" internalcache "k8s.io/kubernetes/pkg/scheduler/internal/cache" internalqueue "k8s.io/kubernetes/pkg/scheduler/internal/queue" st "k8s.io/kubernetes/pkg/scheduler/testing" - schedutil "k8s.io/kubernetes/pkg/scheduler/util" ) -var smallContainers = []v1.Container{ - { - Resources: v1.ResourceRequirements{ - Requests: v1.ResourceList{ - "cpu": resource.MustParse( - strconv.FormatInt(schedutil.DefaultMilliCPURequest, 10) + "m"), - "memory": resource.MustParse( - strconv.FormatInt(schedutil.DefaultMemoryRequest, 10)), - }, - }, - }, -} -var mediumContainers = []v1.Container{ - { - Resources: v1.ResourceRequirements{ - Requests: v1.ResourceList{ - "cpu": resource.MustParse( - strconv.FormatInt(schedutil.DefaultMilliCPURequest*2, 10) + "m"), - "memory": resource.MustParse( - strconv.FormatInt(schedutil.DefaultMemoryRequest*2, 10)), - }, - }, - }, -} -var largeContainers = []v1.Container{ - { - Resources: v1.ResourceRequirements{ - Requests: v1.ResourceList{ - "cpu": resource.MustParse( - strconv.FormatInt(schedutil.DefaultMilliCPURequest*3, 10) + "m"), - "memory": resource.MustParse( - strconv.FormatInt(schedutil.DefaultMemoryRequest*3, 10)), - }, - }, - }, -} -var veryLargeContainers = []v1.Container{ - { - Resources: v1.ResourceRequirements{ - Requests: v1.ResourceList{ - "cpu": resource.MustParse( - strconv.FormatInt(schedutil.DefaultMilliCPURequest*5, 10) + "m"), - "memory": resource.MustParse( - strconv.FormatInt(schedutil.DefaultMemoryRequest*5, 10)), - }, - }, - }, -} +var ( + negPriority, lowPriority, midPriority, highPriority, veryHighPriority = int32(-100), int32(0), int32(100), int32(1000), int32(10000) -var negPriority, lowPriority, midPriority, highPriority, veryHighPriority = int32(-100), int32(0), int32(100), int32(1000), int32(10000) - -var startTime = metav1.Date(2019, 1, 1, 1, 1, 1, 0, time.UTC) -var startTime20190102 = metav1.Date(2019, 1, 2, 1, 1, 1, 0, time.UTC) -var startTime20190103 = metav1.Date(2019, 1, 3, 1, 1, 1, 0, time.UTC) -var startTime20190104 = metav1.Date(2019, 1, 4, 1, 1, 1, 0, time.UTC) -var startTime20190105 = metav1.Date(2019, 1, 5, 1, 1, 1, 0, time.UTC) -var startTime20190106 = metav1.Date(2019, 1, 6, 1, 1, 1, 0, time.UTC) -var startTime20190107 = metav1.Date(2019, 1, 7, 1, 1, 1, 0, time.UTC) - -type victims struct { - pods sets.String - numPDBViolations int64 -} - -func checkPreemptionVictims(expected map[string]victims, nodeToPods map[string]*extenderv1.Victims) error { - if len(expected) == len(nodeToPods) { - for k, victims := range nodeToPods { - if expVictims, ok := expected[k]; ok { - if len(victims.Pods) != len(expVictims.pods) { - return fmt.Errorf("unexpected number of pods. expected: %v, got: %v", expected, printNodeNameToVictims(nodeToPods)) - } - prevPriority := int32(math.MaxInt32) - for _, p := range victims.Pods { - // Check that pods are sorted by their priority. - if *p.Spec.Priority > prevPriority { - return fmt.Errorf("pod %v of node %v was not sorted by priority", p.Name, k) - } - prevPriority = *p.Spec.Priority - if !expVictims.pods.Has(p.Name) { - return fmt.Errorf("pod %v was not expected. Expected: %v", p.Name, expVictims.pods) - } - } - if expVictims.numPDBViolations != victims.NumPDBViolations { - return fmt.Errorf("unexpected numPDBViolations. expected: %d, got: %d", expVictims.numPDBViolations, victims.NumPDBViolations) - } - } else { - return fmt.Errorf("unexpected machines. expected: %v, got: %v", expected, printNodeNameToVictims(nodeToPods)) - } - } - } else { - return fmt.Errorf("unexpected number of machines. expected: %v, got: %v", expected, printNodeNameToVictims(nodeToPods)) + smallRes = map[v1.ResourceName]string{ + v1.ResourceCPU: "100m", + v1.ResourceMemory: "100", } - return nil -} - -func printNodeNameToVictims(nodeNameToVictims map[string]*extenderv1.Victims) string { - var output string - for nodeName, victims := range nodeNameToVictims { - output += nodeName + ": [" - for _, pod := range victims.Pods { - output += pod.Name + ", " - } - output += "]" + mediumRes = map[v1.ResourceName]string{ + v1.ResourceCPU: "200m", + v1.ResourceMemory: "200", } - return output -} - -func assignDefaultStartTime(pods []*v1.Pod) { - now := metav1.Now() - for i := range pods { - pod := pods[i] - if pod.Status.StartTime == nil { - pod.Status.StartTime = &now - } + largeRes = map[v1.ResourceName]string{ + v1.ResourceCPU: "300m", + v1.ResourceMemory: "300", } -} - -func nodesToNodeInfos(nodes []*v1.Node, snapshot *internalcache.Snapshot) ([]*framework.NodeInfo, error) { - var nodeInfos []*framework.NodeInfo - for _, n := range nodes { - nodeInfo, err := snapshot.NodeInfos().Get(n.Name) - if err != nil { - return nil, err - } - nodeInfos = append(nodeInfos, nodeInfo) + veryLargeRes = map[v1.ResourceName]string{ + v1.ResourceCPU: "500m", + v1.ResourceMemory: "500", } - return nodeInfos, nil -} -func makeNode(node string, milliCPU, memory int64) *v1.Node { - return &v1.Node{ - ObjectMeta: metav1.ObjectMeta{Name: node}, - Status: v1.NodeStatus{ - Capacity: v1.ResourceList{ - v1.ResourceCPU: *resource.NewMilliQuantity(milliCPU, resource.DecimalSI), - v1.ResourceMemory: *resource.NewQuantity(memory, resource.BinarySI), - "pods": *resource.NewQuantity(100, resource.DecimalSI), - }, - Allocatable: v1.ResourceList{ - - v1.ResourceCPU: *resource.NewMilliQuantity(milliCPU, resource.DecimalSI), - v1.ResourceMemory: *resource.NewQuantity(memory, resource.BinarySI), - "pods": *resource.NewQuantity(100, resource.DecimalSI), - }, - }, - } -} - -func makeNodeList(nodeNames []string) []*v1.Node { - result := make([]*v1.Node, 0, len(nodeNames)) - for _, nodeName := range nodeNames { - result = append(result, &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: nodeName}}) - } - return result -} + epochTime = metav1.NewTime(time.Unix(0, 0)) + epochTime1 = metav1.NewTime(time.Unix(0, 1)) + epochTime2 = metav1.NewTime(time.Unix(0, 2)) + epochTime3 = metav1.NewTime(time.Unix(0, 3)) + epochTime4 = metav1.NewTime(time.Unix(0, 4)) + epochTime5 = metav1.NewTime(time.Unix(0, 5)) + epochTime6 = metav1.NewTime(time.Unix(0, 6)) +) func mergeObjs(pod *v1.Pod, pods []*v1.Pod) []runtime.Object { var objs []runtime.Object @@ -226,392 +97,535 @@ func mergeObjs(pod *v1.Pod, pods []*v1.Pod) []runtime.Object { return objs } +func TestPostFilter(t *testing.T) { + onePodRes := map[v1.ResourceName]string{v1.ResourcePods: "1"} + tests := []struct { + name string + pod *v1.Pod + pods []*v1.Pod + nodes []*v1.Node + filteredNodesStatuses framework.NodeToStatusMap + extender framework.Extender + wantResult *framework.PostFilterResult + wantStatus *framework.Status + }{ + { + name: "pod with higher priority can be made schedulable", + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Obj(), + pods: []*v1.Pod{ + st.MakePod().Name("p1").UID("p1").Node("node1").Obj(), + }, + nodes: []*v1.Node{ + st.MakeNode().Name("node1").Capacity(onePodRes).Obj(), + }, + filteredNodesStatuses: framework.NodeToStatusMap{ + "node1": framework.NewStatus(framework.Unschedulable), + }, + wantResult: &framework.PostFilterResult{NominatedNodeName: "node1"}, + wantStatus: framework.NewStatus(framework.Success), + }, + { + name: "pod with tied priority is still unschedulable", + pod: st.MakePod().Name("p").UID("p").Obj(), + pods: []*v1.Pod{ + st.MakePod().Name("p1").UID("p1").Node("node1").Obj(), + }, + nodes: []*v1.Node{ + st.MakeNode().Name("node1").Capacity(onePodRes).Obj(), + }, + filteredNodesStatuses: framework.NodeToStatusMap{ + "node1": framework.NewStatus(framework.Unschedulable), + }, + wantResult: nil, + wantStatus: framework.NewStatus(framework.Unschedulable), + }, + { + name: "preemption should respect filteredNodesStatuses", + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Obj(), + pods: []*v1.Pod{ + st.MakePod().Name("p1").UID("p1").Node("node1").Obj(), + }, + nodes: []*v1.Node{ + st.MakeNode().Name("node1").Capacity(onePodRes).Obj(), + }, + filteredNodesStatuses: framework.NodeToStatusMap{ + "node1": framework.NewStatus(framework.UnschedulableAndUnresolvable), + }, + wantResult: nil, + wantStatus: framework.NewStatus(framework.Unschedulable), + }, + { + name: "pod can be made schedulable on one node", + pod: st.MakePod().Name("p").UID("p").Priority(midPriority).Obj(), + pods: []*v1.Pod{ + st.MakePod().Name("p1").UID("p1").Priority(highPriority).Node("node1").Obj(), + st.MakePod().Name("p2").UID("p2").Priority(lowPriority).Node("node2").Obj(), + }, + nodes: []*v1.Node{ + st.MakeNode().Name("node1").Capacity(onePodRes).Obj(), + st.MakeNode().Name("node2").Capacity(onePodRes).Obj(), + }, + filteredNodesStatuses: framework.NodeToStatusMap{ + "node1": framework.NewStatus(framework.Unschedulable), + "node2": framework.NewStatus(framework.Unschedulable), + }, + wantResult: &framework.PostFilterResult{NominatedNodeName: "node2"}, + wantStatus: framework.NewStatus(framework.Success), + }, + { + name: "preemption result filtered out by extenders", + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Obj(), + pods: []*v1.Pod{ + st.MakePod().Name("p1").UID("p1").Node("machine1").Obj(), + st.MakePod().Name("p2").UID("p2").Node("machine2").Obj(), + }, + nodes: []*v1.Node{ + st.MakeNode().Name("machine1").Capacity(onePodRes).Obj(), + st.MakeNode().Name("machine2").Capacity(onePodRes).Obj(), + }, + filteredNodesStatuses: framework.NodeToStatusMap{ + "machine1": framework.NewStatus(framework.Unschedulable), + "machine2": framework.NewStatus(framework.Unschedulable), + }, + extender: &st.FakeExtender{Predicates: []st.FitPredicate{st.Machine1PredicateExtender}}, + wantResult: &framework.PostFilterResult{ + NominatedNodeName: "machine1", + }, + wantStatus: framework.NewStatus(framework.Success), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + apiObjs := mergeObjs(tt.pod, tt.pods /*, tt.nodes */) + cs := clientsetfake.NewSimpleClientset(apiObjs...) + informerFactory := informers.NewSharedInformerFactory(cs, 0) + // Register NodeResourceFit as the Filter & PreFilter plugin. + registeredPlugins := []st.RegisterPluginFunc{ + st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), + st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), + st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), + } + var extenders []framework.Extender + if tt.extender != nil { + extenders = append(extenders, tt.extender) + } + f, err := st.NewFramework(registeredPlugins, + frameworkruntime.WithClientSet(cs), + frameworkruntime.WithEventRecorder(&events.FakeRecorder{}), + frameworkruntime.WithInformerFactory(informerFactory), + frameworkruntime.WithPodNominator(internalqueue.NewPodNominator()), + frameworkruntime.WithExtenders(extenders), + frameworkruntime.WithSnapshotSharedLister(cache.NewSnapshot(tt.pods, tt.nodes)), + ) + if err != nil { + t.Fatal(err) + } + p := DefaultPreemption{fh: f} + + state := framework.NewCycleState() + // Ensure is populated. + if status := f.RunPreFilterPlugins(context.Background(), state, tt.pod); !status.IsSuccess() { + t.Errorf("Unexpected PreFilter Status: %v", status) + } + + gotResult, gotStatus := p.PostFilter(context.TODO(), state, tt.pod, tt.filteredNodesStatuses) + if !reflect.DeepEqual(gotStatus, tt.wantStatus) { + t.Errorf("Status does not match: %v, want: %v", gotStatus, tt.wantStatus) + } + if diff := cmp.Diff(gotResult, tt.wantResult); diff != "" { + t.Errorf("Unexpected postFilterResult (-want, +got): %s", diff) + } + }) + } +} + // TestSelectNodesForPreemption tests selectNodesForPreemption. This test assumes // that podsFitsOnNode works correctly and is tested separately. func TestSelectNodesForPreemption(t *testing.T) { tests := []struct { name string - registerPlugins []st.RegisterPluginFunc - nodes []string + nodeNames []string pod *v1.Pod pods []*v1.Pod + registerPlugins []st.RegisterPluginFunc pdbs []*policy.PodDisruptionBudget - filterReturnCode framework.Code - expected map[string]victims + fakeFilterRC framework.Code // return code for fake filter plugin + expected map[string]*extenderv1.Victims expectedNumFilterCalled int32 }{ { name: "a pod that does not fit on any machine", registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), st.RegisterFilterPlugin("FalseFilter", st.NewFalseFilterPlugin), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), }, - nodes: []string{"machine1", "machine2"}, - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "new", UID: types.UID("new")}, Spec: v1.PodSpec{Priority: &highPriority}}, + nodeNames: []string{"node1", "node2"}, + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "a", UID: types.UID("a")}, Spec: v1.PodSpec{Priority: &midPriority, NodeName: "machine1"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "b", UID: types.UID("b")}, Spec: v1.PodSpec{Priority: &midPriority, NodeName: "machine2"}}}, - expected: map[string]victims{}, + st.MakePod().Name("p1").UID("p1").Node("node1").Priority(midPriority).Obj(), + st.MakePod().Name("p2").UID("p2").Node("node2").Priority(midPriority).Obj(), + }, + expected: map[string]*extenderv1.Victims{}, expectedNumFilterCalled: 2, }, { name: "a pod that fits with no preemption", registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), st.RegisterFilterPlugin("TrueFilter", st.NewTrueFilterPlugin), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), }, - nodes: []string{"machine1", "machine2"}, - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "new", UID: types.UID("new")}, Spec: v1.PodSpec{Priority: &highPriority}}, + nodeNames: []string{"node1", "node2"}, + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "a", UID: types.UID("a")}, Spec: v1.PodSpec{Priority: &midPriority, NodeName: "machine1"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "b", UID: types.UID("b")}, Spec: v1.PodSpec{Priority: &midPriority, NodeName: "machine2"}}}, - expected: map[string]victims{"machine1": {}, "machine2": {}}, + st.MakePod().Name("p1").UID("p1").Node("node1").Priority(midPriority).Obj(), + st.MakePod().Name("p2").UID("p2").Node("node2").Priority(midPriority).Obj(), + }, + expected: map[string]*extenderv1.Victims{ + "node1": {}, + "node2": {}, + }, expectedNumFilterCalled: 4, }, { name: "a pod that fits on one machine with no preemption", registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), st.RegisterFilterPlugin("MatchFilter", st.NewMatchFilterPlugin), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), }, - nodes: []string{"machine1", "machine2"}, - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Priority: &highPriority}}, + nodeNames: []string{"node1", "node2"}, + pod: st.MakePod().Name("node1").UID("node1").Priority(highPriority).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "a", UID: types.UID("a")}, Spec: v1.PodSpec{Priority: &midPriority, NodeName: "machine1"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "b", UID: types.UID("b")}, Spec: v1.PodSpec{Priority: &midPriority, NodeName: "machine2"}}}, - expected: map[string]victims{"machine1": {}}, + st.MakePod().Name("p1").UID("p1").Node("node1").Priority(midPriority).Obj(), + st.MakePod().Name("p2").UID("p2").Node("node2").Priority(midPriority).Obj(), + }, + expected: map[string]*extenderv1.Victims{ + "node1": {}, + }, expectedNumFilterCalled: 3, }, { name: "a pod that fits on both machines when lower priority pods are preempted", registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), }, - nodes: []string{"machine1", "machine2"}, - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority}}, + nodeNames: []string{"node1", "node2"}, + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Req(largeRes).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "a", UID: types.UID("a")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine1"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "b", UID: types.UID("b")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine2"}}}, - expected: map[string]victims{"machine1": {pods: sets.NewString("a")}, "machine2": {pods: sets.NewString("b")}}, + st.MakePod().Name("p1").UID("p1").Node("node1").Priority(midPriority).Req(largeRes).Obj(), + st.MakePod().Name("p2").UID("p2").Node("node2").Priority(midPriority).Req(largeRes).Obj(), + }, + expected: map[string]*extenderv1.Victims{ + "node1": { + Pods: []*v1.Pod{st.MakePod().Name("p1").UID("p1").Node("node1").Priority(midPriority).Req(largeRes).Obj()}, + }, + "node2": { + Pods: []*v1.Pod{st.MakePod().Name("p2").UID("p2").Node("node2").Priority(midPriority).Req(largeRes).Obj()}, + }, + }, expectedNumFilterCalled: 4, }, { name: "a pod that would fit on the machines, but other pods running are higher priority", registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), }, - nodes: []string{"machine1", "machine2"}, - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &lowPriority}}, + nodeNames: []string{"node1", "node2"}, + pod: st.MakePod().Name("p").UID("p").Priority(lowPriority).Req(largeRes).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "a", UID: types.UID("a")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine1"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "b", UID: types.UID("b")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine2"}}}, - expected: map[string]victims{}, + st.MakePod().Name("p1").UID("p1").Node("node1").Priority(midPriority).Req(largeRes).Obj(), + st.MakePod().Name("p2").UID("p2").Node("node2").Priority(midPriority).Req(largeRes).Obj(), + }, + expected: map[string]*extenderv1.Victims{}, expectedNumFilterCalled: 2, }, { name: "medium priority pod is preempted, but lower priority one stays as it is small", registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), }, - nodes: []string{"machine1", "machine2"}, - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority}}, + nodeNames: []string{"node1", "node2"}, + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Req(largeRes).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "a", UID: types.UID("a")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "b", UID: types.UID("b")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine1"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "c", UID: types.UID("c")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine2"}}}, - expected: map[string]victims{"machine1": {pods: sets.NewString("b")}, "machine2": {pods: sets.NewString("c")}}, + st.MakePod().Name("p1.1").UID("p1.1").Node("node1").Priority(lowPriority).Req(smallRes).Obj(), + st.MakePod().Name("p1.2").UID("p1.2").Node("node1").Priority(midPriority).Req(largeRes).Obj(), + st.MakePod().Name("p2").UID("p2").Node("node2").Priority(midPriority).Req(largeRes).Obj(), + }, + expected: map[string]*extenderv1.Victims{ + "node1": { + Pods: []*v1.Pod{st.MakePod().Name("p1.2").UID("p1.2").Node("node1").Priority(midPriority).Req(largeRes).Obj()}, + }, + "node2": { + Pods: []*v1.Pod{st.MakePod().Name("p2").UID("p2").Node("node2").Priority(midPriority).Req(largeRes).Obj()}, + }, + }, expectedNumFilterCalled: 5, }, { name: "mixed priority pods are preempted", registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), }, - nodes: []string{"machine1", "machine2"}, - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority}}, + nodeNames: []string{"node1", "node2"}, + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Req(largeRes).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "a", UID: types.UID("a")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &midPriority, NodeName: "machine1"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "b", UID: types.UID("b")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "c", UID: types.UID("c")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine1"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "d", UID: types.UID("d")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &highPriority, NodeName: "machine1"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "e", UID: types.UID("e")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority, NodeName: "machine2"}}}, - expected: map[string]victims{"machine1": {pods: sets.NewString("b", "c")}}, + st.MakePod().Name("p1.1").UID("p1.1").Node("node1").Priority(midPriority).Req(smallRes).Obj(), + st.MakePod().Name("p1.2").UID("p1.2").Node("node1").Priority(lowPriority).Req(smallRes).Obj(), + st.MakePod().Name("p1.3").UID("p1.3").Node("node1").Priority(midPriority).Req(mediumRes).Obj(), + st.MakePod().Name("p1.4").UID("p1.4").Node("node1").Priority(highPriority).Req(smallRes).Obj(), + st.MakePod().Name("p2").UID("p2").Node("node2").Priority(highPriority).Req(largeRes).Obj(), + }, + expected: map[string]*extenderv1.Victims{ + "node1": { + Pods: []*v1.Pod{ + st.MakePod().Name("p1.2").UID("p1.2").Node("node1").Priority(lowPriority).Req(smallRes).Obj(), + st.MakePod().Name("p1.3").UID("p1.3").Node("node1").Priority(midPriority).Req(mediumRes).Obj(), + }, + }, + }, expectedNumFilterCalled: 5, }, { name: "mixed priority pods are preempted, pick later StartTime one when priorities are equal", registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), }, - nodes: []string{"machine1", "machine2"}, - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority}}, + nodeNames: []string{"node1", "node2"}, + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Req(largeRes).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "a", UID: types.UID("a")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime20190107}}, - {ObjectMeta: metav1.ObjectMeta{Name: "b", UID: types.UID("b")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime20190106}}, - {ObjectMeta: metav1.ObjectMeta{Name: "c", UID: types.UID("c")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime20190105}}, - {ObjectMeta: metav1.ObjectMeta{Name: "d", UID: types.UID("d")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &highPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime20190104}}, - {ObjectMeta: metav1.ObjectMeta{Name: "e", UID: types.UID("e")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime20190103}}}, - expected: map[string]victims{"machine1": {pods: sets.NewString("a", "c")}}, + st.MakePod().Name("p1.1").UID("p1.1").Node("node1").Priority(lowPriority).Req(smallRes).StartTime(epochTime5).Obj(), + st.MakePod().Name("p1.2").UID("p1.2").Node("node1").Priority(lowPriority).Req(smallRes).StartTime(epochTime4).Obj(), + st.MakePod().Name("p1.3").UID("p1.3").Node("node1").Priority(midPriority).Req(mediumRes).StartTime(epochTime3).Obj(), + st.MakePod().Name("p1.4").UID("p1.4").Node("node1").Priority(highPriority).Req(smallRes).StartTime(epochTime2).Obj(), + st.MakePod().Name("p2").UID("p2").Node("node2").Priority(highPriority).Req(largeRes).StartTime(epochTime1).Obj(), + }, + expected: map[string]*extenderv1.Victims{ + "node1": { + Pods: []*v1.Pod{ + st.MakePod().Name("p1.1").UID("p1.1").Node("node1").Priority(lowPriority).Req(smallRes).StartTime(epochTime5).Obj(), + st.MakePod().Name("p1.3").UID("p1.3").Node("node1").Priority(midPriority).Req(mediumRes).StartTime(epochTime3).Obj(), + }, + }, + }, expectedNumFilterCalled: 5, }, { name: "pod with anti-affinity is preempted", registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), st.RegisterPluginAsExtensions(interpodaffinity.Name, interpodaffinity.New, "Filter", "PreFilter"), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), }, - nodes: []string{"machine1", "machine2"}, - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{ - Name: "machine1", - Labels: map[string]string{"pod": "preemptor"}}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &highPriority}}, + nodeNames: []string{"node1", "node2"}, + pod: st.MakePod().Name("p").UID("p").Label("foo", "").Priority(highPriority).Req(smallRes).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "a", UID: types.UID("a"), Labels: map[string]string{"service": "securityscan"}}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1", Affinity: &v1.Affinity{ - PodAntiAffinity: &v1.PodAntiAffinity{ - RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{ - { - LabelSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: "pod", - Operator: metav1.LabelSelectorOpIn, - Values: []string{"preemptor", "value2"}, - }, - }, - }, - TopologyKey: "hostname", - }, - }, - }}}}, - {ObjectMeta: metav1.ObjectMeta{Name: "b", UID: types.UID("b")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &midPriority, NodeName: "machine1"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "d", UID: types.UID("d")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &highPriority, NodeName: "machine1"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "e", UID: types.UID("e")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority, NodeName: "machine2"}}}, - expected: map[string]victims{"machine1": {pods: sets.NewString("a")}, "machine2": {}}, + st.MakePod().Name("p1.1").UID("p1.1").Node("node1").Label("foo", "").Priority(lowPriority).Req(smallRes). + PodAntiAffinityExists("foo", "hostname", st.PodAntiAffinityWithRequiredReq).Obj(), + st.MakePod().Name("p1.2").UID("p1.2").Node("node1").Priority(midPriority).Req(smallRes).Obj(), + st.MakePod().Name("p1.3").UID("p1.3").Node("node1").Priority(highPriority).Req(smallRes).Obj(), + st.MakePod().Name("p2").UID("p2").Node("node2").Priority(highPriority).Req(smallRes).Obj(), + }, + expected: map[string]*extenderv1.Victims{ + "node1": { + Pods: []*v1.Pod{ + st.MakePod().Name("p1.1").UID("p1.1").Node("node1").Label("foo", "").Priority(lowPriority).Req(smallRes). + PodAntiAffinityExists("foo", "hostname", st.PodAntiAffinityWithRequiredReq).Obj(), + }, + }, + "node2": {}, + }, expectedNumFilterCalled: 4, }, { - name: "preemption to resolve even pods spread FitError", + name: "preemption to resolve pod topology spread filter failure", registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), - st.RegisterPluginAsExtensions( - podtopologyspread.Name, - podtopologyspread.New, - "PreFilter", - "Filter", - ), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), - }, - nodes: []string{"node-a/zone1", "node-b/zone1", "node-x/zone2"}, - pod: &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "p", - Labels: map[string]string{"foo": ""}, - }, - Spec: v1.PodSpec{ - Priority: &highPriority, - TopologySpreadConstraints: []v1.TopologySpreadConstraint{ - { - MaxSkew: 1, - TopologyKey: "zone", - WhenUnsatisfiable: v1.DoNotSchedule, - LabelSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: "foo", - Operator: metav1.LabelSelectorOpExists, - }, - }, - }, - }, - { - MaxSkew: 1, - TopologyKey: "hostname", - WhenUnsatisfiable: v1.DoNotSchedule, - LabelSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: "foo", - Operator: metav1.LabelSelectorOpExists, - }, - }, - }, - }, - }, - }, + st.RegisterPluginAsExtensions(podtopologyspread.Name, podtopologyspread.New, "PreFilter", "Filter"), }, + nodeNames: []string{"node-a/zone1", "node-b/zone1", "node-x/zone2"}, + pod: st.MakePod().Name("p").UID("p").Label("foo", "").Priority(highPriority). + SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj()). + SpreadConstraint(1, "hostname", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj()). + Obj(), pods: []*v1.Pod{ - { - ObjectMeta: metav1.ObjectMeta{Name: "pod-a1", UID: types.UID("pod-a1"), Labels: map[string]string{"foo": ""}}, - Spec: v1.PodSpec{NodeName: "node-a", Priority: &midPriority}, - Status: v1.PodStatus{Phase: v1.PodRunning}, - }, - { - ObjectMeta: metav1.ObjectMeta{Name: "pod-a2", UID: types.UID("pod-a2"), Labels: map[string]string{"foo": ""}}, - Spec: v1.PodSpec{NodeName: "node-a", Priority: &lowPriority}, - Status: v1.PodStatus{Phase: v1.PodRunning}, - }, - { - ObjectMeta: metav1.ObjectMeta{Name: "pod-b1", UID: types.UID("pod-b1"), Labels: map[string]string{"foo": ""}}, - Spec: v1.PodSpec{NodeName: "node-b", Priority: &lowPriority}, - Status: v1.PodStatus{Phase: v1.PodRunning}, - }, - { - ObjectMeta: metav1.ObjectMeta{Name: "pod-x1", UID: types.UID("pod-x1"), Labels: map[string]string{"foo": ""}}, - Spec: v1.PodSpec{NodeName: "node-x", Priority: &highPriority}, - Status: v1.PodStatus{Phase: v1.PodRunning}, - }, - { - ObjectMeta: metav1.ObjectMeta{Name: "pod-x2", UID: types.UID("pod-x2"), Labels: map[string]string{"foo": ""}}, - Spec: v1.PodSpec{NodeName: "node-x", Priority: &highPriority}, - Status: v1.PodStatus{Phase: v1.PodRunning}, - }, + st.MakePod().Name("pod-a1").UID("pod-a1").Node("node-a").Label("foo", "").Priority(midPriority).Obj(), + st.MakePod().Name("pod-a2").UID("pod-a2").Node("node-a").Label("foo", "").Priority(lowPriority).Obj(), + st.MakePod().Name("pod-b1").UID("pod-b1").Node("node-b").Label("foo", "").Priority(lowPriority).Obj(), + st.MakePod().Name("pod-x1").UID("pod-x1").Node("node-x").Label("foo", "").Priority(highPriority).Obj(), + st.MakePod().Name("pod-x2").UID("pod-x2").Node("node-x").Label("foo", "").Priority(highPriority).Obj(), }, - expected: map[string]victims{ - "node-a": {pods: sets.NewString("pod-a2")}, - "node-b": {pods: sets.NewString("pod-b1")}, + expected: map[string]*extenderv1.Victims{ + "node-a": { + Pods: []*v1.Pod{st.MakePod().Name("pod-a2").UID("pod-a2").Node("node-a").Label("foo", "").Priority(lowPriority).Obj()}, + }, + "node-b": { + Pods: []*v1.Pod{st.MakePod().Name("pod-b1").UID("pod-b1").Node("node-b").Label("foo", "").Priority(lowPriority).Obj()}, + }, }, expectedNumFilterCalled: 6, }, { name: "get Unschedulable in the preemption phase when the filter plugins filtering the nodes", registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), }, - nodes: []string{"machine1", "machine2"}, - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority}}, + nodeNames: []string{"node1", "node2"}, + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Req(largeRes).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "a", UID: types.UID("a")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine1"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "b", UID: types.UID("b")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine2"}}}, - filterReturnCode: framework.Unschedulable, - expected: map[string]victims{}, + st.MakePod().Name("p1").UID("p1").Node("node1").Priority(midPriority).Req(largeRes).Obj(), + st.MakePod().Name("p2").UID("p2").Node("node2").Priority(midPriority).Req(largeRes).Obj(), + }, + fakeFilterRC: framework.Unschedulable, + expected: map[string]*extenderv1.Victims{}, expectedNumFilterCalled: 2, }, { name: "preemption with violation of same pdb", registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), }, - nodes: []string{"machine1"}, - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1", UID: types.UID("pod1")}, Spec: v1.PodSpec{Containers: veryLargeContainers, Priority: &highPriority}}, + nodeNames: []string{"node1"}, + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Req(veryLargeRes).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "a", UID: types.UID("a"), Labels: map[string]string{"app": "foo"}}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine1"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "b", UID: types.UID("b"), Labels: map[string]string{"app": "foo"}}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine1"}}}, + st.MakePod().Name("p1.1").UID("p1.1").Node("node1").Label("app", "foo").Priority(midPriority).Req(mediumRes).Obj(), + st.MakePod().Name("p1.2").UID("p1.2").Node("node1").Label("app", "foo").Priority(midPriority).Req(mediumRes).Obj(), + }, pdbs: []*policy.PodDisruptionBudget{ - {Spec: policy.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "foo"}}}, Status: policy.PodDisruptionBudgetStatus{DisruptionsAllowed: 1}}}, - expected: map[string]victims{"machine1": {pods: sets.NewString("a", "b"), numPDBViolations: 1}}, + { + Spec: policy.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "foo"}}}, + Status: policy.PodDisruptionBudgetStatus{DisruptionsAllowed: 1}, + }, + }, + expected: map[string]*extenderv1.Victims{ + "node1": { + Pods: []*v1.Pod{ + st.MakePod().Name("p1.1").UID("p1.1").Node("node1").Label("app", "foo").Priority(midPriority).Req(mediumRes).Obj(), + st.MakePod().Name("p1.2").UID("p1.2").Node("node1").Label("app", "foo").Priority(midPriority).Req(mediumRes).Obj(), + }, + NumPDBViolations: 1, + }, + }, expectedNumFilterCalled: 3, }, { name: "preemption with violation of the pdb with pod whose eviction was processed, the victim doesn't belong to DisruptedPods", registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), }, - nodes: []string{"machine1"}, - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1", UID: types.UID("pod1")}, Spec: v1.PodSpec{Containers: veryLargeContainers, Priority: &highPriority}}, + nodeNames: []string{"node1"}, + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Req(veryLargeRes).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "a", UID: types.UID("a"), Labels: map[string]string{"app": "foo"}}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine1"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "b", UID: types.UID("b"), Labels: map[string]string{"app": "foo"}}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine1"}}}, + st.MakePod().Name("p1.1").UID("p1.1").Node("node1").Label("app", "foo").Priority(midPriority).Req(mediumRes).Obj(), + st.MakePod().Name("p1.2").UID("p1.2").Node("node1").Label("app", "foo").Priority(midPriority).Req(mediumRes).Obj(), + }, pdbs: []*policy.PodDisruptionBudget{ - {Spec: policy.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "foo"}}}, Status: policy.PodDisruptionBudgetStatus{DisruptionsAllowed: 1, DisruptedPods: map[string]metav1.Time{"c": {Time: time.Now()}}}}}, - expected: map[string]victims{"machine1": {pods: sets.NewString("a", "b"), numPDBViolations: 1}}, + { + Spec: policy.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "foo"}}}, + Status: policy.PodDisruptionBudgetStatus{DisruptionsAllowed: 1, DisruptedPods: map[string]metav1.Time{"p2": {Time: time.Now()}}}, + }, + }, + expected: map[string]*extenderv1.Victims{ + "node1": { + Pods: []*v1.Pod{ + st.MakePod().Name("p1.1").UID("p1.1").Node("node1").Label("app", "foo").Priority(midPriority).Req(mediumRes).Obj(), + st.MakePod().Name("p1.2").UID("p1.2").Node("node1").Label("app", "foo").Priority(midPriority).Req(mediumRes).Obj(), + }, + NumPDBViolations: 1, + }, + }, expectedNumFilterCalled: 3, }, { name: "preemption with violation of the pdb with pod whose eviction was processed, the victim belongs to DisruptedPods", registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), }, - nodes: []string{"machine1"}, - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1", UID: types.UID("pod1")}, Spec: v1.PodSpec{Containers: veryLargeContainers, Priority: &highPriority}}, + nodeNames: []string{"node1"}, + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Req(veryLargeRes).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "a", UID: types.UID("a"), Labels: map[string]string{"app": "foo"}}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine1"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "b", UID: types.UID("b"), Labels: map[string]string{"app": "foo"}}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine1"}}}, + st.MakePod().Name("p1.1").UID("p1.1").Node("node1").Label("app", "foo").Priority(midPriority).Req(mediumRes).Obj(), + st.MakePod().Name("p1.2").UID("p1.2").Node("node1").Label("app", "foo").Priority(midPriority).Req(mediumRes).Obj(), + }, pdbs: []*policy.PodDisruptionBudget{ - {Spec: policy.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "foo"}}}, Status: policy.PodDisruptionBudgetStatus{DisruptionsAllowed: 1, DisruptedPods: map[string]metav1.Time{"b": {Time: time.Now()}}}}}, - expected: map[string]victims{"machine1": {pods: sets.NewString("a", "b"), numPDBViolations: 0}}, + { + Spec: policy.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "foo"}}}, + Status: policy.PodDisruptionBudgetStatus{DisruptionsAllowed: 1, DisruptedPods: map[string]metav1.Time{"p1.2": {Time: time.Now()}}}, + }, + }, + expected: map[string]*extenderv1.Victims{ + "node1": { + Pods: []*v1.Pod{ + st.MakePod().Name("p1.1").UID("p1.1").Node("node1").Label("app", "foo").Priority(midPriority).Req(mediumRes).Obj(), + st.MakePod().Name("p1.2").UID("p1.2").Node("node1").Label("app", "foo").Priority(midPriority).Req(mediumRes).Obj(), + }, + NumPDBViolations: 0, + }, + }, expectedNumFilterCalled: 3, }, { name: "preemption with violation of the pdb with pod whose eviction was processed, the victim which belongs to DisruptedPods is treated as 'nonViolating'", registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), }, - nodes: []string{"machine1"}, - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1", UID: types.UID("pod1")}, Spec: v1.PodSpec{Containers: veryLargeContainers, Priority: &highPriority}}, + nodeNames: []string{"node1"}, + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Req(veryLargeRes).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "a", UID: types.UID("a"), Labels: map[string]string{"app": "foo"}}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine1"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "b", UID: types.UID("b"), Labels: map[string]string{"app": "foo"}}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine1"}}, - {ObjectMeta: metav1.ObjectMeta{Name: "c", UID: types.UID("c"), Labels: map[string]string{"app": "foo"}}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine1"}}}, + st.MakePod().Name("p1.1").UID("p1.1").Node("node1").Label("app", "foo").Priority(midPriority).Req(mediumRes).Obj(), + st.MakePod().Name("p1.2").UID("p1.2").Node("node1").Label("app", "foo").Priority(midPriority).Req(mediumRes).Obj(), + st.MakePod().Name("p1.3").UID("p1.3").Node("node1").Label("app", "foo").Priority(midPriority).Req(mediumRes).Obj(), + }, pdbs: []*policy.PodDisruptionBudget{ - {Spec: policy.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "foo"}}}, Status: policy.PodDisruptionBudgetStatus{DisruptionsAllowed: 1, DisruptedPods: map[string]metav1.Time{"c": {Time: time.Now()}}}}}, - expected: map[string]victims{"machine1": {pods: sets.NewString("a", "b", "c"), numPDBViolations: 1}}, + { + Spec: policy.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "foo"}}}, + Status: policy.PodDisruptionBudgetStatus{DisruptionsAllowed: 1, DisruptedPods: map[string]metav1.Time{"p1.3": {Time: time.Now()}}}, + }, + }, + expected: map[string]*extenderv1.Victims{ + "node1": { + Pods: []*v1.Pod{ + st.MakePod().Name("p1.1").UID("p1.1").Node("node1").Label("app", "foo").Priority(midPriority).Req(mediumRes).Obj(), + st.MakePod().Name("p1.2").UID("p1.2").Node("node1").Label("app", "foo").Priority(midPriority).Req(mediumRes).Obj(), + st.MakePod().Name("p1.3").UID("p1.3").Node("node1").Label("app", "foo").Priority(midPriority).Req(mediumRes).Obj(), + }, + NumPDBViolations: 1, + }, + }, expectedNumFilterCalled: 4, }, } + labelKeys := []string{"hostname", "zone", "region"} - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - filterFailedNodeReturnCodeMap := map[string]framework.Code{} - cache := internalcache.New(time.Duration(0), wait.NeverStop) - for _, pod := range test.pods { - cache.AddPod(pod) - } - for _, name := range test.nodes { - filterFailedNodeReturnCodeMap[name] = test.filterReturnCode - cache.AddNode(&v1.Node{ObjectMeta: metav1.ObjectMeta{Name: name, Labels: map[string]string{"hostname": name}}}) - } - - var nodes []*v1.Node - for _, n := range test.nodes { - node := makeNode(n, 1000*5, schedutil.DefaultMemoryRequest*5) - // if possible, split node name by '/' to form labels in a format of - // {"hostname": node.Name[0], "zone": node.Name[1], "region": node.Name[2]} - node.ObjectMeta.Labels = make(map[string]string) - for i, label := range strings.Split(node.Name, "/") { - node.ObjectMeta.Labels[labelKeys[i]] = label + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + nodes := make([]*v1.Node, len(tt.nodeNames)) + fakeFilterRCMap := make(map[string]framework.Code, len(tt.nodeNames)) + for i, nodeName := range tt.nodeNames { + nodeWrapper := st.MakeNode().Capacity(veryLargeRes) + // Split node name by '/' to form labels in a format of + // {"hostname": tpKeys[0], "zone": tpKeys[1], "region": tpKeys[2]} + tpKeys := strings.Split(nodeName, "/") + nodeWrapper.Name(tpKeys[0]) + for i, labelVal := range strings.Split(nodeName, "/") { + nodeWrapper.Label(labelKeys[i], labelVal) } - node.Name = node.ObjectMeta.Labels["hostname"] - nodes = append(nodes, node) + nodes[i] = nodeWrapper.Obj() + fakeFilterRCMap[nodeName] = tt.fakeFilterRC } + snapshot := cache.NewSnapshot(tt.pods, nodes) - // For each test, prepend a FakeFilterPlugin. - fakePlugin := st.FakeFilterPlugin{} - fakePlugin.FailedNodeReturnCodeMap = filterFailedNodeReturnCodeMap - registerFakeFilterFunc := st.RegisterFilterPlugin( - "FakeFilter", - func(_ runtime.Object, fh framework.FrameworkHandle) (framework.Plugin, error) { - return &fakePlugin, nil - }, + // For each test, register a FakeFilterPlugin along with essential plugins and tt.registerPlugins. + fakePlugin := st.FakeFilterPlugin{ + FailedNodeReturnCodeMap: fakeFilterRCMap, + } + registeredPlugins := append([]st.RegisterPluginFunc{ + st.RegisterFilterPlugin( + "FakeFilter", + func(_ runtime.Object, fh framework.FrameworkHandle) (framework.Plugin, error) { + return &fakePlugin, nil + }, + )}, + st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), + st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), ) - registerPlugins := append([]st.RegisterPluginFunc{registerFakeFilterFunc}, test.registerPlugins...) - // Use a real snapshot since it's needed in some Filter Plugin (e.g., PodAffinity) - snapshot := internalcache.NewSnapshot(test.pods, nodes) + registeredPlugins = append(registeredPlugins, tt.registerPlugins...) fwk, err := st.NewFramework( - registerPlugins, + registeredPlugins, frameworkruntime.WithPodNominator(internalqueue.NewPodNominator()), frameworkruntime.WithSnapshotSharedLister(snapshot), ) @@ -619,29 +633,33 @@ func TestSelectNodesForPreemption(t *testing.T) { t.Fatal(err) } - assignDefaultStartTime(test.pods) - state := framework.NewCycleState() // Some tests rely on PreFilter plugin to compute its CycleState. - preFilterStatus := fwk.RunPreFilterPlugins(context.Background(), state, test.pod) - if !preFilterStatus.IsSuccess() { - t.Errorf("Unexpected preFilterStatus: %v", preFilterStatus) + if status := fwk.RunPreFilterPlugins(context.Background(), state, tt.pod); !status.IsSuccess() { + t.Errorf("Unexpected PreFilter Status: %v", status) } - nodeInfos, err := nodesToNodeInfos(nodes, snapshot) + + nodeInfos, err := snapshot.NodeInfos().List() if err != nil { t.Fatal(err) } - nodeToPods, err := selectNodesForPreemption(context.Background(), fwk.PreemptHandle(), state, test.pod, nodeInfos, test.pdbs) + got, err := selectNodesForPreemption(context.Background(), fwk.PreemptHandle(), state, tt.pod, nodeInfos, tt.pdbs) if err != nil { - t.Error(err) + t.Fatal(err) + } + // Sort the values (inner victims). + for i := range got { + victims := got[i].Pods + sort.Slice(victims, func(i, j int) bool { + return victims[i].Name < victims[j].Name + }) } - if test.expectedNumFilterCalled != fakePlugin.NumFilterCalled { - t.Errorf("expected fakePlugin.numFilterCalled is %d, but got %d", test.expectedNumFilterCalled, fakePlugin.NumFilterCalled) + if tt.expectedNumFilterCalled != fakePlugin.NumFilterCalled { + t.Errorf("expected fakePlugin.numFilterCalled is %d, but got %d", tt.expectedNumFilterCalled, fakePlugin.NumFilterCalled) } - - if err := checkPreemptionVictims(test.expected, nodeToPods); err != nil { - t.Error(err) + if diff := cmp.Diff(tt.expected, got); diff != "" { + t.Errorf("Unexpected strategies (-want, +got): %s", diff) } }) } @@ -650,248 +668,192 @@ func TestSelectNodesForPreemption(t *testing.T) { // TestPickOneNodeForPreemption tests pickOneNodeForPreemption. func TestPickOneNodeForPreemption(t *testing.T) { tests := []struct { - name string - registerPlugins []st.RegisterPluginFunc - nodes []string - pod *v1.Pod - pods []*v1.Pod - expected []string // any of the items is valid + name string + registerPlugin st.RegisterPluginFunc + nodeNames []string + pod *v1.Pod + pods []*v1.Pod + expected []string // any of the items is valid }{ { - name: "No node needs preemption", - registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), - st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), - }, - nodes: []string{"machine1"}, - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority}}, + name: "No node needs preemption", + registerPlugin: st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), + nodeNames: []string{"node1"}, + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Req(largeRes).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}}}, - expected: []string{"machine1"}, + st.MakePod().Name("p1").UID("p1").Node("node1").Priority(midPriority).Req(smallRes).StartTime(epochTime).Obj(), + }, + expected: []string{"node1"}, }, { - name: "a pod that fits on both machines when lower priority pods are preempted", - registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), - st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), - }, - nodes: []string{"machine1", "machine2"}, - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority}}, + name: "a pod that fits on both nodes when lower priority pods are preempted", + registerPlugin: st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), + nodeNames: []string{"node1", "node2"}, + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Req(largeRes).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}}, - - {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime}}}, - expected: []string{"machine1", "machine2"}, + st.MakePod().Name("p1").UID("p1").Node("node1").Priority(midPriority).Req(largeRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p2").UID("p2").Node("node2").Priority(midPriority).Req(largeRes).StartTime(epochTime).Obj(), + }, + expected: []string{"node1", "node2"}, }, { - name: "a pod that fits on a machine with no preemption", - registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), - st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), - }, - nodes: []string{"machine1", "machine2", "machine3"}, - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority}}, + name: "a pod that fits on a machine with no preemption", + registerPlugin: st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), + nodeNames: []string{"node1", "node2", "node3"}, + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Req(largeRes).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}}, - - {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime}}}, - expected: []string{"machine3"}, + st.MakePod().Name("p1").UID("p1").Node("node1").Priority(midPriority).Req(largeRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p2").UID("p2").Node("node2").Priority(midPriority).Req(largeRes).StartTime(epochTime).Obj(), + }, + expected: []string{"node3"}, }, { - name: "machine with min highest priority pod is picked", - registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), - st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), - }, - nodes: []string{"machine1", "machine2", "machine3"}, - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: veryLargeContainers, Priority: &highPriority}}, + name: "machine with min highest priority pod is picked", + registerPlugin: st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), + nodeNames: []string{"node1", "node2", "node3"}, + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Req(veryLargeRes).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}}, - - {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m2.2", UID: types.UID("m2.2")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &lowPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime}}, - - {ObjectMeta: metav1.ObjectMeta{Name: "m3.1", UID: types.UID("m3.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &lowPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m3.2", UID: types.UID("m3.2")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &lowPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime}}, + st.MakePod().Name("p1.1").UID("p1.1").Node("node1").Priority(midPriority).Req(mediumRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p1.2").UID("p1.2").Node("node1").Priority(midPriority).Req(largeRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p2.1").UID("p2.1").Node("node2").Priority(midPriority).Req(mediumRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p2.2").UID("p2.2").Node("node2").Priority(lowPriority).Req(mediumRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p3.1").UID("p3.1").Node("node3").Priority(lowPriority).Req(mediumRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p3.2").UID("p3.2").Node("node3").Priority(lowPriority).Req(mediumRes).StartTime(epochTime).Obj(), }, - expected: []string{"machine3"}, + expected: []string{"node3"}, }, { - name: "when highest priorities are the same, minimum sum of priorities is picked", - registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), - st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), - }, - nodes: []string{"machine1", "machine2", "machine3"}, - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: veryLargeContainers, Priority: &highPriority}}, + name: "when highest priorities are the same, minimum sum of priorities is picked", + registerPlugin: st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), + nodeNames: []string{"node1", "node2", "node3"}, + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Req(veryLargeRes).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}}, - - {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m2.2", UID: types.UID("m2.2")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &lowPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime}}, - - {ObjectMeta: metav1.ObjectMeta{Name: "m3.1", UID: types.UID("m3.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m3.2", UID: types.UID("m3.2")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime}}, + st.MakePod().Name("p1.1").UID("p1.1").Node("node1").Priority(midPriority).Req(mediumRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p1.2").UID("p1.2").Node("node1").Priority(midPriority).Req(largeRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p2.1").UID("p2.1").Node("node2").Priority(midPriority).Req(largeRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p2.2").UID("p2.2").Node("node2").Priority(lowPriority).Req(mediumRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p3.1").UID("p3.1").Node("node3").Priority(midPriority).Req(mediumRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p3.2").UID("p3.2").Node("node3").Priority(midPriority).Req(mediumRes).StartTime(epochTime).Obj(), }, - expected: []string{"machine2"}, + expected: []string{"node2"}, }, { - name: "when highest priority and sum are the same, minimum number of pods is picked", - registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), - st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), - }, - nodes: []string{"machine1", "machine2", "machine3"}, - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: veryLargeContainers, Priority: &highPriority}}, + name: "when highest priority and sum are the same, minimum number of pods is picked", + registerPlugin: st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), + nodeNames: []string{"node1", "node2", "node3"}, + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Req(veryLargeRes).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &negPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m1.3", UID: types.UID("m1.3")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m1.4", UID: types.UID("m1.4")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &negPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}}, - - {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m2.2", UID: types.UID("m2.2")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &negPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime}}, - - {ObjectMeta: metav1.ObjectMeta{Name: "m3.1", UID: types.UID("m3.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m3.2", UID: types.UID("m3.2")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &negPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m3.3", UID: types.UID("m3.3")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime}}, + st.MakePod().Name("p1.1").UID("p1.1").Node("node1").Priority(midPriority).Req(smallRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p1.2").UID("p1.2").Node("node1").Priority(negPriority).Req(smallRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p1.3").UID("p1.3").Node("node1").Priority(midPriority).Req(smallRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p1.4").UID("p1.4").Node("node1").Priority(negPriority).Req(smallRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p2.1").UID("p2.1").Node("node2").Priority(midPriority).Req(largeRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p2.2").UID("p2.2").Node("node2").Priority(negPriority).Req(mediumRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p3.1").UID("p3.1").Node("node3").Priority(midPriority).Req(mediumRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p3.2").UID("p3.2").Node("node3").Priority(negPriority).Req(smallRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p3.3").UID("p3.3").Node("node3").Priority(lowPriority).Req(smallRes).StartTime(epochTime).Obj(), }, - expected: []string{"machine2"}, + expected: []string{"node2"}, }, { // pickOneNodeForPreemption adjusts pod priorities when finding the sum of the victims. This // test ensures that the logic works correctly. - name: "sum of adjusted priorities is considered", - registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), - st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), - }, - nodes: []string{"machine1", "machine2", "machine3"}, - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: veryLargeContainers, Priority: &highPriority}}, + name: "sum of adjusted priorities is considered", + registerPlugin: st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), + nodeNames: []string{"node1", "node2", "node3"}, + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Req(veryLargeRes).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &negPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m1.3", UID: types.UID("m1.3")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &negPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}}, - - {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m2.2", UID: types.UID("m2.2")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &negPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime}}, - - {ObjectMeta: metav1.ObjectMeta{Name: "m3.1", UID: types.UID("m3.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m3.2", UID: types.UID("m3.2")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &negPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m3.3", UID: types.UID("m3.3")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime}}, + st.MakePod().Name("p1.1").UID("p1.1").Node("node1").Priority(midPriority).Req(smallRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p1.2").UID("p1.2").Node("node1").Priority(negPriority).Req(smallRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p1.3").UID("p1.3").Node("node1").Priority(negPriority).Req(smallRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p2.1").UID("p2.1").Node("node2").Priority(midPriority).Req(largeRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p2.2").UID("p2.2").Node("node2").Priority(negPriority).Req(mediumRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p3.1").UID("p3.1").Node("node3").Priority(midPriority).Req(mediumRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p3.2").UID("p3.2").Node("node3").Priority(negPriority).Req(smallRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p3.3").UID("p3.3").Node("node3").Priority(lowPriority).Req(smallRes).StartTime(epochTime).Obj(), }, - expected: []string{"machine2"}, + expected: []string{"node2"}, }, { - name: "non-overlapping lowest high priority, sum priorities, and number of pods", - registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), - st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), - }, - nodes: []string{"machine1", "machine2", "machine3", "machine4"}, - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1", UID: types.UID("pod1")}, Spec: v1.PodSpec{Containers: veryLargeContainers, Priority: &veryHighPriority}}, + name: "non-overlapping lowest high priority, sum priorities, and number of pods", + registerPlugin: st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), + nodeNames: []string{"node1", "node2", "node3", "node4"}, + pod: st.MakePod().Name("p").UID("p").Priority(veryHighPriority).Req(veryLargeRes).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m1.3", UID: types.UID("m1.3")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}}, - - {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime}}, - - {ObjectMeta: metav1.ObjectMeta{Name: "m3.1", UID: types.UID("m3.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m3.2", UID: types.UID("m3.2")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m3.3", UID: types.UID("m3.3")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m3.4", UID: types.UID("m3.4")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &lowPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime}}, - - {ObjectMeta: metav1.ObjectMeta{Name: "m4.1", UID: types.UID("m4.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine4"}, Status: v1.PodStatus{StartTime: &startTime}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m4.2", UID: types.UID("m4.2")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &midPriority, NodeName: "machine4"}, Status: v1.PodStatus{StartTime: &startTime}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m4.3", UID: types.UID("m4.3")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &midPriority, NodeName: "machine4"}, Status: v1.PodStatus{StartTime: &startTime}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m4.4", UID: types.UID("m4.4")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &negPriority, NodeName: "machine4"}, Status: v1.PodStatus{StartTime: &startTime}}, + st.MakePod().Name("p1.1").UID("p1.1").Node("node1").Priority(midPriority).Req(smallRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p1.2").UID("p1.2").Node("node1").Priority(lowPriority).Req(smallRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p1.3").UID("p1.3").Node("node1").Priority(lowPriority).Req(smallRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p2.1").UID("p2.1").Node("node2").Priority(highPriority).Req(largeRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p3.1").UID("p3.1").Node("node3").Priority(midPriority).Req(mediumRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p3.2").UID("p3.2").Node("node3").Priority(lowPriority).Req(smallRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p3.3").UID("p3.3").Node("node3").Priority(lowPriority).Req(smallRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p3.4").UID("p3.4").Node("node3").Priority(lowPriority).Req(mediumRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p4.1").UID("p4.1").Node("node4").Priority(midPriority).Req(mediumRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p4.2").UID("p4.2").Node("node4").Priority(midPriority).Req(smallRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p4.3").UID("p4.3").Node("node4").Priority(midPriority).Req(smallRes).StartTime(epochTime).Obj(), + st.MakePod().Name("p4.4").UID("p4.4").Node("node4").Priority(negPriority).Req(smallRes).StartTime(epochTime).Obj(), }, - expected: []string{"machine1"}, + expected: []string{"node1"}, }, { - name: "same priority, same number of victims, different start time for each machine's pod", - registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), - st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), - }, - nodes: []string{"machine1", "machine2", "machine3"}, - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: veryLargeContainers, Priority: &highPriority}}, + name: "same priority, same number of victims, different start time for each machine's pod", + registerPlugin: st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), + nodeNames: []string{"node1", "node2", "node3"}, + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Req(veryLargeRes).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime20190103}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime20190103}}, - - {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime20190104}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m2.2", UID: types.UID("m2.2")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime20190104}}, - - {ObjectMeta: metav1.ObjectMeta{Name: "m3.1", UID: types.UID("m3.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime20190102}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m3.2", UID: types.UID("m3.2")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime20190102}}, + st.MakePod().Name("p1.1").UID("p1.1").Node("node1").Priority(midPriority).Req(mediumRes).StartTime(epochTime2).Obj(), + st.MakePod().Name("p1.2").UID("p1.2").Node("node1").Priority(midPriority).Req(mediumRes).StartTime(epochTime2).Obj(), + st.MakePod().Name("p2.1").UID("p2.1").Node("node2").Priority(midPriority).Req(mediumRes).StartTime(epochTime3).Obj(), + st.MakePod().Name("p2.2").UID("p2.2").Node("node2").Priority(midPriority).Req(mediumRes).StartTime(epochTime3).Obj(), + st.MakePod().Name("p3.1").UID("p3.1").Node("node3").Priority(midPriority).Req(mediumRes).StartTime(epochTime1).Obj(), + st.MakePod().Name("p3.2").UID("p3.2").Node("node3").Priority(midPriority).Req(mediumRes).StartTime(epochTime1).Obj(), }, - expected: []string{"machine2"}, + expected: []string{"node2"}, }, { - name: "same priority, same number of victims, different start time for all pods", - registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), - st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), - }, - nodes: []string{"machine1", "machine2", "machine3"}, - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: veryLargeContainers, Priority: &highPriority}}, + name: "same priority, same number of victims, different start time for all pods", + registerPlugin: st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), + nodeNames: []string{"node1", "node2", "node3"}, + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Req(veryLargeRes).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime20190105}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime20190103}}, - - {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime20190106}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m2.2", UID: types.UID("m2.2")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime20190102}}, - - {ObjectMeta: metav1.ObjectMeta{Name: "m3.1", UID: types.UID("m3.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime20190104}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m3.2", UID: types.UID("m3.2")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime20190107}}, + st.MakePod().Name("p1.1").UID("p1.1").Node("node1").Priority(midPriority).Req(mediumRes).StartTime(epochTime4).Obj(), + st.MakePod().Name("p1.2").UID("p1.2").Node("node1").Priority(midPriority).Req(mediumRes).StartTime(epochTime2).Obj(), + st.MakePod().Name("p2.1").UID("p2.1").Node("node2").Priority(midPriority).Req(mediumRes).StartTime(epochTime5).Obj(), + st.MakePod().Name("p2.2").UID("p2.2").Node("node2").Priority(midPriority).Req(mediumRes).StartTime(epochTime1).Obj(), + st.MakePod().Name("p3.1").UID("p3.1").Node("node3").Priority(midPriority).Req(mediumRes).StartTime(epochTime3).Obj(), + st.MakePod().Name("p3.2").UID("p3.2").Node("node3").Priority(midPriority).Req(mediumRes).StartTime(epochTime6).Obj(), }, - expected: []string{"machine3"}, + expected: []string{"node3"}, }, { - name: "different priority, same number of victims, different start time for all pods", - registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), - st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), - }, - nodes: []string{"machine1", "machine2", "machine3"}, - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: veryLargeContainers, Priority: &highPriority}}, + name: "different priority, same number of victims, different start time for all pods", + registerPlugin: st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), + nodeNames: []string{"node1", "node2", "node3"}, + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Req(veryLargeRes).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime20190105}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime20190103}}, - - {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime20190107}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m2.2", UID: types.UID("m2.2")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &lowPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime20190102}}, - - {ObjectMeta: metav1.ObjectMeta{Name: "m3.1", UID: types.UID("m3.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &lowPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime20190104}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m3.2", UID: types.UID("m3.2")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime20190106}}, + st.MakePod().Name("p1.1").UID("p1.1").Node("node1").Priority(lowPriority).Req(mediumRes).StartTime(epochTime4).Obj(), + st.MakePod().Name("p1.2").UID("p1.2").Node("node1").Priority(midPriority).Req(mediumRes).StartTime(epochTime2).Obj(), + st.MakePod().Name("p2.1").UID("p2.1").Node("node2").Priority(midPriority).Req(mediumRes).StartTime(epochTime6).Obj(), + st.MakePod().Name("p2.2").UID("p2.2").Node("node2").Priority(lowPriority).Req(mediumRes).StartTime(epochTime1).Obj(), + st.MakePod().Name("p3.1").UID("p3.1").Node("node3").Priority(lowPriority).Req(mediumRes).StartTime(epochTime3).Obj(), + st.MakePod().Name("p3.2").UID("p3.2").Node("node3").Priority(midPriority).Req(mediumRes).StartTime(epochTime5).Obj(), }, - expected: []string{"machine2"}, + expected: []string{"node2"}, }, } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var nodes []*v1.Node - for _, n := range test.nodes { - nodes = append(nodes, makeNode(n, schedutil.DefaultMilliCPURequest*5, schedutil.DefaultMemoryRequest*5)) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + nodes := make([]*v1.Node, len(tt.nodeNames)) + for i, nodeName := range tt.nodeNames { + nodes[i] = st.MakeNode().Name(nodeName).Capacity(veryLargeRes).Obj() } - snapshot := internalcache.NewSnapshot(test.pods, nodes) + snapshot := internalcache.NewSnapshot(tt.pods, nodes) fwk, err := st.NewFramework( - test.registerPlugins, + []st.RegisterPluginFunc{ + tt.registerPlugin, + st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), + st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), + }, frameworkruntime.WithPodNominator(internalqueue.NewPodNominator()), frameworkruntime.WithSnapshotSharedLister(snapshot), ) @@ -899,22 +861,22 @@ func TestPickOneNodeForPreemption(t *testing.T) { t.Fatal(err) } - assignDefaultStartTime(test.pods) - - nodeInfos, err := nodesToNodeInfos(nodes, snapshot) + state := framework.NewCycleState() + // Some tests rely on PreFilter plugin to compute its CycleState. + if status := fwk.RunPreFilterPlugins(context.Background(), state, tt.pod); !status.IsSuccess() { + t.Errorf("Unexpected PreFilter Status: %v", status) + } + nodeInfos, err := snapshot.NodeInfos().List() if err != nil { t.Fatal(err) } - state := framework.NewCycleState() - // Some tests rely on PreFilter plugin to compute its CycleState. - preFilterStatus := fwk.RunPreFilterPlugins(context.Background(), state, test.pod) - if !preFilterStatus.IsSuccess() { - t.Errorf("Unexpected preFilterStatus: %v", preFilterStatus) + candidateNodes, err := selectNodesForPreemption(context.Background(), fwk.PreemptHandle(), state, tt.pod, nodeInfos, nil) + if err != nil { + t.Fatal(err) } - candidateNodes, _ := selectNodesForPreemption(context.Background(), fwk.PreemptHandle(), state, test.pod, nodeInfos, nil) node := pickOneNodeForPreemption(candidateNodes) found := false - for _, nodeName := range test.expected { + for _, nodeName := range tt.expected { if node == nodeName { found = true break @@ -928,11 +890,8 @@ func TestPickOneNodeForPreemption(t *testing.T) { } func TestNodesWherePreemptionMightHelp(t *testing.T) { - // Prepare 4 node names. - nodeNames := make([]string, 0, 4) - for i := 1; i < 5; i++ { - nodeNames = append(nodeNames, fmt.Sprintf("machine%d", i)) - } + // Prepare 4 nodes names. + nodeNames := []string{"node1", "node2", "node3", "node4"} tests := []struct { name string @@ -942,97 +901,97 @@ func TestNodesWherePreemptionMightHelp(t *testing.T) { { name: "No node should be attempted", nodesStatuses: framework.NodeToStatusMap{ - "machine1": framework.NewStatus(framework.UnschedulableAndUnresolvable, nodeaffinity.ErrReason), - "machine2": framework.NewStatus(framework.UnschedulableAndUnresolvable, nodename.ErrReason), - "machine3": framework.NewStatus(framework.UnschedulableAndUnresolvable, tainttoleration.ErrReasonNotMatch), - "machine4": framework.NewStatus(framework.UnschedulableAndUnresolvable, nodelabel.ErrReasonPresenceViolated), + "node1": framework.NewStatus(framework.UnschedulableAndUnresolvable, nodeaffinity.ErrReason), + "node2": framework.NewStatus(framework.UnschedulableAndUnresolvable, nodename.ErrReason), + "node3": framework.NewStatus(framework.UnschedulableAndUnresolvable, tainttoleration.ErrReasonNotMatch), + "node4": framework.NewStatus(framework.UnschedulableAndUnresolvable, nodelabel.ErrReasonPresenceViolated), }, expected: map[string]bool{}, }, { name: "ErrReasonAffinityNotMatch should be tried as it indicates that the pod is unschedulable due to inter-pod affinity or anti-affinity", nodesStatuses: framework.NodeToStatusMap{ - "machine1": framework.NewStatus(framework.Unschedulable, interpodaffinity.ErrReasonAffinityNotMatch), - "machine2": framework.NewStatus(framework.UnschedulableAndUnresolvable, nodename.ErrReason), - "machine3": framework.NewStatus(framework.UnschedulableAndUnresolvable, nodeunschedulable.ErrReasonUnschedulable), + "node1": framework.NewStatus(framework.Unschedulable, interpodaffinity.ErrReasonAffinityNotMatch), + "node2": framework.NewStatus(framework.UnschedulableAndUnresolvable, nodename.ErrReason), + "node3": framework.NewStatus(framework.UnschedulableAndUnresolvable, nodeunschedulable.ErrReasonUnschedulable), }, - expected: map[string]bool{"machine1": true, "machine4": true}, + expected: map[string]bool{"node1": true, "node4": true}, }, { name: "pod with both pod affinity and anti-affinity should be tried", nodesStatuses: framework.NodeToStatusMap{ - "machine1": framework.NewStatus(framework.Unschedulable, interpodaffinity.ErrReasonAffinityNotMatch), - "machine2": framework.NewStatus(framework.UnschedulableAndUnresolvable, nodename.ErrReason), + "node1": framework.NewStatus(framework.Unschedulable, interpodaffinity.ErrReasonAffinityNotMatch), + "node2": framework.NewStatus(framework.UnschedulableAndUnresolvable, nodename.ErrReason), }, - expected: map[string]bool{"machine1": true, "machine3": true, "machine4": true}, + expected: map[string]bool{"node1": true, "node3": true, "node4": true}, }, { name: "ErrReasonAffinityRulesNotMatch should not be tried as it indicates that the pod is unschedulable due to inter-pod affinity, but ErrReasonAffinityNotMatch should be tried as it indicates that the pod is unschedulable due to inter-pod affinity or anti-affinity", nodesStatuses: framework.NodeToStatusMap{ - "machine1": framework.NewStatus(framework.UnschedulableAndUnresolvable, interpodaffinity.ErrReasonAffinityRulesNotMatch), - "machine2": framework.NewStatus(framework.Unschedulable, interpodaffinity.ErrReasonAffinityNotMatch), + "node1": framework.NewStatus(framework.UnschedulableAndUnresolvable, interpodaffinity.ErrReasonAffinityRulesNotMatch), + "node2": framework.NewStatus(framework.Unschedulable, interpodaffinity.ErrReasonAffinityNotMatch), }, - expected: map[string]bool{"machine2": true, "machine3": true, "machine4": true}, + expected: map[string]bool{"node2": true, "node3": true, "node4": true}, }, { name: "Mix of failed predicates works fine", nodesStatuses: framework.NodeToStatusMap{ - "machine1": framework.NewStatus(framework.UnschedulableAndUnresolvable, volumerestrictions.ErrReasonDiskConflict), - "machine2": framework.NewStatus(framework.Unschedulable, fmt.Sprintf("Insufficient %v", v1.ResourceMemory)), + "node1": framework.NewStatus(framework.UnschedulableAndUnresolvable, volumerestrictions.ErrReasonDiskConflict), + "node2": framework.NewStatus(framework.Unschedulable, fmt.Sprintf("Insufficient %v", v1.ResourceMemory)), }, - expected: map[string]bool{"machine2": true, "machine3": true, "machine4": true}, + expected: map[string]bool{"node2": true, "node3": true, "node4": true}, }, { name: "Node condition errors should be considered unresolvable", nodesStatuses: framework.NodeToStatusMap{ - "machine1": framework.NewStatus(framework.UnschedulableAndUnresolvable, nodeunschedulable.ErrReasonUnknownCondition), + "node1": framework.NewStatus(framework.UnschedulableAndUnresolvable, nodeunschedulable.ErrReasonUnknownCondition), }, - expected: map[string]bool{"machine2": true, "machine3": true, "machine4": true}, + expected: map[string]bool{"node2": true, "node3": true, "node4": true}, }, { name: "ErrVolume... errors should not be tried as it indicates that the pod is unschedulable due to no matching volumes for pod on node", nodesStatuses: framework.NodeToStatusMap{ - "machine1": framework.NewStatus(framework.UnschedulableAndUnresolvable, volumezone.ErrReasonConflict), - "machine2": framework.NewStatus(framework.UnschedulableAndUnresolvable, string(volumescheduling.ErrReasonNodeConflict)), - "machine3": framework.NewStatus(framework.UnschedulableAndUnresolvable, string(volumescheduling.ErrReasonBindConflict)), + "node1": framework.NewStatus(framework.UnschedulableAndUnresolvable, volumezone.ErrReasonConflict), + "node2": framework.NewStatus(framework.UnschedulableAndUnresolvable, string(volumescheduling.ErrReasonNodeConflict)), + "node3": framework.NewStatus(framework.UnschedulableAndUnresolvable, string(volumescheduling.ErrReasonBindConflict)), }, - expected: map[string]bool{"machine4": true}, + expected: map[string]bool{"node4": true}, }, { name: "ErrTopologySpreadConstraintsNotMatch should be tried as it indicates that the pod is unschedulable due to topology spread constraints", nodesStatuses: framework.NodeToStatusMap{ - "machine1": framework.NewStatus(framework.Unschedulable, podtopologyspread.ErrReasonConstraintsNotMatch), - "machine2": framework.NewStatus(framework.UnschedulableAndUnresolvable, nodename.ErrReason), - "machine3": framework.NewStatus(framework.Unschedulable, podtopologyspread.ErrReasonConstraintsNotMatch), + "node1": framework.NewStatus(framework.Unschedulable, podtopologyspread.ErrReasonConstraintsNotMatch), + "node2": framework.NewStatus(framework.UnschedulableAndUnresolvable, nodename.ErrReason), + "node3": framework.NewStatus(framework.Unschedulable, podtopologyspread.ErrReasonConstraintsNotMatch), }, - expected: map[string]bool{"machine1": true, "machine3": true, "machine4": true}, + expected: map[string]bool{"node1": true, "node3": true, "node4": true}, }, { name: "UnschedulableAndUnresolvable status should be skipped but Unschedulable should be tried", nodesStatuses: framework.NodeToStatusMap{ - "machine2": framework.NewStatus(framework.UnschedulableAndUnresolvable, ""), - "machine3": framework.NewStatus(framework.Unschedulable, ""), - "machine4": framework.NewStatus(framework.UnschedulableAndUnresolvable, ""), + "node2": framework.NewStatus(framework.UnschedulableAndUnresolvable, ""), + "node3": framework.NewStatus(framework.Unschedulable, ""), + "node4": framework.NewStatus(framework.UnschedulableAndUnresolvable, ""), }, - expected: map[string]bool{"machine1": true, "machine3": true}, + expected: map[string]bool{"node1": true, "node3": true}, }, } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { var nodeInfos []*framework.NodeInfo - for _, n := range makeNodeList(nodeNames) { + for _, name := range nodeNames { ni := framework.NewNodeInfo() - ni.SetNode(n) + ni.SetNode(st.MakeNode().Name(name).Obj()) nodeInfos = append(nodeInfos, ni) } - nodes := nodesWherePreemptionMightHelp(nodeInfos, test.nodesStatuses) - if len(test.expected) != len(nodes) { - t.Errorf("number of nodes is not the same as expected. exptectd: %d, got: %d. Nodes: %v", len(test.expected), len(nodes), nodes) + nodes := nodesWherePreemptionMightHelp(nodeInfos, tt.nodesStatuses) + if len(tt.expected) != len(nodes) { + t.Errorf("number of nodes is not the same as expected. exptectd: %d, got: %d. Nodes: %v", len(tt.expected), len(nodes), nodes) } for _, node := range nodes { name := node.Node().Name - if _, found := test.expected[name]; !found { + if _, found := tt.expected[name]; !found { t.Errorf("node %v is not expected.", name) } } @@ -1041,314 +1000,155 @@ func TestNodesWherePreemptionMightHelp(t *testing.T) { } func TestPreempt(t *testing.T) { - defaultFailedNodeToStatusMap := framework.NodeToStatusMap{ - "machine1": framework.NewStatus(framework.Unschedulable, fmt.Sprintf("Insufficient %v", v1.ResourceMemory)), - "machine2": framework.NewStatus(framework.Unschedulable, volumerestrictions.ErrReasonDiskConflict), - "machine3": framework.NewStatus(framework.Unschedulable, fmt.Sprintf("Insufficient %v", v1.ResourceMemory)), - } - // Prepare 3 node names. - var defaultNodeNames []string - for i := 1; i < 4; i++ { - defaultNodeNames = append(defaultNodeNames, fmt.Sprintf("machine%d", i)) - } - var ( - preemptLowerPriority = v1.PreemptLowerPriority - preemptNever = v1.PreemptNever - ) tests := []struct { - name string - pod *v1.Pod - pods []*v1.Pod - extenders []*st.FakeExtender - failedNodeToStatusMap framework.NodeToStatusMap - nodeNames []string - registerPlugins []st.RegisterPluginFunc - expectedNode string - expectedPods []string // list of preempted pods + name string + pod *v1.Pod + pods []*v1.Pod + extenders []*st.FakeExtender + nodeNames []string + registerPlugin st.RegisterPluginFunc + expectedNode string + expectedPods []string // list of preempted pods }{ { name: "basic preemption logic", - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1", UID: types.UID("pod1")}, Spec: v1.PodSpec{ - Containers: veryLargeContainers, - Priority: &highPriority, - PreemptionPolicy: &preemptLowerPriority}, - }, + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Req(veryLargeRes).PreemptionPolicy(v1.PreemptLowerPriority).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority, NodeName: "machine2"}, Status: v1.PodStatus{Phase: v1.PodRunning}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m3.1", UID: types.UID("m3.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine3"}, Status: v1.PodStatus{Phase: v1.PodRunning}}, + st.MakePod().Name("p1.1").UID("p1.1").Node("node1").Priority(lowPriority).Req(smallRes).Obj(), + st.MakePod().Name("p1.2").UID("p1.2").Node("node1").Priority(lowPriority).Req(smallRes).Obj(), + st.MakePod().Name("p2.1").UID("p2.1").Node("node2").Priority(highPriority).Req(largeRes).Obj(), + st.MakePod().Name("p3.1").UID("p3.1").Node("node3").Priority(midPriority).Req(mediumRes).Obj(), }, - registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), - st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), - }, - expectedNode: "machine1", - expectedPods: []string{"m1.1", "m1.2"}, + nodeNames: []string{"node1", "node2", "node3"}, + registerPlugin: st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), + expectedNode: "node1", + expectedPods: []string{"p1.1", "p1.2"}, }, { name: "One node doesn't need any preemption", - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1", UID: types.UID("pod1")}, Spec: v1.PodSpec{ - Containers: veryLargeContainers, - Priority: &highPriority, - PreemptionPolicy: &preemptLowerPriority}, - }, + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Req(veryLargeRes).PreemptionPolicy(v1.PreemptLowerPriority).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}}, - - {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority, NodeName: "machine2"}, Status: v1.PodStatus{Phase: v1.PodRunning}}, + st.MakePod().Name("p1.1").UID("p1.1").Node("node1").Priority(lowPriority).Req(smallRes).Obj(), + st.MakePod().Name("p1.2").UID("p1.2").Node("node1").Priority(lowPriority).Req(smallRes).Obj(), + st.MakePod().Name("p2.1").UID("p2.1").Node("node2").Priority(highPriority).Req(largeRes).Obj(), }, - registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), - st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), - }, - expectedNode: "machine3", - expectedPods: []string{}, + nodeNames: []string{"node1", "node2", "node3"}, + registerPlugin: st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), + expectedNode: "node3", + expectedPods: []string{}, }, { name: "preemption for topology spread constraints", - pod: &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "p", - Labels: map[string]string{"foo": ""}, - }, - Spec: v1.PodSpec{ - Priority: &highPriority, - TopologySpreadConstraints: []v1.TopologySpreadConstraint{ - { - MaxSkew: 1, - TopologyKey: "zone", - WhenUnsatisfiable: v1.DoNotSchedule, - LabelSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: "foo", - Operator: metav1.LabelSelectorOpExists, - }, - }, - }, - }, - { - MaxSkew: 1, - TopologyKey: "hostname", - WhenUnsatisfiable: v1.DoNotSchedule, - LabelSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: "foo", - Operator: metav1.LabelSelectorOpExists, - }, - }, - }, - }, - }, - }, - }, + pod: st.MakePod().Name("p").UID("p").Label("foo", "").Priority(highPriority). + SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj()). + SpreadConstraint(1, "hostname", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj()). + Obj(), pods: []*v1.Pod{ - { - ObjectMeta: metav1.ObjectMeta{Name: "pod-a1", UID: types.UID("pod-a1"), Labels: map[string]string{"foo": ""}}, - Spec: v1.PodSpec{NodeName: "node-a", Priority: &highPriority}, - Status: v1.PodStatus{Phase: v1.PodRunning}, - }, - { - ObjectMeta: metav1.ObjectMeta{Name: "pod-a2", UID: types.UID("pod-a2"), Labels: map[string]string{"foo": ""}}, - Spec: v1.PodSpec{NodeName: "node-a", Priority: &highPriority}, - Status: v1.PodStatus{Phase: v1.PodRunning}, - }, - { - ObjectMeta: metav1.ObjectMeta{Name: "pod-b1", UID: types.UID("pod-b1"), Labels: map[string]string{"foo": ""}}, - Spec: v1.PodSpec{NodeName: "node-b", Priority: &lowPriority}, - Status: v1.PodStatus{Phase: v1.PodRunning}, - }, - { - ObjectMeta: metav1.ObjectMeta{Name: "pod-x1", UID: types.UID("pod-x1"), Labels: map[string]string{"foo": ""}}, - Spec: v1.PodSpec{NodeName: "node-x", Priority: &highPriority}, - Status: v1.PodStatus{Phase: v1.PodRunning}, - }, - { - ObjectMeta: metav1.ObjectMeta{Name: "pod-x2", UID: types.UID("pod-x2"), Labels: map[string]string{"foo": ""}}, - Spec: v1.PodSpec{NodeName: "node-x", Priority: &highPriority}, - Status: v1.PodStatus{Phase: v1.PodRunning}, - }, + st.MakePod().Name("p-a1").UID("p-a1").Node("node-a").Label("foo", "").Priority(highPriority).Obj(), + st.MakePod().Name("p-a2").UID("p-a2").Node("node-a").Label("foo", "").Priority(highPriority).Obj(), + st.MakePod().Name("p-b1").UID("p-b1").Node("node-b").Label("foo", "").Priority(lowPriority).Obj(), + st.MakePod().Name("p-x1").UID("p-x1").Node("node-x").Label("foo", "").Priority(highPriority).Obj(), + st.MakePod().Name("p-x2").UID("p-x2").Node("node-x").Label("foo", "").Priority(highPriority).Obj(), }, - failedNodeToStatusMap: framework.NodeToStatusMap{ - "node-a": framework.NewStatus(framework.Unschedulable, podtopologyspread.ErrReasonConstraintsNotMatch), - "node-b": framework.NewStatus(framework.Unschedulable, podtopologyspread.ErrReasonConstraintsNotMatch), - "node-x": framework.NewStatus(framework.Unschedulable, podtopologyspread.ErrReasonConstraintsNotMatch), - }, - nodeNames: []string{"node-a/zone1", "node-b/zone1", "node-x/zone2"}, - registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), - st.RegisterPluginAsExtensions( - podtopologyspread.Name, - podtopologyspread.New, - "PreFilter", - "Filter", - ), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), - }, - expectedNode: "node-b", - expectedPods: []string{"pod-b1"}, + nodeNames: []string{"node-a/zone1", "node-b/zone1", "node-x/zone2"}, + registerPlugin: st.RegisterPluginAsExtensions(podtopologyspread.Name, podtopologyspread.New, "PreFilter", "Filter"), + expectedNode: "node-b", + expectedPods: []string{"p-b1"}, }, { name: "Scheduler extenders allow only machine1, otherwise machine3 would have been chosen", - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1", UID: types.UID("pod1")}, Spec: v1.PodSpec{ - Containers: veryLargeContainers, - Priority: &highPriority, - PreemptionPolicy: &preemptLowerPriority}, - }, + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Req(veryLargeRes).PreemptionPolicy(v1.PreemptLowerPriority).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}}, - - {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine2"}, Status: v1.PodStatus{Phase: v1.PodRunning}}, + st.MakePod().Name("p1.1").UID("p1.1").Node("machine1").Priority(midPriority).Req(smallRes).Obj(), + st.MakePod().Name("p1.2").UID("p1.2").Node("machine1").Priority(lowPriority).Req(smallRes).Obj(), + st.MakePod().Name("p2.1").UID("p2.1").Node("machine3").Priority(midPriority).Req(largeRes).Obj(), }, + nodeNames: []string{"machine1", "machine2", "machine3"}, extenders: []*st.FakeExtender{ - { - Predicates: []st.FitPredicate{st.TruePredicateExtender}, - }, - { - Predicates: []st.FitPredicate{st.Machine1PredicateExtender}, - }, + {Predicates: []st.FitPredicate{st.TruePredicateExtender}}, + {Predicates: []st.FitPredicate{st.Machine1PredicateExtender}}, }, - registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), - st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), - }, - expectedNode: "machine1", - expectedPods: []string{"m1.1", "m1.2"}, + registerPlugin: st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), + expectedNode: "machine1", + expectedPods: []string{"p1.1", "p1.2"}, }, { name: "Scheduler extenders do not allow any preemption", - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1", UID: types.UID("pod1")}, Spec: v1.PodSpec{ - Containers: veryLargeContainers, - Priority: &highPriority, - PreemptionPolicy: &preemptLowerPriority}, - }, + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Req(veryLargeRes).PreemptionPolicy(v1.PreemptLowerPriority).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}}, - - {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine2"}, Status: v1.PodStatus{Phase: v1.PodRunning}}, + st.MakePod().Name("p1.1").UID("p1.1").Node("node1").Priority(midPriority).Req(smallRes).Obj(), + st.MakePod().Name("p1.2").UID("p1.2").Node("node1").Priority(lowPriority).Req(smallRes).Obj(), + st.MakePod().Name("p2.1").UID("p2.1").Node("node2").Priority(midPriority).Req(largeRes).Obj(), }, + nodeNames: []string{"node1", "node2", "node3"}, extenders: []*st.FakeExtender{ - { - Predicates: []st.FitPredicate{st.FalsePredicateExtender}, - }, + {Predicates: []st.FitPredicate{st.FalsePredicateExtender}}, }, - registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), - st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), - }, - expectedNode: "", - expectedPods: []string{}, + registerPlugin: st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), + expectedNode: "", + expectedPods: []string{}, }, { name: "One scheduler extender allows only machine1, the other returns error but ignorable. Only machine1 would be chosen", - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1", UID: types.UID("pod1")}, Spec: v1.PodSpec{ - Containers: veryLargeContainers, - Priority: &highPriority, - PreemptionPolicy: &preemptLowerPriority}, - }, + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Req(veryLargeRes).PreemptionPolicy(v1.PreemptLowerPriority).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}}, - - {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine2"}, Status: v1.PodStatus{Phase: v1.PodRunning}}, + st.MakePod().Name("p1.1").UID("p1.1").Node("machine1").Priority(midPriority).Req(smallRes).Obj(), + st.MakePod().Name("p1.2").UID("p1.2").Node("machine1").Priority(lowPriority).Req(smallRes).Obj(), + st.MakePod().Name("p2.1").UID("p2.1").Node("machine2").Priority(midPriority).Req(largeRes).Obj(), }, + nodeNames: []string{"machine1", "machine2", "machine3"}, extenders: []*st.FakeExtender{ - { - Predicates: []st.FitPredicate{st.ErrorPredicateExtender}, - Ignorable: true, - }, - { - Predicates: []st.FitPredicate{st.Machine1PredicateExtender}, - }, + {Predicates: []st.FitPredicate{st.ErrorPredicateExtender}, Ignorable: true}, + {Predicates: []st.FitPredicate{st.Machine1PredicateExtender}}, }, - registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), - st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), - }, - expectedNode: "machine1", - expectedPods: []string{"m1.1", "m1.2"}, + registerPlugin: st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), + expectedNode: "machine1", + expectedPods: []string{"p1.1", "p1.2"}, }, { - name: "One scheduler extender allows only machine1, but it is not interested in given pod, otherwise machine1 would have been chosen", - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1", UID: types.UID("pod1")}, Spec: v1.PodSpec{ - Containers: veryLargeContainers, - Priority: &highPriority, - PreemptionPolicy: &preemptLowerPriority}, - }, + name: "One scheduler extender allows only machine1, but it is not interested in given pod, otherwise node1 would have been chosen", + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Req(veryLargeRes).PreemptionPolicy(v1.PreemptLowerPriority).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}}, - - {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine2"}, Status: v1.PodStatus{Phase: v1.PodRunning}}, + st.MakePod().Name("p1.1").UID("p1.1").Node("node1").Priority(midPriority).Req(smallRes).Obj(), + st.MakePod().Name("p1.2").UID("p1.2").Node("node1").Priority(lowPriority).Req(smallRes).Obj(), + st.MakePod().Name("p2.1").UID("p2.1").Node("node2").Priority(midPriority).Req(largeRes).Obj(), }, + nodeNames: []string{"node1", "node2", "node3"}, extenders: []*st.FakeExtender{ - { - Predicates: []st.FitPredicate{st.Machine1PredicateExtender}, - UnInterested: true, - }, - { - Predicates: []st.FitPredicate{st.TruePredicateExtender}, - }, + {Predicates: []st.FitPredicate{st.Machine1PredicateExtender}, UnInterested: true}, + {Predicates: []st.FitPredicate{st.TruePredicateExtender}}, }, - registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), - st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), - }, - expectedNode: "machine3", - expectedPods: []string{}, + registerPlugin: st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), + expectedNode: "node3", + expectedPods: []string{}, }, { name: "no preempting in pod", - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1", UID: types.UID("pod1")}, Spec: v1.PodSpec{ - Containers: veryLargeContainers, - Priority: &highPriority, - PreemptionPolicy: &preemptNever}, - }, + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Req(veryLargeRes).PreemptionPolicy(v1.PreemptNever).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority, NodeName: "machine2"}, Status: v1.PodStatus{Phase: v1.PodRunning}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m3.1", UID: types.UID("m3.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine3"}, Status: v1.PodStatus{Phase: v1.PodRunning}}, + st.MakePod().Name("p1.1").UID("p1.1").Node("node1").Priority(lowPriority).Req(smallRes).Obj(), + st.MakePod().Name("p1.2").UID("p1.2").Node("node1").Priority(lowPriority).Req(smallRes).Obj(), + st.MakePod().Name("p2.1").UID("p2.1").Node("node2").Priority(highPriority).Req(largeRes).Obj(), + st.MakePod().Name("p3.1").UID("p3.1").Node("node3").Priority(midPriority).Req(mediumRes).Obj(), }, - registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), - st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), - }, - expectedNode: "", - expectedPods: nil, + nodeNames: []string{"node1", "node2", "node3"}, + registerPlugin: st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), + expectedNode: "", + expectedPods: nil, }, { name: "PreemptionPolicy is nil", - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1", UID: types.UID("pod1")}, Spec: v1.PodSpec{ - Containers: veryLargeContainers, - Priority: &highPriority, - PreemptionPolicy: nil}, - }, + pod: st.MakePod().Name("p").UID("p").Priority(highPriority).Req(veryLargeRes).Obj(), pods: []*v1.Pod{ - {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority, NodeName: "machine2"}, Status: v1.PodStatus{Phase: v1.PodRunning}}, - {ObjectMeta: metav1.ObjectMeta{Name: "m3.1", UID: types.UID("m3.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine3"}, Status: v1.PodStatus{Phase: v1.PodRunning}}, + st.MakePod().Name("p1.1").UID("p1.1").Node("node1").Priority(lowPriority).Req(smallRes).Obj(), + st.MakePod().Name("p1.2").UID("p1.2").Node("node1").Priority(lowPriority).Req(smallRes).Obj(), + st.MakePod().Name("p2.1").UID("p2.1").Node("node2").Priority(highPriority).Req(largeRes).Obj(), + st.MakePod().Name("p3.1").UID("p3.1").Node("node3").Priority(midPriority).Req(mediumRes).Obj(), }, - registerPlugins: []st.RegisterPluginFunc{ - st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), - st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), - st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), - }, - expectedNode: "machine1", - expectedPods: []string{"m1.1", "m1.2"}, + nodeNames: []string{"node1", "node2", "node3"}, + registerPlugin: st.RegisterPluginAsExtensions(noderesources.FitName, noderesources.NewFit, "Filter", "PreFilter"), + expectedNode: "node1", + expectedPods: []string{"p1.1", "p1.2"}, }, } @@ -1364,19 +1164,17 @@ func TestPreempt(t *testing.T) { }) stop := make(chan struct{}) + defer close(stop) + cache := internalcache.New(time.Duration(0), stop) for _, pod := range test.pods { cache.AddPod(pod) } cachedNodeInfoMap := map[string]*framework.NodeInfo{} - nodeNames := defaultNodeNames - if len(test.nodeNames) != 0 { - nodeNames = test.nodeNames - } - var nodes []*v1.Node - for i, name := range nodeNames { - node := makeNode(name, 1000*5, schedutil.DefaultMemoryRequest*5) - // if possible, split node name by '/' to form labels in a format of + nodes := make([]*v1.Node, len(test.nodeNames)) + for i, name := range test.nodeNames { + node := st.MakeNode().Name(name).Capacity(veryLargeRes).Obj() + // Split node name by '/' to form labels in a format of // {"hostname": node.Name[0], "zone": node.Name[1], "region": node.Name[2]} node.ObjectMeta.Labels = make(map[string]string) for i, label := range strings.Split(node.Name, "/") { @@ -1384,8 +1182,7 @@ func TestPreempt(t *testing.T) { } node.Name = node.ObjectMeta.Labels["hostname"] cache.AddNode(node) - nodes = append(nodes, node) - nodeNames[i] = node.Name + nodes[i] = node // Set nodeInfo to extenders to mock extenders' cache for preemption. cachedNodeInfo := framework.NewNodeInfo() @@ -1399,15 +1196,17 @@ func TestPreempt(t *testing.T) { extenders = append(extenders, extender) } - podNominator := internalqueue.NewPodNominator() - snapshot := internalcache.NewSnapshot(test.pods, nodes) fwk, err := st.NewFramework( - test.registerPlugins, + []st.RegisterPluginFunc{ + test.registerPlugin, + st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New), + st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New), + }, frameworkruntime.WithClientSet(client), frameworkruntime.WithEventRecorder(&events.FakeRecorder{}), frameworkruntime.WithExtenders(extenders), - frameworkruntime.WithPodNominator(podNominator), - frameworkruntime.WithSnapshotSharedLister(snapshot), + frameworkruntime.WithPodNominator(internalqueue.NewPodNominator()), + frameworkruntime.WithSnapshotSharedLister(internalcache.NewSnapshot(test.pods, nodes)), frameworkruntime.WithInformerFactory(informers.NewSharedInformerFactory(client, 0)), ) if err != nil { @@ -1421,11 +1220,7 @@ func TestPreempt(t *testing.T) { t.Errorf("Unexpected preFilterStatus: %v", preFilterStatus) } // Call preempt and check the expected results. - failedNodeToStatusMap := defaultFailedNodeToStatusMap - if test.failedNodeToStatusMap != nil { - failedNodeToStatusMap = test.failedNodeToStatusMap - } - node, err := preempt(context.Background(), fwk, state, test.pod, failedNodeToStatusMap) + node, err := preempt(context.Background(), fwk, state, test.pod, make(framework.NodeToStatusMap)) if err != nil { t.Errorf("unexpected error in preemption: %v", err) } @@ -1463,14 +1258,13 @@ func TestPreempt(t *testing.T) { } // Call preempt again and make sure it doesn't preempt any more pods. - node, err = preempt(context.Background(), fwk, state, test.pod, failedNodeToStatusMap) + node, err = preempt(context.Background(), fwk, state, test.pod, make(framework.NodeToStatusMap)) if err != nil { t.Errorf("unexpected error in preemption: %v", err) } if len(node) != 0 && len(deletedPodNames) > 0 { t.Errorf("didn't expect any more preemption. Node %v is selected for preemption.", node) } - close(stop) }) } } diff --git a/pkg/scheduler/testing/wrappers.go b/pkg/scheduler/testing/wrappers.go index ae014c03854..5cf30532ded 100644 --- a/pkg/scheduler/testing/wrappers.go +++ b/pkg/scheduler/testing/wrappers.go @@ -238,6 +238,12 @@ func (p *PodWrapper) NodeAffinityNotIn(key string, vals []string) *PodWrapper { return p } +// StartTime sets `t` as .status.startTime for the inner pod. +func (p *PodWrapper) StartTime(t metav1.Time) *PodWrapper { + p.Status.StartTime = &t + return p +} + // PodAffinityKind represents different kinds of PodAffinity. type PodAffinityKind int @@ -358,6 +364,30 @@ func (p *PodWrapper) Label(k, v string) *PodWrapper { return p } +// Req adds a new container to the inner pod with given resource map. +func (p *PodWrapper) Req(resMap map[v1.ResourceName]string) *PodWrapper { + if len(resMap) == 0 { + return p + } + + res := v1.ResourceList{} + for k, v := range resMap { + res[k] = resource.MustParse(v) + } + p.Spec.Containers = append(p.Spec.Containers, v1.Container{ + Resources: v1.ResourceRequirements{ + Requests: res, + }, + }) + return p +} + +// PreemptionPolicy sets the give preemption policy to the inner pod. +func (p *PodWrapper) PreemptionPolicy(policy v1.PreemptionPolicy) *PodWrapper { + p.Spec.PreemptionPolicy = &policy + return p +} + // NodeWrapper wraps a Node inside. type NodeWrapper struct{ v1.Node }