From 1aaa5fcc807290747d0e0a6e5986517b153d523d Mon Sep 17 00:00:00 2001 From: Aldo Culquicondor Date: Wed, 22 Apr 2020 11:42:02 -0400 Subject: [PATCH] Topology scoring with dynamically weighted topologies Signed-off-by: Aldo Culquicondor --- .../plugins/podtopologyspread/scoring.go | 69 ++++-- .../plugins/podtopologyspread/scoring_test.go | 211 ++++++++---------- 2 files changed, 144 insertions(+), 136 deletions(-) diff --git a/pkg/scheduler/framework/plugins/podtopologyspread/scoring.go b/pkg/scheduler/framework/plugins/podtopologyspread/scoring.go index 29af84a2d0c..a87828020e7 100644 --- a/pkg/scheduler/framework/plugins/podtopologyspread/scoring.go +++ b/pkg/scheduler/framework/plugins/podtopologyspread/scoring.go @@ -39,6 +39,10 @@ type preScoreState struct { IgnoredNodes sets.String // TopologyPairToPodCounts is keyed with topologyPair, and valued with the number of matching pods. TopologyPairToPodCounts map[topologyPair]*int64 + // TopologyNormalizingWeight is the weight we give to the counts per topology. + // This allows the pod counts of smaller topologies to not be watered down by + // bigger ones. + TopologyNormalizingWeight []float64 } // Clone implements the mandatory Clone interface. We don't really copy the data since @@ -51,6 +55,7 @@ func (s *preScoreState) Clone() framework.StateData { // don't have required topologyKey(s), and initialize: // 1) s.TopologyPairToPodCounts: keyed with both eligible topology pair and node names. // 2) s.IgnoredNodes: the set of nodes that shouldn't be scored. +// 3) s.TopologyNormalizingWeight: The weight to be given to each constraint based on the number of values in a topology. func (pl *PodTopologySpread) initPreScoreState(s *preScoreState, pod *v1.Pod, filteredNodes []*v1.Node) error { var err error if len(pod.Spec.TopologySpreadConstraints) > 0 { @@ -67,6 +72,7 @@ func (pl *PodTopologySpread) initPreScoreState(s *preScoreState, pod *v1.Pod, fi if len(s.Constraints) == 0 { return nil } + topoSize := make([]int, len(s.Constraints)) for _, node := range filteredNodes { if !nodeLabelsMatchSpreadConstraints(node.Labels, s.Constraints) { // Nodes which don't have all required topologyKeys present are ignored @@ -74,7 +80,7 @@ func (pl *PodTopologySpread) initPreScoreState(s *preScoreState, pod *v1.Pod, fi s.IgnoredNodes.Insert(node.Name) continue } - for _, constraint := range s.Constraints { + for i, constraint := range s.Constraints { // per-node counts are calculated during Score. if constraint.TopologyKey == v1.LabelHostname { continue @@ -82,9 +88,19 @@ func (pl *PodTopologySpread) initPreScoreState(s *preScoreState, pod *v1.Pod, fi pair := topologyPair{key: constraint.TopologyKey, value: node.Labels[constraint.TopologyKey]} if s.TopologyPairToPodCounts[pair] == nil { s.TopologyPairToPodCounts[pair] = new(int64) + topoSize[i]++ } } } + + s.TopologyNormalizingWeight = make([]float64, len(s.Constraints)) + for i, c := range s.Constraints { + sz := topoSize[i] + if c.TopologyKey == v1.LabelHostname { + sz = len(filteredNodes) - len(s.IgnoredNodes) + } + s.TopologyNormalizingWeight[i] = topologyNormalizingWeight(sz) + } return nil } @@ -174,20 +190,20 @@ func (pl *PodTopologySpread) Score(ctx context.Context, cycleState *framework.Cy // For each present , current node gets a credit of . // And we sum up and return it as this node's score. - var score int64 - for _, c := range s.Constraints { + var score float64 + for i, c := range s.Constraints { if tpVal, ok := node.Labels[c.TopologyKey]; ok { + var cnt int64 if c.TopologyKey == v1.LabelHostname { - count := countPodsMatchSelector(nodeInfo.Pods, c.Selector, pod.Namespace) - score += int64(count) + cnt = int64(countPodsMatchSelector(nodeInfo.Pods, c.Selector, pod.Namespace)) } else { pair := topologyPair{key: c.TopologyKey, value: tpVal} - matchSum := *s.TopologyPairToPodCounts[pair] - score += matchSum + cnt = *s.TopologyPairToPodCounts[pair] } + score += float64(cnt) * s.TopologyNormalizingWeight[i] } } - return score, nil + return int64(score), nil } // NormalizeScore invoked after scoring all nodes. @@ -200,21 +216,22 @@ func (pl *PodTopologySpread) NormalizeScore(ctx context.Context, cycleState *fra return nil } - // Calculate the summed score and . + // Calculate and var minScore int64 = math.MaxInt64 - var total int64 + var maxScore int64 for _, score := range scores { // it's mandatory to check if is present in m.IgnoredNodes if s.IgnoredNodes.Has(score.Name) { continue } - total += score.Score if score.Score < minScore { minScore = score.Score } + if score.Score > maxScore { + maxScore = score.Score + } } - maxMinDiff := total - minScore for i := range scores { nodeInfo, err := pl.sharedLister.NodeInfos().Get(scores[i].Name) if err != nil { @@ -222,19 +239,18 @@ func (pl *PodTopologySpread) NormalizeScore(ctx context.Context, cycleState *fra } node := nodeInfo.Node() - if maxMinDiff == 0 { - scores[i].Score = framework.MaxNodeScore - continue - } - if s.IgnoredNodes.Has(node.Name) { scores[i].Score = 0 continue } - flippedScore := total - scores[i].Score - fScore := float64(framework.MaxNodeScore) * (float64(flippedScore) / float64(maxMinDiff)) - scores[i].Score = int64(fScore) + if maxScore == 0 { + scores[i].Score = framework.MaxNodeScore + continue + } + + s := scores[i].Score + scores[i].Score = framework.MaxNodeScore * (maxScore + minScore - s) / maxScore } return nil } @@ -256,3 +272,16 @@ func getPreScoreState(cycleState *framework.CycleState) (*preScoreState, error) } return s, nil } + +// topologyNormalizingWeight calculates the weight for the topology, based on +// the number of values that exist for a topology. +// Since is at least 1 (all nodes that passed the Filters are in the +// same topology), and k8s supports 5k nodes, the result is in the interval +// <1.09, 8.52>. +// +// Note: could also be zero when no nodes have the required topologies, +// however we don't care about topology weight in this case as we return a 0 +// score for all nodes. +func topologyNormalizingWeight(size int) float64 { + return math.Log(float64(size + 2)) +} diff --git a/pkg/scheduler/framework/plugins/podtopologyspread/scoring_test.go b/pkg/scheduler/framework/plugins/podtopologyspread/scoring_test.go index 3bda625739b..57d61e213a8 100644 --- a/pkg/scheduler/framework/plugins/podtopologyspread/scoring_test.go +++ b/pkg/scheduler/framework/plugins/podtopologyspread/scoring_test.go @@ -48,12 +48,12 @@ func TestPreScoreStateEmptyNodes(t *testing.T) { name: "normal case", pod: st.MakePod().Name("p").Label("foo", ""). SpreadConstraint(1, "zone", v1.ScheduleAnyway, st.MakeLabelSelector().Exists("foo").Obj()). - SpreadConstraint(1, "node", v1.ScheduleAnyway, st.MakeLabelSelector().Exists("foo").Obj()). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, st.MakeLabelSelector().Exists("foo").Obj()). Obj(), nodes: []*v1.Node{ - st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), - st.MakeNode().Name("node-b").Label("zone", "zone1").Label("node", "node-b").Obj(), - st.MakeNode().Name("node-x").Label("zone", "zone2").Label("node", "node-x").Obj(), + st.MakeNode().Name("node-a").Label("zone", "zone1").Label(v1.LabelHostname, "node-a").Obj(), + st.MakeNode().Name("node-b").Label("zone", "zone1").Label(v1.LabelHostname, "node-b").Obj(), + st.MakeNode().Name("node-x").Label("zone", "zone2").Label(v1.LabelHostname, "node-x").Obj(), }, want: &preScoreState{ Constraints: []topologySpreadConstraint{ @@ -64,30 +64,28 @@ func TestPreScoreStateEmptyNodes(t *testing.T) { }, { MaxSkew: 1, - TopologyKey: "node", + TopologyKey: v1.LabelHostname, Selector: mustConvertLabelSelectorAsSelector(t, st.MakeLabelSelector().Exists("foo").Obj()), }, }, IgnoredNodes: sets.NewString(), TopologyPairToPodCounts: map[topologyPair]*int64{ - {key: "zone", value: "zone1"}: pointer.Int64Ptr(0), - {key: "zone", value: "zone2"}: pointer.Int64Ptr(0), - {key: "node", value: "node-a"}: pointer.Int64Ptr(0), - {key: "node", value: "node-b"}: pointer.Int64Ptr(0), - {key: "node", value: "node-x"}: pointer.Int64Ptr(0), + {key: "zone", value: "zone1"}: pointer.Int64Ptr(0), + {key: "zone", value: "zone2"}: pointer.Int64Ptr(0), }, + TopologyNormalizingWeight: []float64{topologyNormalizingWeight(2), topologyNormalizingWeight(3)}, }, }, { name: "node-x doesn't have label zone", pod: st.MakePod().Name("p").Label("foo", ""). SpreadConstraint(1, "zone", v1.ScheduleAnyway, st.MakeLabelSelector().Exists("foo").Obj()). - SpreadConstraint(1, "node", v1.ScheduleAnyway, st.MakeLabelSelector().Exists("bar").Obj()). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, st.MakeLabelSelector().Exists("bar").Obj()). Obj(), nodes: []*v1.Node{ - st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), - st.MakeNode().Name("node-b").Label("zone", "zone1").Label("node", "node-b").Obj(), - st.MakeNode().Name("node-x").Label("node", "node-x").Obj(), + st.MakeNode().Name("node-a").Label("zone", "zone1").Label(v1.LabelHostname, "node-a").Obj(), + st.MakeNode().Name("node-b").Label("zone", "zone1").Label(v1.LabelHostname, "node-b").Obj(), + st.MakeNode().Name("node-x").Label(v1.LabelHostname, "node-x").Obj(), }, want: &preScoreState{ Constraints: []topologySpreadConstraint{ @@ -98,28 +96,27 @@ func TestPreScoreStateEmptyNodes(t *testing.T) { }, { MaxSkew: 1, - TopologyKey: "node", + TopologyKey: v1.LabelHostname, Selector: mustConvertLabelSelectorAsSelector(t, st.MakeLabelSelector().Exists("bar").Obj()), }, }, IgnoredNodes: sets.NewString("node-x"), TopologyPairToPodCounts: map[topologyPair]*int64{ - {key: "zone", value: "zone1"}: pointer.Int64Ptr(0), - {key: "node", value: "node-a"}: pointer.Int64Ptr(0), - {key: "node", value: "node-b"}: pointer.Int64Ptr(0), + {key: "zone", value: "zone1"}: pointer.Int64Ptr(0), }, + TopologyNormalizingWeight: []float64{topologyNormalizingWeight(1), topologyNormalizingWeight(2)}, }, }, { name: "defaults constraints and a replica set", pod: st.MakePod().Name("p").Label("foo", "tar").Label("baz", "sup").Obj(), defaultConstraints: []v1.TopologySpreadConstraint{ - {MaxSkew: 1, TopologyKey: "node", WhenUnsatisfiable: v1.ScheduleAnyway}, + {MaxSkew: 1, TopologyKey: v1.LabelHostname, WhenUnsatisfiable: v1.ScheduleAnyway}, {MaxSkew: 2, TopologyKey: "rack", WhenUnsatisfiable: v1.DoNotSchedule}, {MaxSkew: 2, TopologyKey: "planet", WhenUnsatisfiable: v1.ScheduleAnyway}, }, nodes: []*v1.Node{ - st.MakeNode().Name("node-a").Label("rack", "rack1").Label("node", "node-a").Label("planet", "mars").Obj(), + st.MakeNode().Name("node-a").Label("rack", "rack1").Label(v1.LabelHostname, "node-a").Label("planet", "mars").Obj(), }, objs: []runtime.Object{ &appsv1.ReplicaSet{Spec: appsv1.ReplicaSetSpec{Selector: st.MakeLabelSelector().Exists("foo").Obj()}}, @@ -128,7 +125,7 @@ func TestPreScoreStateEmptyNodes(t *testing.T) { Constraints: []topologySpreadConstraint{ { MaxSkew: 1, - TopologyKey: "node", + TopologyKey: v1.LabelHostname, Selector: mustConvertLabelSelectorAsSelector(t, st.MakeLabelSelector().Exists("foo").Obj()), }, { @@ -139,9 +136,9 @@ func TestPreScoreStateEmptyNodes(t *testing.T) { }, IgnoredNodes: sets.NewString(), TopologyPairToPodCounts: map[topologyPair]*int64{ - {key: "node", value: "node-a"}: pointer.Int64Ptr(0), {key: "planet", value: "mars"}: pointer.Int64Ptr(0), }, + TopologyNormalizingWeight: []float64{topologyNormalizingWeight(1), topologyNormalizingWeight(1)}, }, }, { @@ -186,6 +183,7 @@ func TestPreScoreStateEmptyNodes(t *testing.T) { TopologyPairToPodCounts: map[topologyPair]*int64{ {"planet", "mars"}: pointer.Int64Ptr(0), }, + TopologyNormalizingWeight: []float64{topologyNormalizingWeight(1)}, }, }, } @@ -238,11 +236,11 @@ func TestPodTopologySpreadScore(t *testing.T) { // if there is only one candidate node, it should be scored to 10 name: "one constraint on node, no existing pods", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "node", v1.ScheduleAnyway, st.MakeLabelSelector().Exists("foo").Obj()). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, st.MakeLabelSelector().Exists("foo").Obj()). Obj(), nodes: []*v1.Node{ - st.MakeNode().Name("node-a").Label("node", "node-a").Obj(), - st.MakeNode().Name("node-b").Label("node", "node-b").Obj(), + st.MakeNode().Name("node-a").Label(v1.LabelHostname, "node-a").Obj(), + st.MakeNode().Name("node-b").Label(v1.LabelHostname, "node-b").Obj(), }, want: []framework.NodeScore{ {Name: "node-a", Score: 100}, @@ -253,7 +251,7 @@ func TestPodTopologySpreadScore(t *testing.T) { // if there is only one candidate node, it should be scored to 10 name: "one constraint on node, only one node is candidate", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "node", v1.ScheduleAnyway, st.MakeLabelSelector().Exists("foo").Obj()). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, st.MakeLabelSelector().Exists("foo").Obj()). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), @@ -261,10 +259,10 @@ func TestPodTopologySpreadScore(t *testing.T) { st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Obj(), }, nodes: []*v1.Node{ - st.MakeNode().Name("node-a").Label("node", "node-a").Obj(), + st.MakeNode().Name("node-a").Label(v1.LabelHostname, "node-a").Obj(), }, failedNodes: []*v1.Node{ - st.MakeNode().Name("node-b").Label("node", "node-b").Obj(), + st.MakeNode().Name("node-b").Label(v1.LabelHostname, "node-b").Obj(), }, want: []framework.NodeScore{ {Name: "node-a", Score: 100}, @@ -273,15 +271,15 @@ func TestPodTopologySpreadScore(t *testing.T) { { name: "one constraint on node, all nodes have the same number of matching pods", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "node", v1.ScheduleAnyway, st.MakeLabelSelector().Exists("foo").Obj()). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, st.MakeLabelSelector().Exists("foo").Obj()). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Obj(), }, nodes: []*v1.Node{ - st.MakeNode().Name("node-a").Label("node", "node-a").Obj(), - st.MakeNode().Name("node-b").Label("node", "node-b").Obj(), + st.MakeNode().Name("node-a").Label(v1.LabelHostname, "node-a").Obj(), + st.MakeNode().Name("node-b").Label(v1.LabelHostname, "node-b").Obj(), }, want: []framework.NodeScore{ {Name: "node-a", Score: 100}, @@ -289,12 +287,10 @@ func TestPodTopologySpreadScore(t *testing.T) { }, }, { - // matching pods spread as 2/1/0/3, total = 6 - // after reversing, it's 4/5/6/3 - // so scores = 400/6, 500/6, 600/6, 300/6 + // matching pods spread as 2/1/0/3. name: "one constraint on node, all 4 nodes are candidates", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "node", v1.ScheduleAnyway, st.MakeLabelSelector().Exists("foo").Obj()). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, st.MakeLabelSelector().Exists("foo").Obj()). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), @@ -305,26 +301,24 @@ func TestPodTopologySpreadScore(t *testing.T) { st.MakePod().Name("p-d3").Node("node-d").Label("foo", "").Obj(), }, nodes: []*v1.Node{ - st.MakeNode().Name("node-a").Label("node", "node-a").Obj(), - st.MakeNode().Name("node-b").Label("node", "node-b").Obj(), - st.MakeNode().Name("node-c").Label("node", "node-c").Obj(), - st.MakeNode().Name("node-d").Label("node", "node-d").Obj(), + st.MakeNode().Name("node-a").Label(v1.LabelHostname, "node-a").Obj(), + st.MakeNode().Name("node-b").Label(v1.LabelHostname, "node-b").Obj(), + st.MakeNode().Name("node-c").Label(v1.LabelHostname, "node-c").Obj(), + st.MakeNode().Name("node-d").Label(v1.LabelHostname, "node-d").Obj(), }, failedNodes: []*v1.Node{}, want: []framework.NodeScore{ - {Name: "node-a", Score: 66}, - {Name: "node-b", Score: 83}, + {Name: "node-a", Score: 40}, + {Name: "node-b", Score: 80}, {Name: "node-c", Score: 100}, - {Name: "node-d", Score: 50}, + {Name: "node-d", Score: 0}, }, }, { - // matching pods spread as 4/2/1/~3~, total = 4+2+1 = 7 (as node4 is not a candidate) - // after reversing, it's 3/5/6 - // so scores = 300/6, 500/6, 600/6 + // matching pods spread as 4/2/1/~3~ (node4 is not a candidate) name: "one constraint on node, 3 out of 4 nodes are candidates", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "node", v1.ScheduleAnyway, st.MakeLabelSelector().Exists("foo").Obj()). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, st.MakeLabelSelector().Exists("foo").Obj()). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), @@ -339,26 +333,24 @@ func TestPodTopologySpreadScore(t *testing.T) { st.MakePod().Name("p-y3").Node("node-y").Label("foo", "").Obj(), }, nodes: []*v1.Node{ - st.MakeNode().Name("node-a").Label("node", "node-a").Obj(), - st.MakeNode().Name("node-b").Label("node", "node-b").Obj(), - st.MakeNode().Name("node-x").Label("node", "node-x").Obj(), + st.MakeNode().Name("node-a").Label(v1.LabelHostname, "node-a").Obj(), + st.MakeNode().Name("node-b").Label(v1.LabelHostname, "node-b").Obj(), + st.MakeNode().Name("node-x").Label(v1.LabelHostname, "node-x").Obj(), }, failedNodes: []*v1.Node{ - st.MakeNode().Name("node-y").Label("node", "node-y").Obj(), + st.MakeNode().Name("node-y").Label(v1.LabelHostname, "node-y").Obj(), }, want: []framework.NodeScore{ - {Name: "node-a", Score: 50}, - {Name: "node-b", Score: 83}, + {Name: "node-a", Score: 16}, + {Name: "node-b", Score: 66}, {Name: "node-x", Score: 100}, }, }, { // matching pods spread as 4/?2?/1/~3~, total = 4+?+1 = 5 (as node2 is problematic) - // after reversing, it's 1/?/4 - // so scores = 100/4, 0, 400/4 - name: "one constraint on node, 3 out of 4 nodes are candidates", + name: "one constraint on node, 3 out of 4 nodes are candidates, one node doesn't match topology key", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "node", v1.ScheduleAnyway, st.MakeLabelSelector().Exists("foo").Obj()). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, st.MakeLabelSelector().Exists("foo").Obj()). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), @@ -373,23 +365,21 @@ func TestPodTopologySpreadScore(t *testing.T) { st.MakePod().Name("p-y3").Node("node-y").Label("foo", "").Obj(), }, nodes: []*v1.Node{ - st.MakeNode().Name("node-a").Label("node", "node-a").Obj(), + st.MakeNode().Name("node-a").Label(v1.LabelHostname, "node-a").Obj(), st.MakeNode().Name("node-b").Label("n", "node-b").Obj(), // label `n` doesn't match topologyKey - st.MakeNode().Name("node-x").Label("node", "node-x").Obj(), + st.MakeNode().Name("node-x").Label(v1.LabelHostname, "node-x").Obj(), }, failedNodes: []*v1.Node{ - st.MakeNode().Name("node-y").Label("node", "node-y").Obj(), + st.MakeNode().Name("node-y").Label(v1.LabelHostname, "node-y").Obj(), }, want: []framework.NodeScore{ - {Name: "node-a", Score: 25}, + {Name: "node-a", Score: 20}, {Name: "node-b", Score: 0}, {Name: "node-x", Score: 100}, }, }, { - // matching pods spread as 4/2/1/~3~, total = 6+6+4 = 16 (as topologyKey is zone instead of node) - // after reversing, it's 10/10/12 - // so scores = 1000/12, 1000/12, 1200/12 + // matching pods spread as 4/2/1/~3~ name: "one constraint on zone, 3 out of 4 nodes are candidates", pod: st.MakePod().Name("p").Label("foo", ""). SpreadConstraint(1, "zone", v1.ScheduleAnyway, st.MakeLabelSelector().Exists("foo").Obj()). @@ -407,27 +397,25 @@ func TestPodTopologySpreadScore(t *testing.T) { st.MakePod().Name("p-y3").Node("node-y").Label("foo", "").Obj(), }, nodes: []*v1.Node{ - st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), - st.MakeNode().Name("node-b").Label("zone", "zone1").Label("node", "node-b").Obj(), - st.MakeNode().Name("node-x").Label("zone", "zone2").Label("node", "node-x").Obj(), + st.MakeNode().Name("node-a").Label("zone", "zone1").Label(v1.LabelHostname, "node-a").Obj(), + st.MakeNode().Name("node-b").Label("zone", "zone1").Label(v1.LabelHostname, "node-b").Obj(), + st.MakeNode().Name("node-x").Label("zone", "zone2").Label(v1.LabelHostname, "node-x").Obj(), }, failedNodes: []*v1.Node{ - st.MakeNode().Name("node-y").Label("zone", "zone2").Label("node", "node-y").Obj(), + st.MakeNode().Name("node-y").Label("zone", "zone2").Label(v1.LabelHostname, "node-y").Obj(), }, want: []framework.NodeScore{ - {Name: "node-a", Score: 83}, - {Name: "node-b", Score: 83}, + {Name: "node-a", Score: 62}, + {Name: "node-b", Score: 62}, {Name: "node-x", Score: 100}, }, }, { - // matching pods spread as 2/~1~/2/~4~, total = 2+3 + 2+6 = 13 (zone and node should be both summed up) - // after reversing, it's 8/5 - // so scores = 800/8, 500/8 + // matching pods spread as 2/~1~/2/~4~. name: "two Constraints on zone and node, 2 out of 4 nodes are candidates", pod: st.MakePod().Name("p").Label("foo", ""). SpreadConstraint(1, "zone", v1.ScheduleAnyway, st.MakeLabelSelector().Exists("foo").Obj()). - SpreadConstraint(1, "node", v1.ScheduleAnyway, st.MakeLabelSelector().Exists("foo").Obj()). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, st.MakeLabelSelector().Exists("foo").Obj()). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), @@ -441,16 +429,16 @@ func TestPodTopologySpreadScore(t *testing.T) { st.MakePod().Name("p-y4").Node("node-y").Label("foo", "").Obj(), }, nodes: []*v1.Node{ - st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), - st.MakeNode().Name("node-x").Label("zone", "zone2").Label("node", "node-x").Obj(), + st.MakeNode().Name("node-a").Label("zone", "zone1").Label(v1.LabelHostname, "node-a").Obj(), + st.MakeNode().Name("node-x").Label("zone", "zone2").Label(v1.LabelHostname, "node-x").Obj(), }, failedNodes: []*v1.Node{ - st.MakeNode().Name("node-b").Label("zone", "zone1").Label("node", "node-b").Obj(), - st.MakeNode().Name("node-y").Label("zone", "zone2").Label("node", "node-y").Obj(), + st.MakeNode().Name("node-b").Label("zone", "zone1").Label(v1.LabelHostname, "node-b").Obj(), + st.MakeNode().Name("node-y").Label("zone", "zone2").Label(v1.LabelHostname, "node-y").Obj(), }, want: []framework.NodeScore{ {Name: "node-a", Score: 100}, - {Name: "node-x", Score: 62}, + {Name: "node-x", Score: 54}, }, }, { @@ -464,13 +452,10 @@ func TestPodTopologySpreadScore(t *testing.T) { // +--------+-------------+--------+---------------+ // For the first constraint (zone): the matching pods spread as 2/2/1/1 // For the second constraint (node): the matching pods spread as 0/1/0/1 - // sum them up gets: 2/3/1/2, and total number is 8. - // after reversing, it's 6/5/7/6 - // so scores = 600/7, 500/7, 700/7, 600/7 name: "two Constraints on zone and node, with different labelSelectors", pod: st.MakePod().Name("p").Label("foo", "").Label("bar", ""). SpreadConstraint(1, "zone", v1.ScheduleAnyway, st.MakeLabelSelector().Exists("foo").Obj()). - SpreadConstraint(1, "node", v1.ScheduleAnyway, st.MakeLabelSelector().Exists("bar").Obj()). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, st.MakeLabelSelector().Exists("bar").Obj()). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), @@ -479,29 +464,26 @@ func TestPodTopologySpreadScore(t *testing.T) { st.MakePod().Name("p-y2").Node("node-y").Label("bar", "").Obj(), }, nodes: []*v1.Node{ - st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), - st.MakeNode().Name("node-b").Label("zone", "zone1").Label("node", "node-b").Obj(), - st.MakeNode().Name("node-x").Label("zone", "zone2").Label("node", "node-x").Obj(), - st.MakeNode().Name("node-y").Label("zone", "zone2").Label("node", "node-y").Obj(), + st.MakeNode().Name("node-a").Label("zone", "zone1").Label(v1.LabelHostname, "node-a").Obj(), + st.MakeNode().Name("node-b").Label("zone", "zone1").Label(v1.LabelHostname, "node-b").Obj(), + st.MakeNode().Name("node-x").Label("zone", "zone2").Label(v1.LabelHostname, "node-x").Obj(), + st.MakeNode().Name("node-y").Label("zone", "zone2").Label(v1.LabelHostname, "node-y").Obj(), }, failedNodes: []*v1.Node{}, want: []framework.NodeScore{ - {Name: "node-a", Score: 85}, - {Name: "node-b", Score: 71}, + {Name: "node-a", Score: 75}, + {Name: "node-b", Score: 25}, {Name: "node-x", Score: 100}, - {Name: "node-y", Score: 85}, + {Name: "node-y", Score: 50}, }, }, { // For the first constraint (zone): the matching pods spread as 0/0/2/2 // For the second constraint (node): the matching pods spread as 0/1/0/1 - // sum them up gets: 0/1/2/3, and total number is 6. - // after reversing, it's 6/5/4/3. - // so scores = 600/6, 500/6, 400/6, 300/6 name: "two Constraints on zone and node, with different labelSelectors, some nodes have 0 pods", pod: st.MakePod().Name("p").Label("foo", "").Label("bar", ""). SpreadConstraint(1, "zone", v1.ScheduleAnyway, st.MakeLabelSelector().Exists("foo").Obj()). - SpreadConstraint(1, "node", v1.ScheduleAnyway, st.MakeLabelSelector().Exists("bar").Obj()). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, st.MakeLabelSelector().Exists("bar").Obj()). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p-b1").Node("node-b").Label("bar", "").Obj(), @@ -509,29 +491,26 @@ func TestPodTopologySpreadScore(t *testing.T) { st.MakePod().Name("p-y1").Node("node-y").Label("foo", "").Label("bar", "").Obj(), }, nodes: []*v1.Node{ - st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), - st.MakeNode().Name("node-b").Label("zone", "zone1").Label("node", "node-b").Obj(), - st.MakeNode().Name("node-x").Label("zone", "zone2").Label("node", "node-x").Obj(), - st.MakeNode().Name("node-y").Label("zone", "zone2").Label("node", "node-y").Obj(), + st.MakeNode().Name("node-a").Label("zone", "zone1").Label(v1.LabelHostname, "node-a").Obj(), + st.MakeNode().Name("node-b").Label("zone", "zone1").Label(v1.LabelHostname, "node-b").Obj(), + st.MakeNode().Name("node-x").Label("zone", "zone2").Label(v1.LabelHostname, "node-x").Obj(), + st.MakeNode().Name("node-y").Label("zone", "zone2").Label(v1.LabelHostname, "node-y").Obj(), }, failedNodes: []*v1.Node{}, want: []framework.NodeScore{ {Name: "node-a", Score: 100}, - {Name: "node-b", Score: 83}, - {Name: "node-x", Score: 66}, - {Name: "node-y", Score: 50}, + {Name: "node-b", Score: 75}, + {Name: "node-x", Score: 50}, + {Name: "node-y", Score: 0}, }, }, { // For the first constraint (zone): the matching pods spread as 2/2/1/~1~ // For the second constraint (node): the matching pods spread as 0/1/0/~1~ - // sum them up gets: 2/3/1, and total number is 6. - // after reversing, it's 4/3/5 - // so scores = 400/5, 300/5, 500/5 name: "two Constraints on zone and node, with different labelSelectors, 3 out of 4 nodes are candidates", pod: st.MakePod().Name("p").Label("foo", "").Label("bar", ""). SpreadConstraint(1, "zone", v1.ScheduleAnyway, st.MakeLabelSelector().Exists("foo").Obj()). - SpreadConstraint(1, "node", v1.ScheduleAnyway, st.MakeLabelSelector().Exists("bar").Obj()). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, st.MakeLabelSelector().Exists("bar").Obj()). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), @@ -540,23 +519,23 @@ func TestPodTopologySpreadScore(t *testing.T) { st.MakePod().Name("p-y2").Node("node-y").Label("bar", "").Obj(), }, nodes: []*v1.Node{ - st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), - st.MakeNode().Name("node-b").Label("zone", "zone1").Label("node", "node-b").Obj(), - st.MakeNode().Name("node-x").Label("zone", "zone2").Label("node", "node-x").Obj(), + st.MakeNode().Name("node-a").Label("zone", "zone1").Label(v1.LabelHostname, "node-a").Obj(), + st.MakeNode().Name("node-b").Label("zone", "zone1").Label(v1.LabelHostname, "node-b").Obj(), + st.MakeNode().Name("node-x").Label("zone", "zone2").Label(v1.LabelHostname, "node-x").Obj(), }, failedNodes: []*v1.Node{ - st.MakeNode().Name("node-y").Label("zone", "zone2").Label("node", "node-y").Obj(), + st.MakeNode().Name("node-y").Label("zone", "zone2").Label(v1.LabelHostname, "node-y").Obj(), }, want: []framework.NodeScore{ - {Name: "node-a", Score: 80}, - {Name: "node-b", Score: 60}, + {Name: "node-a", Score: 75}, + {Name: "node-b", Score: 25}, {Name: "node-x", Score: 100}, }, }, { name: "existing pods in a different namespace do not count", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "node", v1.ScheduleAnyway, st.MakeLabelSelector().Exists("foo").Obj()). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, st.MakeLabelSelector().Exists("foo").Obj()). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p-a1").Namespace("ns1").Node("node-a").Label("foo", "").Obj(), @@ -565,8 +544,8 @@ func TestPodTopologySpreadScore(t *testing.T) { st.MakePod().Name("p-b2").Node("node-b").Label("foo", "").Obj(), }, nodes: []*v1.Node{ - st.MakeNode().Name("node-a").Label("node", "node-a").Obj(), - st.MakeNode().Name("node-b").Label("node", "node-b").Obj(), + st.MakeNode().Name("node-a").Label(v1.LabelHostname, "node-a").Obj(), + st.MakeNode().Name("node-b").Label(v1.LabelHostname, "node-b").Obj(), }, want: []framework.NodeScore{ {Name: "node-a", Score: 100}, @@ -576,11 +555,11 @@ func TestPodTopologySpreadScore(t *testing.T) { { name: "terminating Pods should be excluded", pod: st.MakePod().Name("p").Label("foo", "").SpreadConstraint( - 1, "node", v1.ScheduleAnyway, st.MakeLabelSelector().Exists("foo").Obj(), + 1, v1.LabelHostname, v1.ScheduleAnyway, st.MakeLabelSelector().Exists("foo").Obj(), ).Obj(), nodes: []*v1.Node{ - st.MakeNode().Name("node-a").Label("node", "node-a").Obj(), - st.MakeNode().Name("node-b").Label("node", "node-b").Obj(), + st.MakeNode().Name("node-a").Label(v1.LabelHostname, "node-a").Obj(), + st.MakeNode().Name("node-b").Label(v1.LabelHostname, "node-b").Obj(), }, existingPods: []*v1.Pod{ st.MakePod().Name("p-a").Node("node-a").Label("foo", "").Terminating().Obj(),