diff --git a/CHANGELOG/CHANGELOG-1.21.md b/CHANGELOG/CHANGELOG-1.21.md index 58ab84827..4da42ace5 100644 --- a/CHANGELOG/CHANGELOG-1.21.md +++ b/CHANGELOG/CHANGELOG-1.21.md @@ -14,3 +14,5 @@ Changelog for the K8ssandra Operator, new PRs should update the `unreleased` sec When cutting a new release, update the `unreleased` heading to the tag being generated and date, like `## vX.Y.Z - YYYY-MM-DD` and create a new placeholder section for `unreleased` entries. ## unreleased + +* [ENHANCEMENT] [#992](https://github.com/k8ssandra/k8ssandra-operator/issues/992) Use controller references to clean up DC components diff --git a/controllers/k8ssandra/auth_test.go b/controllers/k8ssandra/auth_test.go index 3d33ce79d..e6e08ded3 100644 --- a/controllers/k8ssandra/auth_test.go +++ b/controllers/k8ssandra/auth_test.go @@ -127,8 +127,6 @@ func createSingleDcClusterNoAuth(t *testing.T, ctx context.Context, f *framework err = f.DeleteK8ssandraCluster(ctx, client.ObjectKey{Namespace: kc.Namespace, Name: kc.Name}, timeout, interval) require.NoError(t, err, "failed to delete K8ssandraCluster") f.AssertObjectDoesNotExist(ctx, t, dcKey, &cassdcapi.CassandraDatacenter{}, timeout, interval) - f.AssertObjectDoesNotExist(ctx, t, stargateKey, &stargateapi.Stargate{}, timeout, interval) - f.AssertObjectDoesNotExist(ctx, t, reaperKey, &reaperapi.Reaper{}, timeout, interval) } // createSingleDcClusterAuth verifies that it is possible to create an authenticated cluster with one DC and with @@ -240,8 +238,6 @@ func createSingleDcClusterAuth(t *testing.T, ctx context.Context, f *framework.F err = f.DeleteK8ssandraCluster(ctx, client.ObjectKey{Namespace: kc.Namespace, Name: kc.Name}, timeout, interval) require.NoError(t, err, "failed to delete K8ssandraCluster") f.AssertObjectDoesNotExist(ctx, t, dcKey, &cassdcapi.CassandraDatacenter{}, timeout, interval) - f.AssertObjectDoesNotExist(ctx, t, stargateKey, &stargateapi.Stargate{}, timeout, interval) - f.AssertObjectDoesNotExist(ctx, t, reaperKey, &reaperapi.Reaper{}, timeout, interval) } // createSingleDcClusterAuthExternalSecrets verifies that kubernetes secrets for credentials are not created when @@ -367,8 +363,6 @@ func createSingleDcClusterAuthExternalSecrets(t *testing.T, ctx context.Context, err = f.DeleteK8ssandraCluster(ctx, client.ObjectKey{Namespace: kc.Namespace, Name: kc.Name}, timeout, interval) require.NoError(t, err, "failed to delete K8ssandraCluster") f.AssertObjectDoesNotExist(ctx, t, dcKey, &cassdcapi.CassandraDatacenter{}, timeout, interval) - f.AssertObjectDoesNotExist(ctx, t, stargateKey, &stargateapi.Stargate{}, timeout, interval) - f.AssertObjectDoesNotExist(ctx, t, reaperKey, &reaperapi.Reaper{}, timeout, interval) } func createSingleDcClusterExternalInternode(t *testing.T, ctx context.Context, f *framework.Framework, namespace string) { diff --git a/controllers/k8ssandra/cassandra_metrics_agent_test.go b/controllers/k8ssandra/cassandra_metrics_agent_test.go index e5defbfdc..ecc65ec88 100644 --- a/controllers/k8ssandra/cassandra_metrics_agent_test.go +++ b/controllers/k8ssandra/cassandra_metrics_agent_test.go @@ -4,8 +4,6 @@ import ( "context" "testing" - "github.com/stretchr/testify/assert" - cassdcapi "github.com/k8ssandra/cass-operator/apis/cassandra/v1beta1" api "github.com/k8ssandra/k8ssandra-operator/apis/k8ssandra/v1alpha1" telemetryapi "github.com/k8ssandra/k8ssandra-operator/apis/telemetry/v1alpha1" @@ -78,6 +76,47 @@ func createSingleDcClusterWithMetricsAgent(t *testing.T, ctx context.Context, f t.Log("check that the datacenter was created") dcKey := framework.ClusterKey{NamespacedName: types.NamespacedName{Namespace: namespace, Name: "dc1"}, K8sContext: f.DataPlaneContexts[0]} require.Eventually(f.DatacenterExists(ctx, dcKey), timeout, interval) + + t.Log("update datacenter status to ready") + kcKey := framework.NewClusterKey(f.ControlPlaneContext, namespace, kc.Name) + err = f.PatchDatacenterStatus(ctx, dcKey, func(dc *cassdcapi.CassandraDatacenter) { + dc.Status.CassandraOperatorProgress = cassdcapi.ProgressReady + dc.SetCondition(cassdcapi.DatacenterCondition{ + Type: cassdcapi.DatacenterReady, + Status: corev1.ConditionTrue, + LastTransitionTime: metav1.Now(), + }) + }) + require.NoError(err, "failed to update datacenter status to ready") + + t.Log("check that the K8ssandraCluster status is updated") + require.Eventually(func() bool { + kc := &api.K8ssandraCluster{} + err = f.Get(ctx, kcKey, kc) + + if err != nil { + t.Logf("failed to get K8ssandraCluster: %v", err) + return false + } + + if len(kc.Status.Datacenters) == 0 { + return false + } + + k8ssandraStatus, found := kc.Status.Datacenters[dcKey.Name] + if !found { + t.Logf("status for datacenter %s not found", dcKey) + return false + } + + condition := findDatacenterCondition(k8ssandraStatus.Cassandra, cassdcapi.DatacenterReady) + return condition != nil && condition.Status == corev1.ConditionTrue + }, timeout, interval, "timed out waiting for K8ssandraCluster status update") + + require.Eventually(func() bool { + return f.UpdateDatacenterGeneration(ctx, t, dcKey) + }, timeout, interval, "failed to update dc1 generation") + // Check that we have the right volumes and volume mounts. dc := &cassdcapi.CassandraDatacenter{} if err := f.Get(ctx, dcKey, dc); err != nil { @@ -85,11 +124,15 @@ func createSingleDcClusterWithMetricsAgent(t *testing.T, ctx context.Context, f } // check that we have the right ConfigMap - agentCmKey := framework.ClusterKey{NamespacedName: types.NamespacedName{Name: "test-dc1" + "-metrics-agent-config", Namespace: namespace}, K8sContext: f.DataPlaneContexts[0]} + agentCmKey := framework.ClusterKey{NamespacedName: types.NamespacedName{Name: "test-dc1-metrics-agent-config", Namespace: namespace}, K8sContext: f.DataPlaneContexts[0]} agentCm := corev1.ConfigMap{} - if err := f.Get(ctx, agentCmKey, &agentCm); err != nil { - assert.Fail(t, "could not find expected metrics-agent-config configmap") - } + require.Eventually(func() bool { + if err := f.Get(ctx, agentCmKey, &agentCm); err != nil { + t.Log("could not find expected metrics-agent-config configmap") + return false + } + return f.IsOwnedByCassandraDatacenter(&agentCm) + }, timeout, interval) // Verify the ConfigMap is set to be mounted require.True(len(dc.Spec.StorageConfig.AdditionalVolumes) > 0) @@ -109,9 +152,13 @@ func createSingleDcClusterWithMetricsAgent(t *testing.T, ctx context.Context, f err = f.DeleteK8ssandraCluster(ctx, client.ObjectKey{Namespace: namespace, Name: kc.Name}, timeout, interval) require.NoError(err, "failed to delete K8ssandraCluster") f.AssertObjectDoesNotExist(ctx, t, dcKey, &cassdcapi.CassandraDatacenter{}, timeout, interval) - f.AssertObjectDoesNotExist(ctx, t, - agentCmKey, - &corev1.ConfigMap{}, - timeout, - interval) +} + +func findDatacenterCondition(status *cassdcapi.CassandraDatacenterStatus, condType cassdcapi.DatacenterConditionType) *cassdcapi.DatacenterCondition { + for _, condition := range status.Conditions { + if condition.Type == condType { + return &condition + } + } + return nil } diff --git a/controllers/k8ssandra/cleanup.go b/controllers/k8ssandra/cleanup.go index b3acbe102..e28dbeae7 100644 --- a/controllers/k8ssandra/cleanup.go +++ b/controllers/k8ssandra/cleanup.go @@ -3,23 +3,18 @@ package k8ssandra import ( "context" "fmt" + "k8s.io/apimachinery/pkg/runtime" "github.com/go-logr/logr" cassdcapi "github.com/k8ssandra/cass-operator/apis/cassandra/v1beta1" api "github.com/k8ssandra/k8ssandra-operator/apis/k8ssandra/v1alpha1" - k8ssandraapi "github.com/k8ssandra/k8ssandra-operator/apis/k8ssandra/v1alpha1" - reaperapi "github.com/k8ssandra/k8ssandra-operator/apis/reaper/v1alpha1" - stargateapi "github.com/k8ssandra/k8ssandra-operator/apis/stargate/v1alpha1" "github.com/k8ssandra/k8ssandra-operator/pkg/annotations" "github.com/k8ssandra/k8ssandra-operator/pkg/k8ssandra" k8ssandralabels "github.com/k8ssandra/k8ssandra-operator/pkg/labels" "github.com/k8ssandra/k8ssandra-operator/pkg/result" "github.com/k8ssandra/k8ssandra-operator/pkg/utils" - appsv1 "k8s.io/api/apps/v1" - batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" - apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/labels" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -39,7 +34,6 @@ func (r *K8ssandraClusterReconciler) checkDeletion(ctx context.Context, kc *api. logger.Info("Starting deletion") - kcKey := utils.GetKey(kc) hasErrors := false for _, dcTemplate := range kc.Spec.Cassandra.Datacenters { @@ -66,51 +60,6 @@ func (r *K8ssandraClusterReconciler) checkDeletion(ctx context.Context, kc *api. logger.Error(err, "Failed to delete CassandraDatacenter", "CassandraDatacenter", dcKey, "Context", dcTemplate.K8sContext) hasErrors = true } - - selector := k8ssandralabels.CleanedUpByLabels(kcKey) - stargateList := &stargateapi.StargateList{} - options := client.ListOptions{ - Namespace: namespace, - LabelSelector: labels.SelectorFromSet(selector), - } - - err = remoteClient.List(ctx, stargateList, &options) - if err != nil { - logger.Error(err, "Failed to list Stargate objects", "Context", dcTemplate.K8sContext) - hasErrors = true - continue - } - - for _, sg := range stargateList.Items { - if err = remoteClient.Delete(ctx, &sg); err != nil { - key := client.ObjectKey{Namespace: namespace, Name: sg.Name} - if !errors.IsNotFound(err) { - logger.Error(err, "Failed to delete Stargate", "Stargate", key, - "Context", dcTemplate.K8sContext) - hasErrors = true - } - } - } - - if r.deleteReapers(ctx, kc, dcTemplate, namespace, remoteClient, logger) { - hasErrors = true - } - - if r.deleteDeployments(ctx, kc, dcTemplate, namespace, remoteClient, logger) { - hasErrors = true - } - - if r.deleteServices(ctx, kc, dcTemplate, namespace, remoteClient, logger) { - hasErrors = true - } - - if r.deleteK8ssandraConfigMaps(ctx, kc, dcTemplate, namespace, remoteClient, logger) { - hasErrors = true - } - - if r.deleteCronJobs(ctx, kc, dcTemplate, namespace, remoteClient, logger) { - hasErrors = true - } } if hasErrors { @@ -174,31 +123,7 @@ func (r *K8ssandraClusterReconciler) checkDcDeletion(ctx context.Context, kc *ap func (r *K8ssandraClusterReconciler) deleteDc(ctx context.Context, kc *api.K8ssandraCluster, dcName string, cassDcName string, logger logr.Logger) result.ReconcileResult { kcKey := utils.GetKey(kc) - stargate, remoteClient, err := r.findStargateForDeletion(ctx, kcKey, cassDcName, nil) - if err != nil { - return result.Error(err) - } - - if stargate != nil { - if err = remoteClient.Delete(ctx, stargate); err != nil && !errors.IsNotFound(err) { - return result.Error(fmt.Errorf("failed to delete Stargate for dc (%s): %v", cassDcName, err)) - } - logger.Info("Deleted Stargate", "Stargate", utils.GetKey(stargate)) - } - - reaper, remoteClient, err := r.findReaperForDeletion(ctx, kcKey, cassDcName, remoteClient) - if err != nil { - return result.Error(err) - } - - if reaper != nil { - if err = remoteClient.Delete(ctx, reaper); err != nil && !errors.IsNotFound(err) { - return result.Error(fmt.Errorf("failed to delete Reaper for dc (%s): %v", cassDcName, err)) - } - logger.Info("Deleted Reaper", "Reaper", utils.GetKey(reaper)) - } - - dc, remoteClient, err := r.findDcForDeletion(ctx, kcKey, dcName, remoteClient) + dc, remoteClient, err := r.findDcForDeletion(ctx, kcKey, dcName) if err != nil { return result.Error(err) } @@ -235,111 +160,20 @@ func (r *K8ssandraClusterReconciler) deleteDc(ctx context.Context, kc *api.K8ssa return result.Continue() } -func (r *K8ssandraClusterReconciler) findStargateForDeletion( - ctx context.Context, - kcKey client.ObjectKey, - dcName string, - remoteClient client.Client) (*stargateapi.Stargate, client.Client, error) { - - selector := k8ssandralabels.CleanedUpByLabels(kcKey) - options := &client.ListOptions{LabelSelector: labels.SelectorFromSet(selector)} - stargateList := &stargateapi.StargateList{} - stargateName := kcKey.Name + "-" + dcName + "-stargate" - - if remoteClient == nil { - for _, remoteClient := range r.ClientCache.GetAllClients() { - err := remoteClient.List(ctx, stargateList, options) - if err != nil { - return nil, nil, fmt.Errorf("failed to find Stargate (%s) for DC (%s) deletion: %v", stargateName, dcName, err) - } - for _, stargate := range stargateList.Items { - if stargate.Name == stargateName { - return &stargate, remoteClient, nil - } - } - } - } else { - err := remoteClient.List(ctx, stargateList, options) - if err != nil { - return nil, nil, fmt.Errorf("failed to find Stargate (%s) for DC (%s) deletion: %v", stargateName, dcName, err) - } - - for _, stargate := range stargateList.Items { - if stargate.Name == stargateName { - return &stargate, remoteClient, nil - } - } - } - - return nil, nil, nil -} - -func (r *K8ssandraClusterReconciler) findReaperForDeletion( - ctx context.Context, - kcKey client.ObjectKey, - dcName string, - remoteClient client.Client) (*reaperapi.Reaper, client.Client, error) { - - selector := k8ssandralabels.CleanedUpByLabels(kcKey) - options := &client.ListOptions{LabelSelector: labels.SelectorFromSet(selector)} - reaperList := &reaperapi.ReaperList{} - reaperName := kcKey.Name + "-" + dcName + "-reaper" - - if remoteClient == nil { - for _, remoteClient := range r.ClientCache.GetAllClients() { - err := remoteClient.List(ctx, reaperList, options) - if err != nil { - return nil, nil, fmt.Errorf("failed to find Reaper (%s) for DC (%s) deletion: %v", reaperName, dcName, err) - } - for _, reaper := range reaperList.Items { - if reaper.Name == reaperName { - return &reaper, remoteClient, nil - } - } - } - } else { - err := remoteClient.List(ctx, reaperList, options) - if err != nil { - return nil, nil, fmt.Errorf("failed to find Reaper (%s) for DC (%s) deletion: %v", reaperName, dcName, err) - } - - for _, reaper := range reaperList.Items { - if reaper.Name == reaperName { - return &reaper, remoteClient, nil - } - } - } - - return nil, nil, nil -} - func (r *K8ssandraClusterReconciler) findDcForDeletion( ctx context.Context, kcKey client.ObjectKey, dcName string, - remoteClient client.Client) (*cassdcapi.CassandraDatacenter, client.Client, error) { +) (*cassdcapi.CassandraDatacenter, client.Client, error) { selector := k8ssandralabels.CleanedUpByLabels(kcKey) options := &client.ListOptions{LabelSelector: labels.SelectorFromSet(selector)} dcList := &cassdcapi.CassandraDatacenterList{} - if remoteClient == nil { - for _, remoteClient := range r.ClientCache.GetAllClients() { - err := remoteClient.List(ctx, dcList, options) - if err != nil { - return nil, nil, fmt.Errorf("failed to CassandraDatacenter (%s) for DC (%s) deletion: %v", dcName, dcName, err) - } - for _, dc := range dcList.Items { - if dc.Name == dcName { - return &dc, remoteClient, nil - } - } - } - } else { + for _, remoteClient := range r.ClientCache.GetAllClients() { err := remoteClient.List(ctx, dcList, options) if err != nil { - return nil, nil, fmt.Errorf("failed to find CassandraDatacenter (%s) for deletion: %v", dcName, err) + return nil, nil, fmt.Errorf("failed to CassandraDatacenter (%s) for DC (%s) deletion: %v", dcName, dcName, err) } - for _, dc := range dcList.Items { if dc.Name == dcName { return &dc, remoteClient, nil @@ -350,129 +184,35 @@ func (r *K8ssandraClusterReconciler) findDcForDeletion( return nil, nil, nil } -func (r *K8ssandraClusterReconciler) deleteK8ssandraConfigMaps( - ctx context.Context, - kc *k8ssandraapi.K8ssandraCluster, - dcTemplate k8ssandraapi.CassandraDatacenterTemplate, - namespace string, - remoteClient client.Client, - kcLogger logr.Logger, -) (hasErrors bool) { - selector := k8ssandralabels.CleanedUpByLabels(client.ObjectKey{Namespace: kc.Namespace, Name: kc.SanitizedName()}) - configMaps := &corev1.ConfigMapList{} - options := client.ListOptions{ - Namespace: namespace, - LabelSelector: labels.SelectorFromSet(selector), - } - if err := remoteClient.List(ctx, configMaps, &options); err != nil { - kcLogger.Error(err, "Failed to list ConfigMap objects", "Context", dcTemplate.K8sContext) - return true - } - for _, rp := range configMaps.Items { - if err := remoteClient.Delete(ctx, &rp); err != nil { - key := client.ObjectKey{Namespace: namespace, Name: rp.Name} - if !apierrors.IsNotFound(err) { - kcLogger.Error(err, "Failed to delete configmap", "ConfigMap", key, - "Context", dcTemplate.K8sContext) - hasErrors = true - } - } - } - return -} - -func (r *K8ssandraClusterReconciler) deleteServices( +// setDcOwnership loads the remote resource identified by controlledKey, sets dc as its owner, and writes it back. If +// the remote resource does not exist, this is a no-op. +func setDcOwnership[T client.Object]( ctx context.Context, - kc *k8ssandraapi.K8ssandraCluster, - dcTemplate k8ssandraapi.CassandraDatacenterTemplate, - namespace string, + dc *cassdcapi.CassandraDatacenter, + controlledKey client.ObjectKey, + controlled T, remoteClient client.Client, - kcLogger logr.Logger, -) (hasErrors bool) { - selector := k8ssandralabels.CleanedUpByLabels(client.ObjectKey{Namespace: kc.Namespace, Name: kc.Name}) - options := client.ListOptions{ - Namespace: namespace, - LabelSelector: labels.SelectorFromSet(selector), - } - serviceList := &corev1.ServiceList{} - if err := remoteClient.List(ctx, serviceList, &options); err != nil { - kcLogger.Error(err, "Failed to list K8ssandra services", "Context", dcTemplate.K8sContext) - return true - } - for _, rp := range serviceList.Items { - kcLogger.Info("Deleting service", "Service", utils.GetKey(&rp)) - if err := remoteClient.Delete(ctx, &rp); err != nil { - key := client.ObjectKey{Namespace: namespace, Name: rp.Name} - if !errors.IsNotFound(err) { - kcLogger.Error(err, "Failed to delete Service", "Service", key, - "Context", dcTemplate.K8sContext) - hasErrors = true - } - } - } - - return -} - -func (r *K8ssandraClusterReconciler) deleteDeployments( - ctx context.Context, - kc *k8ssandraapi.K8ssandraCluster, - dcTemplate k8ssandraapi.CassandraDatacenterTemplate, - namespace string, - remoteClient client.Client, - kcLogger logr.Logger, -) (hasErrors bool) { - selector := k8ssandralabels.CleanedUpByLabels(client.ObjectKey{Namespace: kc.Namespace, Name: kc.Name}) - options := client.ListOptions{ - Namespace: namespace, - LabelSelector: labels.SelectorFromSet(selector), - } - deploymentList := &appsv1.DeploymentList{} - if err := remoteClient.List(ctx, deploymentList, &options); err != nil { - kcLogger.Error(err, "Failed to list K8ssandra deployments", "Context", dcTemplate.K8sContext) - return true - } - for _, item := range deploymentList.Items { - kcLogger.Info("Deleting deployment", "Deployment", utils.GetKey(&item)) - if err := remoteClient.Delete(ctx, &item); err != nil { - key := client.ObjectKey{Namespace: namespace, Name: item.Name} - if !errors.IsNotFound(err) { - kcLogger.Error(err, "Failed to delete Deployment", "Deployment", key, - "Context", dcTemplate.K8sContext) - hasErrors = true - } - } + scheme *runtime.Scheme, + logger logr.Logger, +) result.ReconcileResult { + if err := remoteClient.Get(ctx, controlledKey, controlled); err != nil { + if errors.IsNotFound(err) { + return result.Continue() + } + logger.Error(err, "Failed to get controlled resource", "key", controlledKey) + return result.Error(err) } - - return -} - -func (r *K8ssandraClusterReconciler) deleteCronJobs( - ctx context.Context, - kc *k8ssandraapi.K8ssandraCluster, - dcTemplate k8ssandraapi.CassandraDatacenterTemplate, - namespace string, - remoteClient client.Client, - kcLogger logr.Logger, -) (hasErrors bool) { - selector := k8ssandralabels.CleanedUpByLabels(client.ObjectKey{Namespace: kc.Namespace, Name: kc.SanitizedName()}) - options := client.ListOptions{ - Namespace: namespace, - LabelSelector: labels.SelectorFromSet(selector), + if controllerutil.HasControllerReference(controlled) { + // Assume this is us from a previous reconcile loop + return result.Continue() } - cronJobList := &batchv1.CronJobList{} - if err := remoteClient.List(ctx, cronJobList, &options); err != nil { - kcLogger.Error(err, "Failed to list Medusa CronJobs", "Context", dcTemplate.K8sContext) - return true + if err := controllerutil.SetControllerReference(dc, controlled, scheme); err != nil { + logger.Error(err, "Failed to set DC owner reference", "key", controlledKey) + return result.Error(err) } - for _, item := range cronJobList.Items { - kcLogger.Info("Deleting CronJob", "CronJob", utils.GetKey(&item)) - if err := remoteClient.Delete(ctx, &item); err != nil { - key := client.ObjectKey{Namespace: namespace, Name: item.Name} - if !errors.IsNotFound(err) { - kcLogger.Error(err, "Failed to delete CronJob", "CronJob", key, "Context", dcTemplate.K8sContext) - } - } + if err := remoteClient.Update(ctx, controlled); err != nil { + logger.Error(err, "Failed to update controlled resource", "key", controlledKey) + return result.Error(err) } - return + return result.Continue() } diff --git a/controllers/k8ssandra/cleanup_test.go b/controllers/k8ssandra/cleanup_test.go deleted file mode 100644 index 790e63de2..000000000 --- a/controllers/k8ssandra/cleanup_test.go +++ /dev/null @@ -1,98 +0,0 @@ -package k8ssandra - -import ( - "context" - "testing" - - testlogr "github.com/go-logr/logr/testing" - k8ssandraapi "github.com/k8ssandra/k8ssandra-operator/apis/k8ssandra/v1alpha1" - k8ssandralabels "github.com/k8ssandra/k8ssandra-operator/pkg/labels" - "github.com/stretchr/testify/require" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes/scheme" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/fake" -) - -func TestK8ssandraClusterReconciler_DeleteServices(t *testing.T) { - k8sMock := fake.NewClientBuilder().WithScheme(scheme.Scheme).Build() - ctx := context.Background() - logger := testlogr.NewTestLogger(t) - - kc := &k8ssandraapi.K8ssandraCluster{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "test-namespace", - }, - } - - namespace := "test-namespace" - - service := &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "service-1", - Namespace: "test-namespace", - Labels: k8ssandralabels.CleanedUpByLabels(client.ObjectKey{Namespace: kc.Namespace, Name: kc.Name}), - }, - } - - require.NoError(t, k8sMock.Create(ctx, service)) - - res := K8ssandraClusterReconciler{ - Client: k8sMock, - Scheme: scheme.Scheme, - } - - hasError := res.deleteServices(ctx, kc, k8ssandraapi.CassandraDatacenterTemplate{}, namespace, k8sMock, logger) - require.False(t, hasError, "Error while deleting services") - - err := k8sMock.Get(ctx, client.ObjectKeyFromObject(service), service) - require.Error(t, err) - require.True(t, errors.IsNotFound(err)) -} - -func TestK8ssandraClusterReconciler_DeleteDeployments(t *testing.T) { - k8sMock := fake.NewClientBuilder().WithScheme(scheme.Scheme).Build() - ctx := context.Background() - logger := testlogr.NewTestLogger(t) - - kc := &k8ssandraapi.K8ssandraCluster{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "test-namespace", - }, - } - - namespace := "test-namespace" - - deployment := &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: "deployment-1", - Namespace: "test-namespace", - Labels: k8ssandralabels.CleanedUpByLabels(client.ObjectKey{Namespace: kc.Namespace, Name: kc.Name}), - }, - } - - require.NoError(t, k8sMock.Create(ctx, deployment)) - - res := K8ssandraClusterReconciler{ - Client: k8sMock, - Scheme: scheme.Scheme, - } - - hasError := res.deleteDeployments(ctx, kc, k8ssandraapi.CassandraDatacenterTemplate{}, namespace, k8sMock, logger) - - if hasError != false { - t.Errorf("Error while deleting deployments") - } - - err := k8sMock.Get(ctx, client.ObjectKeyFromObject(deployment), deployment) - - if err == nil || !errors.IsNotFound(err) { - t.Errorf("Deployment was not deleted: %v", err) - } - -} diff --git a/controllers/k8ssandra/datacenters.go b/controllers/k8ssandra/datacenters.go index 5961fccef..7d3202d52 100644 --- a/controllers/k8ssandra/datacenters.go +++ b/controllers/k8ssandra/datacenters.go @@ -3,6 +3,7 @@ package k8ssandra import ( "context" "fmt" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sort" "strconv" "strings" @@ -412,7 +413,10 @@ func (r *K8ssandraClusterReconciler) reconcileDcRebuild( } else { if errors.IsNotFound(err) { logger.Info("Creating rebuild task", "Task", taskKey) - if err = remoteClient.Create(ctx, desiredTask); err != nil { + if err = controllerutil.SetControllerReference(dc, desiredTask, r.Scheme); err != nil { + logger.Error(err, "Failed to set controller reference", "Task", taskKey) + return result.Error(err) + } else if err = remoteClient.Create(ctx, desiredTask); err != nil { logger.Error(err, "Failed to create rebuild task", "Task", taskKey) return result.Error(err) } diff --git a/controllers/k8ssandra/k8ssandracluster_controller.go b/controllers/k8ssandra/k8ssandracluster_controller.go index 36035e8f3..4b3177897 100644 --- a/controllers/k8ssandra/k8ssandracluster_controller.go +++ b/controllers/k8ssandra/k8ssandracluster_controller.go @@ -181,6 +181,14 @@ func (r *K8ssandraClusterReconciler) afterCassandraReconciled(ctx context.Contex return recResult } else if recResult := r.reconcileReaper(ctx, kc, dcTemplate, dc, logger, remoteClient); recResult.Completed() { return recResult + } else if recResult := r.setupVectorCleanup(ctx, kc, dc, remoteClient, logger); recResult.Completed() { + return recResult + } else if recResult := r.setupMedusaCleanup(ctx, kc, dc, remoteClient, logger); recResult.Completed() { + return recResult + } else if recResult := r.setupPerNodeConfigurationCleanup(ctx, kc, dc, remoteClient, logger); recResult.Completed() { + return recResult + } else if recResult := r.setupTelemetryCleanup(ctx, kc, dc, remoteClient, logger); recResult.Completed() { + return recResult } } return result.Continue() diff --git a/controllers/k8ssandra/medusa_reconciler.go b/controllers/k8ssandra/medusa_reconciler.go index e55f7ca5e..06b6a45f7 100644 --- a/controllers/k8ssandra/medusa_reconciler.go +++ b/controllers/k8ssandra/medusa_reconciler.go @@ -3,6 +3,7 @@ package k8ssandra import ( "context" "fmt" + cassdcapi "github.com/k8ssandra/cass-operator/apis/cassandra/v1beta1" batchv1 "k8s.io/api/batch/v1" "k8s.io/apimachinery/pkg/api/errors" "os" @@ -346,3 +347,34 @@ func (r *K8ssandraClusterReconciler) getOperatorNamespace() string { } return operatorNamespace } + +// setupMedusaCleanup adds owner references to ensure that the remote resources created by reconcileMedusa are correctly +// cleaned up when the CassandraDatacenter is deleted. We do that in a second pass because the CassandraDatacenter did +// not exist yet at the time those resources were created. +func (r *K8ssandraClusterReconciler) setupMedusaCleanup( + ctx context.Context, + kc *k8ssandraapi.K8ssandraCluster, + dc *cassdcapi.CassandraDatacenter, + remoteClient client.Client, + logger logr.Logger, +) result.ReconcileResult { + // Note: this ConfigMap is an edge case because it is not DC-specific. If two CassandraDatacenters are in the same + // namespace, they would share the same ConfigMap, so setting one of them as the owner is wrong. + // However, this is an unlikely scenario (DCs are usually in different contexts, let alone namespaces). Also, if we + // delete one DC and the other still needs the ConfigMap, it will be recreated. + configMapKey := client.ObjectKey{ + Namespace: dc.Namespace, + // see pgk/medusa/reconcile.go + Name: fmt.Sprintf("%s-medusa", kc.SanitizedName()), + } + result := setDcOwnership(ctx, dc, configMapKey, &corev1.ConfigMap{}, remoteClient, r.Scheme, logger) + if result.Completed() { + return result + } + + cronjobKey := client.ObjectKey{ + Namespace: dc.Namespace, + Name: medusa.MedusaPurgeCronJobName(kc.SanitizedName(), dc.SanitizedName()), + } + return setDcOwnership(ctx, dc, cronjobKey, &batchv1.CronJob{}, remoteClient, r.Scheme, logger) +} diff --git a/controllers/k8ssandra/per_node_config.go b/controllers/k8ssandra/per_node_config.go index 4b0b517c6..7287f1e3d 100644 --- a/controllers/k8ssandra/per_node_config.go +++ b/controllers/k8ssandra/per_node_config.go @@ -3,8 +3,8 @@ package k8ssandra import ( "context" "errors" - "github.com/go-logr/logr" + cassdcapi "github.com/k8ssandra/cass-operator/apis/cassandra/v1beta1" k8ssandraapi "github.com/k8ssandra/k8ssandra-operator/apis/k8ssandra/v1alpha1" "github.com/k8ssandra/k8ssandra-operator/pkg/annotations" "github.com/k8ssandra/k8ssandra-operator/pkg/cassandra" @@ -172,3 +172,20 @@ func (r *K8ssandraClusterReconciler) reconcileUserProvidedPerNodeConfiguration( return result.Continue() } + +// setupMedusaCleanup adds an owner reference to ensure that the remote ConfigMap created by +// reconcileDefaultPerNodeConfiguration is correctly cleaned up when the CassandraDatacenter is deleted. We do that in a +// second pass because the CassandraDatacenter did not exist yet at the time the ConfigMap was created. +func (r *K8ssandraClusterReconciler) setupPerNodeConfigurationCleanup( + ctx context.Context, + kc *k8ssandraapi.K8ssandraCluster, + dc *cassdcapi.CassandraDatacenter, + remoteClient client.Client, + logger logr.Logger, +) result.ReconcileResult { + configMapKey := client.ObjectKey{ + Namespace: dc.Namespace, + Name: nodeconfig.NewDefaultPerNodeConfigMapName(kc.CassClusterName(), dc.DatacenterName()), + } + return setDcOwnership(ctx, dc, configMapKey, &corev1.ConfigMap{}, remoteClient, r.Scheme, logger) +} diff --git a/controllers/k8ssandra/reaper.go b/controllers/k8ssandra/reaper.go index c50852ea6..bc563d8ef 100644 --- a/controllers/k8ssandra/reaper.go +++ b/controllers/k8ssandra/reaper.go @@ -30,9 +30,9 @@ import ( "github.com/k8ssandra/k8ssandra-operator/pkg/reaper" "github.com/k8ssandra/k8ssandra-operator/pkg/result" "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) func (r *K8ssandraClusterReconciler) reconcileReaperSchema( @@ -127,7 +127,10 @@ func (r *K8ssandraClusterReconciler) reconcileReaper( if err := remoteClient.Get(ctx, reaperKey, actualReaper); err != nil { if errors.IsNotFound(err) { logger.Info("Creating Reaper resource") - if err := remoteClient.Create(ctx, desiredReaper); err != nil { + if err := controllerutil.SetControllerReference(actualDc, desiredReaper, r.Scheme); err != nil { + logger.Error(err, "Failed to set controller reference on Reaper resource") + return result.Error(err) + } else if err := remoteClient.Create(ctx, desiredReaper); err != nil { logger.Error(err, "Failed to create Reaper resource") return result.Error(err) } else { @@ -151,7 +154,10 @@ func (r *K8ssandraClusterReconciler) reconcileReaper( resourceVersion := actualReaper.GetResourceVersion() desiredReaper.DeepCopyInto(actualReaper) actualReaper.SetResourceVersion(resourceVersion) - if err := remoteClient.Update(ctx, actualReaper); err != nil { + if err := controllerutil.SetControllerReference(actualDc, actualReaper, r.Scheme); err != nil { + logger.Error(err, "Failed to set controller reference on Reaper resource") + return result.Error(err) + } else if err := remoteClient.Update(ctx, actualReaper); err != nil { logger.Error(err, "Failed to update Reaper resource") return result.Error(err) } @@ -193,37 +199,6 @@ func (r *K8ssandraClusterReconciler) reconcileReaper( } } -func (r *K8ssandraClusterReconciler) deleteReapers( - ctx context.Context, - kc *api.K8ssandraCluster, - dcTemplate api.CassandraDatacenterTemplate, - namespace string, - remoteClient client.Client, - kcLogger logr.Logger, -) (hasErrors bool) { - selector := k8ssandralabels.CleanedUpByLabels(client.ObjectKey{Namespace: kc.Namespace, Name: kc.Name}) - reaperList := &reaperapi.ReaperList{} - options := client.ListOptions{ - Namespace: namespace, - LabelSelector: labels.SelectorFromSet(selector), - } - if err := remoteClient.List(ctx, reaperList, &options); err != nil { - kcLogger.Error(err, "Failed to list Reaper objects", "Context", dcTemplate.K8sContext) - return true - } - for _, rp := range reaperList.Items { - if err := remoteClient.Delete(ctx, &rp); err != nil { - key := client.ObjectKey{Namespace: namespace, Name: rp.Name} - if !errors.IsNotFound(err) { - kcLogger.Error(err, "Failed to delete Reaper", "Reaper", key, - "Context", dcTemplate.K8sContext) - hasErrors = true - } - } - } - return -} - func (r *K8ssandraClusterReconciler) setStatusForReaper(kc *api.K8ssandraCluster, reaper *reaperapi.Reaper, dcName string) error { if len(kc.Status.Datacenters) == 0 { kc.Status.Datacenters = make(map[string]api.K8ssandraStatus) diff --git a/controllers/k8ssandra/reaper_test.go b/controllers/k8ssandra/reaper_test.go index 3ed5c7945..3c8fd6b1e 100644 --- a/controllers/k8ssandra/reaper_test.go +++ b/controllers/k8ssandra/reaper_test.go @@ -254,7 +254,10 @@ func createMultiDcClusterWithControlPlaneReaper(t *testing.T, ctx context.Contex Name: "reaper"}, } t.Log("check that control plane reaper is created") - require.Eventually(f.ReaperExists(ctx, cpReaperKey), timeout, interval) + withReaper := f.NewWithReaper(ctx, cpReaperKey) + require.Eventually(withReaper(func(r *reaperapi.Reaper) bool { + return true + }), timeout, interval) kc := &api.K8ssandraCluster{ ObjectMeta: metav1.ObjectMeta{ diff --git a/controllers/k8ssandra/remove_dc_test.go b/controllers/k8ssandra/remove_dc_test.go index e9184139a..da73f36d5 100644 --- a/controllers/k8ssandra/remove_dc_test.go +++ b/controllers/k8ssandra/remove_dc_test.go @@ -9,7 +9,6 @@ import ( cassdcapi "github.com/k8ssandra/cass-operator/apis/cassandra/v1beta1" api "github.com/k8ssandra/k8ssandra-operator/apis/k8ssandra/v1alpha1" reaperapi "github.com/k8ssandra/k8ssandra-operator/apis/reaper/v1alpha1" - stargateapi "github.com/k8ssandra/k8ssandra-operator/apis/stargate/v1alpha1" "github.com/k8ssandra/k8ssandra-operator/pkg/annotations" "github.com/k8ssandra/k8ssandra-operator/pkg/cassandra" "github.com/k8ssandra/k8ssandra-operator/pkg/stargate" @@ -291,8 +290,6 @@ func deleteDcWithStargateAndReaper(ctx context.Context, t *testing.T, f *framewo assertDatacenterRemovedFromClusterStatus(ctx, t, f, kcKey, dc2Key) f.AssertObjectDoesNotExist(ctx, t, dc2Key, &cassdcapi.CassandraDatacenter{}, timeout, interval) - f.AssertObjectDoesNotExist(ctx, t, sg2Key, &stargateapi.Stargate{}, timeout, interval) - f.AssertObjectDoesNotExist(ctx, t, reaper2Key, &reaperapi.Reaper{}, timeout, interval) verifyReplicationOfInternalKeyspacesUpdated(t, mockMgmtApi, replication, updatedReplication) } diff --git a/controllers/k8ssandra/stargate.go b/controllers/k8ssandra/stargate.go index 9c1032dea..8e3f032a9 100644 --- a/controllers/k8ssandra/stargate.go +++ b/controllers/k8ssandra/stargate.go @@ -2,6 +2,7 @@ package k8ssandra import ( "context" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "github.com/go-logr/logr" cassdcapi "github.com/k8ssandra/cass-operator/apis/cassandra/v1beta1" @@ -52,7 +53,10 @@ func (r *K8ssandraClusterReconciler) reconcileStargate( if err := remoteClient.Get(ctx, stargateKey, actualStargate); err != nil { if errors.IsNotFound(err) { logger.Info("Creating Stargate resource") - if err := remoteClient.Create(ctx, desiredStargate); err != nil { + if err := controllerutil.SetControllerReference(actualDc, desiredStargate, r.Scheme); err != nil { + logger.Error(err, "Failed to set controller reference on Stargate resource") + return result.Error(err) + } else if err := remoteClient.Create(ctx, desiredStargate); err != nil { logger.Error(err, "Failed to create Stargate resource") return result.Error(err) } else { @@ -72,7 +76,10 @@ func (r *K8ssandraClusterReconciler) reconcileStargate( resourceVersion := actualStargate.GetResourceVersion() desiredStargate.DeepCopyInto(actualStargate) actualStargate.SetResourceVersion(resourceVersion) - if err = remoteClient.Update(ctx, actualStargate); err == nil { + if err := controllerutil.SetControllerReference(actualDc, actualStargate, r.Scheme); err != nil { + logger.Error(err, "Failed to set controller reference on Stargate resource") + return result.Error(err) + } else if err = remoteClient.Update(ctx, actualStargate); err == nil { return result.RequeueSoon(r.DefaultDelay) } else { logger.Error(err, "Failed to update Stargate") diff --git a/controllers/k8ssandra/telemetry_cleanup.go b/controllers/k8ssandra/telemetry_cleanup.go new file mode 100644 index 000000000..fa7fce388 --- /dev/null +++ b/controllers/k8ssandra/telemetry_cleanup.go @@ -0,0 +1,29 @@ +package k8ssandra + +import ( + "context" + "github.com/go-logr/logr" + cassdcapi "github.com/k8ssandra/cass-operator/apis/cassandra/v1beta1" + k8ssandraapi "github.com/k8ssandra/k8ssandra-operator/apis/k8ssandra/v1alpha1" + "github.com/k8ssandra/k8ssandra-operator/pkg/result" + "github.com/k8ssandra/k8ssandra-operator/pkg/telemetry/cassandra_agent" + corev1 "k8s.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// setupTelemetryCleanup adds an owner reference to ensure that the remote ConfigMap created by +// cassandra_agent.Configurator is correctly cleaned up when the CassandraDatacenter is deleted. We do that in a +// second pass because the CassandraDatacenter did not exist yet at the time the ConfigMap was created. +func (r *K8ssandraClusterReconciler) setupTelemetryCleanup( + ctx context.Context, + kc *k8ssandraapi.K8ssandraCluster, + dc *cassdcapi.CassandraDatacenter, + remoteClient client.Client, + logger logr.Logger, +) result.ReconcileResult { + configMapKey := client.ObjectKey{ + Namespace: dc.Namespace, + Name: cassandra_agent.ConfigMapName(kc.CassClusterName(), dc.DatacenterName()), + } + return setDcOwnership(ctx, dc, configMapKey, &corev1.ConfigMap{}, remoteClient, r.Scheme, logger) +} diff --git a/controllers/k8ssandra/vector.go b/controllers/k8ssandra/vector.go index 730d4a2ea..3aebdad05 100644 --- a/controllers/k8ssandra/vector.go +++ b/controllers/k8ssandra/vector.go @@ -2,17 +2,18 @@ package k8ssandra import ( "context" - "github.com/k8ssandra/k8ssandra-operator/pkg/shared" - "github.com/go-logr/logr" + cassdcapi "github.com/k8ssandra/cass-operator/apis/cassandra/v1beta1" k8ssandraapi "github.com/k8ssandra/k8ssandra-operator/apis/k8ssandra/v1alpha1" "github.com/k8ssandra/k8ssandra-operator/pkg/cassandra" "github.com/k8ssandra/k8ssandra-operator/pkg/labels" k8ssandralabels "github.com/k8ssandra/k8ssandra-operator/pkg/labels" "github.com/k8ssandra/k8ssandra-operator/pkg/reconciliation" "github.com/k8ssandra/k8ssandra-operator/pkg/result" + "github.com/k8ssandra/k8ssandra-operator/pkg/shared" "github.com/k8ssandra/k8ssandra-operator/pkg/telemetry" "github.com/k8ssandra/k8ssandra-operator/pkg/utils" + v1 "k8s.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -57,3 +58,21 @@ func (r *K8ssandraClusterReconciler) reconcileVector( dcLogger.Info("Vector Agent ConfigMap successfully reconciled") return result.Continue() } + +// setupVectorCleanup adds owner references to ensure that the remote resources created by reconcileVector are correctly +// cleaned up when the CassandraDatacenter is deleted. We do that in a second pass because the CassandraDatacenter did +// not exist yet at the time those resources were created. +func (r *K8ssandraClusterReconciler) setupVectorCleanup( + ctx context.Context, + kc *k8ssandraapi.K8ssandraCluster, + dc *cassdcapi.CassandraDatacenter, + remoteClient client.Client, + logger logr.Logger, +) result.ReconcileResult { + logger.Info("Setting up Vector Agent ConfigMap cleanup") + configMapKey := client.ObjectKey{ + Namespace: dc.Namespace, + Name: telemetry.VectorAgentConfigMapName(kc.SanitizedName(), dc.SanitizedName()), + } + return setDcOwnership(ctx, dc, configMapKey, &v1.ConfigMap{}, remoteClient, r.Scheme, logger) +} diff --git a/controllers/k8ssandra/vector_test.go b/controllers/k8ssandra/vector_test.go index 343c68d1e..37949a2b9 100644 --- a/controllers/k8ssandra/vector_test.go +++ b/controllers/k8ssandra/vector_test.go @@ -152,7 +152,7 @@ func createSingleDcClusterWithVector(t *testing.T, ctx context.Context, f *frame assert.Fail(t, "error setting status ready", err) } - // Check that the Vector config map was created + // Check that the Vector config map was created and is owned by the DC vectorConfigMapKey := types.NamespacedName{Namespace: namespace, Name: telemetry.VectorAgentConfigMapName(kc.Name, dc1Key.Name)} vectorConfigMap := &corev1.ConfigMap{} require.Eventually(func() bool { @@ -161,7 +161,7 @@ func createSingleDcClusterWithVector(t *testing.T, ctx context.Context, f *frame t.Logf("failed to get Vector config map: %v", err) return false } - return true + return f.IsOwnedByCassandraDatacenter(vectorConfigMap) }, timeout, interval, "timed out waiting for Vector config map") // Check that Vector configuration was set to the SystemLoggerResources @@ -184,6 +184,5 @@ func createSingleDcClusterWithVector(t *testing.T, ctx context.Context, f *frame t.Log("deleting K8ssandraCluster") err = f.DeleteK8ssandraCluster(ctx, client.ObjectKey{Namespace: namespace, Name: kc.Name}, timeout, interval) require.NoError(err, "failed to delete K8ssandraCluster") - f.AssertObjectDoesNotExist(ctx, t, framework.ClusterKey{K8sContext: f.DataPlaneContexts[1], NamespacedName: vectorConfigMapKey}, &corev1.ConfigMap{}, timeout, interval) f.AssertObjectDoesNotExist(ctx, t, dcKey, &cassdcapi.CassandraDatacenter{}, timeout, interval) } diff --git a/pkg/nodeconfig/generate.go b/pkg/nodeconfig/generate.go index ad4c3f301..9a0af0d85 100644 --- a/pkg/nodeconfig/generate.go +++ b/pkg/nodeconfig/generate.go @@ -39,11 +39,15 @@ func NewDefaultPerNodeConfigMap(kcKey types.NamespacedName, kc *k8ssandraapi.K8s func NewDefaultPerNodeConfigMapKey(kc *k8ssandraapi.K8ssandraCluster, dcConfig *cassandra.DatacenterConfig) types.NamespacedName { return types.NamespacedName{ - Name: cassdcapi.CleanupForKubernetes(kc.CassClusterName() + "-" + dcConfig.CassDcName() + "-per-node-config"), + Name: NewDefaultPerNodeConfigMapName(kc.CassClusterName(), dcConfig.CassDcName()), Namespace: utils.FirstNonEmptyString(dcConfig.Meta.Namespace, kc.Namespace), } } +func NewDefaultPerNodeConfigMapName(kcCqlName, dcCqlName string) string { + return cassdcapi.CleanupForKubernetes(kcCqlName + "-" + dcCqlName + "-per-node-config") +} + func newPerNodeConfigMap(kcKey, configKey types.NamespacedName) *corev1.ConfigMap { return &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ diff --git a/pkg/telemetry/cassandra_agent/cassandra_agent_config.go b/pkg/telemetry/cassandra_agent/cassandra_agent_config.go index c69062d80..1d951708a 100644 --- a/pkg/telemetry/cassandra_agent/cassandra_agent_config.go +++ b/pkg/telemetry/cassandra_agent/cassandra_agent_config.go @@ -143,7 +143,11 @@ func (c Configurator) GetTelemetryAgentConfigMap() (*corev1.ConfigMap, error) { } func (c Configurator) configMapName() string { - return cassdcapi.CleanupForKubernetes(c.Kluster.CassClusterName() + "-" + c.DcName + "-metrics-agent-config") + return ConfigMapName(c.Kluster.CassClusterName(), c.DcName) +} + +func ConfigMapName(kcCqlName, dcCqlName string) string { + return cassdcapi.CleanupForKubernetes(kcCqlName + "-" + dcCqlName + "-metrics-agent-config") } func (c Configurator) ReconcileTelemetryAgentConfig(dc *cassdcapi.CassandraDatacenter) result.ReconcileResult { diff --git a/test/framework/framework.go b/test/framework/framework.go index fd1f58aed..580e8908e 100644 --- a/test/framework/framework.go +++ b/test/framework/framework.go @@ -704,7 +704,7 @@ func (f *Framework) withStargate(ctx context.Context, key ClusterKey, condition func (f *Framework) StargateExists(ctx context.Context, key ClusterKey) func() bool { withStargate := f.NewWithStargate(ctx, key) return withStargate(func(s *stargateapi.Stargate) bool { - return true + return f.IsOwnedByCassandraDatacenter(s) }) } @@ -735,7 +735,7 @@ func (f *Framework) withReaper(ctx context.Context, key ClusterKey, condition fu func (f *Framework) ReaperExists(ctx context.Context, key ClusterKey) func() bool { withReaper := f.NewWithReaper(ctx, key) return withReaper(func(r *reaperapi.Reaper) bool { - return true + return f.IsOwnedByCassandraDatacenter(r) }) } @@ -815,3 +815,17 @@ func (f *Framework) GetContactPointsService( } return service, endpoints, nil } + +// IsOwnedByCassandraDatacenter checks that the given resource has an owner reference to a CassandraDatacenter. +// We can't directly verify the deletion itself because controller-manager isn't actually running in EnvTest. +// See also: https://github.com/kubernetes-sigs/controller-runtime/issues/626 +func (f *Framework) IsOwnedByCassandraDatacenter(resource metav1.Object) bool { + for _, ref := range resource.GetOwnerReferences() { + // Ideally we'd want to verify that ref.UID matches the CassandraDatacenter UID, but it's always readily + // available in all env tests. This should be good enough. + if ref.Kind == "CassandraDatacenter" && *ref.Controller { + return true + } + } + return false +}