func TestSelectorSpreadPriority(t *testing.T) { labels1 := map[string]string{ "foo": "bar", "baz": "blah", } labels2 := map[string]string{ "bar": "foo", "baz": "blah", } zone1Spec := api.PodSpec{ NodeName: "machine1", } zone2Spec := api.PodSpec{ NodeName: "machine2", } tests := []struct { pod *api.Pod pods []*api.Pod nodes []string rcs []api.ReplicationController services []api.Service expectedList algorithm.HostPriorityList test string }{ { pod: new(api.Pod), nodes: []string{"machine1", "machine2"}, expectedList: []algorithm.HostPriority{{"machine1", 10}, {"machine2", 10}}, test: "nothing scheduled", }, { pod: &api.Pod{ObjectMeta: api.ObjectMeta{Labels: labels1}}, pods: []*api.Pod{{Spec: zone1Spec}}, nodes: []string{"machine1", "machine2"}, expectedList: []algorithm.HostPriority{{"machine1", 10}, {"machine2", 10}}, test: "no services", }, { pod: &api.Pod{ObjectMeta: api.ObjectMeta{Labels: labels1}}, pods: []*api.Pod{{Spec: zone1Spec, ObjectMeta: api.ObjectMeta{Labels: labels2}}}, nodes: []string{"machine1", "machine2"}, services: []api.Service{{Spec: api.ServiceSpec{Selector: map[string]string{"key": "value"}}}}, expectedList: []algorithm.HostPriority{{"machine1", 10}, {"machine2", 10}}, test: "different services", }, { pod: &api.Pod{ObjectMeta: api.ObjectMeta{Labels: labels1}}, pods: []*api.Pod{ {Spec: zone1Spec, ObjectMeta: api.ObjectMeta{Labels: labels2}}, {Spec: zone2Spec, ObjectMeta: api.ObjectMeta{Labels: labels1}}, }, nodes: []string{"machine1", "machine2"}, services: []api.Service{{Spec: api.ServiceSpec{Selector: labels1}}}, expectedList: []algorithm.HostPriority{{"machine1", 10}, {"machine2", 0}}, test: "two pods, one service pod", }, { pod: &api.Pod{ObjectMeta: api.ObjectMeta{Labels: labels1}}, pods: []*api.Pod{ {Spec: zone1Spec, ObjectMeta: api.ObjectMeta{Labels: labels2}}, {Spec: zone1Spec, ObjectMeta: api.ObjectMeta{Labels: labels1, Namespace: api.NamespaceDefault}}, {Spec: zone1Spec, ObjectMeta: api.ObjectMeta{Labels: labels1, Namespace: "ns1"}}, {Spec: zone2Spec, ObjectMeta: api.ObjectMeta{Labels: labels1}}, {Spec: zone2Spec, ObjectMeta: api.ObjectMeta{Labels: labels2}}, }, nodes: []string{"machine1", "machine2"}, services: []api.Service{{Spec: api.ServiceSpec{Selector: labels1}}}, expectedList: []algorithm.HostPriority{{"machine1", 10}, {"machine2", 0}}, test: "five pods, one service pod in no namespace", }, { pod: &api.Pod{ObjectMeta: api.ObjectMeta{Labels: labels1, Namespace: api.NamespaceDefault}}, pods: []*api.Pod{ {Spec: zone1Spec, ObjectMeta: api.ObjectMeta{Labels: labels1}}, {Spec: zone1Spec, ObjectMeta: api.ObjectMeta{Labels: labels1, Namespace: "ns1"}}, {Spec: zone2Spec, ObjectMeta: api.ObjectMeta{Labels: labels1, Namespace: api.NamespaceDefault}}, {Spec: zone2Spec, ObjectMeta: api.ObjectMeta{Labels: labels2}}, }, nodes: []string{"machine1", "machine2"}, services: []api.Service{{Spec: api.ServiceSpec{Selector: labels1}, ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault}}}, expectedList: []algorithm.HostPriority{{"machine1", 10}, {"machine2", 0}}, test: "four pods, one service pod in default namespace", }, { pod: &api.Pod{ObjectMeta: api.ObjectMeta{Labels: labels1, Namespace: "ns1"}}, pods: []*api.Pod{ {Spec: zone1Spec, ObjectMeta: api.ObjectMeta{Labels: labels1}}, {Spec: zone1Spec, ObjectMeta: api.ObjectMeta{Labels: labels1, Namespace: api.NamespaceDefault}}, {Spec: zone1Spec, ObjectMeta: api.ObjectMeta{Labels: labels1, Namespace: "ns2"}}, {Spec: zone2Spec, ObjectMeta: api.ObjectMeta{Labels: labels1, Namespace: "ns1"}}, {Spec: zone2Spec, ObjectMeta: api.ObjectMeta{Labels: labels2}}, }, nodes: []string{"machine1", "machine2"}, services: []api.Service{{Spec: api.ServiceSpec{Selector: labels1}, ObjectMeta: api.ObjectMeta{Namespace: "ns1"}}}, expectedList: []algorithm.HostPriority{{"machine1", 10}, {"machine2", 0}}, test: "five pods, one service pod in specific namespace", }, { pod: &api.Pod{ObjectMeta: api.ObjectMeta{Labels: labels1}}, pods: []*api.Pod{ {Spec: zone1Spec, ObjectMeta: api.ObjectMeta{Labels: labels2}}, {Spec: zone1Spec, ObjectMeta: api.ObjectMeta{Labels: labels1}}, {Spec: zone2Spec, ObjectMeta: api.ObjectMeta{Labels: labels1}}, }, nodes: []string{"machine1", "machine2"}, services: []api.Service{{Spec: api.ServiceSpec{Selector: labels1}}}, expectedList: []algorithm.HostPriority{{"machine1", 0}, {"machine2", 0}}, test: "three pods, two service pods on different machines", }, { pod: &api.Pod{ObjectMeta: api.ObjectMeta{Labels: labels1}}, pods: []*api.Pod{ {Spec: zone1Spec, ObjectMeta: api.ObjectMeta{Labels: labels2}}, {Spec: zone1Spec, ObjectMeta: api.ObjectMeta{Labels: labels1}}, {Spec: zone2Spec, ObjectMeta: api.ObjectMeta{Labels: labels1}}, {Spec: zone2Spec, ObjectMeta: api.ObjectMeta{Labels: labels1}}, }, nodes: []string{"machine1", "machine2"}, services: []api.Service{{Spec: api.ServiceSpec{Selector: labels1}}}, expectedList: []algorithm.HostPriority{{"machine1", 5}, {"machine2", 0}}, test: "four pods, three service pods", }, { pod: &api.Pod{ObjectMeta: api.ObjectMeta{Labels: labels1}}, pods: []*api.Pod{ {Spec: zone1Spec, ObjectMeta: api.ObjectMeta{Labels: labels2}}, {Spec: zone1Spec, ObjectMeta: api.ObjectMeta{Labels: labels1}}, {Spec: zone2Spec, ObjectMeta: api.ObjectMeta{Labels: labels1}}, }, nodes: []string{"machine1", "machine2"}, services: []api.Service{{Spec: api.ServiceSpec{Selector: map[string]string{"baz": "blah"}}}}, expectedList: []algorithm.HostPriority{{"machine1", 0}, {"machine2", 5}}, test: "service with partial pod label matches", }, { pod: &api.Pod{ObjectMeta: api.ObjectMeta{Labels: labels1}}, pods: []*api.Pod{ {Spec: zone1Spec, ObjectMeta: api.ObjectMeta{Labels: labels2}}, {Spec: zone1Spec, ObjectMeta: api.ObjectMeta{Labels: labels1}}, {Spec: zone2Spec, ObjectMeta: api.ObjectMeta{Labels: labels1}}, }, nodes: []string{"machine1", "machine2"}, services: []api.Service{{Spec: api.ServiceSpec{Selector: map[string]string{"baz": "blah"}}}}, rcs: []api.ReplicationController{{Spec: api.ReplicationControllerSpec{Selector: map[string]string{"foo": "bar"}}}}, // "baz=blah" matches both labels1 and labels2, and "foo=bar" matches only labels 1. This means that we assume that we want to // do spreading between all pods. The result should be exactly as above. expectedList: []algorithm.HostPriority{{"machine1", 0}, {"machine2", 5}}, test: "service with partial pod label matches with service and replication controller", }, { pod: &api.Pod{ObjectMeta: api.ObjectMeta{Labels: map[string]string{"foo": "bar", "bar": "foo"}}}, pods: []*api.Pod{ {Spec: zone1Spec, ObjectMeta: api.ObjectMeta{Labels: labels2}}, {Spec: zone1Spec, ObjectMeta: api.ObjectMeta{Labels: labels1}}, {Spec: zone2Spec, ObjectMeta: api.ObjectMeta{Labels: labels1}}, }, nodes: []string{"machine1", "machine2"}, services: []api.Service{{Spec: api.ServiceSpec{Selector: map[string]string{"bar": "foo"}}}}, rcs: []api.ReplicationController{{Spec: api.ReplicationControllerSpec{Selector: map[string]string{"foo": "bar"}}}}, // Taken together Service and Replication Controller should match all Pods, hence result should be equal to one above. expectedList: []algorithm.HostPriority{{"machine1", 0}, {"machine2", 5}}, test: "disjoined service and replication controller should be treated equally", }, { pod: &api.Pod{ObjectMeta: api.ObjectMeta{Labels: labels1}}, pods: []*api.Pod{ {Spec: zone1Spec, ObjectMeta: api.ObjectMeta{Labels: labels2}}, {Spec: zone1Spec, ObjectMeta: api.ObjectMeta{Labels: labels1}}, {Spec: zone2Spec, ObjectMeta: api.ObjectMeta{Labels: labels1}}, }, nodes: []string{"machine1", "machine2"}, rcs: []api.ReplicationController{{Spec: api.ReplicationControllerSpec{Selector: map[string]string{"foo": "bar"}}}}, // Both Nodes have one pod from the given RC, hence both get 0 score. expectedList: []algorithm.HostPriority{{"machine1", 0}, {"machine2", 0}}, test: "Replication controller with partial pod label matches", }, { pod: &api.Pod{ObjectMeta: api.ObjectMeta{Labels: labels1}}, pods: []*api.Pod{ {Spec: zone1Spec, ObjectMeta: api.ObjectMeta{Labels: labels2}}, {Spec: zone1Spec, ObjectMeta: api.ObjectMeta{Labels: labels1}}, {Spec: zone2Spec, ObjectMeta: api.ObjectMeta{Labels: labels1}}, }, nodes: []string{"machine1", "machine2"}, rcs: []api.ReplicationController{{Spec: api.ReplicationControllerSpec{Selector: map[string]string{"baz": "blah"}}}}, expectedList: []algorithm.HostPriority{{"machine1", 0}, {"machine2", 5}}, test: "Replication controller with partial pod label matches", }, } for _, test := range tests { selectorSpread := SelectorSpread{serviceLister: algorithm.FakeServiceLister(test.services), controllerLister: algorithm.FakeControllerLister(test.rcs)} list, err := selectorSpread.CalculateSpreadPriority(test.pod, algorithm.FakePodLister(test.pods), algorithm.FakeNodeLister(makeNodeList(test.nodes))) if err != nil { t.Errorf("unexpected error: %v", err) } if !reflect.DeepEqual(test.expectedList, list) { t.Errorf("%s: expected %#v, got %#v", test.test, test.expectedList, list) } } }
func TestZeroRequest(t *testing.T) { // A pod with no resources. We expect spreading to count it as having the default resources. noResources := api.PodSpec{ Containers: []api.Container{ {}, }, } noResources1 := noResources noResources1.NodeName = "machine1" // A pod with the same resources as a 0-request pod gets by default as its resources (for spreading). small := api.PodSpec{ Containers: []api.Container{ { Resources: api.ResourceRequirements{ Requests: api.ResourceList{ "cpu": resource.MustParse( strconv.FormatInt(defaultMilliCpuRequest, 10) + "m"), "memory": resource.MustParse( strconv.FormatInt(defaultMemoryRequest, 10)), }, }, }, }, } small2 := small small2.NodeName = "machine2" // A larger pod. large := api.PodSpec{ Containers: []api.Container{ { Resources: api.ResourceRequirements{ Requests: api.ResourceList{ "cpu": resource.MustParse( strconv.FormatInt(defaultMilliCpuRequest*3, 10) + "m"), "memory": resource.MustParse( strconv.FormatInt(defaultMemoryRequest*3, 10)), }, }, }, }, } large1 := large large1.NodeName = "machine1" large2 := large large2.NodeName = "machine2" tests := []struct { pod *api.Pod pods []*api.Pod nodes []api.Node test string }{ // The point of these next two tests is to show you get the same priority for a zero-request pod // as for a pod with the defaults requests, both when the zero-request pod is already on the machine // and when the zero-request pod is the one being scheduled. { pod: &api.Pod{Spec: noResources}, nodes: []api.Node{makeNode("machine1", 1000, defaultMemoryRequest*10), makeNode("machine2", 1000, defaultMemoryRequest*10)}, test: "test priority of zero-request pod with machine with zero-request pod", pods: []*api.Pod{ {Spec: large1}, {Spec: noResources1}, {Spec: large2}, {Spec: small2}, }, }, { pod: &api.Pod{Spec: small}, nodes: []api.Node{makeNode("machine1", 1000, defaultMemoryRequest*10), makeNode("machine2", 1000, defaultMemoryRequest*10)}, test: "test priority of nonzero-request pod with machine with zero-request pod", pods: []*api.Pod{ {Spec: large1}, {Spec: noResources1}, {Spec: large2}, {Spec: small2}, }, }, // The point of this test is to verify that we're not just getting the same score no matter what we schedule. { pod: &api.Pod{Spec: large}, nodes: []api.Node{makeNode("machine1", 1000, defaultMemoryRequest*10), makeNode("machine2", 1000, defaultMemoryRequest*10)}, test: "test priority of larger pod with machine with zero-request pod", pods: []*api.Pod{ {Spec: large1}, {Spec: noResources1}, {Spec: large2}, {Spec: small2}, }, }, } const expectedPriority int = 25 for _, test := range tests { list, err := scheduler.PrioritizeNodes( test.pod, algorithm.FakePodLister(test.pods), // This should match the configuration in defaultPriorities() in // plugin/pkg/scheduler/algorithmprovider/defaults/defaults.go if you want // to test what's actually in production. []algorithm.PriorityConfig{{Function: LeastRequestedPriority, Weight: 1}, {Function: BalancedResourceAllocation, Weight: 1}, {Function: NewSelectorSpreadPriority(algorithm.FakeServiceLister([]api.Service{}), algorithm.FakeControllerLister([]api.ReplicationController{})), Weight: 1}}, algorithm.FakeNodeLister(api.NodeList{Items: test.nodes})) if err != nil { t.Errorf("unexpected error: %v", err) } for _, hp := range list { if test.test == "test priority of larger pod with machine with zero-request pod" { if hp.Score == expectedPriority { t.Errorf("%s: expected non-%d for all priorities, got list %#v", test.test, expectedPriority, list) } } else { if hp.Score != expectedPriority { t.Errorf("%s: expected %d for all priorities, got list %#v", test.test, expectedPriority, list) } } } } }