diff --git a/pkg/controllers/resources/pods/translate/translator.go b/pkg/controllers/resources/pods/translate/translator.go index 7b8dbf3959..369536258a 100644 --- a/pkg/controllers/resources/pods/translate/translator.go +++ b/pkg/controllers/resources/pods/translate/translator.go @@ -7,6 +7,7 @@ import ( "path" "path/filepath" "regexp" + "slices" "sort" "strconv" "strings" @@ -916,6 +917,7 @@ func (t *translator) translateResourceClaims(ctx *synccontext.SyncContext, pPod func translateTopologySpreadConstraints(vPod *corev1.Pod, pPod *corev1.Pod) { for i := range pPod.Spec.TopologySpreadConstraints { + sanitizeMatchLabelKeysSelector(vPod, &pPod.Spec.TopologySpreadConstraints[i]) pPod.Spec.TopologySpreadConstraints[i].LabelSelector = translate.HostLabelSelector(pPod.Spec.TopologySpreadConstraints[i].LabelSelector) // make sure we only select pods in the current namespace @@ -929,6 +931,34 @@ func translateTopologySpreadConstraints(vPod *corev1.Pod, pPod *corev1.Pod) { } } +func sanitizeMatchLabelKeysSelector(_ *corev1.Pod, constraint *corev1.TopologySpreadConstraint) { + if constraint.LabelSelector == nil || len(constraint.MatchLabelKeys) == 0 || len(constraint.LabelSelector.MatchExpressions) == 0 { + return + } + + matchExpressions := constraint.LabelSelector.MatchExpressions[:0] + for _, requirement := range constraint.LabelSelector.MatchExpressions { + if shouldStripMatchLabelKeysRequirement(constraint.MatchLabelKeys, requirement) { + continue + } + + matchExpressions = append(matchExpressions, requirement) + } + + if len(matchExpressions) == 0 { + constraint.LabelSelector.MatchExpressions = nil + return + } + + constraint.LabelSelector.MatchExpressions = matchExpressions +} + +func shouldStripMatchLabelKeysRequirement(matchLabelKeys []string, requirement metav1.LabelSelectorRequirement) bool { + return requirement.Operator == metav1.LabelSelectorOpIn && + len(requirement.Values) == 1 && + slices.Contains(matchLabelKeys, requirement.Key) +} + func ServicesToEnvironmentVariables(enableServiceLinks *bool, services []*corev1.Service, kubeIP string) map[string]string { var ( serviceMap = make(map[string]*corev1.Service) diff --git a/pkg/controllers/resources/pods/translate/translator_test.go b/pkg/controllers/resources/pods/translate/translator_test.go index db8aa21786..0a9a585067 100644 --- a/pkg/controllers/resources/pods/translate/translator_test.go +++ b/pkg/controllers/resources/pods/translate/translator_test.go @@ -183,6 +183,72 @@ type translatePodAffinityTermTestCase struct { expectedEvents []string } +func TestTranslateTopologySpreadConstraints(t *testing.T) { + vPod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test-ns", + Labels: map[string]string{ + "app": "demo", + "pod-template-hash": "def456", + }, + }, + } + pPod := &corev1.Pod{ + Spec: corev1.PodSpec{ + TopologySpreadConstraints: []corev1.TopologySpreadConstraint{ + { + MaxSkew: 1, + TopologyKey: "kubernetes.io/hostname", + WhenUnsatisfiable: corev1.ScheduleAnyway, + MatchLabelKeys: []string{"pod-template-hash"}, + LabelSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "demo", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "pod-template-hash", + Operator: metav1.LabelSelectorOpIn, + Values: []string{"abc123"}, + }, + { + Key: "existing", + Operator: metav1.LabelSelectorOpIn, + Values: []string{"value"}, + }, + }, + }, + }, + }, + }, + } + + translateTopologySpreadConstraints(vPod, pPod) + + assert.Assert(t, cmp.DeepEqual(pPod.Spec.TopologySpreadConstraints, []corev1.TopologySpreadConstraint{ + { + MaxSkew: 1, + TopologyKey: "kubernetes.io/hostname", + WhenUnsatisfiable: corev1.ScheduleAnyway, + MatchLabelKeys: []string{"pod-template-hash"}, + LabelSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "demo", + translate.NamespaceLabel: "test-ns", + translate.MarkerLabel: translate.VClusterName, + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "existing", + Operator: metav1.LabelSelectorOpIn, + Values: []string{"value"}, + }, + }, + }, + }, + })) +} + func TestVolumeTranslation(t *testing.T) { virtualPath := fmt.Sprintf(VirtualPathTemplate, testingutil.DefaultTestCurrentNamespace, testingutil.DefaultTestVClusterName) hostToContainer := corev1.MountPropagationHostToContainer