Skip to content

Commit 93d630e

Browse files
committed
:Add functionality to update and manage API secret hash in DatadogAgent
1 parent 35b5bc9 commit 93d630e

File tree

2 files changed

+210
-0
lines changed

2 files changed

+210
-0
lines changed

internal/controller/datadogagent/controller_reconcile_v2.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ package datadogagent
77

88
import (
99
"context"
10+
"crypto/sha256"
11+
"encoding/hex"
1012
"fmt"
1113
"time"
1214

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

100+
func (r *Reconciler) updateSecretHash(ctx context.Context, instance *datadoghqv2alpha1.DatadogAgent) {
101+
// Check if a secret is configured
102+
if instance.Spec.Global.Credentials != nil && instance.Spec.Global.Credentials.APISecret != nil {
103+
secret := &corev1.Secret{}
104+
err := r.client.Get(ctx, types.NamespacedName{
105+
Namespace: instance.Namespace,
106+
Name: instance.Spec.Global.Credentials.APISecret.SecretName,
107+
}, secret)
108+
if err != nil {
109+
r.log.Error(err, "Failed to get secret", "namespace", instance.Namespace, "secretName", instance.Spec.Global.Credentials.APISecret.SecretName)
110+
// Remove the API_SECRET_HASH env variable if the secret can't be found
111+
r.removeSecretHashEnv(instance)
112+
return
113+
}
114+
115+
// Compute the hash of the secret
116+
hash := sha256.New()
117+
for _, value := range secret.Data {
118+
hash.Write(value)
119+
}
120+
secretHash := hex.EncodeToString(hash.Sum(nil))
121+
122+
// Append the hash to spec.global.env
123+
instance.Spec.Global.Env = append(instance.Spec.Global.Env, corev1.EnvVar{
124+
Name: "API_SECRET_HASH",
125+
Value: secretHash,
126+
})
127+
} else {
128+
// Remove the API_SECRET_HASH env variable if no APISecret is configured
129+
r.removeSecretHashEnv(instance)
130+
}
131+
}
132+
133+
func (r *Reconciler) removeSecretHashEnv(instance *datadoghqv2alpha1.DatadogAgent) {
134+
var newEnv []corev1.EnvVar
135+
for _, envVar := range instance.Spec.Global.Env {
136+
if envVar.Name != "API_SECRET_HASH" {
137+
newEnv = append(newEnv, envVar)
138+
}
139+
}
140+
instance.Spec.Global.Env = newEnv
141+
}
142+
98143
func (r *Reconciler) reconcileInstanceV2(ctx context.Context, logger logr.Logger, instance *datadoghqv2alpha1.DatadogAgent) (reconcile.Result, error) {
99144
var result reconcile.Result
100145
newStatus := instance.Status.DeepCopy()
101146
now := metav1.NewTime(time.Now())
102147
features, requiredComponents := feature.BuildFeatures(instance, reconcilerOptionsToFeatureOptions(&r.options, logger))
103148
// update list of enabled features for metrics forwarder
104149
r.updateMetricsForwardersFeatures(instance, features)
150+
// update the secret hash in the instance
151+
r.updateSecretHash(ctx, instance)
105152

106153
// -----------------------
107154
// Manage dependencies

internal/controller/datadogagent/controller_reconcile_v2_test.go

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package datadogagent
22

33
import (
44
"context"
5+
"crypto/sha256"
6+
"encoding/hex"
57
"fmt"
68
"testing"
79
"time"
@@ -650,3 +652,164 @@ func defaultProfile() v1alpha1.DatadogAgentProfile {
650652
},
651653
}
652654
}
655+
656+
func Test_updateSecretHash(t *testing.T) {
657+
sch := runtime.NewScheme()
658+
_ = scheme.AddToScheme(sch)
659+
_ = v1alpha1.AddToScheme(sch)
660+
_ = v2alpha1.AddToScheme(sch)
661+
662+
tests := []struct {
663+
name string
664+
dda *v2alpha1.DatadogAgent
665+
secret *corev1.Secret
666+
expectedEnv string
667+
expectedValue string
668+
}{
669+
{
670+
name: "API key secret present",
671+
dda: &v2alpha1.DatadogAgent{
672+
ObjectMeta: metav1.ObjectMeta{
673+
Name: "test-dda",
674+
Namespace: "default",
675+
},
676+
Spec: v2alpha1.DatadogAgentSpec{
677+
Global: &v2alpha1.GlobalConfig{
678+
Credentials: &v2alpha1.DatadogCredentials{
679+
APISecret: &v2alpha1.SecretConfig{
680+
SecretName: "test-secret",
681+
},
682+
},
683+
},
684+
},
685+
},
686+
secret: &corev1.Secret{
687+
ObjectMeta: metav1.ObjectMeta{
688+
Name: "test-secret",
689+
Namespace: "default",
690+
},
691+
Data: map[string][]byte{
692+
"api-key": []byte("test-api-key"),
693+
},
694+
},
695+
expectedEnv: "API_SECRET_HASH",
696+
expectedValue: "test-api-key",
697+
},
698+
{
699+
name: "API key secret not present",
700+
dda: &v2alpha1.DatadogAgent{
701+
ObjectMeta: metav1.ObjectMeta{
702+
Name: "test-dda",
703+
Namespace: "default",
704+
},
705+
Spec: v2alpha1.DatadogAgentSpec{
706+
Global: &v2alpha1.GlobalConfig{
707+
Credentials: &v2alpha1.DatadogCredentials{},
708+
},
709+
},
710+
},
711+
secret: nil,
712+
expectedEnv: "",
713+
expectedValue: "",
714+
},
715+
{
716+
name: "API key secret was used but not anymore",
717+
dda: &v2alpha1.DatadogAgent{
718+
ObjectMeta: metav1.ObjectMeta{
719+
Name: "test-dda",
720+
Namespace: "default",
721+
},
722+
Spec: v2alpha1.DatadogAgentSpec{
723+
Global: &v2alpha1.GlobalConfig{
724+
Credentials: &v2alpha1.DatadogCredentials{},
725+
Env: []corev1.EnvVar{
726+
{
727+
Name: "API_SECRET_HASH",
728+
Value: "old-hash",
729+
},
730+
},
731+
},
732+
},
733+
},
734+
secret: nil,
735+
expectedEnv: "",
736+
expectedValue: "",
737+
},
738+
{
739+
name: "API key wasn't used, but now is",
740+
dda: &v2alpha1.DatadogAgent{
741+
ObjectMeta: metav1.ObjectMeta{
742+
Name: "test-dda",
743+
Namespace: "default",
744+
},
745+
Spec: v2alpha1.DatadogAgentSpec{
746+
Global: &v2alpha1.GlobalConfig{
747+
Credentials: &v2alpha1.DatadogCredentials{
748+
APISecret: &v2alpha1.SecretConfig{
749+
SecretName: "test-secret",
750+
},
751+
},
752+
},
753+
},
754+
},
755+
secret: &corev1.Secret{
756+
ObjectMeta: metav1.ObjectMeta{
757+
Name: "test-secret",
758+
Namespace: "default",
759+
},
760+
Data: map[string][]byte{
761+
"api-key": []byte("test-api-key"),
762+
},
763+
},
764+
expectedEnv: "API_SECRET_HASH",
765+
expectedValue: "test-api-key",
766+
},
767+
}
768+
769+
for _, tt := range tests {
770+
t.Run(tt.name, func(t *testing.T) {
771+
var objs []client.Object
772+
objs = append(objs, tt.dda)
773+
if tt.secret != nil {
774+
objs = append(objs, tt.secret)
775+
}
776+
777+
client := fake.NewClientBuilder().WithScheme(sch).WithObjects(objs...).Build()
778+
reconciler := &Reconciler{
779+
client: client,
780+
log: logf.Log.WithName("Test_updateSecretHash"),
781+
options: ReconcilerOptions{
782+
DatadogAgentProfileEnabled: true,
783+
},
784+
}
785+
786+
// Call the updateSecretHash function
787+
reconciler.updateSecretHash(context.Background(), tt.dda)
788+
789+
// Verify that the secret hash was appended to spec.global.env if secret is present
790+
if tt.secret != nil {
791+
expectedHash := sha256.New()
792+
expectedHash.Write([]byte(tt.expectedValue))
793+
secretHash := hex.EncodeToString(expectedHash.Sum(nil))
794+
795+
found := false
796+
for _, envVar := range tt.dda.Spec.Global.Env {
797+
if envVar.Name == tt.expectedEnv && envVar.Value == secretHash {
798+
found = true
799+
break
800+
}
801+
}
802+
assert.True(t, found, fmt.Sprintf("%s not found in spec.global.env", tt.expectedEnv))
803+
} else {
804+
found := false
805+
for _, envVar := range tt.dda.Spec.Global.Env {
806+
if envVar.Name == "API_SECRET_HASH" {
807+
found = true
808+
break
809+
}
810+
}
811+
assert.False(t, found, "API_SECRET_HASH should not be present in spec.global.env")
812+
}
813+
})
814+
}
815+
}

0 commit comments

Comments
 (0)