From 26f36773c590817741be151797ea78948f2f56f6 Mon Sep 17 00:00:00 2001 From: "adam.orcholski" Date: Fri, 22 Oct 2021 16:08:38 +0200 Subject: [PATCH 1/4] single data-ingest endpoint secret per namespace (namespace can not match two or more Dynakubes regarding app injection) data-ingest url and token also injected as environment variables init.sh secret is not recreated every few seconds --- .../common/operator/clusterrole-operator.yaml | 3 +- .../common/webhook/clusterrole-webhook.yaml | 1 + .../dataingestendpointsecret/secret.go | 114 ++++++++ .../dataingestendpointsecret/secret_test.go | 276 ++++++++++++++++++ controllers/dynakube/dynakube_controller.go | 7 + controllers/kubeobjects/secret.go | 17 +- controllers/kubeobjects/secret_test.go | 8 +- dtclient/client.go | 5 +- initgeneration/initgeneration.go | 11 +- initgeneration/initgeneration_test.go | 4 +- webhook/config.go | 3 + webhook/mutation/pod_mutator.go | 56 +++- webhook/mutation/pod_mutator_test.go | 218 +++++++++++++- 13 files changed, 690 insertions(+), 33 deletions(-) create mode 100644 controllers/dataingestendpointsecret/secret.go create mode 100644 controllers/dataingestendpointsecret/secret_test.go diff --git a/config/common/operator/clusterrole-operator.yaml b/config/common/operator/clusterrole-operator.yaml index 7f71d52dd0..64633a7177 100644 --- a/config/common/operator/clusterrole-operator.yaml +++ b/config/common/operator/clusterrole-operator.yaml @@ -35,6 +35,7 @@ rules: - secrets resourceNames: - dynatrace-dynakube-config + - dynatrace-data-ingest-endpoint verbs: - get - update @@ -95,4 +96,4 @@ rules: - customresourcedefinitions verbs: - list - - watch \ No newline at end of file + - watch diff --git a/config/common/webhook/clusterrole-webhook.yaml b/config/common/webhook/clusterrole-webhook.yaml index c0731b768b..f6b68c7362 100644 --- a/config/common/webhook/clusterrole-webhook.yaml +++ b/config/common/webhook/clusterrole-webhook.yaml @@ -34,6 +34,7 @@ rules: - secrets resourceNames: - dynatrace-dynakube-config + - dynatrace-data-ingest-endpoint verbs: - get - list diff --git a/controllers/dataingestendpointsecret/secret.go b/controllers/dataingestendpointsecret/secret.go new file mode 100644 index 0000000000..d88e3d2fef --- /dev/null +++ b/controllers/dataingestendpointsecret/secret.go @@ -0,0 +1,114 @@ +package dataingestendpointsecret + +import ( + "bytes" + "context" + "fmt" + + dynatracev1beta1 "github.com/Dynatrace/dynatrace-operator/api/v1beta1" + "github.com/Dynatrace/dynatrace-operator/controllers/kubeobjects" + "github.com/Dynatrace/dynatrace-operator/dtclient" + "github.com/Dynatrace/dynatrace-operator/mapper" + "github.com/Dynatrace/dynatrace-operator/webhook" + "github.com/go-logr/logr" + "github.com/pkg/errors" + corev1 "k8s.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +const ( + UrlSecretField = "DT_METRICS_INGEST_URL" + TokenSecretField = "DT_METRICS_INGEST_API_TOKEN" + configFile = "endpoint.properties" +) + +// EndpointSecretGenerator manages the mint endpoint secret generation for the user namespaces. +type EndpointSecretGenerator struct { + client client.Client + apiReader client.Reader + logger logr.Logger + namespace string +} + +func NewEndpointGenerator(client client.Client, apiReader client.Reader, ns string, logger logr.Logger) *EndpointSecretGenerator { + return &EndpointSecretGenerator{ + client: client, + apiReader: apiReader, + namespace: ns, + logger: logger, + } +} + +// GenerateForNamespace creates the data-ingest-endpoint secret for namespace while only having the name of the corresponding dynakube +// Used by the podInjection webhook in case the namespace lacks the secret. +func (g *EndpointSecretGenerator) GenerateForNamespace(ctx context.Context, dkName, targetNs string) (bool, error) { + g.logger.Info("Reconciling data-ingest endpoint secret for", "namespace", targetNs) + var dk dynatracev1beta1.DynaKube + if err := g.client.Get(ctx, client.ObjectKey{Name: dkName, Namespace: g.namespace}, &dk); err != nil { + return false, err + } + + data, err := g.prepare(ctx, &dk) + if err != nil { + return false, err + } + return kubeobjects.CreateOrUpdateSecretIfNotExists(g.client, g.apiReader, webhook.SecretEndpointName, targetNs, data, corev1.SecretTypeOpaque, g.logger) +} + +// GenerateForDynakube creates/updates the data-ingest-endpoint secret for EVERY namespace for the given dynakube. +// Used by the dynakube controller during reconcile. +func (g *EndpointSecretGenerator) GenerateForDynakube(ctx context.Context, dk *dynatracev1beta1.DynaKube) (bool, error) { + g.logger.Info("Reconciling data-ingest endpoint secret for", "dynakube", dk.Name) + + data, err := g.prepare(ctx, dk) + if err != nil { + return false, err + } + + anyUpdate := false + nsList, err := mapper.GetNamespacesForDynakube(ctx, g.apiReader, dk.Name) + if err != nil { + return false, err + } + for _, targetNs := range nsList { + if upd, err := kubeobjects.CreateOrUpdateSecretIfNotExists(g.client, g.apiReader, webhook.SecretEndpointName, targetNs.Name, data, corev1.SecretTypeOpaque, g.logger); err != nil { + return upd, err + } else if upd { + anyUpdate = true + } + } + g.logger.Info("Done updating data-ingest endpoint secrets") + return anyUpdate, nil +} + +func (g *EndpointSecretGenerator) prepare(ctx context.Context, dk *dynatracev1beta1.DynaKube) (map[string][]byte, error) { + fields, err := g.PrepareFields(ctx, dk) + if err != nil { + return nil, errors.WithStack(err) + } + + var endpointBuf bytes.Buffer + if _, err := endpointBuf.WriteString(fmt.Sprintf("%s=%s\n", UrlSecretField, fields[UrlSecretField])); err != nil { + return nil, errors.WithStack(err) + } + if _, err := endpointBuf.WriteString(fmt.Sprintf("%s=%s\n", TokenSecretField, fields[TokenSecretField])); err != nil { + return nil, errors.WithStack(err) + } + + data := map[string][]byte{ + configFile: endpointBuf.Bytes(), + } + return data, nil +} + +func (g *EndpointSecretGenerator) PrepareFields(ctx context.Context, dk *dynatracev1beta1.DynaKube) (map[string]string, error) { + var tokens corev1.Secret + if err := g.client.Get(ctx, client.ObjectKey{Name: dk.Tokens(), Namespace: g.namespace}, &tokens); err != nil { + return nil, errors.WithMessage(err, "failed to query tokens") + } + + return map[string]string{ + UrlSecretField: fmt.Sprintf("%s/v2/metrics/ingest", dk.Spec.APIURL), + TokenSecretField: string(tokens.Data[dtclient.DynatraceDataIngestToken]), + }, nil +} diff --git a/controllers/dataingestendpointsecret/secret_test.go b/controllers/dataingestendpointsecret/secret_test.go new file mode 100644 index 0000000000..adface4204 --- /dev/null +++ b/controllers/dataingestendpointsecret/secret_test.go @@ -0,0 +1,276 @@ +package dataingestendpointsecret + +import ( + "context" + "testing" + + dynatracev1beta1 "github.com/Dynatrace/dynatrace-operator/api/v1beta1" + "github.com/Dynatrace/dynatrace-operator/logger" + "github.com/Dynatrace/dynatrace-operator/mapper" + "github.com/Dynatrace/dynatrace-operator/scheme/fake" + "github.com/Dynatrace/dynatrace-operator/webhook" + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +const ( + testPaasToken = "test-paas-token" + testAPIToken = "test-api-token" + testDataIngestToken = "test-data-ingest-token" + testUpdatedDataIngestToken = "updated-test-data-ingest-token" + + testApiUrl = "https://test/api" + testUpdatedApiUrl = "https://updated-test/api" + + testDataIngestSecret = `DT_METRICS_INGEST_URL=https://test/api/v2/metrics/ingest +DT_METRICS_INGEST_API_TOKEN=test-data-ingest-token +` + testUpdatedTokenDataIngestSecret = `DT_METRICS_INGEST_URL=https://test/api/v2/metrics/ingest +DT_METRICS_INGEST_API_TOKEN=updated-test-data-ingest-token +` + testUpdatedApiUrlDataIngestSecret = `DT_METRICS_INGEST_URL=https://updated-test/api/v2/metrics/ingest +DT_METRICS_INGEST_API_TOKEN=test-data-ingest-token +` + testNamespace1 = "test-namespace-one" + testNamespace2 = "test-namespace-two" + + testNamespaceDynatrace = "dynatrace" + testDynakubeName = "dynakube" +) + +var log = logger.NewDTLogger() + +func TestGenerateDataIngestSecret_ForDynakube(t *testing.T) { + t.Run(`data-ingest endpoint secret created but not updated`, func(t *testing.T) { + instance := buildDynakube() + fakeClient := buildClient(instance) + + endpointSecretGenerator := NewEndpointGenerator(fakeClient, fakeClient, testNamespaceDynatrace, log) + + upd, err := endpointSecretGenerator.GenerateForNamespace(context.TODO(), testDynakubeName, testNamespace1) + assert.NoError(t, err) + assert.Equal(t, true, upd) + + upd, err = endpointSecretGenerator.GenerateForNamespace(context.TODO(), testDynakubeName, testNamespace1) + assert.NoError(t, err) + assert.Equal(t, false, upd) + + checkSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace1, testDataIngestSecret) + + checkSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespace2) + + checkSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespaceDynatrace) + }) + t.Run(`data-ingest endpoint secret created and token updated`, func(t *testing.T) { + instance := buildDynakube() + fakeClient := buildClient(instance) + + endpointSecretGenerator := NewEndpointGenerator(fakeClient, fakeClient, testNamespaceDynatrace, log) + + upd, err := endpointSecretGenerator.GenerateForNamespace(context.TODO(), testDynakubeName, testNamespace1) + assert.NoError(t, err) + assert.Equal(t, true, upd) + + updateSecret(t, fakeClient) + + upd, err = endpointSecretGenerator.GenerateForNamespace(context.TODO(), testDynakubeName, testNamespace1) + assert.NoError(t, err) + assert.Equal(t, true, upd) + + checkSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace1, testUpdatedTokenDataIngestSecret) + + checkSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespace2) + + checkSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespaceDynatrace) + }) + t.Run(`data-ingest endpoint secret created and apiUrl updated`, func(t *testing.T) { + instance := buildDynakube() + fakeClient := buildClient(instance) + + endpointSecretGenerator := NewEndpointGenerator(fakeClient, fakeClient, testNamespaceDynatrace, log) + + upd, err := endpointSecretGenerator.GenerateForNamespace(context.TODO(), testDynakubeName, testNamespace1) + assert.NoError(t, err) + assert.Equal(t, true, upd) + + updateDynakube(t, fakeClient) + + upd, err = endpointSecretGenerator.GenerateForNamespace(context.TODO(), testDynakubeName, testNamespace1) + assert.NoError(t, err) + assert.Equal(t, true, upd) + + checkSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace1, testUpdatedApiUrlDataIngestSecret) + + checkSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespace2) + + checkSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespaceDynatrace) + }) + + t.Run(`data-ingest endpoint secret created in all namespaces but not updated`, func(t *testing.T) { + instance := buildDynakube() + fakeClient := buildClient(instance) + + endpointSecretGenerator := NewEndpointGenerator(fakeClient, fakeClient, testNamespaceDynatrace, log) + + upd, err := endpointSecretGenerator.GenerateForDynakube(context.TODO(), instance) + assert.NoError(t, err) + assert.Equal(t, true, upd) + + checkSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace1, testDataIngestSecret) + + checkSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace2, testDataIngestSecret) + + checkSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespaceDynatrace) + + upd, err = endpointSecretGenerator.GenerateForDynakube(context.TODO(), instance) + assert.NoError(t, err) + assert.Equal(t, false, upd) + }) + t.Run(`data-ingest endpoint secret created in all namespaces and token updated`, func(t *testing.T) { + instance := buildDynakube() + fakeClient := buildClient(instance) + + endpointSecretGenerator := NewEndpointGenerator(fakeClient, fakeClient, testNamespaceDynatrace, log) + + upd, err := endpointSecretGenerator.GenerateForDynakube(context.TODO(), instance) + assert.NoError(t, err) + assert.Equal(t, true, upd) + + updateSecret(t, fakeClient) + + upd, err = endpointSecretGenerator.GenerateForDynakube(context.TODO(), instance) + assert.NoError(t, err) + assert.Equal(t, true, upd) + + checkSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace1, testUpdatedTokenDataIngestSecret) + + checkSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace2, testUpdatedTokenDataIngestSecret) + + checkSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespaceDynatrace) + }) + t.Run(`data-ingest endpoint secret created in all namespaces and apiUrl updated`, func(t *testing.T) { + instance := buildDynakube() + fakeClient := buildClient(instance) + + endpointSecretGenerator := NewEndpointGenerator(fakeClient, fakeClient, testNamespaceDynatrace, log) + + upd, err := endpointSecretGenerator.GenerateForDynakube(context.TODO(), instance) + assert.NoError(t, err) + assert.Equal(t, true, upd) + + newInstance := updatedDynakube() + + upd, err = endpointSecretGenerator.GenerateForDynakube(context.TODO(), newInstance) + assert.NoError(t, err) + assert.Equal(t, true, upd) + + checkSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace1, testUpdatedApiUrlDataIngestSecret) + + checkSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace2, testUpdatedApiUrlDataIngestSecret) + + checkSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespaceDynatrace) + }) +} + +func checkSecretExists(t *testing.T, fakeClient client.Client, secretName string, namespace string, data string) { + var testSecret corev1.Secret + err := fakeClient.Get(context.TODO(), client.ObjectKey{Name: secretName, Namespace: namespace}, &testSecret) + assert.NoError(t, err) + assert.NotNil(t, testSecret.Data) + assert.NotEmpty(t, testSecret.Data) + assert.Contains(t, testSecret.Data, "endpoint.properties") + assert.Equal(t, data, string(testSecret.Data["endpoint.properties"])) +} + +func checkSecretNotExists(t *testing.T, fakeClient client.Client, secretName string, namespace string) { + var testSecret corev1.Secret + err := fakeClient.Get(context.TODO(), client.ObjectKey{Name: secretName, Namespace: namespace}, &testSecret) + assert.Error(t, err) + assert.Nil(t, testSecret.Data) +} + +func updateSecret(t *testing.T, fakeClient client.Client) { + updatedSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: testDynakubeName, + Namespace: testNamespaceDynatrace, + }, + Data: map[string][]byte{ + "apiToken": []byte(testAPIToken), + "paasToken": []byte(testPaasToken), + "dataIngestToken": []byte(testUpdatedDataIngestToken), + }, + } + + err := fakeClient.Update(context.TODO(), updatedSecret) + assert.NoError(t, err) +} + +func updatedDynakube() *dynatracev1beta1.DynaKube { + return &dynatracev1beta1.DynaKube{ + ObjectMeta: metav1.ObjectMeta{ + Name: testDynakubeName, + Namespace: testNamespaceDynatrace, + }, + Spec: dynatracev1beta1.DynaKubeSpec{ + APIURL: testUpdatedApiUrl, + }, + } +} + +func updateDynakube(t *testing.T, fakeClient client.Client) { + updatedDynakube := updatedDynakube() + + err := fakeClient.Update(context.TODO(), updatedDynakube) + assert.NoError(t, err) +} + +func buildDynakube() *dynatracev1beta1.DynaKube { + return &dynatracev1beta1.DynaKube{ + ObjectMeta: metav1.ObjectMeta{ + Name: testDynakubeName, + Namespace: testNamespaceDynatrace, + }, + Spec: dynatracev1beta1.DynaKubeSpec{ + APIURL: testApiUrl, + }, + } +} + +func buildClient(dk *dynatracev1beta1.DynaKube) client.Client { + return fake.NewClient(dk, + &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: testNamespaceDynatrace, + }, + }, + &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: testNamespace1, + Labels: map[string]string{ + mapper.InstanceLabel: dk.Name, + }, + }, + }, + &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: testNamespace2, + Labels: map[string]string{ + mapper.InstanceLabel: dk.Name, + }, + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: testDynakubeName, + Namespace: testNamespaceDynatrace, + }, + Data: map[string][]byte{ + "apiToken": []byte(testAPIToken), + "paasToken": []byte(testPaasToken), + "dataIngestToken": []byte(testDataIngestToken), + }, + }) +} diff --git a/controllers/dynakube/dynakube_controller.go b/controllers/dynakube/dynakube_controller.go index 9edb8577cb..d486534259 100644 --- a/controllers/dynakube/dynakube_controller.go +++ b/controllers/dynakube/dynakube_controller.go @@ -11,6 +11,8 @@ import ( "github.com/Dynatrace/dynatrace-operator/controllers" "github.com/Dynatrace/dynatrace-operator/controllers/activegate/capability" rcap "github.com/Dynatrace/dynatrace-operator/controllers/activegate/reconciler/capability" + + "github.com/Dynatrace/dynatrace-operator/controllers/dataingestendpointsecret" "github.com/Dynatrace/dynatrace-operator/controllers/dtpullsecret" "github.com/Dynatrace/dynatrace-operator/controllers/dtversion" "github.com/Dynatrace/dynatrace-operator/controllers/dynakube/status" @@ -260,6 +262,11 @@ func (r *ReconcileDynaKube) reconcileDynaKube(ctx context.Context, dkState *cont if dkState.Error(err) || dkState.Update(upd, defaultUpdateInterval, "new init script created") { return } + + upd, err = dataingestendpointsecret.NewEndpointGenerator(r.client, r.apiReader, dkState.Instance.Namespace, r.logger).GenerateForDynakube(ctx, dkState.Instance) + if dkState.Error(err) || dkState.Update(upd, defaultUpdateInterval, "new data-ingest endpoint secret created") { + return + } } else { if err := dkMapper.UnmapFromDynaKube(); err != nil { dkState.Log.Error(err, "could not unmap dynakube from namespace") diff --git a/controllers/kubeobjects/secret.go b/controllers/kubeobjects/secret.go index fb62a14cb7..2b28a74731 100644 --- a/controllers/kubeobjects/secret.go +++ b/controllers/kubeobjects/secret.go @@ -16,11 +16,11 @@ import ( ) // CreateOrUpdateSecretIfNotExists creates a secret in case it does not exist or updates it if there are changes -func CreateOrUpdateSecretIfNotExists(c client.Client, r client.Reader, secretName string, targetNS string, data map[string][]byte, secretType corev1.SecretType, log logr.Logger) error { +func CreateOrUpdateSecretIfNotExists(c client.Client, r client.Reader, secretName string, targetNS string, data map[string][]byte, secretType corev1.SecretType, log logr.Logger) (bool, error) { var cfg corev1.Secret err := r.Get(context.TODO(), client.ObjectKey{Name: secretName, Namespace: targetNS}, &cfg) if k8serrors.IsNotFound(err) { - log.Info("Creating OneAgent config secret") + log.Info("Creating secret", "namespace", targetNS, "secret", secretName) if err := c.Create(context.TODO(), &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: secretName, @@ -29,24 +29,25 @@ func CreateOrUpdateSecretIfNotExists(c client.Client, r client.Reader, secretNam Type: secretType, Data: data, }); err != nil { - return errors.Wrapf(err, "failed to create secret %s", secretName) + return false, errors.Wrapf(err, "failed to create secret %s", secretName) } - return nil + return true, nil } if err != nil { - return errors.Wrapf(err, "failed to query for secret %s", secretName) + return false, errors.Wrapf(err, "failed to query for secret %s", secretName) } if !reflect.DeepEqual(data, cfg.Data) { - log.Info(fmt.Sprintf("Updating secret %s", secretName)) + log.Info("Updating secret", "namespace", targetNS, "secret", secretName) cfg.Data = data if err := c.Update(context.TODO(), &cfg); err != nil { - return errors.Wrapf(err, "failed to update secret %s", secretName) + return false, errors.Wrapf(err, "failed to update secret %s", secretName) } + return true, nil } - return nil + return false, nil } type Tokens struct { diff --git a/controllers/kubeobjects/secret_test.go b/controllers/kubeobjects/secret_test.go index 00fc2a2f46..463e6ee4fe 100644 --- a/controllers/kubeobjects/secret_test.go +++ b/controllers/kubeobjects/secret_test.go @@ -37,7 +37,7 @@ func TestCreateOrUpdateSecretIfNotExists(t *testing.T) { Data: data, }) - err := CreateOrUpdateSecretIfNotExists(client, client, testSecretName, testNamespace, data, "", log) + _, err := CreateOrUpdateSecretIfNotExists(client, client, testSecretName, testNamespace, data, "", log) assert.NoError(t, err) }) t.Run(`Secret present, different data => update data`, func(t *testing.T) { @@ -50,7 +50,7 @@ func TestCreateOrUpdateSecretIfNotExists(t *testing.T) { Data: map[string][]byte{}, }) - err := CreateOrUpdateSecretIfNotExists(client, client, testSecretName, testNamespace, data, testSecretType, log) + _, err := CreateOrUpdateSecretIfNotExists(client, client, testSecretName, testNamespace, data, testSecretType, log) assert.NoError(t, err) var updatedSecret corev1.Secret @@ -67,7 +67,7 @@ func TestCreateOrUpdateSecretIfNotExists(t *testing.T) { Data: map[string][]byte{}, }) - err := CreateOrUpdateSecretIfNotExists(client, client, testSecretName, testNamespace, data, testSecretType, log) + _, err := CreateOrUpdateSecretIfNotExists(client, client, testSecretName, testNamespace, data, testSecretType, log) assert.NoError(t, err) var newSecret corev1.Secret @@ -81,7 +81,7 @@ func TestCreateOrUpdateSecretIfNotExists(t *testing.T) { data := map[string][]byte{testKey1: []byte(testValue1)} client := fake.NewClient() - err := CreateOrUpdateSecretIfNotExists(client, client, testSecretName, testNamespace, data, testSecretType, log) + _, err := CreateOrUpdateSecretIfNotExists(client, client, testSecretName, testNamespace, data, testSecretType, log) assert.NoError(t, err) var newSecret corev1.Secret diff --git a/dtclient/client.go b/dtclient/client.go index 680fb45b78..7787af8b93 100644 --- a/dtclient/client.go +++ b/dtclient/client.go @@ -13,8 +13,9 @@ import ( ) const ( - DynatracePaasToken = "paasToken" - DynatraceApiToken = "apiToken" + DynatracePaasToken = "paasToken" + DynatraceApiToken = "apiToken" + DynatraceDataIngestToken = "dataIngestToken" ) // Client is the interface for the Dynatrace REST API client. diff --git a/initgeneration/initgeneration.go b/initgeneration/initgeneration.go index c9ff06f096..de18bf3f34 100644 --- a/initgeneration/initgeneration.go +++ b/initgeneration/initgeneration.go @@ -75,12 +75,12 @@ func NewInitGenerator(client client.Client, apiReader client.Reader, ns string, // GenerateForNamespace creates the init secret for namespace while only having the name of the corresponding dynakube // Used by the podInjection webhook in case the namespace lacks the init secret. -func (g *InitGenerator) GenerateForNamespace(ctx context.Context, dk dynatracev1beta1.DynaKube, targetNs string) error { +func (g *InitGenerator) GenerateForNamespace(ctx context.Context, dk dynatracev1beta1.DynaKube, targetNs string) (bool, error) { g.logger.Info("Reconciling namespace init secret for", "namespace", targetNs) g.canWatchNodes = false data, err := g.generate(ctx, &dk) if err != nil { - return err + return false, err } return kubeobjects.CreateOrUpdateSecretIfNotExists(g.client, g.apiReader, webhook.SecretConfigName, targetNs, data, corev1.SecretTypeOpaque, g.logger) } @@ -95,17 +95,20 @@ func (g *InitGenerator) GenerateForDynakube(ctx context.Context, dk *dynatracev1 return false, err } + anyUpdate := false nsList, err := mapper.GetNamespacesForDynakube(ctx, g.apiReader, dk.Name) if err != nil { return false, err } for _, targetNs := range nsList { - if err = kubeobjects.CreateOrUpdateSecretIfNotExists(g.client, g.apiReader, webhook.SecretConfigName, targetNs.Name, data, corev1.SecretTypeOpaque, g.logger); err != nil { + if upd, err := kubeobjects.CreateOrUpdateSecretIfNotExists(g.client, g.apiReader, webhook.SecretConfigName, targetNs.Name, data, corev1.SecretTypeOpaque, g.logger); err != nil { return false, err + } else if upd { + anyUpdate = true } } g.logger.Info("Done updating init secrets") - return true, nil + return anyUpdate, nil } // generate gets the necessary info the create the init secret data diff --git a/initgeneration/initgeneration_test.go b/initgeneration/initgeneration_test.go index ac31f3efa3..39af94717c 100644 --- a/initgeneration/initgeneration_test.go +++ b/initgeneration/initgeneration_test.go @@ -159,7 +159,7 @@ func TestGenerateForNamespace(t *testing.T) { clt := fake.NewClient(testDynakubeComplex, &testNamespace, testSecretDynakubeComplex, kubeNamespace, caConfigMap, testNode1, testNode2) ig := NewInitGenerator(clt, clt, operatorNamespace, logger.NewDTLogger()) - err := ig.GenerateForNamespace(context.TODO(), *testDynakubeComplex, testNamespace.Name) + _, err := ig.GenerateForNamespace(context.TODO(), *testDynakubeComplex, testNamespace.Name) assert.NoError(t, err) var initSecret corev1.Secret @@ -186,7 +186,7 @@ func TestGenerateForNamespace(t *testing.T) { clt := fake.NewClient(testDynakubeSimple, &testNamespace, testSecretDynakubeSimple, kubeNamespace, testNode1, testNode2) ig := NewInitGenerator(clt, clt, operatorNamespace, logger.NewDTLogger()) - err := ig.GenerateForNamespace(context.TODO(), *testDynakubeSimple, testNamespace.Name) + _, err := ig.GenerateForNamespace(context.TODO(), *testDynakubeSimple, testNamespace.Name) assert.NoError(t, err) var initSecret corev1.Secret diff --git a/webhook/config.go b/webhook/config.go index 43a04b5f88..5e09433b90 100644 --- a/webhook/config.go +++ b/webhook/config.go @@ -41,4 +41,7 @@ const ( // InstallContainerName is the name used for the install container InstallContainerName = "install-oneagent" + + // SecretEndpointName is the name of the secret where the Operator replicates data-ingest data (data-ingest url, data-ingest token). + SecretEndpointName = "dynatrace-data-ingest-endpoint" ) diff --git a/webhook/mutation/pod_mutator.go b/webhook/mutation/pod_mutator.go index 75a8962e4b..d5764cfe89 100644 --- a/webhook/mutation/pod_mutator.go +++ b/webhook/mutation/pod_mutator.go @@ -12,6 +12,7 @@ import ( dynatracev1beta1 "github.com/Dynatrace/dynatrace-operator/api/v1beta1" dtcsi "github.com/Dynatrace/dynatrace-operator/controllers/csi" + endpoint "github.com/Dynatrace/dynatrace-operator/controllers/dataingestendpointsecret" "github.com/Dynatrace/dynatrace-operator/controllers/kubeobjects" "github.com/Dynatrace/dynatrace-operator/controllers/kubesystem" "github.com/Dynatrace/dynatrace-operator/deploymentmetadata" @@ -174,11 +175,34 @@ func (m *podMutator) Handle(ctx context.Context, req admission.Request) admissio var initSecret corev1.Secret if err := m.apiReader.Get(ctx, client.ObjectKey{Name: dtwebhook.SecretConfigName, Namespace: ns.Name}, &initSecret); k8serrors.IsNotFound(err) { - if err := initgeneration.NewInitGenerator(m.client, m.apiReader, m.namespace, log).GenerateForNamespace(ctx, dk, ns.Name); err != nil { + if _, err := initgeneration.NewInitGenerator(m.client, m.apiReader, m.namespace, log).GenerateForNamespace(ctx, dk, ns.Name); err != nil { log.Error(err, "Failed to create the init secret before pod injection") return admission.Errored(http.StatusBadRequest, err) } + } else if err != nil { + log.Error(err, "failed to query the init secret before pod injection") + return admission.Errored(http.StatusBadRequest, err) } + + endpointGenerator := endpoint.NewEndpointGenerator(m.client, m.apiReader, m.namespace, log) + + var endpointSecret corev1.Secret + if err := m.apiReader.Get(ctx, client.ObjectKey{Name: dtwebhook.SecretEndpointName, Namespace: ns.Name}, &endpointSecret); k8serrors.IsNotFound(err) { + if _, err := endpointGenerator.GenerateForNamespace(ctx, dkName, ns.Name); err != nil { + log.Error(err, "failed to create the data-ingest endpoint secret before pod injection") + return admission.Errored(http.StatusBadRequest, err) + } + } else if err != nil { + log.Error(err, "failed to query the data-ingest endpoint secret before pod injection") + return admission.Errored(http.StatusBadRequest, err) + } + + dataIngestFields, err := endpointGenerator.PrepareFields(ctx, &dk) + if err != nil { + log.Error(err, "AA failed to query the data-ingest endpoint secret before pod injection") + return admission.Errored(http.StatusBadRequest, err) + } + log.Info("injecting into Pod", "name", pod.Name, "generatedName", pod.GenerateName, "namespace", req.Namespace) if pod.Annotations == nil { @@ -205,7 +229,7 @@ func (m *podMutator) Handle(ctx context.Context, req admission.Request) admissio log.Info("instrumenting missing container", "name", c.Name) deploymentMetadata := deploymentmetadata.NewDeploymentMetadata(m.clusterID, deploymentmetadata.DeploymentTypeApplicationMonitoring) - updateContainer(c, &dk, pod, deploymentMetadata) + updateContainer(c, &dk, pod, deploymentMetadata, dataIngestFields) if installContainer == nil { for j := range pod.Spec.InitContainers { @@ -270,6 +294,14 @@ func (m *podMutator) Handle(ctx context.Context, req admission.Request) admissio SecretName: dtwebhook.SecretConfigName, }, }, + }, + corev1.Volume{ + Name: "data-ingest-endpoint", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: dtwebhook.SecretEndpointName, + }, + }, }) var sc *corev1.SecurityContext @@ -332,7 +364,7 @@ func (m *podMutator) Handle(ctx context.Context, req admission.Request) admissio updateInstallContainer(&ic, i+1, c.Name, c.Image) - updateContainer(c, &dk, pod, deploymentMetadata) + updateContainer(c, &dk, pod, deploymentMetadata, dataIngestFields) } pod.Spec.InitContainers = append(pod.Spec.InitContainers, ic) @@ -366,7 +398,7 @@ func updateInstallContainer(ic *corev1.Container, number int, name string, image // updateContainer sets missing preload Variables func updateContainer(c *corev1.Container, oa *dynatracev1beta1.DynaKube, - pod *corev1.Pod, deploymentMetadata *deploymentmetadata.DeploymentMetadata) { + pod *corev1.Pod, deploymentMetadata *deploymentmetadata.DeploymentMetadata, dataIngestFields map[string]string) { log.Info("updating container with missing preload variables", "containerName", c.Name) installPath := kubeobjects.GetField(pod.Annotations, dtwebhook.AnnotationInstallPath, dtwebhook.DefaultInstallPath) @@ -385,7 +417,10 @@ func updateContainer(c *corev1.Container, oa *dynatracev1beta1.DynaKube, Name: "oneagent-share", MountPath: "/var/lib/dynatrace/oneagent/agent/config/container.conf", SubPath: fmt.Sprintf("container_%s.conf", c.Name), - }) + }, + corev1.VolumeMount{ + Name: "data-ingest-endpoint", + MountPath: "/var/lib/dynatrace/enrichment/endpoint"}) c.Env = append(c.Env, corev1.EnvVar{ @@ -395,7 +430,16 @@ func updateContainer(c *corev1.Container, oa *dynatracev1beta1.DynaKube, corev1.EnvVar{ Name: "DT_DEPLOYMENT_METADATA", Value: deploymentMetadata.AsString(), - }) + }, + corev1.EnvVar{ + Name: endpoint.UrlSecretField, + Value: dataIngestFields[endpoint.UrlSecretField], + }, + corev1.EnvVar{ + Name: endpoint.TokenSecretField, + Value: dataIngestFields[endpoint.TokenSecretField], + }, + ) if oa.Spec.Proxy != nil && (oa.Spec.Proxy.Value != "" || oa.Spec.Proxy.ValueFrom != "") { c.Env = append(c.Env, diff --git a/webhook/mutation/pod_mutator_test.go b/webhook/mutation/pod_mutator_test.go index 7dd796e7fb..c32eb8f987 100644 --- a/webhook/mutation/pod_mutator_test.go +++ b/webhook/mutation/pod_mutator_test.go @@ -6,6 +6,7 @@ import ( dynatracev1beta1 "github.com/Dynatrace/dynatrace-operator/api/v1beta1" dtcsi "github.com/Dynatrace/dynatrace-operator/controllers/csi" + endpoint "github.com/Dynatrace/dynatrace-operator/controllers/dataingestendpointsecret" "github.com/Dynatrace/dynatrace-operator/dtclient" "github.com/Dynatrace/dynatrace-operator/mapper" "github.com/Dynatrace/dynatrace-operator/scheme" @@ -27,6 +28,8 @@ import ( const ( fakeEventRecorderBufferSize = 10 + dynakubeName = "dynakube" + dataIngestToken = "data-ingest-token" ) func TestInjectionWithMissingOneAgentAPM(t *testing.T) { @@ -38,7 +41,7 @@ func TestInjectionWithMissingOneAgentAPM(t *testing.T) { &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: "test-namespace", - Labels: map[string]string{mapper.InstanceLabel: "dynakube"}, + Labels: map[string]string{mapper.InstanceLabel: dynakubeName}, }, }), apiReader: fake.NewClient( @@ -82,7 +85,7 @@ func TestInjectionWithMissingOneAgentAPM(t *testing.T) { func createPodInjector(_ *testing.T, decoder *admission.Decoder) (*podMutator, *dynatracev1beta1.DynaKube) { dynakube := &dynatracev1beta1.DynaKube{ - ObjectMeta: metav1.ObjectMeta{Name: "dynakube", Namespace: "dynatrace"}, + ObjectMeta: metav1.ObjectMeta{Name: dynakubeName, Namespace: "dynatrace"}, Spec: dynatracev1beta1.DynaKubeSpec{ APIURL: "https://test-api-url.com/api", OneAgent: dynatracev1beta1.OneAgentSpec{ @@ -110,7 +113,16 @@ func createPodInjector(_ *testing.T, decoder *admission.Decoder) (*podMutator, * &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: "test-namespace", - Labels: map[string]string{mapper.InstanceLabel: "dynakube"}, + Labels: map[string]string{mapper.InstanceLabel: dynakubeName}, + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: dynakubeName, + Namespace: "dynatrace", + }, + Data: map[string][]byte{ + dtclient.DynatraceDataIngestToken: []byte(dataIngestToken), }, }, ), @@ -121,6 +133,21 @@ func createPodInjector(_ *testing.T, decoder *admission.Decoder) (*podMutator, * Namespace: "test-namespace", }, }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: dtwebhook.SecretEndpointName, + Namespace: "test-namespace", + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: dynakubeName, + Namespace: "dynatrace", + }, + Data: map[string][]byte{ + dtclient.DynatraceDataIngestToken: []byte(dataIngestToken), + }, + }, ), decoder: decoder, image: "test-api-url.com/linux/codemodule", @@ -289,7 +316,7 @@ func TestPodInjectionWithCSI(t *testing.T) { func createDynakubeInstance(_ *testing.T) *dynatracev1beta1.DynaKube { instance := &dynatracev1beta1.DynaKube{ - ObjectMeta: metav1.ObjectMeta{Name: "dynakube", Namespace: "dynatrace"}, + ObjectMeta: metav1.ObjectMeta{Name: dynakubeName, Namespace: "dynatrace"}, Spec: dynatracev1beta1.DynaKubeSpec{ APIURL: "https://test-api-url.com/api", OneAgent: dynatracev1beta1.OneAgentSpec{ @@ -314,7 +341,16 @@ func TestUseImmutableImage(t *testing.T) { &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: "test-namespace", - Labels: map[string]string{mapper.InstanceLabel: "dynakube"}, + Labels: map[string]string{mapper.InstanceLabel: dynakubeName}, + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: dynakubeName, + Namespace: "dynatrace", + }, + Data: map[string][]byte{ + dtclient.DynatraceDataIngestToken: []byte(dataIngestToken), }, }, ), @@ -325,6 +361,21 @@ func TestUseImmutableImage(t *testing.T) { Namespace: "test-namespace", }, }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: dtwebhook.SecretEndpointName, + Namespace: "test-namespace", + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: dynakubeName, + Namespace: "dynatrace", + }, + Data: map[string][]byte{ + dtclient.DynatraceDataIngestToken: []byte(dataIngestToken), + }, + }, ), decoder: decoder, image: "test-image", @@ -413,7 +464,16 @@ func TestUseImmutableImage(t *testing.T) { &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: "test-namespace", - Labels: map[string]string{mapper.InstanceLabel: "dynakube"}, + Labels: map[string]string{mapper.InstanceLabel: dynakubeName}, + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: dynakubeName, + Namespace: "dynatrace", + }, + Data: map[string][]byte{ + dtclient.DynatraceDataIngestToken: []byte(dataIngestToken), }, }, ), @@ -424,6 +484,21 @@ func TestUseImmutableImage(t *testing.T) { Namespace: "test-namespace", }, }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: dtwebhook.SecretEndpointName, + Namespace: "test-namespace", + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: dynakubeName, + Namespace: "dynatrace", + }, + Data: map[string][]byte{ + dtclient.DynatraceDataIngestToken: []byte(dataIngestToken), + }, + }, ), decoder: decoder, image: "test-image", @@ -513,6 +588,15 @@ func TestUseImmutableImageWithCSI(t *testing.T) { Labels: map[string]string{mapper.InstanceLabel: instance.Name}, }, }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: dynakubeName, + Namespace: "dynatrace", + }, + Data: map[string][]byte{ + dtclient.DynatraceDataIngestToken: []byte(dataIngestToken), + }, + }, ), apiReader: fake.NewClient( &corev1.Secret{ @@ -521,6 +605,21 @@ func TestUseImmutableImageWithCSI(t *testing.T) { Namespace: "test-namespace", }, }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: dtwebhook.SecretEndpointName, + Namespace: "test-namespace", + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: dynakubeName, + Namespace: "dynatrace", + }, + Data: map[string][]byte{ + dtclient.DynatraceDataIngestToken: []byte(dataIngestToken), + }, + }, ), decoder: decoder, image: "test-image", @@ -602,6 +701,15 @@ func TestUseImmutableImageWithCSI(t *testing.T) { Labels: map[string]string{mapper.InstanceLabel: instance.Name}, }, }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: dynakubeName, + Namespace: "dynatrace", + }, + Data: map[string][]byte{ + dtclient.DynatraceDataIngestToken: []byte(dataIngestToken), + }, + }, ), apiReader: fake.NewClient( &corev1.Secret{ @@ -610,6 +718,21 @@ func TestUseImmutableImageWithCSI(t *testing.T) { Namespace: "test-namespace", }, }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: dtwebhook.SecretEndpointName, + Namespace: "test-namespace", + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: dynakubeName, + Namespace: "dynatrace", + }, + Data: map[string][]byte{ + dtclient.DynatraceDataIngestToken: []byte(dataIngestToken), + }, + }, ), decoder: decoder, image: "test-image", @@ -692,6 +815,15 @@ func TestUseImmutableImageWithCSI(t *testing.T) { Labels: map[string]string{mapper.InstanceLabel: instance.Name}, }, }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: dynakubeName, + Namespace: "dynatrace", + }, + Data: map[string][]byte{ + dtclient.DynatraceDataIngestToken: []byte(dataIngestToken), + }, + }, ), apiReader: fake.NewClient( &corev1.Secret{ @@ -700,6 +832,21 @@ func TestUseImmutableImageWithCSI(t *testing.T) { Namespace: "test-namespace", }, }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: dtwebhook.SecretEndpointName, + Namespace: "test-namespace", + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: dynakubeName, + Namespace: "dynatrace", + }, + Data: map[string][]byte{ + dtclient.DynatraceDataIngestToken: []byte(dataIngestToken), + }, + }, ), decoder: decoder, image: "test-image", @@ -779,6 +926,15 @@ func TestAgentVersion(t *testing.T) { Labels: map[string]string{mapper.InstanceLabel: instance.Name}, }, }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: dynakubeName, + Namespace: "dynatrace", + }, + Data: map[string][]byte{ + dtclient.DynatraceDataIngestToken: []byte(dataIngestToken), + }, + }, ), apiReader: fake.NewClient( &corev1.Secret{ @@ -787,6 +943,21 @@ func TestAgentVersion(t *testing.T) { Namespace: "test-namespace", }, }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: dtwebhook.SecretEndpointName, + Namespace: "test-namespace", + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: dynakubeName, + Namespace: "dynatrace", + }, + Data: map[string][]byte{ + dtclient.DynatraceDataIngestToken: []byte(dataIngestToken), + }, + }, ), decoder: decoder, image: "test-image", @@ -874,6 +1045,15 @@ func TestAgentVersionWithCSI(t *testing.T) { Labels: map[string]string{mapper.InstanceLabel: instance.Name}, }, }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: dynakubeName, + Namespace: "dynatrace", + }, + Data: map[string][]byte{ + dtclient.DynatraceDataIngestToken: []byte(dataIngestToken), + }, + }, ), apiReader: fake.NewClient( &corev1.Secret{ @@ -882,6 +1062,21 @@ func TestAgentVersionWithCSI(t *testing.T) { Namespace: "test-namespace", }, }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: dtwebhook.SecretEndpointName, + Namespace: "test-namespace", + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: dynakubeName, + Namespace: "dynatrace", + }, + Data: map[string][]byte{ + dtclient.DynatraceDataIngestToken: []byte(dataIngestToken), + }, + }, ), decoder: decoder, image: "test-image", @@ -989,6 +1184,8 @@ func buildResultPod(_ *testing.T) corev1.Pod { Env: []corev1.EnvVar{ {Name: "LD_PRELOAD", Value: "/opt/dynatrace/oneagent-paas/agent/lib64/liboneagentproc.so"}, {Name: "DT_DEPLOYMENT_METADATA", Value: "orchestration_tech=Operator-cloud_native_fullstack;script_version=snapshot;orchestrator_id="}, + {Name: endpoint.UrlSecretField, Value: "https://test-api-url.com/api/v2/metrics/ingest"}, + {Name: endpoint.TokenSecretField, Value: dataIngestToken}, }, VolumeMounts: []corev1.VolumeMount{ {Name: "oneagent-share", MountPath: "/etc/ld.so.preload", SubPath: "ld.so.preload"}, @@ -998,6 +1195,7 @@ func buildResultPod(_ *testing.T) corev1.Pod { MountPath: "/var/lib/dynatrace/oneagent/agent/config/container.conf", SubPath: "container_test-container.conf", }, + {Name: "data-ingest-endpoint", MountPath: "/var/lib/dynatrace/enrichment/endpoint"}, }, }}, Volumes: []corev1.Volume{ @@ -1023,6 +1221,14 @@ func buildResultPod(_ *testing.T) corev1.Pod { }, }, }, + { + Name: "data-ingest-endpoint", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: dtwebhook.SecretEndpointName, + }, + }, + }, }, }, } From 4f5e8d8d52148acb8c443de565ab198a44797ab4 Mon Sep 17 00:00:00 2001 From: "adam.orcholski" Date: Thu, 11 Nov 2021 13:12:14 +0100 Subject: [PATCH 2/4] review: test functions renamed, dataingest package renamed --- controllers/dynakube/dynakube_controller.go | 5 +- .../secret.go | 2 +- .../secret_test.go | 92 ++++++++++--------- webhook/mutation/pod_mutator.go | 12 +-- webhook/mutation/pod_mutator_test.go | 6 +- 5 files changed, 60 insertions(+), 57 deletions(-) rename controllers/{dataingestendpointsecret => ingestendpoint}/secret.go (99%) rename controllers/{dataingestendpointsecret => ingestendpoint}/secret_test.go (67%) diff --git a/controllers/dynakube/dynakube_controller.go b/controllers/dynakube/dynakube_controller.go index d486534259..6ea14bcb4f 100644 --- a/controllers/dynakube/dynakube_controller.go +++ b/controllers/dynakube/dynakube_controller.go @@ -11,12 +11,11 @@ import ( "github.com/Dynatrace/dynatrace-operator/controllers" "github.com/Dynatrace/dynatrace-operator/controllers/activegate/capability" rcap "github.com/Dynatrace/dynatrace-operator/controllers/activegate/reconciler/capability" - - "github.com/Dynatrace/dynatrace-operator/controllers/dataingestendpointsecret" "github.com/Dynatrace/dynatrace-operator/controllers/dtpullsecret" "github.com/Dynatrace/dynatrace-operator/controllers/dtversion" "github.com/Dynatrace/dynatrace-operator/controllers/dynakube/status" "github.com/Dynatrace/dynatrace-operator/controllers/dynakube/updates" + dtingestendpoint "github.com/Dynatrace/dynatrace-operator/controllers/ingestendpoint" "github.com/Dynatrace/dynatrace-operator/controllers/istio" "github.com/Dynatrace/dynatrace-operator/controllers/oneagent" "github.com/Dynatrace/dynatrace-operator/controllers/oneagent/daemonset" @@ -263,7 +262,7 @@ func (r *ReconcileDynaKube) reconcileDynaKube(ctx context.Context, dkState *cont return } - upd, err = dataingestendpointsecret.NewEndpointGenerator(r.client, r.apiReader, dkState.Instance.Namespace, r.logger).GenerateForDynakube(ctx, dkState.Instance) + upd, err = dtingestendpoint.NewEndpointGenerator(r.client, r.apiReader, dkState.Instance.Namespace, r.logger).GenerateForDynakube(ctx, dkState.Instance) if dkState.Error(err) || dkState.Update(upd, defaultUpdateInterval, "new data-ingest endpoint secret created") { return } diff --git a/controllers/dataingestendpointsecret/secret.go b/controllers/ingestendpoint/secret.go similarity index 99% rename from controllers/dataingestendpointsecret/secret.go rename to controllers/ingestendpoint/secret.go index d88e3d2fef..b4cbc900a7 100644 --- a/controllers/dataingestendpointsecret/secret.go +++ b/controllers/ingestendpoint/secret.go @@ -1,4 +1,4 @@ -package dataingestendpointsecret +package ingestendpoint import ( "bytes" diff --git a/controllers/dataingestendpointsecret/secret_test.go b/controllers/ingestendpoint/secret_test.go similarity index 67% rename from controllers/dataingestendpointsecret/secret_test.go rename to controllers/ingestendpoint/secret_test.go index adface4204..87956085ac 100644 --- a/controllers/dataingestendpointsecret/secret_test.go +++ b/controllers/ingestendpoint/secret_test.go @@ -1,4 +1,4 @@ -package dataingestendpointsecret +package ingestendpoint import ( "context" @@ -44,8 +44,8 @@ var log = logger.NewDTLogger() func TestGenerateDataIngestSecret_ForDynakube(t *testing.T) { t.Run(`data-ingest endpoint secret created but not updated`, func(t *testing.T) { - instance := buildDynakube() - fakeClient := buildClient(instance) + instance := buildTestDynakube() + fakeClient := buildTestClient(instance) endpointSecretGenerator := NewEndpointGenerator(fakeClient, fakeClient, testNamespaceDynatrace, log) @@ -57,15 +57,15 @@ func TestGenerateDataIngestSecret_ForDynakube(t *testing.T) { assert.NoError(t, err) assert.Equal(t, false, upd) - checkSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace1, testDataIngestSecret) + checkTestSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace1, testDataIngestSecret) - checkSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespace2) + checkTestSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespace2) - checkSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespaceDynatrace) + checkTestSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespaceDynatrace) }) t.Run(`data-ingest endpoint secret created and token updated`, func(t *testing.T) { - instance := buildDynakube() - fakeClient := buildClient(instance) + instance := buildTestDynakube() + fakeClient := buildTestClient(instance) endpointSecretGenerator := NewEndpointGenerator(fakeClient, fakeClient, testNamespaceDynatrace, log) @@ -73,21 +73,21 @@ func TestGenerateDataIngestSecret_ForDynakube(t *testing.T) { assert.NoError(t, err) assert.Equal(t, true, upd) - updateSecret(t, fakeClient) + updateTestSecret(t, fakeClient) upd, err = endpointSecretGenerator.GenerateForNamespace(context.TODO(), testDynakubeName, testNamespace1) assert.NoError(t, err) assert.Equal(t, true, upd) - checkSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace1, testUpdatedTokenDataIngestSecret) + checkTestSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace1, testUpdatedTokenDataIngestSecret) - checkSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespace2) + checkTestSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespace2) - checkSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespaceDynatrace) + checkTestSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespaceDynatrace) }) t.Run(`data-ingest endpoint secret created and apiUrl updated`, func(t *testing.T) { - instance := buildDynakube() - fakeClient := buildClient(instance) + instance := buildTestDynakube() + fakeClient := buildTestClient(instance) endpointSecretGenerator := NewEndpointGenerator(fakeClient, fakeClient, testNamespaceDynatrace, log) @@ -95,22 +95,22 @@ func TestGenerateDataIngestSecret_ForDynakube(t *testing.T) { assert.NoError(t, err) assert.Equal(t, true, upd) - updateDynakube(t, fakeClient) + updateTestDynakube(t, fakeClient) upd, err = endpointSecretGenerator.GenerateForNamespace(context.TODO(), testDynakubeName, testNamespace1) assert.NoError(t, err) assert.Equal(t, true, upd) - checkSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace1, testUpdatedApiUrlDataIngestSecret) + checkTestSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace1, testUpdatedApiUrlDataIngestSecret) - checkSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespace2) + checkTestSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespace2) - checkSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespaceDynatrace) + checkTestSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespaceDynatrace) }) t.Run(`data-ingest endpoint secret created in all namespaces but not updated`, func(t *testing.T) { - instance := buildDynakube() - fakeClient := buildClient(instance) + instance := buildTestDynakube() + fakeClient := buildTestClient(instance) endpointSecretGenerator := NewEndpointGenerator(fakeClient, fakeClient, testNamespaceDynatrace, log) @@ -118,19 +118,19 @@ func TestGenerateDataIngestSecret_ForDynakube(t *testing.T) { assert.NoError(t, err) assert.Equal(t, true, upd) - checkSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace1, testDataIngestSecret) + checkTestSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace1, testDataIngestSecret) - checkSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace2, testDataIngestSecret) + checkTestSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace2, testDataIngestSecret) - checkSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespaceDynatrace) + checkTestSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespaceDynatrace) upd, err = endpointSecretGenerator.GenerateForDynakube(context.TODO(), instance) assert.NoError(t, err) assert.Equal(t, false, upd) }) t.Run(`data-ingest endpoint secret created in all namespaces and token updated`, func(t *testing.T) { - instance := buildDynakube() - fakeClient := buildClient(instance) + instance := buildTestDynakube() + fakeClient := buildTestClient(instance) endpointSecretGenerator := NewEndpointGenerator(fakeClient, fakeClient, testNamespaceDynatrace, log) @@ -138,21 +138,21 @@ func TestGenerateDataIngestSecret_ForDynakube(t *testing.T) { assert.NoError(t, err) assert.Equal(t, true, upd) - updateSecret(t, fakeClient) + updateTestSecret(t, fakeClient) upd, err = endpointSecretGenerator.GenerateForDynakube(context.TODO(), instance) assert.NoError(t, err) assert.Equal(t, true, upd) - checkSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace1, testUpdatedTokenDataIngestSecret) + checkTestSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace1, testUpdatedTokenDataIngestSecret) - checkSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace2, testUpdatedTokenDataIngestSecret) + checkTestSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace2, testUpdatedTokenDataIngestSecret) - checkSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespaceDynatrace) + checkTestSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespaceDynatrace) }) t.Run(`data-ingest endpoint secret created in all namespaces and apiUrl updated`, func(t *testing.T) { - instance := buildDynakube() - fakeClient := buildClient(instance) + instance := buildTestDynakube() + fakeClient := buildTestClient(instance) endpointSecretGenerator := NewEndpointGenerator(fakeClient, fakeClient, testNamespaceDynatrace, log) @@ -160,21 +160,21 @@ func TestGenerateDataIngestSecret_ForDynakube(t *testing.T) { assert.NoError(t, err) assert.Equal(t, true, upd) - newInstance := updatedDynakube() + newInstance := updatedTestDynakube() upd, err = endpointSecretGenerator.GenerateForDynakube(context.TODO(), newInstance) assert.NoError(t, err) assert.Equal(t, true, upd) - checkSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace1, testUpdatedApiUrlDataIngestSecret) + checkTestSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace1, testUpdatedApiUrlDataIngestSecret) - checkSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace2, testUpdatedApiUrlDataIngestSecret) + checkTestSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace2, testUpdatedApiUrlDataIngestSecret) - checkSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespaceDynatrace) + checkTestSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespaceDynatrace) }) } -func checkSecretExists(t *testing.T, fakeClient client.Client, secretName string, namespace string, data string) { +func checkTestSecretExists(t *testing.T, fakeClient client.Client, secretName string, namespace string, data string) { var testSecret corev1.Secret err := fakeClient.Get(context.TODO(), client.ObjectKey{Name: secretName, Namespace: namespace}, &testSecret) assert.NoError(t, err) @@ -184,14 +184,14 @@ func checkSecretExists(t *testing.T, fakeClient client.Client, secretName string assert.Equal(t, data, string(testSecret.Data["endpoint.properties"])) } -func checkSecretNotExists(t *testing.T, fakeClient client.Client, secretName string, namespace string) { +func checkTestSecretNotExists(t *testing.T, fakeClient client.Client, secretName string, namespace string) { var testSecret corev1.Secret err := fakeClient.Get(context.TODO(), client.ObjectKey{Name: secretName, Namespace: namespace}, &testSecret) assert.Error(t, err) assert.Nil(t, testSecret.Data) } -func updateSecret(t *testing.T, fakeClient client.Client) { +func updateTestSecret(t *testing.T, fakeClient client.Client) { updatedSecret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: testDynakubeName, @@ -208,7 +208,7 @@ func updateSecret(t *testing.T, fakeClient client.Client) { assert.NoError(t, err) } -func updatedDynakube() *dynatracev1beta1.DynaKube { +func updatedTestDynakube() *dynatracev1beta1.DynaKube { return &dynatracev1beta1.DynaKube{ ObjectMeta: metav1.ObjectMeta{ Name: testDynakubeName, @@ -220,14 +220,18 @@ func updatedDynakube() *dynatracev1beta1.DynaKube { } } -func updateDynakube(t *testing.T, fakeClient client.Client) { - updatedDynakube := updatedDynakube() +func updateTestDynakube(t *testing.T, fakeClient client.Client) { + var dk dynatracev1beta1.DynaKube + err := fakeClient.Get(context.TODO(), client.ObjectKey{Name: testDynakubeName, Namespace: testNamespaceDynatrace}, &dk) + assert.NoError(t, err) + + dk.Spec.APIURL = testUpdatedApiUrl - err := fakeClient.Update(context.TODO(), updatedDynakube) + err = fakeClient.Update(context.TODO(), &dk) assert.NoError(t, err) } -func buildDynakube() *dynatracev1beta1.DynaKube { +func buildTestDynakube() *dynatracev1beta1.DynaKube { return &dynatracev1beta1.DynaKube{ ObjectMeta: metav1.ObjectMeta{ Name: testDynakubeName, @@ -239,7 +243,7 @@ func buildDynakube() *dynatracev1beta1.DynaKube { } } -func buildClient(dk *dynatracev1beta1.DynaKube) client.Client { +func buildTestClient(dk *dynatracev1beta1.DynaKube) client.Client { return fake.NewClient(dk, &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ diff --git a/webhook/mutation/pod_mutator.go b/webhook/mutation/pod_mutator.go index d5764cfe89..21bd4b7a6a 100644 --- a/webhook/mutation/pod_mutator.go +++ b/webhook/mutation/pod_mutator.go @@ -12,7 +12,7 @@ import ( dynatracev1beta1 "github.com/Dynatrace/dynatrace-operator/api/v1beta1" dtcsi "github.com/Dynatrace/dynatrace-operator/controllers/csi" - endpoint "github.com/Dynatrace/dynatrace-operator/controllers/dataingestendpointsecret" + dtingestendpoint "github.com/Dynatrace/dynatrace-operator/controllers/ingestendpoint" "github.com/Dynatrace/dynatrace-operator/controllers/kubeobjects" "github.com/Dynatrace/dynatrace-operator/controllers/kubesystem" "github.com/Dynatrace/dynatrace-operator/deploymentmetadata" @@ -184,7 +184,7 @@ func (m *podMutator) Handle(ctx context.Context, req admission.Request) admissio return admission.Errored(http.StatusBadRequest, err) } - endpointGenerator := endpoint.NewEndpointGenerator(m.client, m.apiReader, m.namespace, log) + endpointGenerator := dtingestendpoint.NewEndpointGenerator(m.client, m.apiReader, m.namespace, log) var endpointSecret corev1.Secret if err := m.apiReader.Get(ctx, client.ObjectKey{Name: dtwebhook.SecretEndpointName, Namespace: ns.Name}, &endpointSecret); k8serrors.IsNotFound(err) { @@ -432,12 +432,12 @@ func updateContainer(c *corev1.Container, oa *dynatracev1beta1.DynaKube, Value: deploymentMetadata.AsString(), }, corev1.EnvVar{ - Name: endpoint.UrlSecretField, - Value: dataIngestFields[endpoint.UrlSecretField], + Name: dtingestendpoint.UrlSecretField, + Value: dataIngestFields[dtingestendpoint.UrlSecretField], }, corev1.EnvVar{ - Name: endpoint.TokenSecretField, - Value: dataIngestFields[endpoint.TokenSecretField], + Name: dtingestendpoint.TokenSecretField, + Value: dataIngestFields[dtingestendpoint.TokenSecretField], }, ) diff --git a/webhook/mutation/pod_mutator_test.go b/webhook/mutation/pod_mutator_test.go index c32eb8f987..58010e11eb 100644 --- a/webhook/mutation/pod_mutator_test.go +++ b/webhook/mutation/pod_mutator_test.go @@ -6,7 +6,7 @@ import ( dynatracev1beta1 "github.com/Dynatrace/dynatrace-operator/api/v1beta1" dtcsi "github.com/Dynatrace/dynatrace-operator/controllers/csi" - endpoint "github.com/Dynatrace/dynatrace-operator/controllers/dataingestendpointsecret" + dtingestendpoint "github.com/Dynatrace/dynatrace-operator/controllers/ingestendpoint" "github.com/Dynatrace/dynatrace-operator/dtclient" "github.com/Dynatrace/dynatrace-operator/mapper" "github.com/Dynatrace/dynatrace-operator/scheme" @@ -1184,8 +1184,8 @@ func buildResultPod(_ *testing.T) corev1.Pod { Env: []corev1.EnvVar{ {Name: "LD_PRELOAD", Value: "/opt/dynatrace/oneagent-paas/agent/lib64/liboneagentproc.so"}, {Name: "DT_DEPLOYMENT_METADATA", Value: "orchestration_tech=Operator-cloud_native_fullstack;script_version=snapshot;orchestrator_id="}, - {Name: endpoint.UrlSecretField, Value: "https://test-api-url.com/api/v2/metrics/ingest"}, - {Name: endpoint.TokenSecretField, Value: dataIngestToken}, + {Name: dtingestendpoint.UrlSecretField, Value: "https://test-api-url.com/api/v2/metrics/ingest"}, + {Name: dtingestendpoint.TokenSecretField, Value: dataIngestToken}, }, VolumeMounts: []corev1.VolumeMount{ {Name: "oneagent-share", MountPath: "/etc/ld.so.preload", SubPath: "ld.so.preload"}, From 0aaf195149027900d18de978b1cd659325e4b037 Mon Sep 17 00:00:00 2001 From: "adam.orcholski" Date: Tue, 16 Nov 2021 15:58:52 +0100 Subject: [PATCH 3/4] SecretEndpointName constant moved to ingestendpoint package. --- controllers/ingestendpoint/config.go | 6 ++++ controllers/ingestendpoint/secret.go | 5 ++- controllers/ingestendpoint/secret_test.go | 37 +++++++++++------------ webhook/config.go | 3 -- webhook/mutation/pod_mutator.go | 6 ++-- webhook/mutation/pod_mutator_test.go | 18 +++++------ 6 files changed, 38 insertions(+), 37 deletions(-) create mode 100644 controllers/ingestendpoint/config.go diff --git a/controllers/ingestendpoint/config.go b/controllers/ingestendpoint/config.go new file mode 100644 index 0000000000..7978ba545a --- /dev/null +++ b/controllers/ingestendpoint/config.go @@ -0,0 +1,6 @@ +package ingestendpoint + +const ( + // SecretEndpointName is the name of the secret where the Operator replicates data-ingest data (data-ingest url, data-ingest token). + SecretEndpointName = "dynatrace-data-ingest-endpoint" +) diff --git a/controllers/ingestendpoint/secret.go b/controllers/ingestendpoint/secret.go index b4cbc900a7..6eff8c7cfd 100644 --- a/controllers/ingestendpoint/secret.go +++ b/controllers/ingestendpoint/secret.go @@ -9,7 +9,6 @@ import ( "github.com/Dynatrace/dynatrace-operator/controllers/kubeobjects" "github.com/Dynatrace/dynatrace-operator/dtclient" "github.com/Dynatrace/dynatrace-operator/mapper" - "github.com/Dynatrace/dynatrace-operator/webhook" "github.com/go-logr/logr" "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" @@ -52,7 +51,7 @@ func (g *EndpointSecretGenerator) GenerateForNamespace(ctx context.Context, dkNa if err != nil { return false, err } - return kubeobjects.CreateOrUpdateSecretIfNotExists(g.client, g.apiReader, webhook.SecretEndpointName, targetNs, data, corev1.SecretTypeOpaque, g.logger) + return kubeobjects.CreateOrUpdateSecretIfNotExists(g.client, g.apiReader, SecretEndpointName, targetNs, data, corev1.SecretTypeOpaque, g.logger) } // GenerateForDynakube creates/updates the data-ingest-endpoint secret for EVERY namespace for the given dynakube. @@ -71,7 +70,7 @@ func (g *EndpointSecretGenerator) GenerateForDynakube(ctx context.Context, dk *d return false, err } for _, targetNs := range nsList { - if upd, err := kubeobjects.CreateOrUpdateSecretIfNotExists(g.client, g.apiReader, webhook.SecretEndpointName, targetNs.Name, data, corev1.SecretTypeOpaque, g.logger); err != nil { + if upd, err := kubeobjects.CreateOrUpdateSecretIfNotExists(g.client, g.apiReader, SecretEndpointName, targetNs.Name, data, corev1.SecretTypeOpaque, g.logger); err != nil { return upd, err } else if upd { anyUpdate = true diff --git a/controllers/ingestendpoint/secret_test.go b/controllers/ingestendpoint/secret_test.go index 87956085ac..5e8b6b5c79 100644 --- a/controllers/ingestendpoint/secret_test.go +++ b/controllers/ingestendpoint/secret_test.go @@ -8,7 +8,6 @@ import ( "github.com/Dynatrace/dynatrace-operator/logger" "github.com/Dynatrace/dynatrace-operator/mapper" "github.com/Dynatrace/dynatrace-operator/scheme/fake" - "github.com/Dynatrace/dynatrace-operator/webhook" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -57,11 +56,11 @@ func TestGenerateDataIngestSecret_ForDynakube(t *testing.T) { assert.NoError(t, err) assert.Equal(t, false, upd) - checkTestSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace1, testDataIngestSecret) + checkTestSecretExists(t, fakeClient, SecretEndpointName, testNamespace1, testDataIngestSecret) - checkTestSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespace2) + checkTestSecretNotExists(t, fakeClient, SecretEndpointName, testNamespace2) - checkTestSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespaceDynatrace) + checkTestSecretNotExists(t, fakeClient, SecretEndpointName, testNamespaceDynatrace) }) t.Run(`data-ingest endpoint secret created and token updated`, func(t *testing.T) { instance := buildTestDynakube() @@ -79,11 +78,11 @@ func TestGenerateDataIngestSecret_ForDynakube(t *testing.T) { assert.NoError(t, err) assert.Equal(t, true, upd) - checkTestSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace1, testUpdatedTokenDataIngestSecret) + checkTestSecretExists(t, fakeClient, SecretEndpointName, testNamespace1, testUpdatedTokenDataIngestSecret) - checkTestSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespace2) + checkTestSecretNotExists(t, fakeClient, SecretEndpointName, testNamespace2) - checkTestSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespaceDynatrace) + checkTestSecretNotExists(t, fakeClient, SecretEndpointName, testNamespaceDynatrace) }) t.Run(`data-ingest endpoint secret created and apiUrl updated`, func(t *testing.T) { instance := buildTestDynakube() @@ -101,11 +100,11 @@ func TestGenerateDataIngestSecret_ForDynakube(t *testing.T) { assert.NoError(t, err) assert.Equal(t, true, upd) - checkTestSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace1, testUpdatedApiUrlDataIngestSecret) + checkTestSecretExists(t, fakeClient, SecretEndpointName, testNamespace1, testUpdatedApiUrlDataIngestSecret) - checkTestSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespace2) + checkTestSecretNotExists(t, fakeClient, SecretEndpointName, testNamespace2) - checkTestSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespaceDynatrace) + checkTestSecretNotExists(t, fakeClient, SecretEndpointName, testNamespaceDynatrace) }) t.Run(`data-ingest endpoint secret created in all namespaces but not updated`, func(t *testing.T) { @@ -118,11 +117,11 @@ func TestGenerateDataIngestSecret_ForDynakube(t *testing.T) { assert.NoError(t, err) assert.Equal(t, true, upd) - checkTestSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace1, testDataIngestSecret) + checkTestSecretExists(t, fakeClient, SecretEndpointName, testNamespace1, testDataIngestSecret) - checkTestSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace2, testDataIngestSecret) + checkTestSecretExists(t, fakeClient, SecretEndpointName, testNamespace2, testDataIngestSecret) - checkTestSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespaceDynatrace) + checkTestSecretNotExists(t, fakeClient, SecretEndpointName, testNamespaceDynatrace) upd, err = endpointSecretGenerator.GenerateForDynakube(context.TODO(), instance) assert.NoError(t, err) @@ -144,11 +143,11 @@ func TestGenerateDataIngestSecret_ForDynakube(t *testing.T) { assert.NoError(t, err) assert.Equal(t, true, upd) - checkTestSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace1, testUpdatedTokenDataIngestSecret) + checkTestSecretExists(t, fakeClient, SecretEndpointName, testNamespace1, testUpdatedTokenDataIngestSecret) - checkTestSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace2, testUpdatedTokenDataIngestSecret) + checkTestSecretExists(t, fakeClient, SecretEndpointName, testNamespace2, testUpdatedTokenDataIngestSecret) - checkTestSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespaceDynatrace) + checkTestSecretNotExists(t, fakeClient, SecretEndpointName, testNamespaceDynatrace) }) t.Run(`data-ingest endpoint secret created in all namespaces and apiUrl updated`, func(t *testing.T) { instance := buildTestDynakube() @@ -166,11 +165,11 @@ func TestGenerateDataIngestSecret_ForDynakube(t *testing.T) { assert.NoError(t, err) assert.Equal(t, true, upd) - checkTestSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace1, testUpdatedApiUrlDataIngestSecret) + checkTestSecretExists(t, fakeClient, SecretEndpointName, testNamespace1, testUpdatedApiUrlDataIngestSecret) - checkTestSecretExists(t, fakeClient, webhook.SecretEndpointName, testNamespace2, testUpdatedApiUrlDataIngestSecret) + checkTestSecretExists(t, fakeClient, SecretEndpointName, testNamespace2, testUpdatedApiUrlDataIngestSecret) - checkTestSecretNotExists(t, fakeClient, webhook.SecretEndpointName, testNamespaceDynatrace) + checkTestSecretNotExists(t, fakeClient, SecretEndpointName, testNamespaceDynatrace) }) } diff --git a/webhook/config.go b/webhook/config.go index 5e09433b90..43a04b5f88 100644 --- a/webhook/config.go +++ b/webhook/config.go @@ -41,7 +41,4 @@ const ( // InstallContainerName is the name used for the install container InstallContainerName = "install-oneagent" - - // SecretEndpointName is the name of the secret where the Operator replicates data-ingest data (data-ingest url, data-ingest token). - SecretEndpointName = "dynatrace-data-ingest-endpoint" ) diff --git a/webhook/mutation/pod_mutator.go b/webhook/mutation/pod_mutator.go index 21bd4b7a6a..eab5ce1853 100644 --- a/webhook/mutation/pod_mutator.go +++ b/webhook/mutation/pod_mutator.go @@ -187,7 +187,7 @@ func (m *podMutator) Handle(ctx context.Context, req admission.Request) admissio endpointGenerator := dtingestendpoint.NewEndpointGenerator(m.client, m.apiReader, m.namespace, log) var endpointSecret corev1.Secret - if err := m.apiReader.Get(ctx, client.ObjectKey{Name: dtwebhook.SecretEndpointName, Namespace: ns.Name}, &endpointSecret); k8serrors.IsNotFound(err) { + if err := m.apiReader.Get(ctx, client.ObjectKey{Name: dtingestendpoint.SecretEndpointName, Namespace: ns.Name}, &endpointSecret); k8serrors.IsNotFound(err) { if _, err := endpointGenerator.GenerateForNamespace(ctx, dkName, ns.Name); err != nil { log.Error(err, "failed to create the data-ingest endpoint secret before pod injection") return admission.Errored(http.StatusBadRequest, err) @@ -199,7 +199,7 @@ func (m *podMutator) Handle(ctx context.Context, req admission.Request) admissio dataIngestFields, err := endpointGenerator.PrepareFields(ctx, &dk) if err != nil { - log.Error(err, "AA failed to query the data-ingest endpoint secret before pod injection") + log.Error(err, "failed to query the data-ingest endpoint secret before pod injection") return admission.Errored(http.StatusBadRequest, err) } @@ -299,7 +299,7 @@ func (m *podMutator) Handle(ctx context.Context, req admission.Request) admissio Name: "data-ingest-endpoint", VolumeSource: corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ - SecretName: dtwebhook.SecretEndpointName, + SecretName: dtingestendpoint.SecretEndpointName, }, }, }) diff --git a/webhook/mutation/pod_mutator_test.go b/webhook/mutation/pod_mutator_test.go index 58010e11eb..d7d37b1576 100644 --- a/webhook/mutation/pod_mutator_test.go +++ b/webhook/mutation/pod_mutator_test.go @@ -135,7 +135,7 @@ func createPodInjector(_ *testing.T, decoder *admission.Decoder) (*podMutator, * }, &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: dtwebhook.SecretEndpointName, + Name: dtingestendpoint.SecretEndpointName, Namespace: "test-namespace", }, }, @@ -363,7 +363,7 @@ func TestUseImmutableImage(t *testing.T) { }, &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: dtwebhook.SecretEndpointName, + Name: dtingestendpoint.SecretEndpointName, Namespace: "test-namespace", }, }, @@ -486,7 +486,7 @@ func TestUseImmutableImage(t *testing.T) { }, &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: dtwebhook.SecretEndpointName, + Name: dtingestendpoint.SecretEndpointName, Namespace: "test-namespace", }, }, @@ -607,7 +607,7 @@ func TestUseImmutableImageWithCSI(t *testing.T) { }, &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: dtwebhook.SecretEndpointName, + Name: dtingestendpoint.SecretEndpointName, Namespace: "test-namespace", }, }, @@ -720,7 +720,7 @@ func TestUseImmutableImageWithCSI(t *testing.T) { }, &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: dtwebhook.SecretEndpointName, + Name: dtingestendpoint.SecretEndpointName, Namespace: "test-namespace", }, }, @@ -834,7 +834,7 @@ func TestUseImmutableImageWithCSI(t *testing.T) { }, &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: dtwebhook.SecretEndpointName, + Name: dtingestendpoint.SecretEndpointName, Namespace: "test-namespace", }, }, @@ -945,7 +945,7 @@ func TestAgentVersion(t *testing.T) { }, &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: dtwebhook.SecretEndpointName, + Name: dtingestendpoint.SecretEndpointName, Namespace: "test-namespace", }, }, @@ -1064,7 +1064,7 @@ func TestAgentVersionWithCSI(t *testing.T) { }, &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: dtwebhook.SecretEndpointName, + Name: dtingestendpoint.SecretEndpointName, Namespace: "test-namespace", }, }, @@ -1225,7 +1225,7 @@ func buildResultPod(_ *testing.T) corev1.Pod { Name: "data-ingest-endpoint", VolumeSource: corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ - SecretName: dtwebhook.SecretEndpointName, + SecretName: dtingestendpoint.SecretEndpointName, }, }, }, From a3890d04a5bcaedc18af8869791bdc9c30ff8fdf Mon Sep 17 00:00:00 2001 From: "adam.orcholski" Date: Wed, 17 Nov 2021 14:52:05 +0100 Subject: [PATCH 4/4] creation of test secrets moved to a separate function webhook doesn't crash if dataIngestToken doesn't exist --- controllers/ingestendpoint/secret.go | 7 +- webhook/mutation/pod_mutator_test.go | 219 +++++---------------------- 2 files changed, 41 insertions(+), 185 deletions(-) diff --git a/controllers/ingestendpoint/secret.go b/controllers/ingestendpoint/secret.go index 6eff8c7cfd..d0b578b668 100644 --- a/controllers/ingestendpoint/secret.go +++ b/controllers/ingestendpoint/secret.go @@ -106,8 +106,13 @@ func (g *EndpointSecretGenerator) PrepareFields(ctx context.Context, dk *dynatra return nil, errors.WithMessage(err, "failed to query tokens") } + dataIngestToken := "" + if token, ok := tokens.Data[dtclient.DynatraceDataIngestToken]; ok { + dataIngestToken = string(token) + } + return map[string]string{ UrlSecretField: fmt.Sprintf("%s/v2/metrics/ingest", dk.Spec.APIURL), - TokenSecretField: string(tokens.Data[dtclient.DynatraceDataIngestToken]), + TokenSecretField: dataIngestToken, }, nil } diff --git a/webhook/mutation/pod_mutator_test.go b/webhook/mutation/pod_mutator_test.go index d7d37b1576..d227b14aec 100644 --- a/webhook/mutation/pod_mutator_test.go +++ b/webhook/mutation/pod_mutator_test.go @@ -23,6 +23,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/json" "k8s.io/client-go/tools/record" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) @@ -126,29 +127,7 @@ func createPodInjector(_ *testing.T, decoder *admission.Decoder) (*podMutator, * }, }, ), - apiReader: fake.NewClient( - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: dtwebhook.SecretConfigName, - Namespace: "test-namespace", - }, - }, - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: dtingestendpoint.SecretEndpointName, - Namespace: "test-namespace", - }, - }, - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: dynakubeName, - Namespace: "dynatrace", - }, - Data: map[string][]byte{ - dtclient.DynatraceDataIngestToken: []byte(dataIngestToken), - }, - }, - ), + apiReader: buildTestSecrets(), decoder: decoder, image: "test-api-url.com/linux/codemodule", namespace: "dynatrace", @@ -354,29 +333,7 @@ func TestUseImmutableImage(t *testing.T) { }, }, ), - apiReader: fake.NewClient( - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: dtwebhook.SecretConfigName, - Namespace: "test-namespace", - }, - }, - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: dtingestendpoint.SecretEndpointName, - Namespace: "test-namespace", - }, - }, - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: dynakubeName, - Namespace: "dynatrace", - }, - Data: map[string][]byte{ - dtclient.DynatraceDataIngestToken: []byte(dataIngestToken), - }, - }, - ), + apiReader: buildTestSecrets(), decoder: decoder, image: "test-image", namespace: "dynatrace", @@ -477,29 +434,7 @@ func TestUseImmutableImage(t *testing.T) { }, }, ), - apiReader: fake.NewClient( - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: dtwebhook.SecretConfigName, - Namespace: "test-namespace", - }, - }, - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: dtingestendpoint.SecretEndpointName, - Namespace: "test-namespace", - }, - }, - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: dynakubeName, - Namespace: "dynatrace", - }, - Data: map[string][]byte{ - dtclient.DynatraceDataIngestToken: []byte(dataIngestToken), - }, - }, - ), + apiReader: buildTestSecrets(), decoder: decoder, image: "test-image", namespace: "dynatrace", @@ -598,29 +533,7 @@ func TestUseImmutableImageWithCSI(t *testing.T) { }, }, ), - apiReader: fake.NewClient( - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: dtwebhook.SecretConfigName, - Namespace: "test-namespace", - }, - }, - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: dtingestendpoint.SecretEndpointName, - Namespace: "test-namespace", - }, - }, - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: dynakubeName, - Namespace: "dynatrace", - }, - Data: map[string][]byte{ - dtclient.DynatraceDataIngestToken: []byte(dataIngestToken), - }, - }, - ), + apiReader: buildTestSecrets(), decoder: decoder, image: "test-image", namespace: "dynatrace", @@ -711,29 +624,7 @@ func TestUseImmutableImageWithCSI(t *testing.T) { }, }, ), - apiReader: fake.NewClient( - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: dtwebhook.SecretConfigName, - Namespace: "test-namespace", - }, - }, - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: dtingestendpoint.SecretEndpointName, - Namespace: "test-namespace", - }, - }, - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: dynakubeName, - Namespace: "dynatrace", - }, - Data: map[string][]byte{ - dtclient.DynatraceDataIngestToken: []byte(dataIngestToken), - }, - }, - ), + apiReader: buildTestSecrets(), decoder: decoder, image: "test-image", namespace: "dynatrace", @@ -825,29 +716,7 @@ func TestUseImmutableImageWithCSI(t *testing.T) { }, }, ), - apiReader: fake.NewClient( - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: dtwebhook.SecretConfigName, - Namespace: "test-namespace", - }, - }, - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: dtingestendpoint.SecretEndpointName, - Namespace: "test-namespace", - }, - }, - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: dynakubeName, - Namespace: "dynatrace", - }, - Data: map[string][]byte{ - dtclient.DynatraceDataIngestToken: []byte(dataIngestToken), - }, - }, - ), + apiReader: buildTestSecrets(), decoder: decoder, image: "test-image", namespace: "dynatrace", @@ -936,29 +805,7 @@ func TestAgentVersion(t *testing.T) { }, }, ), - apiReader: fake.NewClient( - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: dtwebhook.SecretConfigName, - Namespace: "test-namespace", - }, - }, - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: dtingestendpoint.SecretEndpointName, - Namespace: "test-namespace", - }, - }, - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: dynakubeName, - Namespace: "dynatrace", - }, - Data: map[string][]byte{ - dtclient.DynatraceDataIngestToken: []byte(dataIngestToken), - }, - }, - ), + apiReader: buildTestSecrets(), decoder: decoder, image: "test-image", namespace: "dynatrace", @@ -1055,29 +902,7 @@ func TestAgentVersionWithCSI(t *testing.T) { }, }, ), - apiReader: fake.NewClient( - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: dtwebhook.SecretConfigName, - Namespace: "test-namespace", - }, - }, - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: dtingestendpoint.SecretEndpointName, - Namespace: "test-namespace", - }, - }, - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: dynakubeName, - Namespace: "dynatrace", - }, - Data: map[string][]byte{ - dtclient.DynatraceDataIngestToken: []byte(dataIngestToken), - }, - }, - ), + apiReader: buildTestSecrets(), decoder: decoder, image: "test-image", namespace: "dynatrace", @@ -1352,3 +1177,29 @@ func TestInstrumentThirdPartyContainers(t *testing.T) { }, ) } + +func buildTestSecrets() client.Client { + return fake.NewClient( + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: dtwebhook.SecretConfigName, + Namespace: "test-namespace", + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: dtingestendpoint.SecretEndpointName, + Namespace: "test-namespace", + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: dynakubeName, + Namespace: "dynatrace", + }, + Data: map[string][]byte{ + dtclient.DynatraceDataIngestToken: []byte(dataIngestToken), + }, + }, + ) +}