From 5d5b7a79024e00221d7c5eda781817e64a730ffa Mon Sep 17 00:00:00 2001 From: Andrii Soldatenko Date: Tue, 18 Feb 2025 09:25:14 +0100 Subject: [PATCH 1/3] init --- .../dynakube/telemetryservice_props.go | 5 + .../dynakube/otelc/secret/conditions.go | 5 + .../dynakube/otelc/secret/config.go | 7 ++ .../dynakube/otelc/secret/reconciler.go | 105 ++++++++++++++++++ .../dynakube/otelc/secret/reconciler_test.go | 47 ++++++++ 5 files changed, 169 insertions(+) create mode 100644 pkg/controllers/dynakube/otelc/secret/conditions.go create mode 100644 pkg/controllers/dynakube/otelc/secret/config.go create mode 100644 pkg/controllers/dynakube/otelc/secret/reconciler.go create mode 100644 pkg/controllers/dynakube/otelc/secret/reconciler_test.go diff --git a/pkg/api/v1beta3/dynakube/telemetryservice_props.go b/pkg/api/v1beta3/dynakube/telemetryservice_props.go index 11fa60e724..152c7038e4 100644 --- a/pkg/api/v1beta3/dynakube/telemetryservice_props.go +++ b/pkg/api/v1beta3/dynakube/telemetryservice_props.go @@ -2,6 +2,7 @@ package dynakube import ( "github.com/Dynatrace/dynatrace-operator/pkg/api/v1beta3/dynakube/telemetryservice" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func (dk *DynaKube) TelemetryService() *telemetryservice.TelemetryService { @@ -12,3 +13,7 @@ func (dk *DynaKube) TelemetryService() *telemetryservice.TelemetryService { return ts } + +func (dk *DynaKube) TelemetryApiCredentialsSecretName() *metav1.LabelSelector { + return &dk.Spec.MetadataEnrichment.NamespaceSelector +} diff --git a/pkg/controllers/dynakube/otelc/secret/conditions.go b/pkg/controllers/dynakube/otelc/secret/conditions.go new file mode 100644 index 0000000000..be6add7272 --- /dev/null +++ b/pkg/controllers/dynakube/otelc/secret/conditions.go @@ -0,0 +1,5 @@ +package secret + +const ( + secretConditionType = "TelemetryApiCredentialsSecretCondition" +) diff --git a/pkg/controllers/dynakube/otelc/secret/config.go b/pkg/controllers/dynakube/otelc/secret/config.go new file mode 100644 index 0000000000..601416bdcd --- /dev/null +++ b/pkg/controllers/dynakube/otelc/secret/config.go @@ -0,0 +1,7 @@ +package secret + +import "github.com/Dynatrace/dynatrace-operator/pkg/logd" + +var ( + log = logd.Get().WithName("open-signal-api-credentials-secret") +) diff --git a/pkg/controllers/dynakube/otelc/secret/reconciler.go b/pkg/controllers/dynakube/otelc/secret/reconciler.go new file mode 100644 index 0000000000..e1ef547030 --- /dev/null +++ b/pkg/controllers/dynakube/otelc/secret/reconciler.go @@ -0,0 +1,105 @@ +package secret + +import ( + "context" + "fmt" + "github.com/Dynatrace/dynatrace-operator/pkg/util/conditions" + "github.com/Dynatrace/dynatrace-operator/pkg/util/hasher" + k8slabels "github.com/Dynatrace/dynatrace-operator/pkg/util/kubeobjects/labels" + k8ssecret "github.com/Dynatrace/dynatrace-operator/pkg/util/kubeobjects/secret" + corev1 "k8s.io/api/core/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/Dynatrace/dynatrace-operator/pkg/api/v1beta3/dynakube" +) + +const telemetryApiCredentialsSecretName = "dynatrace-telemetry-api-credentials" + +type Reconciler struct { + client client.Client + apiReader client.Reader + dk *dynakube.DynaKube +} + +type ReconcilerBuilder func(client client.Client, apiReader client.Reader, dk *dynakube.DynaKube) *Reconciler + +func NewReconciler(client client.Client, apiReader client.Reader, dk *dynakube.DynaKube) *Reconciler { + return &Reconciler{ + client: client, + dk: dk, + apiReader: apiReader, + } +} + +func (r *Reconciler) Reconcile(ctx context.Context) error { + return ensureOpenSignalAPISecret(ctx, r.client, r.apiReader, r.dk) +} + +func ensureOpenSignalAPISecret(ctx context.Context, client client.Client, apiReader client.Reader, dk *dynakube.DynaKube) error { + query := k8ssecret.Query(client, apiReader, log) + _, err := query.Get(ctx, types.NamespacedName{Name: telemetryApiCredentialsSecretName, Namespace: dk.Namespace}) + + if err != nil && k8serrors.IsNotFound(err) { + log.Info("creating new secret for telemetry api credentials") + + secretConfig, err := generateTelemetryApiCredentialsSecret(telemetryApiCredentialsSecretName, dk) + + if err != nil { + conditions.SetSecretGenFailed(dk.Conditions(), secretConditionType, err) + + return err + } + + _, err = hasher.GenerateHash(secretConfig.Data) + if err != nil { + conditions.SetSecretGenFailed(dk.Conditions(), secretConditionType, err) + + return err + } + + err = query.Create(ctx, secretConfig) + if err != nil { + log.Info("could not create secret for telemetry api credentials", "name", secretConfig.Name) + conditions.SetKubeApiError(dk.Conditions(), secretConditionType, err) + + return err + } + + //dk.TokenSecretHash = tokenHash + conditions.SetSecretCreated(dk.Conditions(), secretConditionType, telemetryApiCredentialsSecretName) + } + + return nil +} + +func generateTelemetryApiCredentialsSecret(name string, dk *dynakube.DynaKube) (secret *corev1.Secret, err error) { + secretData := make(map[string][]byte) + // TODO: read api token from secret + // dk.Tokens() + secretData["DT_API_TOKEN"] = []byte("") + + tenantUUID, err := dk.TenantUUID() + if err != nil { + return nil, err + } + + if dk.ActiveGate().IsApiEnabled() { + secretData["DT_ENDPOINT"] = []byte(fmt.Sprintf("https://%s-activegate.dynatrace.svc/e/%s/api/v2/otlp", dk.Name, tenantUUID)) + } else { + secretData["DT_ENDPOINT"] = []byte(fmt.Sprintf("https://%s.dev.dynatracelabs.com/api/v2/otlp", tenantUUID)) + } + + secretConfig, err := k8ssecret.Build(dk, + name, + secretData, + k8ssecret.SetLabels(k8slabels.NewCoreLabels(dk.Name, k8slabels.OtelCComponentLabel).BuildLabels()), + ) + + if err != nil { + return nil, err + } + + return secretConfig, nil +} diff --git a/pkg/controllers/dynakube/otelc/secret/reconciler_test.go b/pkg/controllers/dynakube/otelc/secret/reconciler_test.go new file mode 100644 index 0000000000..40177e0609 --- /dev/null +++ b/pkg/controllers/dynakube/otelc/secret/reconciler_test.go @@ -0,0 +1,47 @@ +package secret + +import ( + "context" + "github.com/Dynatrace/dynatrace-operator/pkg/api/v1beta3/dynakube" + "github.com/Dynatrace/dynatrace-operator/pkg/util/conditions" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + "testing" +) + +func TestSecretCreation(t *testing.T) { + ctx := context.Background() + + t.Run("creates secret if it does not exist", func(t *testing.T) { + clt := fake.NewFakeClient() + + dk := createDynaKube() + + err := ensureOpenSignalAPISecret(ctx, clt, clt, &dk) + require.NoError(t, err) + + var secret corev1.Secret + err = clt.Get(ctx, types.NamespacedName{Name: telemetryApiCredentialsSecretName, Namespace: dk.Namespace}, &secret) + require.NoError(t, err) + assert.NotEmpty(t, secret) + require.NotNil(t, meta.FindStatusCondition(*dk.Conditions(), secretConditionType)) + assert.Equal(t, conditions.SecretCreatedReason, meta.FindStatusCondition(*dk.Conditions(), secretConditionType).Reason) + }) + +} + +func createDynaKube() dynakube.DynaKube { + dk := dynakube.DynaKube{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-dk", + }, + Spec: dynakube.DynaKubeSpec{}, + } + + return dk +} From e22a1ffb567a0c454a511781d45cb981adbd79f7 Mon Sep 17 00:00:00 2001 From: Andrii Soldatenko Date: Tue, 25 Feb 2025 08:58:47 +0100 Subject: [PATCH 2/3] add tests --- pkg/api/v1beta3/dynakube/dynakube_props.go | 2 +- pkg/controllers/dynakube/otelc/reconciler.go | 8 ++ .../dynakube/otelc/secret/reconciler.go | 102 ++++++++++++++---- .../dynakube/otelc/secret/reconciler_test.go | 78 ++++++++++++-- 4 files changed, 158 insertions(+), 32 deletions(-) diff --git a/pkg/api/v1beta3/dynakube/dynakube_props.go b/pkg/api/v1beta3/dynakube/dynakube_props.go index 66b73ad319..f9efab2903 100644 --- a/pkg/api/v1beta3/dynakube/dynakube_props.go +++ b/pkg/api/v1beta3/dynakube/dynakube_props.go @@ -70,7 +70,7 @@ func (dk *DynaKube) ImagePullSecretReferences() []corev1.LocalObjectReference { return imagePullSecrets } -// Tokens returns the name of the Secret to be used for tokens. +// Tokens return the name of the Secret to be used for tokens. func (dk *DynaKube) Tokens() string { if tkns := dk.Spec.Tokens; tkns != "" { return tkns diff --git a/pkg/controllers/dynakube/otelc/reconciler.go b/pkg/controllers/dynakube/otelc/reconciler.go index 4ae09df19a..716947ec62 100644 --- a/pkg/controllers/dynakube/otelc/reconciler.go +++ b/pkg/controllers/dynakube/otelc/reconciler.go @@ -5,6 +5,7 @@ import ( "github.com/Dynatrace/dynatrace-operator/pkg/api/v1beta3/dynakube" "github.com/Dynatrace/dynatrace-operator/pkg/controllers" + "github.com/Dynatrace/dynatrace-operator/pkg/controllers/dynakube/otelc/secret" "github.com/Dynatrace/dynatrace-operator/pkg/controllers/dynakube/otelc/service" "github.com/Dynatrace/dynatrace-operator/pkg/controllers/dynakube/otelc/statefulset" "sigs.k8s.io/controller-runtime/pkg/client" @@ -16,6 +17,7 @@ type Reconciler struct { dk *dynakube.DynaKube statefulsetReconciler controllers.Reconciler serviceReconciler *service.Reconciler + secretReconciler *secret.Reconciler } type ReconcilerBuilder func(client client.Client, apiReader client.Reader, dk *dynakube.DynaKube) controllers.Reconciler @@ -27,6 +29,7 @@ func NewReconciler(client client.Client, apiReader client.Reader, dk *dynakube.D dk: dk, statefulsetReconciler: statefulset.NewReconciler(client, apiReader, dk), serviceReconciler: service.NewReconciler(client, apiReader, dk), + secretReconciler: secret.NewReconciler(client, apiReader, dk), } } @@ -36,6 +39,11 @@ func (r *Reconciler) Reconcile(ctx context.Context) error { return err } + err = r.secretReconciler.Reconcile(ctx) + if err != nil { + return err + } + err = r.statefulsetReconciler.Reconcile(ctx) if err != nil { log.Info("failed to reconcile Dynatrace OTELc statefulset") diff --git a/pkg/controllers/dynakube/otelc/secret/reconciler.go b/pkg/controllers/dynakube/otelc/secret/reconciler.go index e1ef547030..5af52750ec 100644 --- a/pkg/controllers/dynakube/otelc/secret/reconciler.go +++ b/pkg/controllers/dynakube/otelc/secret/reconciler.go @@ -3,16 +3,21 @@ package secret import ( "context" "fmt" + + "github.com/Dynatrace/dynatrace-operator/pkg/api/v1beta3/dynakube" + dtclient "github.com/Dynatrace/dynatrace-operator/pkg/clients/dynatrace" + "github.com/Dynatrace/dynatrace-operator/pkg/controllers/dynakube/token" "github.com/Dynatrace/dynatrace-operator/pkg/util/conditions" "github.com/Dynatrace/dynatrace-operator/pkg/util/hasher" k8slabels "github.com/Dynatrace/dynatrace-operator/pkg/util/kubeobjects/labels" k8ssecret "github.com/Dynatrace/dynatrace-operator/pkg/util/kubeobjects/secret" + "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" - - "github.com/Dynatrace/dynatrace-operator/pkg/api/v1beta3/dynakube" ) const telemetryApiCredentialsSecretName = "dynatrace-telemetry-api-credentials" @@ -34,27 +39,31 @@ func NewReconciler(client client.Client, apiReader client.Reader, dk *dynakube.D } func (r *Reconciler) Reconcile(ctx context.Context) error { - return ensureOpenSignalAPISecret(ctx, r.client, r.apiReader, r.dk) + if r.dk.TelemetryService().IsEnabled() { + return r.ensureOpenSignalAPISecret(ctx) + } + + return r.removeOpenSignalAPISecret(ctx) } -func ensureOpenSignalAPISecret(ctx context.Context, client client.Client, apiReader client.Reader, dk *dynakube.DynaKube) error { - query := k8ssecret.Query(client, apiReader, log) - _, err := query.Get(ctx, types.NamespacedName{Name: telemetryApiCredentialsSecretName, Namespace: dk.Namespace}) +func (r *Reconciler) ensureOpenSignalAPISecret(ctx context.Context) error { + query := k8ssecret.Query(r.client, r.apiReader, log) + _, err := query.Get(ctx, types.NamespacedName{Name: telemetryApiCredentialsSecretName, Namespace: r.dk.Namespace}) if err != nil && k8serrors.IsNotFound(err) { log.Info("creating new secret for telemetry api credentials") - secretConfig, err := generateTelemetryApiCredentialsSecret(telemetryApiCredentialsSecretName, dk) + secretConfig, err := r.generateTelemetryApiCredentialsSecret(ctx, telemetryApiCredentialsSecretName) if err != nil { - conditions.SetSecretGenFailed(dk.Conditions(), secretConditionType, err) + conditions.SetSecretGenFailed(r.dk.Conditions(), secretConditionType, err) return err } _, err = hasher.GenerateHash(secretConfig.Data) if err != nil { - conditions.SetSecretGenFailed(dk.Conditions(), secretConditionType, err) + conditions.SetSecretGenFailed(r.dk.Conditions(), secretConditionType, err) return err } @@ -62,39 +71,67 @@ func ensureOpenSignalAPISecret(ctx context.Context, client client.Client, apiRea err = query.Create(ctx, secretConfig) if err != nil { log.Info("could not create secret for telemetry api credentials", "name", secretConfig.Name) - conditions.SetKubeApiError(dk.Conditions(), secretConditionType, err) + conditions.SetKubeApiError(r.dk.Conditions(), secretConditionType, err) return err } - //dk.TokenSecretHash = tokenHash - conditions.SetSecretCreated(dk.Conditions(), secretConditionType, telemetryApiCredentialsSecretName) + conditions.SetSecretCreated(r.dk.Conditions(), secretConditionType, telemetryApiCredentialsSecretName) } return nil } -func generateTelemetryApiCredentialsSecret(name string, dk *dynakube.DynaKube) (secret *corev1.Secret, err error) { +func (r *Reconciler) getApiToken(ctx context.Context) ([]byte, error) { + tokenReader := token.NewReader(r.apiReader, r.dk) + + tokens, err := tokenReader.ReadTokens(ctx) + if err != nil { + return nil, errors.Wrapf(err, "'%s:%s' secret is missing or invalid", r.dk.Namespace, r.dk.Tokens()) + } + + apiToken, hasApiToken := tokens[dtclient.ApiToken] + if !hasApiToken { + return nil, errors.New(fmt.Sprintf("'%s' token is missing in '%s:%s' secret", dtclient.ApiToken, r.dk.Namespace, r.dk.Tokens())) + } + + return []byte(apiToken.Value), nil +} + +func (r *Reconciler) getDtEndpoint() ([]byte, error) { + tenantUUID, err := r.dk.TenantUUID() + if err != nil { + return nil, err + } + + if r.dk.ActiveGate().IsApiEnabled() { + return []byte(fmt.Sprintf("https://%s-activegate.dynatrace.svc/e/%s/api/v2/otlp", r.dk.Name, tenantUUID)), nil + } + + return []byte(fmt.Sprintf("https://%s.dev.dynatracelabs.com/api/v2/otlp", tenantUUID)), nil +} + +func (r *Reconciler) generateTelemetryApiCredentialsSecret(ctx context.Context, name string) (secret *corev1.Secret, err error) { secretData := make(map[string][]byte) - // TODO: read api token from secret - // dk.Tokens() - secretData["DT_API_TOKEN"] = []byte("") - tenantUUID, err := dk.TenantUUID() + apiToken, err := r.getApiToken(ctx) if err != nil { return nil, err } - if dk.ActiveGate().IsApiEnabled() { - secretData["DT_ENDPOINT"] = []byte(fmt.Sprintf("https://%s-activegate.dynatrace.svc/e/%s/api/v2/otlp", dk.Name, tenantUUID)) - } else { - secretData["DT_ENDPOINT"] = []byte(fmt.Sprintf("https://%s.dev.dynatracelabs.com/api/v2/otlp", tenantUUID)) + secretData["DT_API_TOKEN"] = apiToken + + dtEndpoint, err := r.getDtEndpoint() + if err != nil { + return nil, err } - secretConfig, err := k8ssecret.Build(dk, + secretData["DT_ENDPOINT"] = dtEndpoint + + secretConfig, err := k8ssecret.Build(r.dk, name, secretData, - k8ssecret.SetLabels(k8slabels.NewCoreLabels(dk.Name, k8slabels.OtelCComponentLabel).BuildLabels()), + k8ssecret.SetLabels(k8slabels.NewCoreLabels(r.dk.Name, k8slabels.OtelCComponentLabel).BuildLabels()), ) if err != nil { @@ -103,3 +140,22 @@ func generateTelemetryApiCredentialsSecret(name string, dk *dynakube.DynaKube) ( return secretConfig, nil } + +func (r *Reconciler) removeOpenSignalAPISecret(ctx context.Context) error { + if meta.FindStatusCondition(*r.dk.Conditions(), secretConditionType) == nil { + return nil // no condition == nothing is there to clean up + } + + query := k8ssecret.Query(r.client, r.apiReader, log) + err := query.Delete(ctx, &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: telemetryApiCredentialsSecretName, Namespace: r.dk.Namespace}}) + + if err != nil { + log.Info("could not delete apiCredential secret", "name", telemetryApiCredentialsSecretName) + + return err + } + + meta.RemoveStatusCondition(r.dk.Conditions(), secretConditionType) + + return nil +} diff --git a/pkg/controllers/dynakube/otelc/secret/reconciler_test.go b/pkg/controllers/dynakube/otelc/secret/reconciler_test.go index 40177e0609..cedac5feec 100644 --- a/pkg/controllers/dynakube/otelc/secret/reconciler_test.go +++ b/pkg/controllers/dynakube/otelc/secret/reconciler_test.go @@ -2,45 +2,107 @@ package secret import ( "context" + "testing" + + schemeFake "github.com/Dynatrace/dynatrace-operator/pkg/api/scheme/fake" + "github.com/Dynatrace/dynatrace-operator/pkg/api/shared/communication" + "github.com/Dynatrace/dynatrace-operator/pkg/api/status" "github.com/Dynatrace/dynatrace-operator/pkg/api/v1beta3/dynakube" + "github.com/Dynatrace/dynatrace-operator/pkg/api/v1beta3/dynakube/activegate" + "github.com/Dynatrace/dynatrace-operator/pkg/api/v1beta3/dynakube/telemetryservice" + dtclient "github.com/Dynatrace/dynatrace-operator/pkg/clients/dynatrace" "github.com/Dynatrace/dynatrace-operator/pkg/util/conditions" + "github.com/Dynatrace/dynatrace-operator/pkg/util/kubeobjects/secret" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" - "testing" +) + +const ( + testApiToken = "apiTokenValue" + testTenantUUID = "abc12345" + testKubeSystemUUID = "12345" ) func TestSecretCreation(t *testing.T) { ctx := context.Background() t.Run("creates secret if it does not exist", func(t *testing.T) { - clt := fake.NewFakeClient() + dk := createDynaKube(true) - dk := createDynaKube() + testSecret, err := secret.Build(&dk, dk.Name, map[string][]byte{ + dtclient.ApiToken: []byte(testApiToken), + }) + require.NoError(t, err) + + clt := fake.NewFakeClient(testSecret) - err := ensureOpenSignalAPISecret(ctx, clt, clt, &dk) + r := NewReconciler(clt, clt, &dk) + + err = r.ensureOpenSignalAPISecret(ctx) require.NoError(t, err) - var secret corev1.Secret - err = clt.Get(ctx, types.NamespacedName{Name: telemetryApiCredentialsSecretName, Namespace: dk.Namespace}, &secret) + var apiCredsSecret corev1.Secret + err = clt.Get(ctx, types.NamespacedName{Name: telemetryApiCredentialsSecretName, Namespace: dk.Namespace}, &apiCredsSecret) require.NoError(t, err) - assert.NotEmpty(t, secret) + assert.NotEmpty(t, apiCredsSecret) require.NotNil(t, meta.FindStatusCondition(*dk.Conditions(), secretConditionType)) assert.Equal(t, conditions.SecretCreatedReason, meta.FindStatusCondition(*dk.Conditions(), secretConditionType).Reason) }) + t.Run("removes secret if exists but we don't need it", func(t *testing.T) { + dk := createDynaKube(false) + conditions.SetSecretCreated(dk.Conditions(), secretConditionType, telemetryApiCredentialsSecretName) + + objs := []client.Object{ + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: telemetryApiCredentialsSecretName, + Namespace: dk.Namespace, + }, + }, + } + + clt := schemeFake.NewClient(objs...) + r := NewReconciler(clt, clt, &dk) + + err := r.Reconcile(ctx) + require.NoError(t, err) + + var apiTokenSecret corev1.Secret + err = clt.Get(ctx, types.NamespacedName{Name: telemetryApiCredentialsSecretName, Namespace: dk.Namespace}, &apiTokenSecret) + + require.Error(t, err) + assert.Empty(t, apiTokenSecret) + }) } -func createDynaKube() dynakube.DynaKube { +func createDynaKube(telemetryServiceEnabled bool) dynakube.DynaKube { dk := dynakube.DynaKube{ ObjectMeta: metav1.ObjectMeta{ Name: "test-dk", }, Spec: dynakube.DynaKubeSpec{}, + Status: dynakube.DynaKubeStatus{ + ActiveGate: activegate.Status{ + ConnectionInfo: communication.ConnectionInfo{ + TenantUUID: testTenantUUID, + }, + VersionStatus: status.VersionStatus{}, + }, + KubeSystemUUID: testKubeSystemUUID, + }, + } + + if telemetryServiceEnabled { + dk.TelemetryService().Spec = &telemetryservice.Spec{} + } else { + dk.TelemetryService().Spec = nil } return dk From 494b5045317569f3ca24a2e0b1aad2f85c8dd316 Mon Sep 17 00:00:00 2001 From: Andrii Soldatenko Date: Wed, 26 Feb 2025 13:33:09 +0100 Subject: [PATCH 3/3] add env var --- pkg/controllers/dynakube/otelc/consts/consts.go | 3 +++ pkg/controllers/dynakube/otelc/secret/reconciler.go | 13 ++++++------- .../dynakube/otelc/secret/reconciler_test.go | 9 +++++---- pkg/controllers/dynakube/otelc/statefulset/env.go | 8 ++++++++ 4 files changed, 22 insertions(+), 11 deletions(-) create mode 100644 pkg/controllers/dynakube/otelc/consts/consts.go diff --git a/pkg/controllers/dynakube/otelc/consts/consts.go b/pkg/controllers/dynakube/otelc/consts/consts.go new file mode 100644 index 0000000000..d4abfd4d44 --- /dev/null +++ b/pkg/controllers/dynakube/otelc/consts/consts.go @@ -0,0 +1,3 @@ +package consts + +const TelemetryApiCredentialsSecretName = "dynatrace-telemetry-api-credentials" diff --git a/pkg/controllers/dynakube/otelc/secret/reconciler.go b/pkg/controllers/dynakube/otelc/secret/reconciler.go index 5af52750ec..eead9b5fa4 100644 --- a/pkg/controllers/dynakube/otelc/secret/reconciler.go +++ b/pkg/controllers/dynakube/otelc/secret/reconciler.go @@ -6,6 +6,7 @@ import ( "github.com/Dynatrace/dynatrace-operator/pkg/api/v1beta3/dynakube" dtclient "github.com/Dynatrace/dynatrace-operator/pkg/clients/dynatrace" + "github.com/Dynatrace/dynatrace-operator/pkg/controllers/dynakube/otelc/consts" "github.com/Dynatrace/dynatrace-operator/pkg/controllers/dynakube/token" "github.com/Dynatrace/dynatrace-operator/pkg/util/conditions" "github.com/Dynatrace/dynatrace-operator/pkg/util/hasher" @@ -20,8 +21,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -const telemetryApiCredentialsSecretName = "dynatrace-telemetry-api-credentials" - type Reconciler struct { client client.Client apiReader client.Reader @@ -48,12 +47,12 @@ func (r *Reconciler) Reconcile(ctx context.Context) error { func (r *Reconciler) ensureOpenSignalAPISecret(ctx context.Context) error { query := k8ssecret.Query(r.client, r.apiReader, log) - _, err := query.Get(ctx, types.NamespacedName{Name: telemetryApiCredentialsSecretName, Namespace: r.dk.Namespace}) + _, err := query.Get(ctx, types.NamespacedName{Name: consts.TelemetryApiCredentialsSecretName, Namespace: r.dk.Namespace}) if err != nil && k8serrors.IsNotFound(err) { log.Info("creating new secret for telemetry api credentials") - secretConfig, err := r.generateTelemetryApiCredentialsSecret(ctx, telemetryApiCredentialsSecretName) + secretConfig, err := r.generateTelemetryApiCredentialsSecret(ctx, consts.TelemetryApiCredentialsSecretName) if err != nil { conditions.SetSecretGenFailed(r.dk.Conditions(), secretConditionType, err) @@ -76,7 +75,7 @@ func (r *Reconciler) ensureOpenSignalAPISecret(ctx context.Context) error { return err } - conditions.SetSecretCreated(r.dk.Conditions(), secretConditionType, telemetryApiCredentialsSecretName) + conditions.SetSecretCreated(r.dk.Conditions(), secretConditionType, consts.TelemetryApiCredentialsSecretName) } return nil @@ -147,10 +146,10 @@ func (r *Reconciler) removeOpenSignalAPISecret(ctx context.Context) error { } query := k8ssecret.Query(r.client, r.apiReader, log) - err := query.Delete(ctx, &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: telemetryApiCredentialsSecretName, Namespace: r.dk.Namespace}}) + err := query.Delete(ctx, &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: consts.TelemetryApiCredentialsSecretName, Namespace: r.dk.Namespace}}) if err != nil { - log.Info("could not delete apiCredential secret", "name", telemetryApiCredentialsSecretName) + log.Info("could not delete apiCredential secret", "name", consts.TelemetryApiCredentialsSecretName) return err } diff --git a/pkg/controllers/dynakube/otelc/secret/reconciler_test.go b/pkg/controllers/dynakube/otelc/secret/reconciler_test.go index cedac5feec..597c66087f 100644 --- a/pkg/controllers/dynakube/otelc/secret/reconciler_test.go +++ b/pkg/controllers/dynakube/otelc/secret/reconciler_test.go @@ -11,6 +11,7 @@ import ( "github.com/Dynatrace/dynatrace-operator/pkg/api/v1beta3/dynakube/activegate" "github.com/Dynatrace/dynatrace-operator/pkg/api/v1beta3/dynakube/telemetryservice" dtclient "github.com/Dynatrace/dynatrace-operator/pkg/clients/dynatrace" + "github.com/Dynatrace/dynatrace-operator/pkg/controllers/dynakube/otelc/consts" "github.com/Dynatrace/dynatrace-operator/pkg/util/conditions" "github.com/Dynatrace/dynatrace-operator/pkg/util/kubeobjects/secret" "github.com/stretchr/testify/assert" @@ -48,7 +49,7 @@ func TestSecretCreation(t *testing.T) { require.NoError(t, err) var apiCredsSecret corev1.Secret - err = clt.Get(ctx, types.NamespacedName{Name: telemetryApiCredentialsSecretName, Namespace: dk.Namespace}, &apiCredsSecret) + err = clt.Get(ctx, types.NamespacedName{Name: consts.TelemetryApiCredentialsSecretName, Namespace: dk.Namespace}, &apiCredsSecret) require.NoError(t, err) assert.NotEmpty(t, apiCredsSecret) require.NotNil(t, meta.FindStatusCondition(*dk.Conditions(), secretConditionType)) @@ -57,12 +58,12 @@ func TestSecretCreation(t *testing.T) { t.Run("removes secret if exists but we don't need it", func(t *testing.T) { dk := createDynaKube(false) - conditions.SetSecretCreated(dk.Conditions(), secretConditionType, telemetryApiCredentialsSecretName) + conditions.SetSecretCreated(dk.Conditions(), secretConditionType, consts.TelemetryApiCredentialsSecretName) objs := []client.Object{ &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: telemetryApiCredentialsSecretName, + Name: consts.TelemetryApiCredentialsSecretName, Namespace: dk.Namespace, }, }, @@ -75,7 +76,7 @@ func TestSecretCreation(t *testing.T) { require.NoError(t, err) var apiTokenSecret corev1.Secret - err = clt.Get(ctx, types.NamespacedName{Name: telemetryApiCredentialsSecretName, Namespace: dk.Namespace}, &apiTokenSecret) + err = clt.Get(ctx, types.NamespacedName{Name: consts.TelemetryApiCredentialsSecretName, Namespace: dk.Namespace}, &apiTokenSecret) require.Error(t, err) assert.Empty(t, apiTokenSecret) diff --git a/pkg/controllers/dynakube/otelc/statefulset/env.go b/pkg/controllers/dynakube/otelc/statefulset/env.go index 5277b59c8f..89413fc8b5 100644 --- a/pkg/controllers/dynakube/otelc/statefulset/env.go +++ b/pkg/controllers/dynakube/otelc/statefulset/env.go @@ -5,6 +5,7 @@ import ( "github.com/Dynatrace/dynatrace-operator/pkg/api/v1beta3/dynakube" "github.com/Dynatrace/dynatrace-operator/pkg/consts" + otelcConsts "github.com/Dynatrace/dynatrace-operator/pkg/controllers/dynakube/otelc/consts" corev1 "k8s.io/api/core/v1" ) @@ -27,6 +28,7 @@ const ( envK8sClusterName = "K8S_CLUSTER_NAME" envK8sClusterUid = "K8S_CLUSTER_UID" envDTentityK8sCluster = "DT_ENTITY_KUBERNETES_CLUSTER" + envDTendpoint = "DT_ENDPOINT" // certDirEnv is the environment variable that identifies which directory // to check for SSL certificate files. If set, this overrides the system default. // It is a colon separated list of directories. @@ -77,6 +79,12 @@ func getEnvs(dk *dynakube.DynaKube) []corev1.EnvVar { {Name: envK8sClusterName, Value: dk.Name}, {Name: envK8sClusterUid, Value: dk.Status.KubeSystemUUID}, {Name: envDTentityK8sCluster, Value: dk.Status.KubernetesClusterMEID}, + {Name: envDTendpoint, ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{Name: otelcConsts.TelemetryApiCredentialsSecretName}, + Key: envDTendpoint, + }, + }}, } if dk.Spec.TrustedCAs != "" { envs = append(envs, corev1.EnvVar{Name: envTrustedCAs, Value: trustedCAVolumePath})