Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions internal/controller/datadogagent/controller_reconcile_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ package datadogagent

import (
"context"
"crypto/sha256"
"encoding/hex"
"fmt"
"time"

Expand Down Expand Up @@ -95,13 +97,58 @@ func (r *Reconciler) internalReconcileV2(ctx context.Context, request reconcile.
return r.reconcileInstanceV2(ctx, reqLogger, instanceCopy)
}

func (r *Reconciler) updateSecretHash(ctx context.Context, instance *datadoghqv2alpha1.DatadogAgent) {
// Check if a secret is configured
if instance.Spec.Global.Credentials != nil && instance.Spec.Global.Credentials.APISecret != nil {
secret := &corev1.Secret{}
err := r.client.Get(ctx, types.NamespacedName{
Namespace: instance.Namespace,
Name: instance.Spec.Global.Credentials.APISecret.SecretName,
}, secret)
if err != nil {
r.log.Error(err, "Failed to get secret", "namespace", instance.Namespace, "secretName", instance.Spec.Global.Credentials.APISecret.SecretName)
// Remove the API_SECRET_HASH env variable if the secret can't be found
r.removeSecretHashEnv(instance)
return
}

// Compute the hash of the secret
hash := sha256.New()
for _, value := range secret.Data {
hash.Write(value)
}
secretHash := hex.EncodeToString(hash.Sum(nil))

// Append the hash to spec.global.env
instance.Spec.Global.Env = append(instance.Spec.Global.Env, corev1.EnvVar{
Name: "API_SECRET_HASH",
Value: secretHash,
})
} else {
// Remove the API_SECRET_HASH env variable if no APISecret is configured
r.removeSecretHashEnv(instance)
}
}

func (r *Reconciler) removeSecretHashEnv(instance *datadoghqv2alpha1.DatadogAgent) {
var newEnv []corev1.EnvVar
for _, envVar := range instance.Spec.Global.Env {
if envVar.Name != "API_SECRET_HASH" {
newEnv = append(newEnv, envVar)
}
}
instance.Spec.Global.Env = newEnv
}

func (r *Reconciler) reconcileInstanceV2(ctx context.Context, logger logr.Logger, instance *datadoghqv2alpha1.DatadogAgent) (reconcile.Result, error) {
var result reconcile.Result
newStatus := instance.Status.DeepCopy()
now := metav1.NewTime(time.Now())
features, requiredComponents := feature.BuildFeatures(instance, reconcilerOptionsToFeatureOptions(&r.options, logger))
// update list of enabled features for metrics forwarder
r.updateMetricsForwardersFeatures(instance, features)
// update the secret hash in the instance
r.updateSecretHash(ctx, instance)

// -----------------------
// Manage dependencies
Expand Down
134 changes: 134 additions & 0 deletions internal/controller/datadogagent/controller_reconcile_v2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package datadogagent

import (
"context"
"crypto/sha256"
"encoding/hex"
"fmt"
"testing"
"time"
Expand Down Expand Up @@ -650,3 +652,135 @@ func defaultProfile() v1alpha1.DatadogAgentProfile {
},
}
}

func Test_updateSecretHash(t *testing.T) {
sch := runtime.NewScheme()
_ = scheme.AddToScheme(sch)
_ = v1alpha1.AddToScheme(sch)
_ = v2alpha1.AddToScheme(sch)
const agentName = "test-dda"
const secretName = "test-secret"

tests := []struct {
name string
dda *v2alpha1.DatadogAgent
secret *corev1.Secret
expectedEnv string
expectedValue string
}{
{
name: "API key secret present",
dda: &v2alpha1.DatadogAgent{
ObjectMeta: metav1.ObjectMeta{
Name: agentName,
Namespace: "default",
},
Spec: v2alpha1.DatadogAgentSpec{
Global: &v2alpha1.GlobalConfig{
Credentials: &v2alpha1.DatadogCredentials{
APISecret: &v2alpha1.SecretConfig{
SecretName: secretName,
},
},
},
},
},
secret: &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
Namespace: "default",
},
Data: map[string][]byte{
"api-key": []byte("test-api-key"),
},
},
expectedEnv: "API_SECRET_HASH",
expectedValue: "test-api-key",
},
{
name: "API key secret not present",
dda: &v2alpha1.DatadogAgent{
ObjectMeta: metav1.ObjectMeta{
Name: agentName,
Namespace: "default",
},
Spec: v2alpha1.DatadogAgentSpec{
Global: &v2alpha1.GlobalConfig{
Credentials: &v2alpha1.DatadogCredentials{},
},
},
},
secret: nil,
expectedEnv: "",
expectedValue: "",
},
{
name: "API key secret present but Secret does not exist",
dda: &v2alpha1.DatadogAgent{
ObjectMeta: metav1.ObjectMeta{
Name: agentName,
Namespace: "default",
},
Spec: v2alpha1.DatadogAgentSpec{
Global: &v2alpha1.GlobalConfig{
Credentials: &v2alpha1.DatadogCredentials{
APISecret: &v2alpha1.SecretConfig{
SecretName: "test-secret",
},
},
},
},
},
secret: nil,
expectedEnv: "",
expectedValue: "",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var objs []client.Object
objs = append(objs, tt.dda)
if tt.secret != nil {
objs = append(objs, tt.secret)
}

client := fake.NewClientBuilder().WithScheme(sch).WithObjects(objs...).Build()
reconciler := &Reconciler{
client: client,
log: logf.Log.WithName("Test_updateSecretHash"),
options: ReconcilerOptions{
DatadogAgentProfileEnabled: true,
},
}

// Call the updateSecretHash function
reconciler.updateSecretHash(context.Background(), tt.dda)

// Verify that the secret hash was appended to spec.global.env if secret is present
if tt.secret != nil {
expectedHash := sha256.New()
expectedHash.Write([]byte(tt.expectedValue))
secretHash := hex.EncodeToString(expectedHash.Sum(nil))

found := false
for _, envVar := range tt.dda.Spec.Global.Env {
if envVar.Name == tt.expectedEnv && envVar.Value == secretHash {
found = true
break
}
}
assert.True(t, found, fmt.Sprintf("%s not found in spec.global.env", tt.expectedEnv))
} else {
found := false
for _, envVar := range tt.dda.Spec.Global.Env {
if envVar.Name == "API_SECRET_HASH" {
found = true
break
}
}
assert.False(t, found, "API_SECRET_HASH should not be present in spec.global.env")
}
})
}
}
Loading