From f4bc904376fd6c57cc864ce4e26fa40da7477db3 Mon Sep 17 00:00:00 2001 From: Alex Wang Date: Sat, 30 Jul 2022 13:23:49 +0800 Subject: [PATCH] implementation for MatchLabelKeys in TopologySpreadConstraint Signed-off-by: Alex Wang --- pkg/features/kube_features.go | 9 + .../default_preemption_test.go | 8 +- .../framework/plugins/feature/feature.go | 1 + .../plugins/podtopologyspread/common.go | 32 +- .../plugins/podtopologyspread/filtering.go | 5 +- .../podtopologyspread/filtering_test.go | 371 +++++++++++++----- .../plugins/podtopologyspread/plugin.go | 2 + .../plugins/podtopologyspread/scoring.go | 5 +- .../plugins/podtopologyspread/scoring_test.go | 164 ++++++-- pkg/scheduler/framework/plugins/registry.go | 1 + pkg/scheduler/schedule_one_test.go | 4 +- pkg/scheduler/testing/wrappers.go | 3 +- .../scheduler/filters/filters_test.go | 89 ++++- .../scheduler/scoring/priorities_test.go | 70 +++- 14 files changed, 576 insertions(+), 188 deletions(-) diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go index e0b828a9f41..93a29ec0b84 100644 --- a/pkg/features/kube_features.go +++ b/pkg/features/kube_features.go @@ -515,6 +515,13 @@ const ( // Enables scaling down replicas via logarithmic comparison of creation/ready timestamps LogarithmicScaleDown featuregate.Feature = "LogarithmicScaleDown" + // owner: @denkensk + // kep: http://kep.k8s.io/3243 + // alpha: v1.25 + // + // Enable MatchLabelKeys in PodTopologySpread. + MatchLabelKeysInPodTopologySpread featuregate.Feature = "MatchLabelKeysInPodTopologySpread" + // owner: @krmayankk // alpha: v1.24 // @@ -948,6 +955,8 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS LogarithmicScaleDown: {Default: true, PreRelease: featuregate.Beta}, + MatchLabelKeysInPodTopologySpread: {Default: false, PreRelease: featuregate.Alpha}, + MaxUnavailableStatefulSet: {Default: false, PreRelease: featuregate.Alpha}, MemoryManager: {Default: true, PreRelease: featuregate.Beta}, diff --git a/pkg/scheduler/framework/plugins/defaultpreemption/default_preemption_test.go b/pkg/scheduler/framework/plugins/defaultpreemption/default_preemption_test.go index ebbbcaf7291..9cc7641a5cd 100644 --- a/pkg/scheduler/framework/plugins/defaultpreemption/default_preemption_test.go +++ b/pkg/scheduler/framework/plugins/defaultpreemption/default_preemption_test.go @@ -644,8 +644,8 @@ func TestDryRunPreemption(t *testing.T) { nodeNames: []string{"node-a/zone1", "node-b/zone1", "node-x/zone2"}, testPods: []*v1.Pod{ st.MakePod().Name("p").UID("p").Label("foo", "").Priority(highPriority). - SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil). - SpreadConstraint(1, "hostname", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil). + SpreadConstraint(1, "hostname", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil). Obj(), }, initPods: []*v1.Pod{ @@ -1486,8 +1486,8 @@ func TestPreempt(t *testing.T) { { name: "preemption for topology spread constraints", pod: st.MakePod().Name("p").UID("p").Namespace(v1.NamespaceDefault).Label("foo", "").Priority(highPriority). - SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil). - SpreadConstraint(1, "hostname", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil). + SpreadConstraint(1, "hostname", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil). Obj(), pods: []*v1.Pod{ st.MakePod().Name("p-a1").UID("p-a1").Namespace(v1.NamespaceDefault).Node("node-a").Label("foo", "").Priority(highPriority).Obj(), diff --git a/pkg/scheduler/framework/plugins/feature/feature.go b/pkg/scheduler/framework/plugins/feature/feature.go index 32be4e2dce8..55bd2991518 100644 --- a/pkg/scheduler/framework/plugins/feature/feature.go +++ b/pkg/scheduler/framework/plugins/feature/feature.go @@ -24,4 +24,5 @@ type Features struct { EnableVolumeCapacityPriority bool EnableMinDomainsInPodTopologySpread bool EnableNodeInclusionPolicyInPodTopologySpread bool + EnableMatchLabelKeysInPodTopologySpread bool } diff --git a/pkg/scheduler/framework/plugins/podtopologyspread/common.go b/pkg/scheduler/framework/plugins/podtopologyspread/common.go index 29c61a4c770..c7f4d19287a 100644 --- a/pkg/scheduler/framework/plugins/podtopologyspread/common.go +++ b/pkg/scheduler/framework/plugins/podtopologyspread/common.go @@ -63,7 +63,7 @@ func (tsc *topologySpreadConstraint) matchNodeInclusionPolicies(pod *v1.Pod, nod // .DefaultConstraints and the selectors from the services, replication // controllers, replica sets and stateful sets that match the pod. func (pl *PodTopologySpread) buildDefaultConstraints(p *v1.Pod, action v1.UnsatisfiableConstraintAction) ([]topologySpreadConstraint, error) { - constraints, err := filterTopologySpreadConstraints(pl.defaultConstraints, action, pl.enableMinDomainsInPodTopologySpread, pl.enableNodeInclusionPolicyInPodTopologySpread) + constraints, err := pl.filterTopologySpreadConstraints(pl.defaultConstraints, p.Labels, action) if err != nil || len(constraints) == 0 { return nil, err } @@ -87,7 +87,7 @@ func nodeLabelsMatchSpreadConstraints(nodeLabels map[string]string, constraints return true } -func filterTopologySpreadConstraints(constraints []v1.TopologySpreadConstraint, action v1.UnsatisfiableConstraintAction, enableMinDomainsInPodTopologySpread, enableNodeInclusionPolicyInPodTopologySpread bool) ([]topologySpreadConstraint, error) { +func (pl *PodTopologySpread) filterTopologySpreadConstraints(constraints []v1.TopologySpreadConstraint, podLabels map[string]string, action v1.UnsatisfiableConstraintAction) ([]topologySpreadConstraint, error) { var result []topologySpreadConstraint for _, c := range constraints { if c.WhenUnsatisfiable == action { @@ -95,6 +95,19 @@ func filterTopologySpreadConstraints(constraints []v1.TopologySpreadConstraint, if err != nil { return nil, err } + + if pl.enableMatchLabelKeysInPodTopologySpread && len(c.MatchLabelKeys) > 0 { + matchLabels := make(labels.Set) + for _, labelKey := range c.MatchLabelKeys { + if value, ok := podLabels[labelKey]; ok { + matchLabels[labelKey] = value + } + } + if len(matchLabels) > 0 { + selector = mergeLabelSetWithSelector(matchLabels, selector) + } + } + tsc := topologySpreadConstraint{ MaxSkew: c.MaxSkew, TopologyKey: c.TopologyKey, @@ -103,10 +116,10 @@ func filterTopologySpreadConstraints(constraints []v1.TopologySpreadConstraint, NodeAffinityPolicy: v1.NodeInclusionPolicyHonor, // If NodeAffinityPolicy is nil, we treat NodeAffinityPolicy as "Honor". NodeTaintsPolicy: v1.NodeInclusionPolicyIgnore, // If NodeTaintsPolicy is nil, we treat NodeTaintsPolicy as "Ignore". } - if enableMinDomainsInPodTopologySpread && c.MinDomains != nil { + if pl.enableMinDomainsInPodTopologySpread && c.MinDomains != nil { tsc.MinDomains = *c.MinDomains } - if enableNodeInclusionPolicyInPodTopologySpread { + if pl.enableNodeInclusionPolicyInPodTopologySpread { if c.NodeAffinityPolicy != nil { tsc.NodeAffinityPolicy = *c.NodeAffinityPolicy } @@ -120,6 +133,17 @@ func filterTopologySpreadConstraints(constraints []v1.TopologySpreadConstraint, return result, nil } +func mergeLabelSetWithSelector(matchLabels labels.Set, s labels.Selector) labels.Selector { + mergedSelector := labels.SelectorFromSet(matchLabels) + if requirements, ok := s.Requirements(); ok { + for _, r := range requirements { + mergedSelector = mergedSelector.Add(r) + } + } + + return mergedSelector +} + func countPodsMatchSelector(podInfos []*framework.PodInfo, selector labels.Selector, ns string) int { count := 0 for _, p := range podInfos { diff --git a/pkg/scheduler/framework/plugins/podtopologyspread/filtering.go b/pkg/scheduler/framework/plugins/podtopologyspread/filtering.go index 8e2f03225fd..800f2b4d755 100644 --- a/pkg/scheduler/framework/plugins/podtopologyspread/filtering.go +++ b/pkg/scheduler/framework/plugins/podtopologyspread/filtering.go @@ -244,11 +244,10 @@ func (pl *PodTopologySpread) calPreFilterState(ctx context.Context, pod *v1.Pod) if len(pod.Spec.TopologySpreadConstraints) > 0 { // We have feature gating in APIServer to strip the spec // so don't need to re-check feature gate, just check length of Constraints. - constraints, err = filterTopologySpreadConstraints( + constraints, err = pl.filterTopologySpreadConstraints( pod.Spec.TopologySpreadConstraints, + pod.Labels, v1.DoNotSchedule, - pl.enableMinDomainsInPodTopologySpread, - pl.enableNodeInclusionPolicyInPodTopologySpread, ) if err != nil { return nil, fmt.Errorf("obtaining pod's hard topology spread constraints: %w", err) diff --git a/pkg/scheduler/framework/plugins/podtopologyspread/filtering_test.go b/pkg/scheduler/framework/plugins/podtopologyspread/filtering_test.go index 0184df35090..cd7ea098e3f 100644 --- a/pkg/scheduler/framework/plugins/podtopologyspread/filtering_test.go +++ b/pkg/scheduler/framework/plugins/podtopologyspread/filtering_test.go @@ -55,7 +55,8 @@ var ( honorPolicy = v1.NodeInclusionPolicyHonor fooSelector = st.MakeLabelSelector().Exists("foo").Obj() barSelector = st.MakeLabelSelector().Exists("bar").Obj() - taints = []v1.Taint{{Key: v1.TaintNodeUnschedulable, Value: "", Effect: v1.TaintEffectPreferNoSchedule}} + + taints = []v1.Taint{{Key: v1.TaintNodeUnschedulable, Value: "", Effect: v1.TaintEffectPreferNoSchedule}} ) func (p *criticalPaths) sort() { @@ -77,11 +78,12 @@ func TestPreFilterState(t *testing.T) { want *preFilterState enableMinDomains bool enableNodeInclustionPolicy bool + enableMatchLabelKeys bool }{ { name: "clean cluster with one spreadConstraint", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(5, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Label("foo", "bar").Obj(), nil, nil, nil). + SpreadConstraint(5, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Label("foo", "bar").Obj(), nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -112,7 +114,7 @@ func TestPreFilterState(t *testing.T) { { name: "normal case with one spreadConstraint", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -150,7 +152,7 @@ func TestPreFilterState(t *testing.T) { { name: "normal case with one spreadConstraint, on a 3-zone cluster", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -191,7 +193,7 @@ func TestPreFilterState(t *testing.T) { { name: "namespace mismatch doesn't count", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -229,8 +231,8 @@ func TestPreFilterState(t *testing.T) { { name: "normal case with two spreadConstraints", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -283,10 +285,10 @@ func TestPreFilterState(t *testing.T) { { name: "soft spreadConstraints should be bypassed", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). - SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, nil, nil). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -337,8 +339,8 @@ func TestPreFilterState(t *testing.T) { { name: "different labelSelectors - simple version", pod: st.MakePod().Name("p").Label("foo", "").Label("bar", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). - SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -384,8 +386,8 @@ func TestPreFilterState(t *testing.T) { { name: "different labelSelectors - complex pods", pod: st.MakePod().Name("p").Label("foo", "").Label("bar", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). - SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -437,8 +439,8 @@ func TestPreFilterState(t *testing.T) { name: "two spreadConstraints, and with podAffinity", pod: st.MakePod().Name("p").Label("foo", ""). NodeAffinityNotIn("node", []string{"node-x"}). // exclude node-x - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -538,8 +540,8 @@ func TestPreFilterState(t *testing.T) { { name: "default constraints and a service, but pod has constraints", pod: st.MakePod().Name("p").Label("foo", "bar").Label("baz", "tar"). - SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Label("baz", "tar").Obj(), nil, nil, nil). - SpreadConstraint(2, "planet", v1.ScheduleAnyway, st.MakeLabelSelector().Label("fot", "rok").Obj(), nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Label("baz", "tar").Obj(), nil, nil, nil, nil). + SpreadConstraint(2, "planet", v1.ScheduleAnyway, st.MakeLabelSelector().Label("fot", "rok").Obj(), nil, nil, nil, nil). Obj(), defaultConstraints: []v1.TopologySpreadConstraint{ {MaxSkew: 2, TopologyKey: "node", WhenUnsatisfiable: v1.DoNotSchedule}, @@ -578,8 +580,8 @@ func TestPreFilterState(t *testing.T) { { name: "TpKeyToDomainsNum is calculated when MinDomains is enabled", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -638,7 +640,7 @@ func TestPreFilterState(t *testing.T) { name: "feature gate disabled with NodeAffinityPolicy", pod: st.MakePod().Name("p").Label("foo", ""). NodeSelector(map[string]string{"foo": ""}). - SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(), @@ -676,7 +678,7 @@ func TestPreFilterState(t *testing.T) { name: "NodeAffinityPolicy honored with labelSelectors", pod: st.MakePod().Name("p").Label("foo", ""). NodeSelector(map[string]string{"foo": ""}). - SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(), @@ -714,7 +716,7 @@ func TestPreFilterState(t *testing.T) { name: "NodeAffinityPolicy ignored with labelSelectors", pod: st.MakePod().Name("p").Label("foo", ""). NodeSelector(map[string]string{"foo": ""}). - SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, &ignorePolicy, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, &ignorePolicy, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(), @@ -753,7 +755,7 @@ func TestPreFilterState(t *testing.T) { name: "NodeAffinityPolicy honored with nodeAffinity", pod: st.MakePod().Name("p").Label("foo", ""). NodeAffinityIn("foo", []string{""}). - SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(), @@ -791,7 +793,7 @@ func TestPreFilterState(t *testing.T) { name: "NodeAffinityPolicy ignored with nodeAffinity", pod: st.MakePod().Name("p").Label("foo", ""). NodeAffinityIn("foo", []string{""}). - SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, &ignorePolicy, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, &ignorePolicy, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(), @@ -829,7 +831,7 @@ func TestPreFilterState(t *testing.T) { { name: "feature gate disabled with NodeTaintsPolicy", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("node", "node-a").Obj(), @@ -867,7 +869,7 @@ func TestPreFilterState(t *testing.T) { { name: "NodeTaintsPolicy ignored", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("node", "node-a").Obj(), @@ -905,7 +907,7 @@ func TestPreFilterState(t *testing.T) { { name: "NodeTaintsPolicy honored", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, &honorPolicy). + SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, &honorPolicy, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("node", "node-a").Obj(), @@ -943,7 +945,7 @@ func TestPreFilterState(t *testing.T) { name: "NodeTaintsPolicy honored with tolerated taints", pod: st.MakePod().Name("p").Label("foo", ""). Toleration(v1.TaintNodeUnschedulable). - SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, &honorPolicy). + SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, &honorPolicy, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("node", "node-a").Obj(), @@ -982,8 +984,8 @@ func TestPreFilterState(t *testing.T) { name: "two node inclusion Constraints, zone: honor/ignore, node: ignore/ignore", pod: st.MakePod().Name("p").Label("foo", ""). NodeSelector(map[string]string{"foo": ""}). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, &ignorePolicy, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, &ignorePolicy, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Label("foo", "").Obj(), @@ -1031,8 +1033,8 @@ func TestPreFilterState(t *testing.T) { { name: "two node inclusion Constraints, zone: honor/honor, node: honor/ignore", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, &honorPolicy). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, &honorPolicy, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Label("foo", "").Obj(), @@ -1081,8 +1083,8 @@ func TestPreFilterState(t *testing.T) { name: "two node inclusion Constraints, zone: honor/ignore, node: honor/ignore", pod: st.MakePod().Name("p").Label("foo", ""). NodeSelector(map[string]string{"foo": ""}). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -1132,8 +1134,8 @@ func TestPreFilterState(t *testing.T) { name: "two node inclusion Constraints, zone: ignore/ignore, node: honor/honor", pod: st.MakePod().Name("p").Label("foo", ""). NodeSelector(map[string]string{"foo": ""}). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, &ignorePolicy, nil). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, &honorPolicy). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, &ignorePolicy, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, &honorPolicy, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -1180,6 +1182,162 @@ func TestPreFilterState(t *testing.T) { }, enableNodeInclustionPolicy: true, }, + { + name: "matchLabelKeys ignored when feature gate disabled", + pod: st.MakePod().Name("p").Label("foo", "").Label("bar", ""). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, []string{"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(), + }, + existingPods: []*v1.Pod{ + st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), + st.MakePod().Name("p-a2").Node("node-a").Label("foo", "").Obj(), + st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Obj(), + st.MakePod().Name("p-y1").Node("node-y").Label("foo", "").Obj(), + st.MakePod().Name("p-y2").Node("node-y").Label("foo", "").Obj(), + }, + want: &preFilterState{ + Constraints: []topologySpreadConstraint{ + { + MaxSkew: 1, + TopologyKey: "zone", + Selector: mustConvertLabelSelectorAsSelector(t, fooSelector), + MinDomains: 1, + NodeAffinityPolicy: v1.NodeInclusionPolicyHonor, + NodeTaintsPolicy: v1.NodeInclusionPolicyIgnore, + }, + }, + TpKeyToCriticalPaths: map[string]*criticalPaths{ + "zone": {{"zone2", 2}, {"zone1", 3}}, + }, + TpPairToMatchNum: map[topologyPair]int{ + {key: "zone", value: "zone1"}: 3, + {key: "zone", value: "zone2"}: 2, + }, + }, + enableMatchLabelKeys: false, + }, + { + name: "matchLabelKeys ANDed with LabelSelector when LabelSelector isn't empty", + pod: st.MakePod().Name("p").Label("foo", "").Label("bar", "a"). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, []string{"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(), + }, + existingPods: []*v1.Pod{ + st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), + st.MakePod().Name("p-a2").Node("node-a").Label("foo", "").Label("bar", "a").Obj(), + st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Obj(), + st.MakePod().Name("p-y1").Node("node-y").Label("foo", "").Label("bar", "a").Obj(), + st.MakePod().Name("p-y2").Node("node-y").Label("bar", "").Obj(), + }, + want: &preFilterState{ + Constraints: []topologySpreadConstraint{ + { + MaxSkew: 1, + TopologyKey: "zone", + Selector: mustConvertLabelSelectorAsSelector(t, st.MakeLabelSelector().Exists("foo").Label("bar", "a").Obj()), + MinDomains: 1, + NodeAffinityPolicy: v1.NodeInclusionPolicyHonor, + NodeTaintsPolicy: v1.NodeInclusionPolicyIgnore, + }, + }, + TpKeyToCriticalPaths: map[string]*criticalPaths{ + "zone": {{"zone2", 1}, {"zone1", 1}}, + }, + TpPairToMatchNum: map[topologyPair]int{ + {key: "zone", value: "zone1"}: 1, + {key: "zone", value: "zone2"}: 1, + }, + }, + enableMatchLabelKeys: true, + }, + { + name: "matchLabelKeys ANDed with LabelSelector when LabelSelector is empty", + pod: st.MakePod().Name("p").Label("foo", "a"). + SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Obj(), nil, nil, nil, []string{"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-y").Label("zone", "zone2").Label("node", "node-y").Obj(), + }, + existingPods: []*v1.Pod{ + st.MakePod().Name("p-a1").Node("node-a").Label("foo", "a").Obj(), + st.MakePod().Name("p-a2").Node("node-a").Label("foo", "a").Obj(), + st.MakePod().Name("p-b1").Node("node-b").Label("foo", "a").Obj(), + st.MakePod().Name("p-y1").Node("node-y").Label("foo", "a").Obj(), + st.MakePod().Name("p-y2").Node("node-y").Label("foo", "a").Obj(), + }, + want: &preFilterState{ + Constraints: []topologySpreadConstraint{ + { + MaxSkew: 1, + TopologyKey: "zone", + Selector: mustConvertLabelSelectorAsSelector(t, st.MakeLabelSelector().Label("foo", "a").Obj()), + MinDomains: 1, + NodeAffinityPolicy: v1.NodeInclusionPolicyHonor, + NodeTaintsPolicy: v1.NodeInclusionPolicyIgnore, + }, + }, + TpKeyToCriticalPaths: map[string]*criticalPaths{ + "zone": {{"zone2", 2}, {"zone1", 3}}, + }, + TpPairToMatchNum: map[topologyPair]int{ + {key: "zone", value: "zone1"}: 3, + {key: "zone", value: "zone2"}: 2, + }, + }, + enableMatchLabelKeys: true, + }, + { + name: "key in matchLabelKeys is ignored when it isn't exist in pod.labels", + pod: st.MakePod().Name("p").Label("foo", ""). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, []string{"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(), + }, + existingPods: []*v1.Pod{ + st.MakePod().Name("p-a1").Node("node-a").Label("foo", "a").Obj(), + st.MakePod().Name("p-a2").Node("node-a").Label("foo", "a").Obj(), + st.MakePod().Name("p-b1").Node("node-b").Label("foo", "a").Obj(), + st.MakePod().Name("p-y1").Node("node-y").Label("foo", "a").Obj(), + st.MakePod().Name("p-y2").Node("node-y").Label("foo", "a").Obj(), + }, + want: &preFilterState{ + Constraints: []topologySpreadConstraint{ + { + MaxSkew: 1, + TopologyKey: "zone", + Selector: mustConvertLabelSelectorAsSelector(t, fooSelector), + MinDomains: 1, + NodeAffinityPolicy: v1.NodeInclusionPolicyHonor, + NodeTaintsPolicy: v1.NodeInclusionPolicyIgnore, + }, + }, + TpKeyToCriticalPaths: map[string]*criticalPaths{ + "zone": {{"zone2", 2}, {"zone1", 3}}, + }, + TpPairToMatchNum: map[topologyPair]int{ + {key: "zone", value: "zone1"}: 3, + {key: "zone", value: "zone2"}: 2, + }, + }, + enableMatchLabelKeys: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -1193,6 +1351,7 @@ func TestPreFilterState(t *testing.T) { p := plugintesting.SetupPluginWithInformers(ctx, t, topologySpreadFunc, args, cache.NewSnapshot(tt.existingPods, tt.nodes), tt.objs) p.(*PodTopologySpread).enableMinDomainsInPodTopologySpread = tt.enableMinDomains p.(*PodTopologySpread).enableNodeInclusionPolicyInPodTopologySpread = tt.enableNodeInclustionPolicy + p.(*PodTopologySpread).enableMatchLabelKeysInPodTopologySpread = tt.enableMatchLabelKeys cs := framework.NewCycleState() if _, s := p.(*PodTopologySpread).PreFilter(ctx, cs, tt.pod); !s.IsSuccess() { @@ -1233,7 +1392,7 @@ func TestPreFilterStateAddPod(t *testing.T) { { name: "node a and b both impact current min match", preemptor: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), addedPod: st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), existingPods: nil, // it's an empty cluster @@ -1256,7 +1415,7 @@ func TestPreFilterStateAddPod(t *testing.T) { { name: "only node a impacts current min match", preemptor: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), addedPod: st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), existingPods: []*v1.Pod{ @@ -1281,7 +1440,7 @@ func TestPreFilterStateAddPod(t *testing.T) { { name: "add a pod in a different namespace doesn't change topologyKeyToMinPodsMap", preemptor: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), addedPod: st.MakePod().Name("p-a1").Namespace("ns1").Node("node-a").Label("foo", "").Obj(), existingPods: []*v1.Pod{ @@ -1306,7 +1465,7 @@ func TestPreFilterStateAddPod(t *testing.T) { { name: "add pod on non-critical node won't trigger re-calculation", preemptor: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), addedPod: st.MakePod().Name("p-b2").Node("node-b").Label("foo", "").Obj(), existingPods: []*v1.Pod{ @@ -1331,8 +1490,8 @@ func TestPreFilterStateAddPod(t *testing.T) { { name: "node a and x both impact topologyKeyToMinPodsMap on zone and node", preemptor: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), addedPod: st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), existingPods: nil, // it's an empty cluster @@ -1358,8 +1517,8 @@ func TestPreFilterStateAddPod(t *testing.T) { { name: "only node a impacts topologyKeyToMinPodsMap on zone and node", preemptor: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), addedPod: st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), existingPods: []*v1.Pod{ @@ -1387,8 +1546,8 @@ func TestPreFilterStateAddPod(t *testing.T) { { name: "node a impacts topologyKeyToMinPodsMap on node, node x impacts topologyKeyToMinPodsMap on zone", preemptor: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), addedPod: st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), existingPods: []*v1.Pod{ @@ -1420,8 +1579,8 @@ func TestPreFilterStateAddPod(t *testing.T) { { name: "Constraints hold different labelSelectors, node a impacts topologyKeyToMinPodsMap on zone", preemptor: st.MakePod().Name("p").Label("foo", "").Label("bar", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). - SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil, nil). Obj(), addedPod: st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), existingPods: []*v1.Pod{ @@ -1463,8 +1622,8 @@ func TestPreFilterStateAddPod(t *testing.T) { { name: "Constraints hold different labelSelectors, node a impacts topologyKeyToMinPodsMap on both zone and node", preemptor: st.MakePod().Name("p").Label("foo", "").Label("bar", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). - SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil, nil). Obj(), addedPod: st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Label("bar", "").Obj(), existingPods: []*v1.Pod{ @@ -1506,7 +1665,7 @@ func TestPreFilterStateAddPod(t *testing.T) { { name: "add a pod that doesn't match node affinity when NodeInclustionPolicy disabled", preemptor: st.MakePod().Name("p").Label("foo", "").NodeAffinityNotIn("foo", []string{"bar"}). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodeIdx: 0, addedPod: st.MakePod().Name("p-a1").Node("node-b").Label("foo", "").Label("zone", "zone2").Obj(), @@ -1532,7 +1691,7 @@ func TestPreFilterStateAddPod(t *testing.T) { { name: "add a pod that doesn't match node affinity when NodeInclustionPolicy enabled", preemptor: st.MakePod().Name("p").Label("foo", "").NodeAffinityNotIn("foo", []string{"bar"}). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodeIdx: 0, addedPod: st.MakePod().Name("p-a1").Node("node-b").Label("foo", "").Label("zone", "zone2").Obj(), @@ -1612,7 +1771,7 @@ func TestPreFilterStateRemovePod(t *testing.T) { // So preemption is triggered. name: "one spreadConstraint on zone, topologyKeyToMinPodsMap unchanged", preemptor: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -1640,7 +1799,7 @@ func TestPreFilterStateRemovePod(t *testing.T) { { name: "one spreadConstraint on node, topologyKeyToMinPodsMap changed", preemptor: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -1670,7 +1829,7 @@ func TestPreFilterStateRemovePod(t *testing.T) { { name: "delete an irrelevant pod won't help", preemptor: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -1701,7 +1860,7 @@ func TestPreFilterStateRemovePod(t *testing.T) { { name: "delete a non-existing pod won't help", preemptor: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -1732,8 +1891,8 @@ func TestPreFilterStateRemovePod(t *testing.T) { { name: "two spreadConstraints", preemptor: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -1811,7 +1970,7 @@ func BenchmarkFilter(b *testing.B) { { name: "1000nodes/single-constraint-zone", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, v1.LabelTopologyZone, v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, v1.LabelTopologyZone, v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), existingPodsNum: 10000, allNodesNum: 1000, @@ -1820,7 +1979,7 @@ func BenchmarkFilter(b *testing.B) { { name: "1000nodes/single-constraint-node", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, v1.LabelHostname, v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, v1.LabelHostname, v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), existingPodsNum: 10000, allNodesNum: 1000, @@ -1829,8 +1988,8 @@ func BenchmarkFilter(b *testing.B) { { name: "1000nodes/two-Constraints-zone-node", pod: st.MakePod().Name("p").Label("foo", "").Label("bar", ""). - SpreadConstraint(1, v1.LabelTopologyZone, v1.DoNotSchedule, fooSelector, nil, nil, nil). - SpreadConstraint(1, v1.LabelHostname, v1.DoNotSchedule, barSelector, nil, nil, nil). + SpreadConstraint(1, v1.LabelTopologyZone, v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, v1.LabelHostname, v1.DoNotSchedule, barSelector, nil, nil, nil, nil). Obj(), existingPodsNum: 10000, allNodesNum: 1000, @@ -1887,7 +2046,7 @@ func TestSingleConstraint(t *testing.T) { { name: "no existing pods", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -1905,7 +2064,7 @@ func TestSingleConstraint(t *testing.T) { { name: "no existing pods, incoming pod doesn't match itself", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, barSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, barSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -1923,7 +2082,7 @@ func TestSingleConstraint(t *testing.T) { { name: "existing pods in a different namespace do not count", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -1947,7 +2106,7 @@ func TestSingleConstraint(t *testing.T) { { name: "pods spread across zones as 3/3, all nodes fit", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -1975,7 +2134,7 @@ func TestSingleConstraint(t *testing.T) { // can cause unexpected behavior name: "pods spread across zones as 1/2 due to absence of label 'zone' on node-b", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -1999,7 +2158,7 @@ func TestSingleConstraint(t *testing.T) { { name: "pod cannot be scheduled as all nodes don't have label 'rack'", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "rack", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "rack", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -2013,7 +2172,7 @@ func TestSingleConstraint(t *testing.T) { { name: "pods spread across nodes as 2/1/0/3, only node-x fits", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -2039,7 +2198,7 @@ func TestSingleConstraint(t *testing.T) { { name: "pods spread across nodes as 2/1/0/3, maxSkew is 2, node-b and node-x fit", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(2, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(2, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -2069,7 +2228,7 @@ func TestSingleConstraint(t *testing.T) { // as the incoming pod doesn't have label "foo" name: "pods spread across nodes as 2/1/0/3, but pod doesn't match itself", pod: st.MakePod().Name("p").Label("bar", ""). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -2101,7 +2260,7 @@ func TestSingleConstraint(t *testing.T) { name: "incoming pod has nodeAffinity, pods spread as 2/~1~/~0~/3, hence node-a fits", pod: st.MakePod().Name("p").Label("foo", ""). NodeAffinityIn("node", []string{"node-a", "node-y"}). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -2127,7 +2286,7 @@ func TestSingleConstraint(t *testing.T) { { name: "terminating Pods should be excluded", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("node", "node-a").Obj(), @@ -2147,7 +2306,7 @@ func TestSingleConstraint(t *testing.T) { name: "incoming pod has nodeAffinity, pods spread as 0/~2~/0/1, hence node-a fits", pod: st.MakePod().Name("p").Label("foo", ""). NodeAffinityNotIn("node", []string{"node-b"}). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -2177,6 +2336,7 @@ func TestSingleConstraint(t *testing.T) { pointer.Int32(4), // larger than the number of domains(3) nil, nil, + nil, ).Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("node", "node-a").Obj(), @@ -2207,6 +2367,7 @@ func TestSingleConstraint(t *testing.T) { pointer.Int32(2), // smaller than the number of domains(3) nil, nil, + nil, ).Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("node", "node-a").Obj(), @@ -2237,6 +2398,7 @@ func TestSingleConstraint(t *testing.T) { pointer.Int32(3), // larger than the number of domains(2) nil, nil, + nil, ).Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -2267,6 +2429,7 @@ func TestSingleConstraint(t *testing.T) { pointer.Int32(1), // smaller than the number of domains(2) nil, nil, + nil, ).Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -2292,7 +2455,7 @@ func TestSingleConstraint(t *testing.T) { name: "NodeAffinityPolicy honored with labelSelectors", pod: st.MakePod().Name("p").Label("foo", ""). NodeSelector(map[string]string{"foo": ""}). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(), @@ -2318,7 +2481,7 @@ func TestSingleConstraint(t *testing.T) { name: "NodeAffinityPolicy ignored with labelSelectors", pod: st.MakePod().Name("p").Label("foo", ""). NodeSelector(map[string]string{"foo": ""}). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, &ignorePolicy, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, &ignorePolicy, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(), @@ -2344,7 +2507,7 @@ func TestSingleConstraint(t *testing.T) { name: "NodeAffinityPolicy honored with nodeAffinity", pod: st.MakePod().Name("p").Label("foo", ""). NodeAffinityIn("foo", []string{""}). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(), @@ -2370,7 +2533,7 @@ func TestSingleConstraint(t *testing.T) { name: "NodeAffinityPolicy ignored with labelSelectors", pod: st.MakePod().Name("p").Label("foo", ""). NodeAffinityIn("foo", []string{""}). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, &ignorePolicy, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, &ignorePolicy, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(), @@ -2395,7 +2558,7 @@ func TestSingleConstraint(t *testing.T) { // pods spread across node as 1/1/0/~0~ name: "NodeTaintsPolicy honored", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, &honorPolicy). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, &honorPolicy, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(), @@ -2420,7 +2583,7 @@ func TestSingleConstraint(t *testing.T) { // pods spread across node as 1/1/0/~1~ name: "NodeTaintsPolicy ignored", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(), @@ -2480,8 +2643,8 @@ func TestMultipleConstraints(t *testing.T) { // intersection of (1) and (2) returns node-x name: "two Constraints on zone and node, spreads = [3/3, 2/1/0/3]", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -2510,8 +2673,8 @@ func TestMultipleConstraints(t *testing.T) { // intersection of (1) and (2) returns no node name: "two Constraints on zone and node, spreads = [3/4, 2/1/0/4]", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -2541,8 +2704,8 @@ func TestMultipleConstraints(t *testing.T) { // intersection of (1) and (2) returns node-x name: "Constraints hold different labelSelectors, spreads = [1/0, 1/0/0/1]", pod: st.MakePod().Name("p").Label("foo", "").Label("bar", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). - SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -2567,8 +2730,8 @@ func TestMultipleConstraints(t *testing.T) { // intersection of (1) and (2) returns no node name: "Constraints hold different labelSelectors, spreads = [1/0, 0/0/1/1]", pod: st.MakePod().Name("p").Label("foo", "").Label("bar", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). - SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -2594,8 +2757,8 @@ func TestMultipleConstraints(t *testing.T) { // intersection of (1) and (2) returns node-b name: "Constraints hold different labelSelectors, spreads = [2/3, 1/0/0/1]", pod: st.MakePod().Name("p").Label("foo", "").Label("bar", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). - SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -2623,8 +2786,8 @@ func TestMultipleConstraints(t *testing.T) { // intersection of (1) and (2) returns node-a and node-b name: "Constraints hold different labelSelectors but pod doesn't match itself on 'zone' constraint", pod: st.MakePod().Name("p").Label("bar", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). - SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -2650,8 +2813,8 @@ func TestMultipleConstraints(t *testing.T) { // intersection of (1) and (2) returns node-b name: "two Constraints on zone and node, absence of label 'node' on node-x, spreads = [1/1, 1/0/0/1]", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(), @@ -2677,8 +2840,8 @@ func TestMultipleConstraints(t *testing.T) { name: "two node inclusion Constraints, zone: honor/ignore, node: ignore/ignore", pod: st.MakePod().Name("p").Label("foo", ""). NodeSelector(map[string]string{"foo": ""}). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, &ignorePolicy, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, &ignorePolicy, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Label("foo", "").Obj(), @@ -2705,8 +2868,8 @@ func TestMultipleConstraints(t *testing.T) { // intersection of (1) and (2) returns node-x name: "two node inclusion Constraints, zone: honor/honor, node: honor/ignore", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, &honorPolicy). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, &honorPolicy, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Label("foo", "").Obj(), @@ -2734,8 +2897,8 @@ func TestMultipleConstraints(t *testing.T) { name: "two node inclusion Constraints, zone: honor/ignore, node: honor/ignore", pod: st.MakePod().Name("p").Label("foo", ""). NodeSelector(map[string]string{"foo": ""}). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Label("foo", "").Obj(), @@ -2763,8 +2926,8 @@ func TestMultipleConstraints(t *testing.T) { name: "two node inclusion Constraints, zone: honor/honor, node: ignore/ignore", pod: st.MakePod().Name("p").Label("foo", ""). NodeSelector(map[string]string{"foo": ""}). - SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, &honorPolicy). - SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, &ignorePolicy, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, &honorPolicy, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, &ignorePolicy, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Label("foo", "").Obj(), diff --git a/pkg/scheduler/framework/plugins/podtopologyspread/plugin.go b/pkg/scheduler/framework/plugins/podtopologyspread/plugin.go index 4ca762ca4c8..ed2333e76df 100644 --- a/pkg/scheduler/framework/plugins/podtopologyspread/plugin.go +++ b/pkg/scheduler/framework/plugins/podtopologyspread/plugin.go @@ -64,6 +64,7 @@ type PodTopologySpread struct { statefulSets appslisters.StatefulSetLister enableMinDomainsInPodTopologySpread bool enableNodeInclusionPolicyInPodTopologySpread bool + enableMatchLabelKeysInPodTopologySpread bool } var _ framework.PreFilterPlugin = &PodTopologySpread{} @@ -98,6 +99,7 @@ func New(plArgs runtime.Object, h framework.Handle, fts feature.Features) (frame defaultConstraints: args.DefaultConstraints, enableMinDomainsInPodTopologySpread: fts.EnableMinDomainsInPodTopologySpread, enableNodeInclusionPolicyInPodTopologySpread: fts.EnableNodeInclusionPolicyInPodTopologySpread, + enableMatchLabelKeysInPodTopologySpread: fts.EnableMatchLabelKeysInPodTopologySpread, } if args.DefaultingType == config.SystemDefaulting { pl.defaultConstraints = systemDefaultConstraints diff --git a/pkg/scheduler/framework/plugins/podtopologyspread/scoring.go b/pkg/scheduler/framework/plugins/podtopologyspread/scoring.go index 1cb7355e2a7..2bf96ccf277 100644 --- a/pkg/scheduler/framework/plugins/podtopologyspread/scoring.go +++ b/pkg/scheduler/framework/plugins/podtopologyspread/scoring.go @@ -59,11 +59,10 @@ func (s *preScoreState) Clone() framework.StateData { func (pl *PodTopologySpread) initPreScoreState(s *preScoreState, pod *v1.Pod, filteredNodes []*v1.Node, requireAllTopologies bool) error { var err error if len(pod.Spec.TopologySpreadConstraints) > 0 { - s.Constraints, err = filterTopologySpreadConstraints( + s.Constraints, err = pl.filterTopologySpreadConstraints( pod.Spec.TopologySpreadConstraints, + pod.Labels, v1.ScheduleAnyway, - pl.enableMinDomainsInPodTopologySpread, - pl.enableNodeInclusionPolicyInPodTopologySpread, ) if err != nil { return fmt.Errorf("obtaining pod's soft topology spread constraints: %w", err) diff --git a/pkg/scheduler/framework/plugins/podtopologyspread/scoring_test.go b/pkg/scheduler/framework/plugins/podtopologyspread/scoring_test.go index 97e3e9e3a09..863f4b48c68 100644 --- a/pkg/scheduler/framework/plugins/podtopologyspread/scoring_test.go +++ b/pkg/scheduler/framework/plugins/podtopologyspread/scoring_test.go @@ -55,8 +55,8 @@ func TestPreScoreStateEmptyNodes(t *testing.T) { { name: "normal case", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil). - SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label(v1.LabelHostname, "node-a").Obj(), @@ -96,8 +96,8 @@ func TestPreScoreStateEmptyNodes(t *testing.T) { { name: "node-x doesn't have label zone", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil). - SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label(v1.LabelHostname, "node-a").Obj(), @@ -253,8 +253,8 @@ func TestPreScoreStateEmptyNodes(t *testing.T) { name: "default constraints and a replicaset, but pod has constraints", pod: st.MakePod().Name("p").Namespace("default").Label("foo", "bar").Label("baz", "sup"). OwnerReference("rs1", appsv1.SchemeGroupVersion.WithKind("ReplicaSet")). - SpreadConstraint(1, "zone", v1.DoNotSchedule, barSelector, nil, nil, nil). - SpreadConstraint(2, "planet", v1.ScheduleAnyway, st.MakeLabelSelector().Label("baz", "sup").Obj(), nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, barSelector, nil, nil, nil, nil). + SpreadConstraint(2, "planet", v1.ScheduleAnyway, st.MakeLabelSelector().Label("baz", "sup").Obj(), nil, nil, nil, nil). Obj(), config: config.PodTopologySpreadArgs{ DefaultConstraints: []v1.TopologySpreadConstraint{ @@ -294,7 +294,7 @@ func TestPreScoreStateEmptyNodes(t *testing.T) { name: "NodeAffinityPolicy honored with labelSelectors", pod: st.MakePod().Name("p").Label("foo", ""). NodeSelector(map[string]string{"foo": ""}). - SpreadConstraint(1, "zone", v1.ScheduleAnyway, barSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.ScheduleAnyway, barSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("foo", "").Label(v1.LabelHostname, "node-a").Obj(), @@ -328,7 +328,7 @@ func TestPreScoreStateEmptyNodes(t *testing.T) { name: "NodeAffinityPolicy ignored with labelSelectors", pod: st.MakePod().Name("p").Label("foo", ""). NodeSelector(map[string]string{"foo": ""}). - SpreadConstraint(1, "zone", v1.ScheduleAnyway, barSelector, nil, &ignorePolicy, nil). + SpreadConstraint(1, "zone", v1.ScheduleAnyway, barSelector, nil, &ignorePolicy, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("foo", "").Label(v1.LabelHostname, "node-a").Obj(), @@ -362,7 +362,7 @@ func TestPreScoreStateEmptyNodes(t *testing.T) { name: "NodeAffinityPolicy honored with nodeAffinity", pod: st.MakePod().Name("p").Label("foo", ""). NodeAffinityIn("foo", []string{""}). - SpreadConstraint(1, "zone", v1.ScheduleAnyway, barSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.ScheduleAnyway, barSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("foo", "").Label(v1.LabelHostname, "node-a").Obj(), @@ -396,7 +396,7 @@ func TestPreScoreStateEmptyNodes(t *testing.T) { name: "NodeAffinityPolicy ignored with nodeAffinity", pod: st.MakePod().Name("p").Label("foo", ""). NodeAffinityIn("foo", []string{""}). - SpreadConstraint(1, "zone", v1.ScheduleAnyway, barSelector, nil, &ignorePolicy, nil). + SpreadConstraint(1, "zone", v1.ScheduleAnyway, barSelector, nil, &ignorePolicy, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("foo", "").Label(v1.LabelHostname, "node-a").Obj(), @@ -429,7 +429,7 @@ func TestPreScoreStateEmptyNodes(t *testing.T) { { name: "NodeTaintsPolicy honored", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "zone", v1.ScheduleAnyway, barSelector, nil, nil, &honorPolicy). + SpreadConstraint(1, "zone", v1.ScheduleAnyway, barSelector, nil, nil, &honorPolicy, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("foo", "").Label(v1.LabelHostname, "node-a").Obj(), @@ -462,7 +462,7 @@ func TestPreScoreStateEmptyNodes(t *testing.T) { { name: "NodeTaintsPolicy ignored", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "zone", v1.ScheduleAnyway, barSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.ScheduleAnyway, barSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("zone", "zone1").Label("foo", "").Label(v1.LabelHostname, "node-a").Obj(), @@ -537,6 +537,7 @@ func TestPodTopologySpreadScore(t *testing.T) { objs []runtime.Object want framework.NodeScoreList enableNodeInclustionPolicy bool + enableMatchLabelKeys bool }{ // Explanation on the Legend: // a) X/Y means there are X matching pods on node1 and Y on node2, both nodes are candidates @@ -547,7 +548,7 @@ func TestPodTopologySpreadScore(t *testing.T) { { name: "one constraint on node, no existing pods", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label(v1.LabelHostname, "node-a").Obj(), @@ -562,7 +563,7 @@ func TestPodTopologySpreadScore(t *testing.T) { // if there is only one candidate node, it should be scored to 100 name: "one constraint on node, only one node is candidate", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), @@ -582,7 +583,7 @@ 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, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), @@ -601,7 +602,7 @@ func TestPodTopologySpreadScore(t *testing.T) { // 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, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), @@ -628,7 +629,7 @@ func TestPodTopologySpreadScore(t *testing.T) { { name: "one constraint on node, all 4 nodes are candidates, maxSkew=2", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(2, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil). + SpreadConstraint(2, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). Obj(), // matching pods spread as 2/1/0/3. existingPods: []*v1.Pod{ @@ -656,7 +657,7 @@ func TestPodTopologySpreadScore(t *testing.T) { { name: "one constraint on node, all 4 nodes are candidates, maxSkew=3", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(3, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil). + SpreadConstraint(3, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). Obj(), existingPods: []*v1.Pod{ // matching pods spread as 4/3/2/1. @@ -723,7 +724,7 @@ func TestPodTopologySpreadScore(t *testing.T) { // 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, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), @@ -755,7 +756,7 @@ func TestPodTopologySpreadScore(t *testing.T) { // matching pods spread as 4/?2?/1/~3~, total = 4+?+1 = 5 (as node2 is problematic) 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, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), @@ -787,7 +788,7 @@ func TestPodTopologySpreadScore(t *testing.T) { // 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, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), @@ -819,8 +820,8 @@ func TestPodTopologySpreadScore(t *testing.T) { // 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, fooSelector, nil, nil, nil). - SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), @@ -859,8 +860,8 @@ func TestPodTopologySpreadScore(t *testing.T) { // For the second constraint (node): the matching pods spread as 0/1/0/1 name: "two Constraints on zone and node, with different labelSelectors", pod: st.MakePod().Name("p").Label("foo", "").Label("bar", ""). - SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil). - SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil, nil). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), @@ -887,8 +888,8 @@ func TestPodTopologySpreadScore(t *testing.T) { // For the second constraint (node): the matching pods spread as 0/1/0/1 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, fooSelector, nil, nil, nil). - SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil, nil). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p-b1").Node("node-b").Label("bar", "").Obj(), @@ -914,8 +915,8 @@ func TestPodTopologySpreadScore(t *testing.T) { // For the second constraint (node): the matching pods spread as 0/1/0/~1~ 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, fooSelector, nil, nil, nil). - SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil). + SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil, nil). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), @@ -940,7 +941,7 @@ func TestPodTopologySpreadScore(t *testing.T) { { name: "existing pods in a different namespace do not count", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p-a1").Namespace("ns1").Node("node-a").Label("foo", "").Obj(), @@ -960,7 +961,7 @@ func TestPodTopologySpreadScore(t *testing.T) { { name: "terminating Pods should be excluded", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label(v1.LabelHostname, "node-a").Obj(), @@ -987,6 +988,7 @@ func TestPodTopologySpreadScore(t *testing.T) { pointer.Int32(10), // larger than the number of domains(3) nil, nil, + nil, ).Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("node", "node-a").Obj(), @@ -1010,7 +1012,7 @@ func TestPodTopologySpreadScore(t *testing.T) { name: "NodeAffinityPolicy honoed with labelSelectors", pod: st.MakePod().Name("p").Label("foo", ""). NodeSelector(map[string]string{"foo": ""}). - SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, nil, nil). + SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(), @@ -1034,7 +1036,7 @@ func TestPodTopologySpreadScore(t *testing.T) { name: "NodeAffinityPolicy ignored with labelSelectors", pod: st.MakePod().Name("p").Label("foo", ""). NodeSelector(map[string]string{"foo": ""}). - SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, &ignorePolicy, nil). + SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, &ignorePolicy, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(), @@ -1058,7 +1060,7 @@ func TestPodTopologySpreadScore(t *testing.T) { name: "NodeAffinityPolicy honoed with nodeAffinity", pod: st.MakePod().Name("p").Label("foo", ""). NodeAffinityIn("foo", []string{""}). - SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, nil, nil). + SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(), @@ -1082,7 +1084,7 @@ func TestPodTopologySpreadScore(t *testing.T) { name: "NodeAffinityPolicy ignored with nodeAffinity", pod: st.MakePod().Name("p").Label("foo", ""). NodeAffinityIn("foo", []string{""}). - SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, &ignorePolicy, nil). + SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, &ignorePolicy, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(), @@ -1105,7 +1107,7 @@ func TestPodTopologySpreadScore(t *testing.T) { { name: "NodeTaintsPolicy honored", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, nil, &honorPolicy). + SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, nil, &honorPolicy, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("node", "node-a").Obj(), @@ -1128,7 +1130,7 @@ func TestPodTopologySpreadScore(t *testing.T) { { name: "NodeTaintsPolicy ignored", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, nil, nil). + SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). Obj(), nodes: []*v1.Node{ st.MakeNode().Name("node-a").Label("node", "node-a").Obj(), @@ -1148,6 +1150,85 @@ func TestPodTopologySpreadScore(t *testing.T) { }, enableNodeInclustionPolicy: true, }, + { + name: "matchLabelKeys ignored when feature gate disabled", + pod: st.MakePod().Name("p").Label("foo", "").Label("bar", "").Label("baz", ""). + SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil, []string{"baz"}). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil, []string{"baz"}). + 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", "").Label("bar", "").Obj(), + st.MakePod().Name("p-y1").Node("node-c").Label("foo", "").Obj(), + st.MakePod().Name("p-y2").Node("node-c").Label("bar", "").Obj(), + }, + nodes: []*v1.Node{ + 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-c").Label("zone", "zone2").Label(v1.LabelHostname, "node-c").Obj(), + st.MakeNode().Name("node-d").Label("zone", "zone2").Label(v1.LabelHostname, "node-d").Obj(), + }, + want: []framework.NodeScore{ + {Name: "node-a", Score: 60}, + {Name: "node-b", Score: 20}, + {Name: "node-c", Score: 60}, + {Name: "node-d", Score: 100}, + }, + enableMatchLabelKeys: false, + }, + { + name: "matchLabelKeys ANDed with LabelSelector when LabelSelector is empty", + pod: st.MakePod().Name("p").Label("foo", "").Label("bar", ""). + SpreadConstraint(1, "zone", v1.ScheduleAnyway, st.MakeLabelSelector().Obj(), nil, nil, nil, []string{"foo"}). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, st.MakeLabelSelector().Obj(), nil, nil, nil, []string{"bar"}). + 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", "").Label("bar", "").Obj(), + st.MakePod().Name("p-y1").Node("node-c").Label("foo", "").Obj(), + st.MakePod().Name("p-y2").Node("node-c").Label("bar", "").Obj(), + }, + nodes: []*v1.Node{ + 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-c").Label("zone", "zone2").Label(v1.LabelHostname, "node-c").Obj(), + st.MakeNode().Name("node-d").Label("zone", "zone2").Label(v1.LabelHostname, "node-d").Obj(), + }, + want: []framework.NodeScore{ + {Name: "node-a", Score: 60}, + {Name: "node-b", Score: 20}, + {Name: "node-c", Score: 60}, + {Name: "node-d", Score: 100}, + }, + enableMatchLabelKeys: true, + }, + { + name: "matchLabelKeys ANDed with LabelSelector when LabelSelector isn't empty", + pod: st.MakePod().Name("p").Label("foo", "").Label("bar", "").Label("baz", ""). + SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil, []string{"baz"}). + 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", "").Label("bar", "").Label("baz", "").Obj(), + st.MakePod().Name("p-c1").Node("node-c").Label("foo", "").Obj(), + st.MakePod().Name("p-c2").Node("node-c").Label("bar", "").Obj(), + st.MakePod().Name("p-d3").Node("node-c").Label("bar", "").Label("baz", "").Obj(), + }, + nodes: []*v1.Node{ + 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-c").Label("zone", "zone2").Label(v1.LabelHostname, "node-c").Obj(), + st.MakeNode().Name("node-d").Label("zone", "zone2").Label(v1.LabelHostname, "node-d").Obj(), + }, + want: []framework.NodeScore{ + {Name: "node-a", Score: 60}, + {Name: "node-b", Score: 20}, + {Name: "node-c", Score: 60}, + {Name: "node-d", Score: 100}, + }, + enableMatchLabelKeys: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -1159,6 +1240,7 @@ func TestPodTopologySpreadScore(t *testing.T) { pl := plugintesting.SetupPluginWithInformers(ctx, t, podTopologySpreadFunc, &config.PodTopologySpreadArgs{DefaultingType: config.SystemDefaulting}, cache.NewSnapshot(tt.existingPods, allNodes), tt.objs) p := pl.(*PodTopologySpread) p.enableNodeInclusionPolicyInPodTopologySpread = tt.enableNodeInclustionPolicy + p.enableMatchLabelKeysInPodTopologySpread = tt.enableMatchLabelKeys status := p.PreScore(context.Background(), state, tt.pod, tt.nodes) if !status.IsSuccess() { @@ -1199,7 +1281,7 @@ func BenchmarkTestPodTopologySpreadScore(b *testing.B) { { name: "1000nodes/single-constraint-zone", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, v1.LabelTopologyZone, v1.ScheduleAnyway, fooSelector, nil, nil, nil). + SpreadConstraint(1, v1.LabelTopologyZone, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). Obj(), existingPodsNum: 10000, allNodesNum: 1000, @@ -1208,7 +1290,7 @@ func BenchmarkTestPodTopologySpreadScore(b *testing.B) { { name: "1000nodes/single-constraint-node", pod: st.MakePod().Name("p").Label("foo", ""). - SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). Obj(), existingPodsNum: 10000, allNodesNum: 1000, @@ -1217,8 +1299,8 @@ func BenchmarkTestPodTopologySpreadScore(b *testing.B) { { name: "1000nodes/two-Constraints-zone-node", pod: st.MakePod().Name("p").Label("foo", "").Label("bar", ""). - SpreadConstraint(1, v1.LabelTopologyZone, v1.ScheduleAnyway, fooSelector, nil, nil, nil). - SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil). + SpreadConstraint(1, v1.LabelTopologyZone, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). + SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil, nil). Obj(), existingPodsNum: 10000, allNodesNum: 1000, diff --git a/pkg/scheduler/framework/plugins/registry.go b/pkg/scheduler/framework/plugins/registry.go index 3093e681c93..0f29e6ce96a 100644 --- a/pkg/scheduler/framework/plugins/registry.go +++ b/pkg/scheduler/framework/plugins/registry.go @@ -49,6 +49,7 @@ func NewInTreeRegistry() runtime.Registry { EnableVolumeCapacityPriority: feature.DefaultFeatureGate.Enabled(features.VolumeCapacityPriority), EnableMinDomainsInPodTopologySpread: feature.DefaultFeatureGate.Enabled(features.MinDomainsInPodTopologySpread), EnableNodeInclusionPolicyInPodTopologySpread: feature.DefaultFeatureGate.Enabled(features.NodeInclusionPolicyInPodTopologySpread), + EnableMatchLabelKeysInPodTopologySpread: feature.DefaultFeatureGate.Enabled(features.MatchLabelKeysInPodTopologySpread), } return runtime.Registry{ diff --git a/pkg/scheduler/schedule_one_test.go b/pkg/scheduler/schedule_one_test.go index 1315557c4f3..a02f1e14427 100644 --- a/pkg/scheduler/schedule_one_test.go +++ b/pkg/scheduler/schedule_one_test.go @@ -1759,7 +1759,7 @@ func TestSchedulerSchedulePod(t *testing.T) { Operator: metav1.LabelSelectorOpExists, }, }, - }, nil, nil, nil).Obj(), + }, nil, nil, nil, nil).Obj(), pods: []*v1.Pod{ st.MakePod().Name("pod1").UID("pod1").Label("foo", "").Node("node1").Phase(v1.PodRunning).Obj(), }, @@ -1786,7 +1786,7 @@ func TestSchedulerSchedulePod(t *testing.T) { Operator: metav1.LabelSelectorOpExists, }, }, - }, nil, nil, nil).Obj(), + }, nil, nil, nil, nil).Obj(), pods: []*v1.Pod{ st.MakePod().Name("pod1a").UID("pod1a").Label("foo", "").Node("node1").Phase(v1.PodRunning).Obj(), st.MakePod().Name("pod1b").UID("pod1b").Label("foo", "").Node("node1").Phase(v1.PodRunning).Obj(), diff --git a/pkg/scheduler/testing/wrappers.go b/pkg/scheduler/testing/wrappers.go index fe84987e439..a77cdc40ad6 100644 --- a/pkg/scheduler/testing/wrappers.go +++ b/pkg/scheduler/testing/wrappers.go @@ -547,7 +547,7 @@ func (p *PodWrapper) PodAntiAffinityNotIn(labelKey, topologyKey string, vals []s // SpreadConstraint constructs a TopologySpreadConstraint object and injects // into the inner pod. -func (p *PodWrapper) SpreadConstraint(maxSkew int, tpKey string, mode v1.UnsatisfiableConstraintAction, selector *metav1.LabelSelector, minDomains *int32, nodeAffinityPolicy, nodeTaintsPolicy *v1.NodeInclusionPolicy) *PodWrapper { +func (p *PodWrapper) SpreadConstraint(maxSkew int, tpKey string, mode v1.UnsatisfiableConstraintAction, selector *metav1.LabelSelector, minDomains *int32, nodeAffinityPolicy, nodeTaintsPolicy *v1.NodeInclusionPolicy, matchLabelKeys []string) *PodWrapper { c := v1.TopologySpreadConstraint{ MaxSkew: int32(maxSkew), TopologyKey: tpKey, @@ -556,6 +556,7 @@ func (p *PodWrapper) SpreadConstraint(maxSkew int, tpKey string, mode v1.Unsatis MinDomains: minDomains, NodeAffinityPolicy: nodeAffinityPolicy, NodeTaintsPolicy: nodeTaintsPolicy, + MatchLabelKeys: matchLabelKeys, } p.Spec.TopologySpreadConstraints = append(p.Spec.TopologySpreadConstraints, c) return p diff --git a/test/integration/scheduler/filters/filters_test.go b/test/integration/scheduler/filters/filters_test.go index 7428938e03b..edd3241e236 100644 --- a/test/integration/scheduler/filters/filters_test.go +++ b/test/integration/scheduler/filters/filters_test.go @@ -1076,12 +1076,13 @@ func TestPodTopologySpreadFilter(t *testing.T) { candidateNodes []string // nodes expected to schedule onto enableMinDomains bool enableNodeInclustionPolicy bool + enableMatchLabelKeys bool }{ // note: naming starts at index 0 { name: "place pod on a 1/1/0/1 cluster with MaxSkew=1, node-2 is the only fit", incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause). - SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil). + SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p0").Node("node-0").Label("foo", "").Container(pause).Obj(), @@ -1095,7 +1096,7 @@ func TestPodTopologySpreadFilter(t *testing.T) { { name: "place pod on a 2/0/0/1 cluster with MaxSkew=2, node-{1,2,3} are good fits", incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause). - SpreadConstraint(2, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil). + SpreadConstraint(2, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p0a").Node("node-0").Label("foo", "").Container(pause).Obj(), @@ -1110,7 +1111,7 @@ func TestPodTopologySpreadFilter(t *testing.T) { name: "pod is required to be placed on zone0, so only node-1 fits", incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause). NodeAffinityIn("zone", []string{"zone-0"}). - SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil). + SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p0").Node("node-0").Label("foo", "").Container(pause).Obj(), @@ -1123,8 +1124,8 @@ func TestPodTopologySpreadFilter(t *testing.T) { { name: "two constraints: pod can only be placed to zone-1/node-2", incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause). - SpreadConstraint(1, "zone", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil). - SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil). + SpreadConstraint(1, "zone", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil). + SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p0").Node("node-0").Label("foo", "").Container(pause).Obj(), @@ -1140,8 +1141,8 @@ func TestPodTopologySpreadFilter(t *testing.T) { name: "pod cannot be placed onto any node", incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause). NodeAffinityNotIn("node", []string{"node-0"}). // mock a 3-node cluster - SpreadConstraint(1, "zone", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil). - SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil). + SpreadConstraint(1, "zone", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil). + SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(), @@ -1157,8 +1158,8 @@ func TestPodTopologySpreadFilter(t *testing.T) { name: "high priority pod can preempt others", incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).Priority(100). NodeAffinityNotIn("node", []string{"node-0"}). // mock a 3-node cluster - SpreadConstraint(1, "zone", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil). - SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil). + SpreadConstraint(1, "zone", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil). + SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil). Obj(), existingPods: []*v1.Pod{ st.MakePod().ZeroTerminationGracePeriod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(), @@ -1183,6 +1184,7 @@ func TestPodTopologySpreadFilter(t *testing.T) { pointer.Int32(4), // larger than the number of domains (= 3) nil, nil, + nil, ). Obj(), existingPods: []*v1.Pod{ @@ -1209,6 +1211,7 @@ func TestPodTopologySpreadFilter(t *testing.T) { pointer.Int32(2), // smaller than the number of domains (= 3) nil, nil, + nil, ). Obj(), existingPods: []*v1.Pod{ @@ -1234,6 +1237,7 @@ func TestPodTopologySpreadFilter(t *testing.T) { pointer.Int32(3), // larger than the number of domains(2) nil, nil, + nil, ).Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p1a").Node("node-0").Label("foo", "").Container(pause).Obj(), @@ -1256,6 +1260,7 @@ func TestPodTopologySpreadFilter(t *testing.T) { pointer.Int32(1), // smaller than the number of domains(2) nil, nil, + nil, ).Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(), @@ -1271,7 +1276,7 @@ func TestPodTopologySpreadFilter(t *testing.T) { name: "NodeAffinityPolicy honored with labelSelectors, pods spread across zone as 2/1", incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause). NodeSelector(map[string]string{"foo": ""}). - SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(), @@ -1293,7 +1298,7 @@ func TestPodTopologySpreadFilter(t *testing.T) { name: "NodeAffinityPolicy ignored with nodeAffinity, pods spread across zone as 1/~2~", incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause). NodeAffinityIn("foo", []string{""}). - SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, &ignorePolicy, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, &ignorePolicy, nil, nil). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(), @@ -1313,7 +1318,7 @@ func TestPodTopologySpreadFilter(t *testing.T) { { name: "NodeTaintsPolicy honored, pods spread across zone as 2/1", incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause). - SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, &honorPolicy). + SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, &honorPolicy, nil). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(), @@ -1334,7 +1339,7 @@ func TestPodTopologySpreadFilter(t *testing.T) { { name: "NodeTaintsPolicy ignored, pods spread across zone as 2/2", incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause). - SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(), @@ -1359,8 +1364,8 @@ func TestPodTopologySpreadFilter(t *testing.T) { name: "two node inclusion Constraints, zone: honor/ignore, node: honor/ignore", incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause). NodeSelector(map[string]string{"foo": ""}). - SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil). - SpreadConstraint(1, "node", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(), @@ -1385,8 +1390,8 @@ func TestPodTopologySpreadFilter(t *testing.T) { name: "feature gate disabled, two node inclusion Constraints, zone: honor/ignore, node: honor/ignore", incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause). NodeSelector(map[string]string{"foo": ""}). - SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil). - SpreadConstraint(1, "node", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil). + SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(), @@ -1411,8 +1416,8 @@ func TestPodTopologySpreadFilter(t *testing.T) { name: "two node inclusion Constraints, zone: ignore/ignore, node: honor/honor", incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause). NodeSelector(map[string]string{"foo": ""}). - SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, &ignorePolicy, nil). - SpreadConstraint(1, "node", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, &honorPolicy). + SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, &ignorePolicy, nil, nil). + SpreadConstraint(1, "node", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, &honorPolicy, nil). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(), @@ -1430,11 +1435,57 @@ func TestPodTopologySpreadFilter(t *testing.T) { candidateNodes: []string{"node-1", "node-4"}, enableNodeInclustionPolicy: true, }, + { + name: "matchLabelKeys ignored when feature gate disabled, pods spread across zone as 2/1", + incomingPod: st.MakePod().Name("p").Label("foo", "").Label("bar", "").Container(pause). + SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, []string{"bar"}). + Obj(), + existingPods: []*v1.Pod{ + st.MakePod().Name("p1a").Node("node-0").Label("foo", "").Container(pause).Obj(), + st.MakePod().Name("p2a").Node("node-1").Label("foo", "").Container(pause).Obj(), + st.MakePod().Name("p3a").Node("node-2").Label("foo", "").Label("bar", "").Container(pause).Obj(), + }, + fits: true, + nodes: defaultNodes, + candidateNodes: []string{"node-2", "node-3"}, + enableMatchLabelKeys: false, + }, + { + name: "matchLabelKeys ANDed with LabelSelector when LabelSelector isn't empty, pods spread across zone as 0/1", + incomingPod: st.MakePod().Name("p").Label("foo", "").Label("bar", "").Container(pause). + SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, []string{"bar"}). + Obj(), + existingPods: []*v1.Pod{ + st.MakePod().Name("p1a").Node("node-0").Label("foo", "").Container(pause).Obj(), + st.MakePod().Name("p2a").Node("node-1").Label("foo", "").Container(pause).Obj(), + st.MakePod().Name("p3a").Node("node-2").Label("foo", "").Label("bar", "").Container(pause).Obj(), + }, + fits: true, + nodes: defaultNodes, + candidateNodes: []string{"node-0", "node-1"}, + enableMatchLabelKeys: true, + }, + { + name: "matchLabelKeys ANDed with LabelSelector when LabelSelector is empty, pods spread across zone as 2/1", + incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause). + SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Obj(), nil, nil, nil, []string{"foo"}). + Obj(), + existingPods: []*v1.Pod{ + st.MakePod().Name("p1a").Node("node-0").Label("foo", "").Container(pause).Obj(), + st.MakePod().Name("p2a").Node("node-1").Label("foo", "").Container(pause).Obj(), + st.MakePod().Name("p3a").Node("node-2").Label("foo", "").Container(pause).Obj(), + }, + fits: true, + nodes: defaultNodes, + candidateNodes: []string{"node-2", "node-3"}, + enableMatchLabelKeys: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MinDomainsInPodTopologySpread, tt.enableMinDomains)() defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.NodeInclusionPolicyInPodTopologySpread, tt.enableNodeInclustionPolicy)() + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MatchLabelKeysInPodTopologySpread, tt.enableMatchLabelKeys)() testCtx := initTest(t, "pts-predicate") cs := testCtx.ClientSet diff --git a/test/integration/scheduler/scoring/priorities_test.go b/test/integration/scheduler/scoring/priorities_test.go index c757d6bbaae..a8ea534228f 100644 --- a/test/integration/scheduler/scoring/priorities_test.go +++ b/test/integration/scheduler/scoring/priorities_test.go @@ -449,13 +449,14 @@ func TestPodTopologySpreadScoring(t *testing.T) { nodes []*v1.Node want []string // nodes expected to schedule onto enableNodeInclustionPolicy bool + enableMatchLabelKeys bool }{ // note: naming starts at index 0 // the symbol ~X~ means that node is infeasible { name: "place pod on a ~0~/1/2/3 cluster with MaxSkew=1, node-1 is the preferred fit", incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause). - SpreadConstraint(1, "node", softSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil). + SpreadConstraint(1, "node", softSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p1").Node("node-1").Label("foo", "").Container(pause).Obj(), @@ -472,8 +473,8 @@ func TestPodTopologySpreadScoring(t *testing.T) { { name: "combined with hardSpread constraint on a ~4~/0/1/2 cluster", incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause). - SpreadConstraint(1, "node", softSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil). - SpreadConstraint(1, "zone", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil). + SpreadConstraint(1, "node", softSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil). + SpreadConstraint(1, "zone", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p0a").Node("node-0").Label("foo", "").Container(pause).Obj(), @@ -495,8 +496,8 @@ func TestPodTopologySpreadScoring(t *testing.T) { name: "soft constraint with two node inclusion Constraints, zone: honor/ignore, node: honor/ignore", incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause). NodeSelector(map[string]string{"foo": ""}). - SpreadConstraint(1, "zone", softSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil). - SpreadConstraint(1, "node", softSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil). + SpreadConstraint(1, "zone", softSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil). + SpreadConstraint(1, "node", softSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(), @@ -521,8 +522,8 @@ func TestPodTopologySpreadScoring(t *testing.T) { name: "soft constraint with two node inclusion Constraints, zone: ignore/ignore, node: honor/honor", incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause). NodeSelector(map[string]string{"foo": ""}). - SpreadConstraint(1, "zone", softSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, &ignorePolicy, nil). - SpreadConstraint(1, "node", softSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, &honorPolicy). + SpreadConstraint(1, "zone", softSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, &ignorePolicy, nil, nil). + SpreadConstraint(1, "node", softSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, &honorPolicy, nil). Obj(), existingPods: []*v1.Pod{ st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(), @@ -540,10 +541,65 @@ func TestPodTopologySpreadScoring(t *testing.T) { want: []string{"node-3"}, enableNodeInclustionPolicy: true, }, + { + name: "matchLabelKeys ignored when feature gate disabled, node-1 is the preferred fit", + incomingPod: st.MakePod().Name("p").Label("foo", "").Label("bar", "").Container(pause). + SpreadConstraint(1, "node", softSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, []string{"bar"}). + Obj(), + existingPods: []*v1.Pod{ + st.MakePod().Name("p1").Node("node-1").Label("foo", "").Label("bar", "").Container(pause).Obj(), + st.MakePod().Name("p2a").Node("node-2").Label("foo", "").Container(pause).Obj(), + st.MakePod().Name("p2b").Node("node-2").Label("foo", "").Container(pause).Obj(), + st.MakePod().Name("p3a").Node("node-3").Label("foo", "").Label("bar", "").Container(pause).Obj(), + st.MakePod().Name("p3b").Node("node-3").Label("foo", "").Label("bar", "").Container(pause).Obj(), + st.MakePod().Name("p3c").Node("node-3").Label("foo", "").Container(pause).Obj(), + }, + fits: true, + nodes: defaultNodes, + want: []string{"node-1"}, + enableMatchLabelKeys: false, + }, + { + name: "matchLabelKeys ANDed with LabelSelector when LabelSelector isn't empty, node-2 is the preferred fit", + incomingPod: st.MakePod().Name("p").Label("foo", "").Label("bar", "").Container(pause). + SpreadConstraint(1, "node", softSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, []string{"bar"}). + Obj(), + existingPods: []*v1.Pod{ + st.MakePod().Name("p1").Node("node-1").Label("foo", "").Label("bar", "").Container(pause).Obj(), + st.MakePod().Name("p2a").Node("node-2").Label("foo", "").Container(pause).Obj(), + st.MakePod().Name("p2b").Node("node-2").Label("foo", "").Container(pause).Obj(), + st.MakePod().Name("p3a").Node("node-3").Label("foo", "").Label("bar", "").Container(pause).Obj(), + st.MakePod().Name("p3b").Node("node-3").Label("foo", "").Label("bar", "").Container(pause).Obj(), + st.MakePod().Name("p3c").Node("node-3").Label("foo", "").Container(pause).Obj(), + }, + fits: true, + nodes: defaultNodes, + want: []string{"node-2"}, + enableMatchLabelKeys: true, + }, + { + name: "matchLabelKeys ANDed with LabelSelector when LabelSelector is empty, node-1 is the preferred fit", + incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause). + SpreadConstraint(1, "node", softSpread, st.MakeLabelSelector().Obj(), nil, nil, nil, []string{"foo"}). + Obj(), + existingPods: []*v1.Pod{ + st.MakePod().Name("p1").Node("node-1").Label("foo", "").Container(pause).Obj(), + st.MakePod().Name("p2a").Node("node-2").Label("foo", "").Container(pause).Obj(), + st.MakePod().Name("p2b").Node("node-2").Label("foo", "").Container(pause).Obj(), + st.MakePod().Name("p3a").Node("node-3").Label("foo", "").Container(pause).Obj(), + st.MakePod().Name("p3b").Node("node-3").Label("foo", "").Container(pause).Obj(), + st.MakePod().Name("p3c").Node("node-3").Label("foo", "").Container(pause).Obj(), + }, + fits: true, + nodes: defaultNodes, + want: []string{"node-1"}, + enableMatchLabelKeys: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.NodeInclusionPolicyInPodTopologySpread, tt.enableNodeInclustionPolicy)() + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MatchLabelKeysInPodTopologySpread, tt.enableMatchLabelKeys)() testCtx := initTestSchedulerForPriorityTest(t, podtopologyspread.Name) defer testutils.CleanupTest(t, testCtx)