diff --git a/pkg/controllers/provisioning/scheduling/topology.go b/pkg/controllers/provisioning/scheduling/topology.go index 37d36e4d1c..8b4287ab2c 100644 --- a/pkg/controllers/provisioning/scheduling/topology.go +++ b/pkg/controllers/provisioning/scheduling/topology.go @@ -333,6 +333,11 @@ func (t *Topology) countDomains(ctx context.Context, tg *TopologyGroup) error { func (t *Topology) newForTopologies(p *v1.Pod) []*TopologyGroup { var topologyGroups []*TopologyGroup for _, cs := range p.Spec.TopologySpreadConstraints { + for _, label := range cs.MatchLabelKeys { + if value, ok := p.ObjectMeta.Labels[label]; ok { + cs.LabelSelector.MatchLabels[label] = value + } + } topologyGroups = append(topologyGroups, NewTopologyGroup(TopologyTypeSpread, cs.TopologyKey, p, sets.New(p.Namespace), cs.LabelSelector, cs.MaxSkew, cs.MinDomains, cs.NodeTaintsPolicy, cs.NodeAffinityPolicy, t.domainGroups[cs.TopologyKey])) } diff --git a/pkg/controllers/provisioning/scheduling/topology_test.go b/pkg/controllers/provisioning/scheduling/topology_test.go index 01026288c4..3da1294c26 100644 --- a/pkg/controllers/provisioning/scheduling/topology_test.go +++ b/pkg/controllers/provisioning/scheduling/topology_test.go @@ -1127,11 +1127,69 @@ var _ = Describe("Topology", func() { }) }) + Context("MatchLabelKeys", func() { + BeforeEach(func() { + if env.Version.Minor() < 27 { + Skip("MatchLabelKeys only enabled by default forK8S >= 1.27.x") + } + }) + + It("should support matchLabelKeys", func() { + matchedLabel := "test-label" + topology := []v1.TopologySpreadConstraint{{ + TopologyKey: v1.LabelHostname, + WhenUnsatisfiable: v1.DoNotSchedule, + LabelSelector: &metav1.LabelSelector{MatchLabels: labels}, + MatchLabelKeys: []string{matchedLabel}, + MaxSkew: 1, + }} + count := 2 + pods := test.UnschedulablePods(test.PodOptions{ + ObjectMeta: metav1.ObjectMeta{ + Labels: lo.Assign(labels, map[string]string{matchedLabel: "value-a"}), + }, + TopologySpreadConstraints: topology, + }, count) + pods = append(pods, test.UnschedulablePods(test.PodOptions{ + ObjectMeta: metav1.ObjectMeta{ + Labels: lo.Assign(labels, map[string]string{matchedLabel: "value-b"}), + }, + TopologySpreadConstraints: topology, + }, count)...) + ExpectApplied(ctx, env.Client, nodePool) + ExpectProvisioned(ctx, env.Client, cluster, cloudProvider, prov, pods...) + ExpectSkew(ctx, env.Client, "default", &topology[0]).To(ConsistOf(2, 2)) + }) + + It("should ignore unknown labels specified in matchLabelKeys", func() { + matchedLabel := "test-label" + topology := []v1.TopologySpreadConstraint{{ + TopologyKey: v1.LabelHostname, + WhenUnsatisfiable: v1.DoNotSchedule, + LabelSelector: &metav1.LabelSelector{MatchLabels: labels}, + MatchLabelKeys: []string{matchedLabel}, + MaxSkew: 1, + }} + pods := test.UnschedulablePods(test.PodOptions{ + ObjectMeta: metav1.ObjectMeta{ + Labels: labels, + }, + TopologySpreadConstraints: topology, + }, 4) + ExpectApplied(ctx, env.Client, nodePool) + ExpectProvisioned(ctx, env.Client, cluster, cloudProvider, prov, pods...) + ExpectSkew(ctx, env.Client, "default", &topology[0]).To(ConsistOf(1, 1, 1, 1)) + }) + }) + Context("NodeTaintsPolicy", func() { - It("should balance pods across a label (NodeTaintsPolicy=ignore)", func() { + BeforeEach(func() { if env.Version.Minor() < 26 { - Skip("NodeTaintsPolicy ony enabled by default for K8s >= 1.26.x") + Skip("NodeTaintsPolicy only enabled by default for K8s >= 1.26.x") } + }) + + It("should balance pods across a label (NodeTaintsPolicy=ignore)", func() { const spreadLabel = "fake-label" nodePool.Spec.Template.Labels = map[string]string{ @@ -1204,9 +1262,6 @@ var _ = Describe("Topology", func() { ExpectSkew(ctx, env.Client, "default", &topology[0]).To(ConsistOf(1)) }) It("should balance pods across a label (NodeTaintsPolicy=honor)", func() { - if env.Version.Minor() < 26 { - Skip("NodeTaintsPolicy ony enabled by default for K8s >= 1.26.x") - } const spreadLabel = "fake-label" nodePool.Spec.Template.Labels = map[string]string{ spreadLabel: "baz", @@ -1340,10 +1395,6 @@ var _ = Describe("Topology", func() { ExpectSkew(ctx, env.Client, "default", &topology[0]).To(ConsistOf(1)) }) It("should balance pods across a label when discovered from the provisioner (NodeTaintsPolicy=honor)", func() { - if env.Version.Minor() < 26 { - Skip("NodeTaintsPolicy ony enabled by default for K8s >= 1.26.x") - } - const spreadLabel = "fake-label" const taintKey = "taint-key" nodePool.Spec.Template.Spec.Requirements = append(nodePool.Spec.Template.Spec.Requirements, v1beta1.NodeSelectorRequirementWithMinValues{ @@ -1475,10 +1526,12 @@ var _ = Describe("Topology", func() { }) Context("NodeAffinityPolicy", func() { - It("should balance pods across a label (NodeAffinityPolicy=ignore)", func() { + BeforeEach(func() { if env.Version.Minor() < 26 { Skip("NodeAffinityPolicy ony enabled by default for K8s >= 1.26.x") } + }) + It("should balance pods across a label (NodeAffinityPolicy=ignore)", func() { const spreadLabel = "fake-label" const affinityLabel = "selector" const affinityMismatch = "mismatch" @@ -1547,9 +1600,6 @@ var _ = Describe("Topology", func() { ExpectSkew(ctx, env.Client, "default", &topology[0]).To(ConsistOf(1)) }) It("should balance pods across a label (NodeAffinityPolicy=honor)", func() { - if env.Version.Minor() < 26 { - Skip("NodeAffinityPolicy ony enabled by default for K8s >= 1.26.x") - } const spreadLabel = "fake-label" const affinityLabel = "selector" const affinityMismatch = "mismatch"