diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 2353e464f..6ce424df0 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -516,11 +516,12 @@ func (c *Cluster) nodeAffinity(nodeReadinessLabel map[string]string, nodeAffinit } if len(nodeReadinessLabel) > 0 { matchExpressions := make([]v1.NodeSelectorRequirement, 0) - for k, v := range nodeReadinessLabel { + // sorted keys keep the generated affinity stable between syncs + for _, k := range slices.Sorted(maps.Keys(nodeReadinessLabel)) { matchExpressions = append(matchExpressions, v1.NodeSelectorRequirement{ Key: k, Operator: v1.NodeSelectorOpIn, - Values: []string{v}, + Values: []string{nodeReadinessLabel[k]}, }) } nodeReadinessSelectorTerm := v1.NodeSelectorTerm{MatchExpressions: matchExpressions} diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index 3200dc2e7..f24a6e5f1 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -1514,6 +1514,30 @@ func TestNodeAffinity(t *testing.T) { assert.Equal(t, s.Spec.Template.Spec.Affinity.NodeAffinity, nodeAff, "cluster template has correct node affinity") } +func TestNodeAffinityReadinessLabelOrder(t *testing.T) { + cluster := New(Config{}, k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger, eventRecorder) + nodeReadinessLabel := map[string]string{ + "lifecycle-status": "ready", + "nodepool": "platform", + "postgres_ready": "true", + "zone": "a", + } + + expected := []v1.NodeSelectorRequirement{ + {Key: "lifecycle-status", Operator: v1.NodeSelectorOpIn, Values: []string{"ready"}}, + {Key: "nodepool", Operator: v1.NodeSelectorOpIn, Values: []string{"platform"}}, + {Key: "postgres_ready", Operator: v1.NodeSelectorOpIn, Values: []string{"true"}}, + {Key: "zone", Operator: v1.NodeSelectorOpIn, Values: []string{"a"}}, + } + + // map iteration order is random, so an unsorted result would differ between syncs + for i := 0; i < 20; i++ { + affinity := cluster.nodeAffinity(nodeReadinessLabel, nil) + terms := affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms + assert.Equal(t, expected, terms[0].MatchExpressions, "node readiness match expressions should be sorted by key") + } +} + func TestPodAffinity(t *testing.T) { clusterName := "acid-test-cluster" namespace := "default"