diff --git a/controllers/clusterprofile_predicates.go b/controllers/clusterprofile_predicates.go index 02d7034..63100bf 100644 --- a/controllers/clusterprofile_predicates.go +++ b/controllers/clusterprofile_predicates.go @@ -49,11 +49,6 @@ func ClusterProfilePredicates(logger logr.Logger) predicate.Predicate { return true } - // Skip reconciliation when only finalizers changed (e.g., we added/removed our own). - if !reflect.DeepEqual(newCP.Finalizers, oldCP.Finalizers) { - return false - } - // React when access providers change. if !reflect.DeepEqual(newCP.Status.AccessProviders, oldCP.Status.AccessProviders) { logger.V(logs.LogVerbose).Info("ClusterProfile access providers changed, will reconcile") diff --git a/controllers/utils.go b/controllers/utils.go index 19c55f8..5da3c7c 100644 --- a/controllers/utils.go +++ b/controllers/utils.go @@ -23,6 +23,7 @@ import ( "fmt" "os" "os/exec" + "reflect" "time" "github.com/go-logr/logr" @@ -246,6 +247,19 @@ func reconcileKubeconfigSecret(ctx context.Context, c client.Client, return c.Update(ctx, existing) } +// sveltosClusterLabels returns the labels to set on the SveltosCluster. +// All labels from the ClusterProfile are copied so that Sveltos selectors can +// match the SveltosCluster using the same labels. The managed-by label is +// always added (and takes precedence) so the controller can identify its own objects. +func sveltosClusterLabels(cp *clusterinventoryv1alpha1.ClusterProfile) map[string]string { + labels := make(map[string]string, len(cp.Labels)+1) + for k, v := range cp.Labels { + labels[k] = v + } + labels[managedByLabel] = managedByValue + return labels +} + // reconcileSveltosCluster creates or updates the SveltosCluster for cp. func reconcileSveltosCluster(ctx context.Context, c client.Client, cp *clusterinventoryv1alpha1.ClusterProfile, logger logr.Logger) error { @@ -253,6 +267,8 @@ func reconcileSveltosCluster(ctx context.Context, c client.Client, secretName := kubeconfigSecretName(cp.Name) logger = logger.WithValues("sveltoscluster", fmt.Sprintf("%s/%s", cp.Namespace, cp.Name)) + desiredLabels := sveltosClusterLabels(cp) + existing := &libsveltosv1beta1.SveltosCluster{} err := c.Get(ctx, types.NamespacedName{Namespace: cp.Namespace, Name: cp.Name}, existing) if err != nil { @@ -263,7 +279,7 @@ func reconcileSveltosCluster(ctx context.Context, c client.Client, ObjectMeta: metav1.ObjectMeta{ Name: cp.Name, Namespace: cp.Namespace, - Labels: map[string]string{managedByLabel: managedByValue}, + Labels: desiredLabels, }, Spec: libsveltosv1beta1.SveltosClusterSpec{ KubeconfigName: secretName, @@ -284,12 +300,16 @@ func reconcileSveltosCluster(ctx context.Context, c client.Client, } } - if existing.Spec.KubeconfigName == secretName && existing.Spec.KubeconfigKeyName == kubeconfigKey { + if existing.Spec.KubeconfigName == secretName && + existing.Spec.KubeconfigKeyName == kubeconfigKey && + reflect.DeepEqual(existing.Labels, desiredLabels) { + return nil } existing.Spec.KubeconfigName = secretName existing.Spec.KubeconfigKeyName = kubeconfigKey + existing.Labels = desiredLabels logger.V(logs.LogDebug).Info("updating SveltosCluster") return c.Update(ctx, existing) } diff --git a/controllers/utils_test.go b/controllers/utils_test.go index 7f0042c..0e1ab6c 100644 --- a/controllers/utils_test.go +++ b/controllers/utils_test.go @@ -237,6 +237,46 @@ var _ = Describe("Utils", func() { Expect(sc.Labels[controller.ManagedByLabel]).To(Equal(controller.ManagedByValue)) }) + It("copies ClusterProfile labels onto the SveltosCluster at creation", func() { + cpName := randomString() + cp := buildClusterProfile(cpName, namespace, "", nil) + cp.Labels = map[string]string{"env": "prod", "region": "us-east-1"} + + Expect(controller.ReconcileSveltosCluster(context.TODO(), testEnv.Client, cp, logger)).To(Succeed()) + + sc := &libsveltosv1beta1.SveltosCluster{} + Eventually(func() error { + return testEnv.Get(context.TODO(), + types.NamespacedName{Namespace: namespace, Name: cpName}, sc) + }, timeout, pollingInterval).Should(Succeed()) + Expect(sc.Labels["env"]).To(Equal("prod")) + Expect(sc.Labels["region"]).To(Equal("us-east-1")) + Expect(sc.Labels[controller.ManagedByLabel]).To(Equal(controller.ManagedByValue)) + }) + + It("updates SveltosCluster labels when ClusterProfile labels change", func() { + cpName := randomString() + cp := buildClusterProfile(cpName, namespace, "", nil) + cp.Labels = map[string]string{"env": "staging"} + + Expect(controller.ReconcileSveltosCluster(context.TODO(), testEnv.Client, cp, logger)).To(Succeed()) + Expect(waitForObject(context.TODO(), testEnv.Client, &libsveltosv1beta1.SveltosCluster{ + ObjectMeta: metav1.ObjectMeta{Name: cpName, Namespace: namespace}, + })).To(Succeed()) + + cp.Labels = map[string]string{"env": "prod"} + Expect(controller.ReconcileSveltosCluster(context.TODO(), testEnv.Client, cp, logger)).To(Succeed()) + + sc := &libsveltosv1beta1.SveltosCluster{} + Eventually(func() string { + if err := testEnv.Get(context.TODO(), + types.NamespacedName{Namespace: namespace, Name: cpName}, sc); err != nil { + return "" + } + return sc.Labels["env"] + }, timeout, pollingInterval).Should(Equal("prod")) + }) + It("is idempotent on repeated calls", func() { cpName := randomString() cp := buildClusterProfile(cpName, namespace, "", nil)