Merge pull request #90475 from alculquicondor/topology-scoring

Topology spreading scoring with automatically weighted topologies
This commit is contained in:
Kubernetes Prow Robot 2020-05-01 00:40:04 -07:00 committed by GitHub
commit ba43630708
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 144 additions and 136 deletions

View File

@ -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 <pair>, current node gets a credit of <matchSum>.
// And we sum up <matchSum> 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 <total> score and <minScore>.
// Calculate <minScore> and <maxScore>
var minScore int64 = math.MaxInt64
var total int64
var maxScore int64
for _, score := range scores {
// it's mandatory to check if <score.Name> 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 <size> 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: <size> 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))
}

View File

@ -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(),