From 3852747c6baaba333cd34f52e4486ae134b5c886 Mon Sep 17 00:00:00 2001 From: Fanny Jiang Date: Thu, 25 Apr 2024 13:39:16 -0400 Subject: [PATCH] Combine DS cleanup methods (#1100) (#1153) (cherry picked from commit 472ff244d7259e61776081066ef0c10c0bad1e28) Co-authored-by: khewonc <39867936+khewonc@users.noreply.github.com> --- .../controller_reconcile_agent.go | 305 ++-- .../controller_reconcile_agent_test.go | 1346 +++++++++++++++-- .../datadogagent/controller_reconcile_v2.go | 11 +- 3 files changed, 1387 insertions(+), 275 deletions(-) diff --git a/controllers/datadogagent/controller_reconcile_agent.go b/controllers/datadogagent/controller_reconcile_agent.go index ef88049bb..95a069f80 100644 --- a/controllers/datadogagent/controller_reconcile_agent.go +++ b/controllers/datadogagent/controller_reconcile_agent.go @@ -15,6 +15,7 @@ import ( "github.com/DataDog/datadog-operator/apis/datadoghq/v1alpha1" datadoghqv2alpha1 "github.com/DataDog/datadog-operator/apis/datadoghq/v2alpha1" apiutils "github.com/DataDog/datadog-operator/apis/utils" + "github.com/DataDog/datadog-operator/controllers/datadogagent/component" componentagent "github.com/DataDog/datadog-operator/controllers/datadogagent/component/agent" "github.com/DataDog/datadog-operator/controllers/datadogagent/feature" "github.com/DataDog/datadog-operator/controllers/datadogagent/override" @@ -22,6 +23,7 @@ import ( "github.com/DataDog/datadog-operator/pkg/controller/utils/datadog" "github.com/DataDog/datadog-operator/pkg/kubernetes" edsv1alpha1 "github.com/DataDog/extendeddaemonset/api/v1alpha1" + "github.com/go-logr/logr" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -117,7 +119,10 @@ func (r *Reconciler) reconcileV2Agent(logger logr.Logger, requiredComponents fea true, ) } - return r.cleanupV2ExtendedDaemonSet(daemonsetLogger, dda, eds, newStatus) + if err := r.deleteV2ExtendedDaemonSet(daemonsetLogger, dda, eds, newStatus); err != nil { + return reconcile.Result{}, err + } + return reconcile.Result{}, nil } return r.createOrUpdateExtendedDaemonset(daemonsetLogger, dda, eds, newStatus, updateEDSStatusV2WithAgent) @@ -189,7 +194,10 @@ func (r *Reconciler) reconcileV2Agent(logger logr.Logger, requiredComponents fea true, ) } - return r.cleanupV2DaemonSet(daemonsetLogger, dda, daemonset, newStatus) + if err := r.deleteV2DaemonSet(daemonsetLogger, dda, daemonset, newStatus); err != nil { + return reconcile.Result{}, err + } + return reconcile.Result{}, nil } return r.createOrUpdateDaemonset(daemonsetLogger, dda, daemonset, newStatus, updateDSStatusV2WithAgent) @@ -207,117 +215,28 @@ func updateEDSStatusV2WithAgent(eds *edsv1alpha1.ExtendedDaemonSet, newStatus *d newStatus.Agent = datadoghqv2alpha1.UpdateCombinedDaemonSetStatus(newStatus.AgentList) } -func (r *Reconciler) cleanupV2DaemonSet(logger logr.Logger, dda *datadoghqv2alpha1.DatadogAgent, ds *appsv1.DaemonSet, newStatus *datadoghqv2alpha1.DatadogAgentStatus) (reconcile.Result, error) { - nsName := types.NamespacedName{ - Name: ds.GetName(), - Namespace: ds.GetNamespace(), - } - - // DS attached to this instance - instance := &appsv1.DaemonSet{} - if err := r.client.Get(context.TODO(), nsName, instance); err != nil { - if !errors.IsNotFound(err) { - return reconcile.Result{}, err - } - } else { - err := r.client.Delete(context.TODO(), ds) - if err != nil { - return reconcile.Result{}, err - } - logger.Info("Delete DaemonSet", "daemonSet.Namespace", ds.Namespace, "daemonSet.Name", ds.Name) - event := buildEventInfo(ds.Name, ds.Namespace, daemonSetKind, datadog.DeletionEvent) - r.recordEvent(dda, event) - } - newStatus.Agent = nil - - return reconcile.Result{}, nil -} - -func (r *Reconciler) cleanupV2ExtendedDaemonSet(logger logr.Logger, dda *datadoghqv2alpha1.DatadogAgent, eds *edsv1alpha1.ExtendedDaemonSet, newStatus *datadoghqv2alpha1.DatadogAgentStatus) (reconcile.Result, error) { - nsName := types.NamespacedName{ - Name: eds.GetName(), - Namespace: eds.GetNamespace(), - } - - // EDS attached to this instance - instance := &edsv1alpha1.ExtendedDaemonSet{} - if err := r.client.Get(context.TODO(), nsName, instance); err != nil { - if !errors.IsNotFound(err) { - return reconcile.Result{}, err - } - } else { - err := r.client.Delete(context.TODO(), eds) - if err != nil { - return reconcile.Result{}, err - } - logger.Info("Delete DaemonSet", "extendedDaemonSet.Namespace", eds.Namespace, "extendedDaemonSet.Name", eds.Name) - event := buildEventInfo(eds.Name, eds.Namespace, extendedDaemonSetKind, datadog.DeletionEvent) - r.recordEvent(dda, event) - } - newStatus.Agent = nil - - return reconcile.Result{}, nil -} - -func (r *Reconciler) handleProviders(ctx context.Context, dda *datadoghqv2alpha1.DatadogAgent, ddaStatus *datadoghqv2alpha1.DatadogAgentStatus, - nodeList []corev1.Node, logger logr.Logger) (map[string]struct{}, error) { - providerList := kubernetes.GetProviderListFromNodeList(nodeList, logger) - - if err := r.cleanupDaemonSetsForProvidersThatNoLongerApply(ctx, dda, ddaStatus, providerList); err != nil { - return nil, err +func (r *Reconciler) deleteV2DaemonSet(logger logr.Logger, dda *datadoghqv2alpha1.DatadogAgent, ds *appsv1.DaemonSet, newStatus *datadoghqv2alpha1.DatadogAgentStatus) error { + err := r.client.Delete(context.TODO(), ds) + if err != nil { + return err } + logger.Info("Delete DaemonSet", "daemonSet.Namespace", ds.Namespace, "daemonSet.Name", ds.Name) + event := buildEventInfo(ds.Name, ds.Namespace, daemonSetKind, datadog.DeletionEvent) + r.recordEvent(dda, event) + removeStaleStatus(newStatus, ds.Name) - return providerList, nil + return nil } -// cleanupDaemonSetsForProvidersThatNoLongerApply deletes ds/eds from providers -// that are not present in the provider store. If there are no providers in the -// provider store, do not delete any ds/eds since that would delete all node -// agents. -func (r *Reconciler) cleanupDaemonSetsForProvidersThatNoLongerApply(ctx context.Context, dda *datadoghqv2alpha1.DatadogAgent, - ddaStatus *datadoghqv2alpha1.DatadogAgentStatus, providerList map[string]struct{}) error { - if len(providerList) > 0 { - if r.options.ExtendedDaemonsetOptions.Enabled { - edsList := edsv1alpha1.ExtendedDaemonSetList{} - if err := r.client.List(ctx, &edsList, client.HasLabels{apicommon.MD5AgentDeploymentProviderLabelKey}); err != nil { - return err - } - - for _, eds := range edsList.Items { - provider := eds.Labels[apicommon.MD5AgentDeploymentProviderLabelKey] - if _, ok := providerList[provider]; !ok { - if err := r.client.Delete(ctx, &eds); err != nil { - return err - } - r.log.Info("Deleted ExtendedDaemonSet", "extendedDaemonSet.Namespace", eds.Namespace, "extendedDaemonSet.Name", eds.Name) - event := buildEventInfo(eds.Name, eds.Namespace, extendedDaemonSetKind, datadog.DeletionEvent) - r.recordEvent(dda, event) - - removeStaleStatus(ddaStatus, eds.Name) - } - } - - return nil - } - - daemonSetList := appsv1.DaemonSetList{} - if err := r.client.List(ctx, &daemonSetList, client.HasLabels{apicommon.MD5AgentDeploymentProviderLabelKey}); err != nil { - return err - } - for _, ds := range daemonSetList.Items { - provider := ds.Labels[apicommon.MD5AgentDeploymentProviderLabelKey] - if _, ok := providerList[provider]; !ok { - if err := r.client.Delete(ctx, &ds); err != nil { - return err - } - r.log.Info("Deleted DaemonSet", "daemonSet.Namespace", ds.Namespace, "daemonSet.Name", ds.Name) - event := buildEventInfo(ds.Name, ds.Namespace, daemonSetKind, datadog.DeletionEvent) - r.recordEvent(dda, event) - - removeStaleStatus(ddaStatus, ds.Name) - } - } +func (r *Reconciler) deleteV2ExtendedDaemonSet(logger logr.Logger, dda *datadoghqv2alpha1.DatadogAgent, eds *edsv1alpha1.ExtendedDaemonSet, newStatus *datadoghqv2alpha1.DatadogAgentStatus) error { + err := r.client.Delete(context.TODO(), eds) + if err != nil { + return err } + logger.Info("Delete DaemonSet", "extendedDaemonSet.Namespace", eds.Namespace, "extendedDaemonSet.Name", eds.Name) + event := buildEventInfo(eds.Name, eds.Namespace, extendedDaemonSetKind, datadog.DeletionEvent) + r.recordEvent(dda, event) + removeStaleStatus(newStatus, eds.Name) return nil } @@ -336,12 +255,7 @@ func removeStaleStatus(ddaStatus *datadoghqv2alpha1.DatadogAgentStatus, name str } } -func (r *Reconciler) handleProfiles(ctx context.Context, profiles []v1alpha1.DatadogAgentProfile, profilesByNode map[string]types.NamespacedName, - ddaNamespace string, providerList map[string]struct{}) error { - if err := r.cleanupDaemonSetsForProfilesThatNoLongerApply(ctx, profiles, providerList); err != nil { - return err - } - +func (r *Reconciler) handleProfiles(ctx context.Context, profilesByNode map[string]types.NamespacedName, ddaNamespace string) error { if err := r.labelNodesWithProfiles(ctx, profilesByNode); err != nil { return err } @@ -353,42 +267,6 @@ func (r *Reconciler) handleProfiles(ctx context.Context, profiles []v1alpha1.Dat return nil } -func (r *Reconciler) cleanupDaemonSetsForProfilesThatNoLongerApply(ctx context.Context, profiles []v1alpha1.DatadogAgentProfile, providerList map[string]struct{}) error { - daemonSets, err := r.agentProfileDaemonSetsCreatedByOperator(ctx) - if err != nil { - return err - } - - daemonSetNamesBelongingToProfiles := map[string]struct{}{} - for _, profile := range profiles { - name := types.NamespacedName{ - Namespace: profile.Namespace, - Name: profile.Name, - } - dsProfileName := agentprofile.DaemonSetName(name) - - if r.options.IntrospectionEnabled { - for provider := range providerList { - daemonSetNamesBelongingToProfiles[kubernetes.GetAgentNameWithProvider(dsProfileName, provider)] = struct{}{} - } - } else { - daemonSetNamesBelongingToProfiles[dsProfileName] = struct{}{} - } - } - - for _, daemonSet := range daemonSets { - if _, belongsToProfile := daemonSetNamesBelongingToProfiles[daemonSet.Name]; belongsToProfile { - continue - } - - if err = r.client.Delete(ctx, &daemonSet); err != nil { - return err - } - } - - return nil -} - // labelNodesWithProfiles sets the "agent.datadoghq.com/profile" label only in // the nodes where a profile is applied func (r *Reconciler) labelNodesWithProfiles(ctx context.Context, profilesByNode map[string]types.NamespacedName) error { @@ -496,12 +374,131 @@ func (r *Reconciler) cleanupPodsForProfilesThatNoLongerApply(ctx context.Context return nil } -func (r *Reconciler) agentProfileDaemonSetsCreatedByOperator(ctx context.Context) ([]appsv1.DaemonSet, error) { +// cleanupExtraneousDaemonSets deletes DSs/EDSs that no longer apply. +// Use cases include deleting old DSs/EDSs when: +// - a DaemonSet's name is changed using node overrides +// - introspection is disabled or enabled +// - a profile is deleted +func (r *Reconciler) cleanupExtraneousDaemonSets(ctx context.Context, logger logr.Logger, dda *datadoghqv2alpha1.DatadogAgent, newStatus *datadoghqv2alpha1.DatadogAgentStatus, + providerList map[string]struct{}, profiles []v1alpha1.DatadogAgentProfile) error { + matchLabels := client.MatchingLabels{ + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + } + + dsName := getDaemonSetNameFromDatadogAgent(dda) + validDaemonSetNames, validExtendedDaemonSetNames := r.getValidDaemonSetNames(dsName, providerList, profiles) + + // Only the default profile uses an EDS when profiles are enabled + // Multiple EDSs can be created with introspection + if r.options.ExtendedDaemonsetOptions.Enabled { + edsList := edsv1alpha1.ExtendedDaemonSetList{} + if err := r.client.List(ctx, &edsList, matchLabels); err != nil { + return err + } + + for _, eds := range edsList.Items { + if _, ok := validExtendedDaemonSetNames[eds.Name]; !ok { + if err := r.deleteV2ExtendedDaemonSet(logger, dda, &eds, newStatus); err != nil { + return err + } + } + } + } + daemonSetList := appsv1.DaemonSetList{} + if err := r.client.List(ctx, &daemonSetList, matchLabels); err != nil { + return err + } + + for _, daemonSet := range daemonSetList.Items { + if _, ok := validDaemonSetNames[daemonSet.Name]; !ok { + if err := r.deleteV2DaemonSet(logger, dda, &daemonSet, newStatus); err != nil { + return err + } + } + } + + return nil +} + +// getValidDaemonSetNames generates a list of valid DS and EDS names +func (r *Reconciler) getValidDaemonSetNames(dsName string, providerList map[string]struct{}, profiles []v1alpha1.DatadogAgentProfile) (map[string]struct{}, map[string]struct{}) { + validDaemonSetNames := map[string]struct{}{} + validExtendedDaemonSetNames := map[string]struct{}{} - if err := r.client.List(ctx, &daemonSetList, client.HasLabels{agentprofile.ProfileLabelKey}); err != nil { - return nil, err + // Introspection includes names with a provider suffix + if r.options.IntrospectionEnabled { + for provider := range providerList { + if r.options.ExtendedDaemonsetOptions.Enabled { + validExtendedDaemonSetNames[kubernetes.GetAgentNameWithProvider(dsName, provider)] = struct{}{} + } else { + validDaemonSetNames[kubernetes.GetAgentNameWithProvider(dsName, provider)] = struct{}{} + } + } + } + // Profiles include names with the profile prefix and the DS/EDS name for the default profile + if r.options.DatadogAgentProfileEnabled { + for _, profile := range profiles { + name := types.NamespacedName{ + Namespace: profile.Namespace, + Name: profile.Name, + } + dsProfileName := agentprofile.DaemonSetName(name) + + // The default profile can be a DS or an EDS and uses the DS/EDS name + if agentprofile.IsDefaultProfile(profile.Namespace, profile.Name) { + if r.options.IntrospectionEnabled { + for provider := range providerList { + if r.options.ExtendedDaemonsetOptions.Enabled { + validExtendedDaemonSetNames[kubernetes.GetAgentNameWithProvider(dsName, provider)] = struct{}{} + } else { + validDaemonSetNames[kubernetes.GetAgentNameWithProvider(dsName, provider)] = struct{}{} + } + } + } else { + if r.options.ExtendedDaemonsetOptions.Enabled { + validExtendedDaemonSetNames[dsName] = struct{}{} + } else { + validDaemonSetNames[dsName] = struct{}{} + } + } + } + // Non-default profiles can only be DaemonSets + if r.options.IntrospectionEnabled { + for provider := range providerList { + validDaemonSetNames[kubernetes.GetAgentNameWithProvider(dsProfileName, provider)] = struct{}{} + } + } else { + validDaemonSetNames[dsProfileName] = struct{}{} + } + } } - return daemonSetList.Items, nil + // If neither introspection nor profiles are enabled, only the current DS/EDS name is valid + if !r.options.IntrospectionEnabled && !r.options.DatadogAgentProfileEnabled { + if r.options.ExtendedDaemonsetOptions.Enabled { + validExtendedDaemonSetNames = map[string]struct{}{ + dsName: {}, + } + } else { + validDaemonSetNames = map[string]struct{}{ + dsName: {}, + } + } + } + + return validDaemonSetNames, validExtendedDaemonSetNames +} + +// getDaemonSetNameFromDatadogAgent returns the expected DS/EDS name based on +// the DDA name and nodeAgent name override +func getDaemonSetNameFromDatadogAgent(dda *datadoghqv2alpha1.DatadogAgent) string { + dsName := component.GetAgentName(dda) + if componentOverride, ok := dda.Spec.Override[datadoghqv2alpha1.NodeAgentComponentName]; ok { + if componentOverride.Name != nil && *componentOverride.Name != "" { + dsName = *componentOverride.Name + } + } + return dsName } diff --git a/controllers/datadogagent/controller_reconcile_agent_test.go b/controllers/datadogagent/controller_reconcile_agent_test.go index 75ff4ea1c..ea9f54492 100644 --- a/controllers/datadogagent/controller_reconcile_agent_test.go +++ b/controllers/datadogagent/controller_reconcile_agent_test.go @@ -6,7 +6,9 @@ import ( apicommon "github.com/DataDog/datadog-operator/apis/datadoghq/common" "github.com/DataDog/datadog-operator/apis/datadoghq/common/v1" + "github.com/DataDog/datadog-operator/apis/datadoghq/v1alpha1" datadoghqv2alpha1 "github.com/DataDog/datadog-operator/apis/datadoghq/v2alpha1" + apiutils "github.com/DataDog/datadog-operator/apis/utils" "github.com/DataDog/datadog-operator/controllers/datadogagent/component/agent" "github.com/DataDog/datadog-operator/pkg/kubernetes" @@ -26,36 +28,256 @@ import ( const defaultProvider = kubernetes.DefaultProvider const gkeCosProvider = kubernetes.GKECloudProvider + "-" + kubernetes.GKECosType -func Test_cleanupDaemonSetsForProvidersThatNoLongerApply(t *testing.T) { +func Test_getValidDaemonSetNames(t *testing.T) { + testCases := []struct { + name string + dsName string + introspectionEnabled bool + profilesEnabled bool + edsEnabled bool + existingProviders map[string]struct{} + existingProfiles []v1alpha1.DatadogAgentProfile + wantDS map[string]struct{} + wantEDS map[string]struct{} + }{ + { + name: "introspection disabled, profiles disabled, eds disabled", + dsName: "foo", + introspectionEnabled: false, + profilesEnabled: false, + edsEnabled: false, + existingProviders: map[string]struct{}{ + gkeCosProvider: {}, + }, + existingProfiles: []v1alpha1.DatadogAgentProfile{}, + wantDS: map[string]struct{}{"foo": {}}, + wantEDS: map[string]struct{}{}, + }, + { + name: "introspection disabled, profiles disabled, eds enabled", + dsName: "foo", + introspectionEnabled: false, + profilesEnabled: false, + edsEnabled: true, + existingProviders: map[string]struct{}{ + gkeCosProvider: {}, + }, + existingProfiles: []v1alpha1.DatadogAgentProfile{}, + wantDS: map[string]struct{}{}, + wantEDS: map[string]struct{}{"foo": {}}, + }, + { + name: "introspection enabled, profiles disabled, eds disabled", + dsName: "foo", + introspectionEnabled: true, + profilesEnabled: false, + edsEnabled: false, + existingProviders: map[string]struct{}{ + gkeCosProvider: {}, + }, + existingProfiles: []v1alpha1.DatadogAgentProfile{}, + wantDS: map[string]struct{}{"foo-gke-cos": {}}, + wantEDS: map[string]struct{}{}, + }, + { + name: "introspection enabled, profiles disabled, eds enabled", + dsName: "foo", + introspectionEnabled: true, + profilesEnabled: false, + edsEnabled: true, + existingProviders: map[string]struct{}{ + gkeCosProvider: {}, + }, + existingProfiles: []v1alpha1.DatadogAgentProfile{}, + wantDS: map[string]struct{}{}, + wantEDS: map[string]struct{}{"foo-gke-cos": {}}, + }, + { + name: "introspection enabled, profiles enabled, eds disabled", + dsName: "foo", + introspectionEnabled: true, + profilesEnabled: true, + edsEnabled: false, + existingProviders: map[string]struct{}{ + gkeCosProvider: {}, + defaultProvider: {}, + }, + existingProfiles: []v1alpha1.DatadogAgentProfile{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "profile-1", + Namespace: "ns-1", + }, + }, + }, + wantDS: map[string]struct{}{ + "foo-default": {}, + "foo-gke-cos": {}, + "datadog-agent-with-profile-ns-1-profile-1-default": {}, + "datadog-agent-with-profile-ns-1-profile-1-gke-cos": {}, + }, + wantEDS: map[string]struct{}{}, + }, + { + name: "introspection enabled, profiles enabled, eds enabled", + dsName: "foo", + introspectionEnabled: true, + profilesEnabled: true, + edsEnabled: true, + existingProviders: map[string]struct{}{ + gkeCosProvider: {}, + defaultProvider: {}, + }, + existingProfiles: []v1alpha1.DatadogAgentProfile{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "profile-1", + Namespace: "ns-1", + }, + }, + }, + wantDS: map[string]struct{}{ + "datadog-agent-with-profile-ns-1-profile-1-default": {}, + "datadog-agent-with-profile-ns-1-profile-1-gke-cos": {}, + }, + wantEDS: map[string]struct{}{ + "foo-default": {}, + "foo-gke-cos": {}, + }, + }, + } + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + r := &Reconciler{ + options: ReconcilerOptions{ + IntrospectionEnabled: tt.introspectionEnabled, + DatadogAgentProfileEnabled: tt.profilesEnabled, + ExtendedDaemonsetOptions: agent.ExtendedDaemonsetOptions{ + Enabled: tt.edsEnabled, + }, + }, + } + + validDSNames, validEDSNames := r.getValidDaemonSetNames(tt.dsName, tt.existingProviders, tt.existingProfiles) + assert.Equal(t, tt.wantDS, validDSNames) + assert.Equal(t, tt.wantEDS, validEDSNames) + }) + } +} + +func Test_getDaemonSetNameFromDatadogAgent(t *testing.T) { + testCases := []struct { + name string + dda *datadoghqv2alpha1.DatadogAgent + wantDSName string + }{ + { + name: "no node override", + dda: &datadoghqv2alpha1.DatadogAgent{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + }, + wantDSName: "foo-agent", + }, + { + name: "node override with no name override", + dda: &datadoghqv2alpha1.DatadogAgent{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: datadoghqv2alpha1.DatadogAgentSpec{ + Override: map[datadoghqv2alpha1.ComponentName]*datadoghqv2alpha1.DatadogAgentComponentOverride{ + datadoghqv2alpha1.NodeAgentComponentName: { + Replicas: apiutils.NewInt32Pointer(10), + }, + }, + }, + }, + wantDSName: "foo-agent", + }, + { + name: "node override with name override", + dda: &datadoghqv2alpha1.DatadogAgent{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: datadoghqv2alpha1.DatadogAgentSpec{ + Override: map[datadoghqv2alpha1.ComponentName]*datadoghqv2alpha1.DatadogAgentComponentOverride{ + datadoghqv2alpha1.NodeAgentComponentName: { + Name: apiutils.NewStringPointer("bar"), + Replicas: apiutils.NewInt32Pointer(10), + }, + }, + }, + }, + wantDSName: "bar", + }, + { + name: "dca override with name override", + dda: &datadoghqv2alpha1.DatadogAgent{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: datadoghqv2alpha1.DatadogAgentSpec{ + Override: map[datadoghqv2alpha1.ComponentName]*datadoghqv2alpha1.DatadogAgentComponentOverride{ + datadoghqv2alpha1.ClusterAgentComponentName: { + Name: apiutils.NewStringPointer("bar"), + Replicas: apiutils.NewInt32Pointer(10), + }, + }, + }, + }, + wantDSName: "foo-agent", + }, + } + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + dsName := getDaemonSetNameFromDatadogAgent(tt.dda) + assert.Equal(t, tt.wantDSName, dsName) + }) + } +} + +func Test_cleanupExtraneousDaemonSets(t *testing.T) { sch := runtime.NewScheme() _ = scheme.AddToScheme(sch) _ = edsdatadoghqv1alpha1.AddToScheme(sch) ctx := context.Background() testCases := []struct { - name string - agents []client.Object - edsEnabled bool - existingProviders map[string]struct{} - wantDS *appsv1.DaemonSetList - wantEDS *edsdatadoghqv1alpha1.ExtendedDaemonSetList + name string + description string + existingAgents []client.Object + introspectionEnabled bool + profilesEnabled bool + edsEnabled bool + providerList map[string]struct{} + profiles []v1alpha1.DatadogAgentProfile + wantDS *appsv1.DaemonSetList + wantEDS *edsdatadoghqv1alpha1.ExtendedDaemonSetList }{ { - name: "no unused ds", - agents: []client.Object{ + name: "no unused ds, introspection disabled, profiles disabled", + description: "DS `dda-foo-agent` should not be deleted", + existingAgents: []client.Object{ &appsv1.DaemonSet{ ObjectMeta: metav1.ObjectMeta{ - Name: "gke-cos-node", + Name: "dda-foo-agent", Labels: map[string]string{ - apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", }, }, }, }, - edsEnabled: false, - existingProviders: map[string]struct{}{ - gkeCosProvider: {}, - }, + introspectionEnabled: false, + profilesEnabled: false, + edsEnabled: false, + providerList: map[string]struct{}{}, + profiles: []v1alpha1.DatadogAgentProfile{}, wantDS: &appsv1.DaemonSetList{ TypeMeta: metav1.TypeMeta{ Kind: "DaemonSetList", @@ -64,31 +286,47 @@ func Test_cleanupDaemonSetsForProvidersThatNoLongerApply(t *testing.T) { Items: []appsv1.DaemonSet{ { ObjectMeta: metav1.ObjectMeta{ - Name: "gke-cos-node", + Name: "dda-foo-agent", + ResourceVersion: "999", Labels: map[string]string{ - apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", }, - ResourceVersion: "999", }, }, }, }, + wantEDS: &edsdatadoghqv1alpha1.ExtendedDaemonSetList{ + TypeMeta: metav1.TypeMeta{ + Kind: "ExtendedDaemonSetList", + APIVersion: "datadoghq.com/v1alpha1", + }, + }, }, { - name: "no unused eds", - agents: []client.Object{ + name: "no unused eds, introspection disabled, profiles disabled", + description: "EDS `dda-foo-agent` should not be deleted", + existingAgents: []client.Object{ &edsdatadoghqv1alpha1.ExtendedDaemonSet{ ObjectMeta: metav1.ObjectMeta{ - Name: "gke-cos-node", + Name: "dda-foo-agent", Labels: map[string]string{ - apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", }, }, }, }, - edsEnabled: true, - existingProviders: map[string]struct{}{ - gkeCosProvider: {}, + introspectionEnabled: false, + profilesEnabled: false, + edsEnabled: true, + providerList: map[string]struct{}{}, + profiles: []v1alpha1.DatadogAgentProfile{}, + wantDS: &appsv1.DaemonSetList{ + TypeMeta: metav1.TypeMeta{ + Kind: "DaemonSetList", + APIVersion: "apps/v1", + }, }, wantEDS: &edsdatadoghqv1alpha1.ExtendedDaemonSetList{ TypeMeta: metav1.TypeMeta{ @@ -98,108 +336,180 @@ func Test_cleanupDaemonSetsForProvidersThatNoLongerApply(t *testing.T) { Items: []edsdatadoghqv1alpha1.ExtendedDaemonSet{ { ObjectMeta: metav1.ObjectMeta{ - Name: "gke-cos-node", + Name: "dda-foo-agent", + ResourceVersion: "999", Labels: map[string]string{ - apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", }, - ResourceVersion: "999", }, }, }, }, }, { - name: "unused ds", - agents: []client.Object{ + name: "no unused ds, introspection enabled, profiles enabled", + description: "DS `datadog-agent-with-profile-ns-1-profile-1-gke-cos` should not be deleted", + existingAgents: []client.Object{ &appsv1.DaemonSet{ ObjectMeta: metav1.ObjectMeta{ - Name: "gke-cos-node", + Name: "datadog-agent-with-profile-ns-1-profile-1-gke-cos", + Namespace: "ns-1", Labels: map[string]string{ apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", }, }, }, }, - edsEnabled: false, - existingProviders: map[string]struct{}{ - defaultProvider: {}, + introspectionEnabled: true, + profilesEnabled: true, + edsEnabled: false, + providerList: map[string]struct{}{ + gkeCosProvider: {}, + }, + profiles: []v1alpha1.DatadogAgentProfile{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "profile-1", + Namespace: "ns-1", + }, + }, }, wantDS: &appsv1.DaemonSetList{ TypeMeta: metav1.TypeMeta{ Kind: "DaemonSetList", APIVersion: "apps/v1", }, - Items: []appsv1.DaemonSet{}, - }, - }, - { - name: "unused eds", - agents: []client.Object{ - &edsdatadoghqv1alpha1.ExtendedDaemonSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gke-cos-node", - Labels: map[string]string{ - apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + Items: []appsv1.DaemonSet{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1-gke-cos", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + ResourceVersion: "999", }, }, }, }, - edsEnabled: true, - existingProviders: map[string]struct{}{ - defaultProvider: {}, - }, wantEDS: &edsdatadoghqv1alpha1.ExtendedDaemonSetList{ TypeMeta: metav1.TypeMeta{ Kind: "ExtendedDaemonSetList", APIVersion: "datadoghq.com/v1alpha1", }, - Items: []edsdatadoghqv1alpha1.ExtendedDaemonSet{}, }, }, { - name: "no ds to delete", - agents: []client.Object{}, - edsEnabled: false, - existingProviders: map[string]struct{}{ + name: "no unused eds, introspection enabled, profiles enabled", + description: "EDS `dda-foo-agent-gke-cos` should not be deleted. The EDS name comes from the default profile", + existingAgents: []client.Object{ + &edsdatadoghqv1alpha1.ExtendedDaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "dda-foo-agent-gke-cos", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + }, + }, + }, + introspectionEnabled: true, + profilesEnabled: true, + edsEnabled: true, + providerList: map[string]struct{}{ gkeCosProvider: {}, }, + profiles: []v1alpha1.DatadogAgentProfile{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "profile-1", + Namespace: "ns-1", + }, + }, + }, wantDS: &appsv1.DaemonSetList{ TypeMeta: metav1.TypeMeta{ Kind: "DaemonSetList", APIVersion: "apps/v1", }, - Items: nil, - }, - }, - { - name: "no eds to delete", - agents: []client.Object{}, - edsEnabled: true, - existingProviders: map[string]struct{}{ - gkeCosProvider: {}, }, wantEDS: &edsdatadoghqv1alpha1.ExtendedDaemonSetList{ TypeMeta: metav1.TypeMeta{ Kind: "ExtendedDaemonSetList", APIVersion: "datadoghq.com/v1alpha1", }, - Items: nil, + Items: []edsdatadoghqv1alpha1.ExtendedDaemonSet{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "dda-foo-agent-gke-cos", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + ResourceVersion: "999", + }, + }, + }, }, }, { - name: "no providers for ds", - agents: []client.Object{ + name: "multiple unused ds, introspection enabled, profiles enabled", + description: "All DS except `datadog-agent-with-profile-ns-1-profile-1-gke-cos` should be deleted", + existingAgents: []client.Object{ + &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }}, + }, &appsv1.DaemonSet{ ObjectMeta: metav1.ObjectMeta{ - Name: "gke-cos-node", + Name: "datadog-agent-with-profile-ns-1-profile-1", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + }, + }, + &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1-gke-cos", + Namespace: "ns-1", Labels: map[string]string{ apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", }, }, }, }, - edsEnabled: false, - existingProviders: map[string]struct{}{}, + introspectionEnabled: true, + profilesEnabled: true, + edsEnabled: false, + providerList: map[string]struct{}{ + gkeCosProvider: {}, + }, + profiles: []v1alpha1.DatadogAgentProfile{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "profile-1", + Namespace: "ns-1", + }, + }, + }, wantDS: &appsv1.DaemonSetList{ TypeMeta: metav1.TypeMeta{ Kind: "DaemonSetList", @@ -208,30 +518,117 @@ func Test_cleanupDaemonSetsForProvidersThatNoLongerApply(t *testing.T) { Items: []appsv1.DaemonSet{ { ObjectMeta: metav1.ObjectMeta{ - Name: "gke-cos-node", + Name: "datadog-agent-with-profile-ns-1-profile-1-gke-cos", + Namespace: "ns-1", Labels: map[string]string{ apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", }, ResourceVersion: "999", }, }, }, }, + wantEDS: &edsdatadoghqv1alpha1.ExtendedDaemonSetList{ + TypeMeta: metav1.TypeMeta{ + Kind: "ExtendedDaemonSetList", + APIVersion: "datadoghq.com/v1alpha1", + }, + }, }, { - name: "no providers for eds", - agents: []client.Object{ + name: "multiple unused eds, introspection enabled, profiles enabled", + description: "All but EDS `dda-foo-agent-gke-cos` and DS `datadog-agent-with-profile-ns-1-profile-1-gke-cos` should be deleted", + existingAgents: []client.Object{ + &edsdatadoghqv1alpha1.ExtendedDaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + }, + }, + &edsdatadoghqv1alpha1.ExtendedDaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + }, + }, + &edsdatadoghqv1alpha1.ExtendedDaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1-gke-cos", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + }, + }, &edsdatadoghqv1alpha1.ExtendedDaemonSet{ ObjectMeta: metav1.ObjectMeta{ - Name: "gke-cos-node", + Name: "dda-foo-agent-gke-cos", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + }, + }, + &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1-gke-cos", + Namespace: "ns-1", Labels: map[string]string{ apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + }, + }, + }, + introspectionEnabled: true, + profilesEnabled: true, + edsEnabled: true, + providerList: map[string]struct{}{ + gkeCosProvider: {}, + }, + profiles: []v1alpha1.DatadogAgentProfile{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "profile-1", + Namespace: "ns-1", + }, + }, + }, + wantDS: &appsv1.DaemonSetList{ + TypeMeta: metav1.TypeMeta{ + Kind: "DaemonSetList", + APIVersion: "apps/v1", + }, + Items: []appsv1.DaemonSet{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1-gke-cos", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + ResourceVersion: "999", }, }, }, }, - edsEnabled: true, - existingProviders: map[string]struct{}{}, wantEDS: &edsdatadoghqv1alpha1.ExtendedDaemonSetList{ TypeMeta: metav1.TypeMeta{ Kind: "ExtendedDaemonSetList", @@ -240,9 +637,12 @@ func Test_cleanupDaemonSetsForProvidersThatNoLongerApply(t *testing.T) { Items: []edsdatadoghqv1alpha1.ExtendedDaemonSet{ { ObjectMeta: metav1.ObjectMeta{ - Name: "gke-cos-node", + Name: "dda-foo-agent-gke-cos", + Namespace: "ns-1", Labels: map[string]string{ apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", }, ResourceVersion: "999", }, @@ -250,59 +650,773 @@ func Test_cleanupDaemonSetsForProvidersThatNoLongerApply(t *testing.T) { }, }, }, - } - - for _, tt := range testCases { - t.Run(tt.name, func(t *testing.T) { - fakeClient := fake.NewClientBuilder().WithScheme(sch).WithObjects(tt.agents...).Build() - logger := logf.Log.WithName("test_cleanupDaemonSetsForProvidersThatNoLongerApply") - eventBroadcaster := record.NewBroadcaster() - recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: "TestReconcileDatadogAgent_createNewExtendedDaemonSet"}) - - r := &Reconciler{ - client: fakeClient, - log: logger, - recorder: recorder, - options: ReconcilerOptions{ - ExtendedDaemonsetOptions: agent.ExtendedDaemonsetOptions{ - Enabled: tt.edsEnabled, - }, + { + name: "multiple unused ds, introspection enabled, profiles disabled", + description: "All DS except `dda-foo-agent-gke-cos` should be deleted", + existingAgents: []client.Object{ + &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }}, + }, + &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "dda-foo-agent-gke-cos", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }}, + }, + &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + }, + }, + &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1-gke-cos", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + }, + }, + }, + introspectionEnabled: true, + profilesEnabled: false, + edsEnabled: false, + providerList: map[string]struct{}{ + gkeCosProvider: {}, + }, + profiles: []v1alpha1.DatadogAgentProfile{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "profile-1", + Namespace: "ns-1", + }, + }, + }, + wantDS: &appsv1.DaemonSetList{ + TypeMeta: metav1.TypeMeta{ + Kind: "DaemonSetList", + APIVersion: "apps/v1", + }, + Items: []appsv1.DaemonSet{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "dda-foo-agent-gke-cos", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + ResourceVersion: "999", + }, + }, + }, + }, + wantEDS: &edsdatadoghqv1alpha1.ExtendedDaemonSetList{ + TypeMeta: metav1.TypeMeta{ + Kind: "ExtendedDaemonSetList", + APIVersion: "datadoghq.com/v1alpha1", + }, + }, + }, + { + name: "multiple unused eds, introspection enabled, profiles disabled", + description: "All but EDS `dda-foo-agent-gke-cos` should be deleted", + existingAgents: []client.Object{ + &edsdatadoghqv1alpha1.ExtendedDaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + }, + }, + &edsdatadoghqv1alpha1.ExtendedDaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + }, + }, + &edsdatadoghqv1alpha1.ExtendedDaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1-gke-cos", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + }, + }, + &edsdatadoghqv1alpha1.ExtendedDaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "dda-foo-agent-gke-cos", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + }, + }, + &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1-gke-cos", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + }, + }, + }, + introspectionEnabled: true, + profilesEnabled: false, + edsEnabled: true, + providerList: map[string]struct{}{ + gkeCosProvider: {}, + }, + profiles: []v1alpha1.DatadogAgentProfile{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "profile-1", + Namespace: "ns-1", + }, + }, + }, + wantDS: &appsv1.DaemonSetList{ + TypeMeta: metav1.TypeMeta{ + Kind: "DaemonSetList", + APIVersion: "apps/v1", + }, + Items: []appsv1.DaemonSet{}, + }, + wantEDS: &edsdatadoghqv1alpha1.ExtendedDaemonSetList{ + TypeMeta: metav1.TypeMeta{ + Kind: "ExtendedDaemonSetList", + APIVersion: "datadoghq.com/v1alpha1", + }, + Items: []edsdatadoghqv1alpha1.ExtendedDaemonSet{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "dda-foo-agent-gke-cos", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + ResourceVersion: "999", + }, + }, + }, + }, + }, + { + name: "multiple unused ds, introspection disabled, profiles enabled", + description: "DS `datadog-agent-with-profile-ns-1-profile-1-gke-cos` should be deleted", + existingAgents: []client.Object{ + &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "dda-foo-agent", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }}, + }, + &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + }, + }, + &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1-gke-cos", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + }, + }, + }, + introspectionEnabled: false, + profilesEnabled: true, + edsEnabled: false, + providerList: map[string]struct{}{ + kubernetes.LegacyProvider: {}, + }, + profiles: []v1alpha1.DatadogAgentProfile{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "profile-1", + Namespace: "ns-1", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Namespace: "", + Name: "default", + }, + }, + }, + wantDS: &appsv1.DaemonSetList{ + TypeMeta: metav1.TypeMeta{ + Kind: "DaemonSetList", + APIVersion: "apps/v1", + }, + Items: []appsv1.DaemonSet{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + ResourceVersion: "999", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "dda-foo-agent", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + ResourceVersion: "999", + }, + }, + }, + }, + wantEDS: &edsdatadoghqv1alpha1.ExtendedDaemonSetList{ + TypeMeta: metav1.TypeMeta{ + Kind: "ExtendedDaemonSetList", + APIVersion: "datadoghq.com/v1alpha1", + }, + }, + }, + { + name: "multiple unused eds, introspection disabled, profiles enabled", + description: "All but EDS `dda-foo-agent` and DS `datadog-agent-with-profile-ns-1-profile-1` should be deleted", + existingAgents: []client.Object{ + &edsdatadoghqv1alpha1.ExtendedDaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "dda-foo-agent", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + }, + }, + &edsdatadoghqv1alpha1.ExtendedDaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + }, + }, + &edsdatadoghqv1alpha1.ExtendedDaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1-gke-cos", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + }, + }, + &edsdatadoghqv1alpha1.ExtendedDaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "dda-foo-agent-gke-cos", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + }, + }, + &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1-gke-cos", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + }, + }, + &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + }, + }, + }, + introspectionEnabled: false, + profilesEnabled: true, + edsEnabled: true, + providerList: map[string]struct{}{ + gkeCosProvider: {}, + }, + profiles: []v1alpha1.DatadogAgentProfile{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "profile-1", + Namespace: "ns-1", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "default", + Namespace: "", + }, + }, + }, + wantDS: &appsv1.DaemonSetList{ + TypeMeta: metav1.TypeMeta{ + Kind: "DaemonSetList", + APIVersion: "apps/v1", + }, + Items: []appsv1.DaemonSet{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + ResourceVersion: "999", + }, + }, + }, + }, + wantEDS: &edsdatadoghqv1alpha1.ExtendedDaemonSetList{ + TypeMeta: metav1.TypeMeta{ + Kind: "ExtendedDaemonSetList", + APIVersion: "datadoghq.com/v1alpha1", + }, + Items: []edsdatadoghqv1alpha1.ExtendedDaemonSet{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "dda-foo-agent", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + ResourceVersion: "999", + }, + }, + }, + }, + }, + { + name: "DSs are not created by the operator (do not have the expected labels) and should not be removed", + description: "No DSs should be deleted", + existingAgents: []client.Object{ + &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "dda-foo-agent", + Namespace: "ns-1", + }, + }, + &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1", + Namespace: "ns-1", + Labels: map[string]string{ + "foo": "bar", + }, + }, + }, + &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1-gke-cos", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + }, + }, + }, + introspectionEnabled: true, + profilesEnabled: true, + edsEnabled: false, + providerList: map[string]struct{}{ + kubernetes.LegacyProvider: {}, + }, + profiles: []v1alpha1.DatadogAgentProfile{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "profile-1", + Namespace: "ns-1", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Namespace: "", + Name: "default", + }, + }, + }, + wantDS: &appsv1.DaemonSetList{ + TypeMeta: metav1.TypeMeta{ + Kind: "DaemonSetList", + APIVersion: "apps/v1", + }, + Items: []appsv1.DaemonSet{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1", + Namespace: "ns-1", + Labels: map[string]string{ + "foo": "bar", + }, + ResourceVersion: "999", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1-gke-cos", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + ResourceVersion: "999", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "dda-foo-agent", + Namespace: "ns-1", + ResourceVersion: "999", + }, + }, + }, + }, + wantEDS: &edsdatadoghqv1alpha1.ExtendedDaemonSetList{ + TypeMeta: metav1.TypeMeta{ + Kind: "ExtendedDaemonSetList", + APIVersion: "datadoghq.com/v1alpha1", + }, + }, + }, + { + name: "EDSs are not created by the operator (do not have the expected labels) and should not be removed", + description: "Nothing should be deleted", + existingAgents: []client.Object{ + &edsdatadoghqv1alpha1.ExtendedDaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "dda-foo-agent", + Namespace: "ns-1", + }, + }, + &edsdatadoghqv1alpha1.ExtendedDaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1", + Namespace: "ns-1", + Labels: map[string]string{ + "foo": "bar", + }, + }, + }, + &edsdatadoghqv1alpha1.ExtendedDaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1-gke-cos", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + }, + }, + &edsdatadoghqv1alpha1.ExtendedDaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "dda-foo-agent-gke-cos", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + }, + }, + }, + &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1-gke-cos", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + }, + }, + &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + }, + }, + }, + }, + introspectionEnabled: true, + profilesEnabled: true, + edsEnabled: true, + providerList: map[string]struct{}{ + gkeCosProvider: {}, + }, + profiles: []v1alpha1.DatadogAgentProfile{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "profile-1", + Namespace: "ns-1", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "default", + Namespace: "", + }, + }, + }, + wantDS: &appsv1.DaemonSetList{ + TypeMeta: metav1.TypeMeta{ + Kind: "DaemonSetList", + APIVersion: "apps/v1", + }, + Items: []appsv1.DaemonSet{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + }, + ResourceVersion: "999", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1-gke-cos", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + ResourceVersion: "999", + }, + }, + }, + }, + wantEDS: &edsdatadoghqv1alpha1.ExtendedDaemonSetList{ + TypeMeta: metav1.TypeMeta{ + Kind: "ExtendedDaemonSetList", + APIVersion: "datadoghq.com/v1alpha1", + }, + Items: []edsdatadoghqv1alpha1.ExtendedDaemonSet{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1", + Namespace: "ns-1", + Labels: map[string]string{ + "foo": "bar", + }, + ResourceVersion: "999", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "datadog-agent-with-profile-ns-1-profile-1-gke-cos", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + kubernetes.AppKubernetesManageByLabelKey: "datadog-operator", + }, + ResourceVersion: "999", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "dda-foo-agent", + Namespace: "ns-1", + ResourceVersion: "999", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "dda-foo-agent-gke-cos", + Namespace: "ns-1", + Labels: map[string]string{ + apicommon.MD5AgentDeploymentProviderLabelKey: gkeCosProvider, + apicommon.AgentDeploymentComponentLabelKey: apicommon.DefaultAgentResourceSuffix, + }, + ResourceVersion: "999", + }, + }, + }, + }, + }, + { + name: "no existing ds, introspection enabled, profiles enabled", + description: "DS list should be empty (nothing to delete)", + existingAgents: []client.Object{}, + introspectionEnabled: true, + profilesEnabled: true, + edsEnabled: false, + providerList: map[string]struct{}{ + gkeCosProvider: {}, + }, + profiles: []v1alpha1.DatadogAgentProfile{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "profile-1", + Namespace: "ns-1", + }, + }, + }, + wantDS: &appsv1.DaemonSetList{ + TypeMeta: metav1.TypeMeta{ + Kind: "DaemonSetList", + APIVersion: "apps/v1", + }, + }, + wantEDS: &edsdatadoghqv1alpha1.ExtendedDaemonSetList{ + TypeMeta: metav1.TypeMeta{ + Kind: "ExtendedDaemonSetList", + APIVersion: "datadoghq.com/v1alpha1", + }, + }, + }, + { + name: "no existing eds, introspection enabled, profiles enabled", + description: "DS and EDS list should be empty (nothing to delete)", + existingAgents: []client.Object{}, + introspectionEnabled: true, + profilesEnabled: true, + edsEnabled: true, + providerList: map[string]struct{}{ + gkeCosProvider: {}, + }, + profiles: []v1alpha1.DatadogAgentProfile{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "profile-1", + Namespace: "ns-1", + }, + }, + }, + wantDS: &appsv1.DaemonSetList{ + TypeMeta: metav1.TypeMeta{ + Kind: "DaemonSetList", + APIVersion: "apps/v1", + }, + }, + wantEDS: &edsdatadoghqv1alpha1.ExtendedDaemonSetList{ + TypeMeta: metav1.TypeMeta{ + Kind: "ExtendedDaemonSetList", + APIVersion: "datadoghq.com/v1alpha1", + }, + }, + }, + } + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + fakeClient := fake.NewClientBuilder().WithScheme(sch).WithObjects(tt.existingAgents...).Build() + logger := logf.Log.WithName("test_cleanupExtraneousDaemonSets") + eventBroadcaster := record.NewBroadcaster() + recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: "test_cleanupExtraneousDaemonSets"}) + + r := &Reconciler{ + client: fakeClient, + log: logger, + recorder: recorder, + options: ReconcilerOptions{ + IntrospectionEnabled: tt.introspectionEnabled, + DatadogAgentProfileEnabled: tt.profilesEnabled, + ExtendedDaemonsetOptions: agent.ExtendedDaemonsetOptions{ + Enabled: tt.edsEnabled, + }, }, } - dda := datadoghqv2alpha1.DatadogAgent{} + dda := datadoghqv2alpha1.DatadogAgent{ + TypeMeta: metav1.TypeMeta{ + Kind: "DatadogAgent", + APIVersion: "datadoghq.com/v2alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "dda-foo", + Namespace: "ns-1", + }, + } ddaStatus := datadoghqv2alpha1.DatadogAgentStatus{} - err := r.cleanupDaemonSetsForProvidersThatNoLongerApply(ctx, &dda, &ddaStatus, tt.existingProviders) + err := r.cleanupExtraneousDaemonSets(ctx, logger, &dda, &ddaStatus, tt.providerList, tt.profiles) assert.NoError(t, err) - kind := "daemonsets" - if tt.edsEnabled { - kind = "extendeddaemonsets" - } - objList := getObjectListFromKind(kind) - err = fakeClient.List(ctx, objList) + dsList := &appsv1.DaemonSetList{} + edsList := &edsdatadoghqv1alpha1.ExtendedDaemonSetList{} + + err = fakeClient.List(ctx, dsList) + assert.NoError(t, err) + err = fakeClient.List(ctx, edsList) assert.NoError(t, err) - if tt.edsEnabled { - assert.Equal(t, tt.wantEDS, objList) - } else { - assert.Equal(t, tt.wantDS, objList) - } + assert.Equal(t, tt.wantDS, dsList) + assert.Equal(t, tt.wantEDS, edsList) }) } } -func getObjectListFromKind(kind string) client.ObjectList { - switch kind { - case "daemonsets": - return &appsv1.DaemonSetList{} - case "extendeddaemonsets": - return &edsdatadoghqv1alpha1.ExtendedDaemonSetList{} - } - return nil -} - func Test_removeStaleStatus(t *testing.T) { testCases := []struct { name string diff --git a/controllers/datadogagent/controller_reconcile_v2.go b/controllers/datadogagent/controller_reconcile_v2.go index 46af25b34..e82a172b6 100644 --- a/controllers/datadogagent/controller_reconcile_v2.go +++ b/controllers/datadogagent/controller_reconcile_v2.go @@ -157,10 +157,7 @@ func (r *Reconciler) reconcileInstanceV2(ctx context.Context, logger logr.Logger } if r.options.IntrospectionEnabled { - providerList, err = r.handleProviders(ctx, instance, newStatus, nodeList, logger) - if err != nil { - errs = append(errs, err) - } + providerList = kubernetes.GetProviderListFromNodeList(nodeList, logger) } if r.options.DatadogAgentProfileEnabled { @@ -170,7 +167,7 @@ func (r *Reconciler) reconcileInstanceV2(ctx context.Context, logger logr.Logger } profiles = profileList - if err = r.handleProfiles(ctx, profiles, profilesByNode, instance.Namespace, providerList); err != nil { + if err = r.handleProfiles(ctx, profilesByNode, instance.Namespace); err != nil { return reconcile.Result{}, err } } @@ -185,6 +182,10 @@ func (r *Reconciler) reconcileInstanceV2(ctx context.Context, logger logr.Logger } } + if err = r.cleanupExtraneousDaemonSets(ctx, logger, instance, newStatus, providerList, profiles); err != nil { + logger.Error(err, "Error cleaning up old DaemonSets") + } + result, err = r.reconcileV2ClusterChecksRunner(logger, requiredComponents, features, instance, resourceManagers, newStatus) if utils.ShouldReturn(result, err) { return r.updateStatusIfNeededV2(logger, instance, newStatus, result, err)