diff --git a/controllers/classifier_controller.go b/controllers/classifier_controller.go index 33f4550..c77cac1 100644 --- a/controllers/classifier_controller.go +++ b/controllers/classifier_controller.go @@ -217,15 +217,13 @@ func (r *ClassifierReconciler) reconcileDelete( return reconcile.Result{}, err } - if !r.AgentInMgmtCluster { - // In agentless mode, Classifier instances are not copied to managed clusters. - // So there is nothing to remove from managed cluster. - f := getHandlersForFeature(libsveltosv1beta1.FeatureClassifier) - err = r.undeployClassifier(ctx, classifierScope, f, logger) - if err != nil { - logger.V(logs.LogInfo).Error(err, "failed to undeploy") - return reconcile.Result{RequeueAfter: deleteRequeueAfter}, nil - } + // In agentless mode, Classifier instances are not copied to managed clusters in push mode: there is nothing + // to remove from those. Clusters in pull mode always get a copy, so undeployClassifier still has work to do. + f := getHandlersForFeature(libsveltosv1beta1.FeatureClassifier) + err = r.undeployClassifier(ctx, classifierScope, f, logger) + if err != nil { + logger.V(logs.LogInfo).Error(err, "failed to undeploy") + return reconcile.Result{RequeueAfter: deleteRequeueAfter}, nil } err = removeClassifierReports(ctx, r.Client, classifierScope.Classifier, logger) diff --git a/controllers/classifier_deployer.go b/controllers/classifier_deployer.go index b1af03c..6a9d3c4 100644 --- a/controllers/classifier_deployer.go +++ b/controllers/classifier_deployer.go @@ -274,6 +274,15 @@ func (r *ClassifierReconciler) undeployClassifier(ctx context.Context, classifie logger.V(logs.LogInfo).Error(err, "failed to get cluster") return err } + + hasInstance, err := r.hasClassifierInstance(ctx, c, logger) + if err != nil { + return err + } + if !hasInstance { + logger.V(logs.LogDebug).Info(fmt.Sprintf("cluster %s/%s has no Classifier instance", c.Namespace, c.Name)) + continue + } clusters = append(clusters, c) } @@ -314,6 +323,19 @@ func (r *ClassifierReconciler) undeployClassifier(ctx context.Context, classifie return nil } +// hasClassifierInstance returns true if a Classifier instance is deployed to the cluster. When sveltos-agent runs in +// the management cluster, instances are deployed only to clusters in pull mode. +func (r *ClassifierReconciler) hasClassifierInstance(ctx context.Context, cluster *corev1.ObjectReference, + logger logr.Logger) (bool, error) { + + if !r.AgentInMgmtCluster { + return true, nil + } + + return clusterproxy.IsClusterInPullMode(ctx, r.Client, cluster.Namespace, cluster.Name, + clusterproxy.GetClusterType(cluster), logger) +} + // classifierHash returns the Classifier hash func classifierHash(classifier *libsveltosv1beta1.Classifier) []byte { h := sha256.New() diff --git a/controllers/classifier_deployer_test.go b/controllers/classifier_deployer_test.go index 2ee4a19..5df3628 100644 --- a/controllers/classifier_deployer_test.go +++ b/controllers/classifier_deployer_test.go @@ -590,6 +590,43 @@ var _ = Describe("Classifier Deployer", func() { }, 3*time.Second, time.Second).Should(BeTrue()) }) + It("undeployClassifier does not queue a job for a push mode cluster when sveltos-agent runs in the management cluster", + func() { + sveltosCluster, classifier, c := prepareAgentlessUndeploy(false) + dep := fakedeployer.GetClient(context.TODO(), logger, c) + Expect(dep.RegisterFeatureID(libsveltosv1beta1.FeatureClassifier)).To(Succeed()) + classifierReconciler := getClassifierReconciler(c, dep) + classifierReconciler.AgentInMgmtCluster = true + + classifierScope := getClassifierScope(c, logger, classifier) + f := controllers.GetHandlersForFeature(libsveltosv1beta1.FeatureClassifier) + Expect(controllers.UndeployClassifier(classifierReconciler, context.TODO(), classifierScope, f, + logger)).To(Succeed()) + + key := deployer.GetKey(sveltosCluster.Namespace, sveltosCluster.Name, + classifier.Name, libsveltosv1beta1.FeatureClassifier, libsveltosv1beta1.ClusterTypeSveltos, true) + Expect(dep.IsKeyInProgress(key)).To(BeFalse()) + }) + + It("undeployClassifier queues a job for a pull mode cluster when sveltos-agent runs in the management cluster", + func() { + sveltosCluster, classifier, c := prepareAgentlessUndeploy(true) + dep := fakedeployer.GetClient(context.TODO(), logger, c) + Expect(dep.RegisterFeatureID(libsveltosv1beta1.FeatureClassifier)).To(Succeed()) + classifierReconciler := getClassifierReconciler(c, dep) + classifierReconciler.AgentInMgmtCluster = true + + classifierScope := getClassifierScope(c, logger, classifier) + f := controllers.GetHandlersForFeature(libsveltosv1beta1.FeatureClassifier) + err := controllers.UndeployClassifier(classifierReconciler, context.TODO(), classifierScope, f, logger) + Expect(err).ToNot(BeNil()) + Expect(err.Error()).To(Equal("still in the process of removing Classifier from 1 clusters")) + + key := deployer.GetKey(sveltosCluster.Namespace, sveltosCluster.Name, + classifier.Name, libsveltosv1beta1.FeatureClassifier, libsveltosv1beta1.ClusterTypeSveltos, true) + Expect(dep.IsKeyInProgress(key)).To(BeTrue()) + }) + It("deploySveltosAgent deploys sveltos agent", func() { watchNamespaces := []string{watchNamespaceNs1, watchNamespaceNs2} Expect(controllers.DeploySveltosAgentInManagedCluster(ctx, testEnv.Config, randomString(), randomString(), @@ -1670,3 +1707,41 @@ func createClassifierReport(classifier *libsveltosv1beta1.Classifier, cluster *c return report } + +// prepareAgentlessUndeploy returns a SveltosCluster (in pull mode if pullMode is true), a Classifier and a fake client +// containing both, plus the ClassifierReport tying the Classifier to the cluster. +func prepareAgentlessUndeploy(pullMode bool) (*libsveltosv1beta1.SveltosCluster, *libsveltosv1beta1.Classifier, + client.Client) { + + sveltosCluster := &libsveltosv1beta1.SveltosCluster{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: randomString(), + Name: randomString(), + }, + Spec: libsveltosv1beta1.SveltosClusterSpec{ + PullMode: pullMode, + }, + } + + classifier := getClassifierInstance(randomString()) + + clusterType := libsveltosv1beta1.ClusterTypeSveltos + report := &libsveltosv1beta1.ClassifierReport{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: sveltosCluster.Namespace, + Name: libsveltosv1beta1.GetClassifierReportName(classifier.Name, sveltosCluster.Name, &clusterType), + Labels: libsveltosv1beta1.GetClassifierReportLabels(classifier.Name, sveltosCluster.Name, &clusterType), + }, + Spec: libsveltosv1beta1.ClassifierReportSpec{ + ClusterNamespace: sveltosCluster.Namespace, + ClusterName: sveltosCluster.Name, + ClusterType: clusterType, + ClassifierName: classifier.Name, + }, + } + + c := fake.NewClientBuilder().WithScheme(scheme).WithObjects(sveltosCluster, classifier, report). + WithStatusSubresource(sveltosCluster, classifier, report).Build() + + return sveltosCluster, classifier, c +} diff --git a/pkg/agent/sveltos-agent-in-mgmt-cluster.go b/pkg/agent/sveltos-agent-in-mgmt-cluster.go index 784b09c..65a1648 100644 --- a/pkg/agent/sveltos-agent-in-mgmt-cluster.go +++ b/pkg/agent/sveltos-agent-in-mgmt-cluster.go @@ -63,7 +63,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - image: docker.io/projectsveltos/sveltos-agent@sha256:59272b4f68af3cdce0d59ba221530ea7783b7e30901750474a1e805f17eab673 + image: docker.io/projectsveltos/sveltos-agent@sha256:ef0eebe3716214e941ebd66c2467ec5e1aad8ab4f80279bf48d9e5f989e0ecd1 livenessProbe: failureThreshold: 3 httpGet: diff --git a/pkg/agent/sveltos-agent-in-mgmt-cluster.yaml b/pkg/agent/sveltos-agent-in-mgmt-cluster.yaml index bf1b2c5..28208df 100644 --- a/pkg/agent/sveltos-agent-in-mgmt-cluster.yaml +++ b/pkg/agent/sveltos-agent-in-mgmt-cluster.yaml @@ -45,7 +45,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - image: docker.io/projectsveltos/sveltos-agent@sha256:59272b4f68af3cdce0d59ba221530ea7783b7e30901750474a1e805f17eab673 + image: docker.io/projectsveltos/sveltos-agent@sha256:ef0eebe3716214e941ebd66c2467ec5e1aad8ab4f80279bf48d9e5f989e0ecd1 livenessProbe: failureThreshold: 3 httpGet: diff --git a/pkg/agent/sveltos-agent.go b/pkg/agent/sveltos-agent.go index ba31133..3008ef8 100644 --- a/pkg/agent/sveltos-agent.go +++ b/pkg/agent/sveltos-agent.go @@ -212,7 +212,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - image: docker.io/projectsveltos/sveltos-agent@sha256:59272b4f68af3cdce0d59ba221530ea7783b7e30901750474a1e805f17eab673 + image: docker.io/projectsveltos/sveltos-agent@sha256:ef0eebe3716214e941ebd66c2467ec5e1aad8ab4f80279bf48d9e5f989e0ecd1 livenessProbe: failureThreshold: 3 httpGet: diff --git a/pkg/agent/sveltos-agent.yaml b/pkg/agent/sveltos-agent.yaml index c0d7b0f..770b6b8 100644 --- a/pkg/agent/sveltos-agent.yaml +++ b/pkg/agent/sveltos-agent.yaml @@ -194,7 +194,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace - image: docker.io/projectsveltos/sveltos-agent@sha256:59272b4f68af3cdce0d59ba221530ea7783b7e30901750474a1e805f17eab673 + image: docker.io/projectsveltos/sveltos-agent@sha256:ef0eebe3716214e941ebd66c2467ec5e1aad8ab4f80279bf48d9e5f989e0ecd1 livenessProbe: failureThreshold: 3 httpGet: