Skip to content

Commit

Permalink
[datadogagent/finalizer] Delete dependencies in V2
Browse files Browse the repository at this point in the history
  • Loading branch information
davidor committed Jul 27, 2022
1 parent d3c8099 commit 4be3dd2
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 15 deletions.
12 changes: 0 additions & 12 deletions controllers/datadogagent/controller_reconcile_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,10 @@ import (
"time"

"github.com/go-logr/logr"

apiequality "k8s.io/apimachinery/pkg/api/equality"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/errors"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

datadoghqv2alpha1 "github.com/DataDog/datadog-operator/apis/datadoghq/v2alpha1"
Expand Down Expand Up @@ -191,13 +189,3 @@ func (r *Reconciler) updateStatusIfNeededV2(logger logr.Logger, agentdeployment

return result, currentError
}

func (r *Reconciler) finalizeDadV2(reqLogger logr.Logger, obj client.Object) {
dda := obj.(*datadoghqv2alpha1.DatadogAgent)

if r.options.OperatorMetricsEnabled {
r.forwarders.Unregister(dda)
}

reqLogger.Info("Successfully finalized DatadogAgent")
}
56 changes: 56 additions & 0 deletions controllers/datadogagent/finalizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import (
"context"

datadoghqv1alpha1 "github.com/DataDog/datadog-operator/apis/datadoghq/v1alpha1"
datadoghqv2alpha1 "github.com/DataDog/datadog-operator/apis/datadoghq/v2alpha1"
"github.com/DataDog/datadog-operator/controllers/datadogagent/dependencies"
"github.com/DataDog/datadog-operator/controllers/datadogagent/feature"
"github.com/DataDog/datadog-operator/controllers/datadogagent/override"
"github.com/DataDog/datadog-operator/pkg/controller/utils"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
Expand Down Expand Up @@ -76,6 +80,58 @@ func (r *Reconciler) finalizeDadV1(reqLogger logr.Logger, obj client.Object) {
reqLogger.Info("Successfully finalized DatadogAgent")
}

func (r *Reconciler) finalizeDadV2(reqLogger logr.Logger, obj client.Object) {
// We need to apply the defaults to be able to delete the resources
// associated with those defaults.
dda := obj.(*datadoghqv2alpha1.DatadogAgent).DeepCopy()
datadoghqv2alpha1.DefaultDatadogAgent(dda)

if r.options.OperatorMetricsEnabled {
r.forwarders.Unregister(dda)
}

// To delete the resources associated with the DatadogAgent that we need to
// delete, we figure out its dependencies, store them in the dependencies
// store, and then call the DeleteAll function of the store.

features, requiredComponents := feature.BuildFeatures(
dda, reconcilerOptionsToFeatureOptions(&r.options, reqLogger))

storeOptions := &dependencies.StoreOptions{
SupportCilium: r.options.SupportCilium,
Logger: reqLogger,
Scheme: r.scheme,
}
depsStore := dependencies.NewStore(dda, storeOptions)
resourceManagers := feature.NewResourceManagers(depsStore)

var errs []error

// Set up dependencies required by enabled features
for _, feat := range features {
if featErr := feat.ManageDependencies(resourceManagers, requiredComponents); featErr != nil {
errs = append(errs, featErr)
}
}

// Examine user configuration to override any external dependencies (e.g. RBACs)
errs = append(errs, override.Dependencies(resourceManagers, dda.Spec.Override, dda.Namespace)...)

if len(errs) > 0 {
reqLogger.Info("Errors calculating dependencies while finalizing the DatadogAgent", "errors", errs)
}

deleteErrs := depsStore.DeleteAll(context.TODO(), r.client)

if len(deleteErrs) == 0 {
reqLogger.Info("Successfully finalized DatadogAgent")
} else {
for _, deleteErr := range deleteErrs {
reqLogger.Error(deleteErr, "Error deleting dependencies while finalizing the DatadogAgent")
}
}
}

func (r *Reconciler) addFinalizer(reqLogger logr.Logger, dda client.Object) error {
reqLogger.Info("Adding Finalizer for the DatadogAgent")
dda.SetFinalizers(append(dda.GetFinalizers(), datadogAgentFinalizer))
Expand Down
135 changes: 132 additions & 3 deletions controllers/datadogagent/finalizer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import (

datadoghqv1alpha1 "github.com/DataDog/datadog-operator/apis/datadoghq/v1alpha1"
"github.com/DataDog/datadog-operator/apis/datadoghq/v1alpha1/test"
datadoghqv2alpha1 "github.com/DataDog/datadog-operator/apis/datadoghq/v2alpha1"
"github.com/DataDog/datadog-operator/controllers/datadogagent/component/agent"
"github.com/DataDog/datadog-operator/controllers/datadogagent/component/clusteragent"
testutils "github.com/DataDog/datadog-operator/controllers/datadogagent/testutils"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
Expand All @@ -20,7 +24,7 @@ import (
logf "sigs.k8s.io/controller-runtime/pkg/log"
)

func Test_handleFinalizer(t *testing.T) {
func Test_handleFinalizer_V1(t *testing.T) {
// TODO: This tests that the associated cluster roles and cluster role
// bindings are deleted when the dda is marked to be deleted. However, the
// finalizer does more than that.
Expand All @@ -39,7 +43,7 @@ func Test_handleFinalizer(t *testing.T) {
for _, clusterRoleBinding := range clusterRoleBindings {
initialKubeObjects = append(initialKubeObjects, clusterRoleBinding)
}
reconciler := reconcilerForFinalizerTest(initialKubeObjects)
reconciler := reconcilerV1ForFinalizerTest(initialKubeObjects)

for _, resourceName := range rbacNamesForDda(dda, reconciler.versionInfo) {
clusterRoles = append(clusterRoles, buildClusterRole(dda, true, resourceName, ""))
Expand Down Expand Up @@ -70,7 +74,116 @@ func Test_handleFinalizer(t *testing.T) {
}
}

func reconcilerForFinalizerTest(initialKubeObjects []client.Object) Reconciler {
func Test_handleFinalizer_V2(t *testing.T) {
// This is not an exhaustive test. The finalizer should remove all the
// kubernetes resources associated with the Datadog Agent being removed, but
// to simplify a bit, this test doesn't check all the resources, it just
// checks a few ones (cluster roles, cluster role bindings).

now := metav1.Now()

dda := &datadoghqv2alpha1.DatadogAgent{
ObjectMeta: metav1.ObjectMeta{
Namespace: "foo",
Name: "bar",
Finalizers: []string{"finalizer.agent.datadoghq.com"},
},
}
dda.DeletionTimestamp = &now // Mark for deletion

initialKubeObjects := []client.Object{dda}

// These are some cluster roles that we know that the reconciler creates by
// default
existingClusterRoles := []*rbacv1.ClusterRole{
{
TypeMeta: metav1.TypeMeta{
Kind: clusterRoleKind,
APIVersion: rbacv1.SchemeGroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: agent.GetAgentRoleName(dda),
Labels: map[string]string{
"operator.datadoghq.com/managed-by-store": "true",
},
},
},
{
TypeMeta: metav1.TypeMeta{
Kind: clusterRoleKind,
APIVersion: rbacv1.SchemeGroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: clusteragent.GetClusterAgentName(dda),
Labels: map[string]string{
"operator.datadoghq.com/managed-by-store": "true",
},
},
},
}

// These are some cluster role bindings that we know that the reconciler
// creates by default
existingClusterRoleBindings := []*rbacv1.ClusterRoleBinding{
{
TypeMeta: metav1.TypeMeta{
Kind: clusterRoleBindingKind,
APIVersion: rbacv1.SchemeGroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: agent.GetAgentRoleName(dda), // Same name as the cluster role
Labels: map[string]string{
"operator.datadoghq.com/managed-by-store": "true",
},
},
},
{
TypeMeta: metav1.TypeMeta{
Kind: clusterRoleBindingKind,
APIVersion: rbacv1.SchemeGroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: clusteragent.GetClusterAgentName(dda),
Labels: map[string]string{
"operator.datadoghq.com/managed-by-store": "true",
},
},
},
}

for _, clusterRole := range existingClusterRoles {
initialKubeObjects = append(initialKubeObjects, clusterRole)
}

for _, clusterRoleBinding := range existingClusterRoleBindings {
initialKubeObjects = append(initialKubeObjects, clusterRoleBinding)
}

reconciler := reconcilerV2ForFinalizerTest(initialKubeObjects)

_, err := reconciler.handleFinalizer(logf.Log.WithName("Handle Finalizer V2 test"), dda, reconciler.finalizeDadV2)
assert.NoError(t, err)

// Check that the cluster roles associated with the Datadog Agent have been deleted
for _, clusterRole := range existingClusterRoles {
err = reconciler.client.Get(context.TODO(), types.NamespacedName{Name: clusterRole.Name}, &rbacv1.ClusterRole{})
assert.Error(t, err, fmt.Sprintf("ClusterRole %s not deleted", clusterRole.Name))
if err != nil {
assert.True(t, apierrors.IsNotFound(err), fmt.Sprintf("Unexpected error %s", err))
}
}

// Check that the cluster role bindings associated with the Datadog Agent have been deleted
for _, clusterRoleBinding := range existingClusterRoleBindings {
err = reconciler.client.Get(context.TODO(), types.NamespacedName{Name: clusterRoleBinding.Name}, &rbacv1.ClusterRoleBinding{})
assert.Error(t, err, fmt.Sprintf("ClusterRoleBinding %s not deleted", clusterRoleBinding.Name))
if err != nil {
assert.True(t, apierrors.IsNotFound(err), fmt.Sprintf("Unexpected error %s", err))
}
}
}

func reconcilerV1ForFinalizerTest(initialKubeObjects []client.Object) Reconciler {
reconcilerScheme := scheme.Scheme
reconcilerScheme.AddKnownTypes(rbacv1.SchemeGroupVersion, &rbacv1.ClusterRoleBinding{}, &rbacv1.ClusterRole{})
reconcilerScheme.AddKnownTypes(datadoghqv1alpha1.GroupVersion, &datadoghqv1alpha1.DatadogAgent{})
Expand All @@ -82,5 +195,21 @@ func reconcilerForFinalizerTest(initialKubeObjects []client.Object) Reconciler {
scheme: reconcilerScheme,
recorder: record.NewBroadcaster().NewRecorder(reconcilerScheme, corev1.EventSource{}),
forwarders: dummyManager{},
options: ReconcilerOptions{V2Enabled: false},
}
}

func reconcilerV2ForFinalizerTest(initialKubeObjects []client.Object) Reconciler {
s := testutils.TestScheme(true)

fakeClient := fake.NewClientBuilder().WithObjects(initialKubeObjects...).WithScheme(s).Build()

return Reconciler{
client: fakeClient,
scheme: s,
recorder: record.NewBroadcaster().NewRecorder(s, corev1.EventSource{}),
forwarders: dummyManager{},
options: ReconcilerOptions{V2Enabled: true},
log: logf.Log.WithName("reconciler_v2"),
}
}

0 comments on commit 4be3dd2

Please sign in to comment.