diff --git a/.gitignore b/.gitignore index a878e490aa..d685d5e243 100644 --- a/.gitignore +++ b/.gitignore @@ -105,3 +105,5 @@ kind-logs-* /csi.db test/testdata/secrets/* + +local/ diff --git a/src/api/v1beta1/properties.go b/src/api/v1beta1/properties.go index 1f11798418..9e818b7ddb 100644 --- a/src/api/v1beta1/properties.go +++ b/src/api/v1beta1/properties.go @@ -29,11 +29,13 @@ import ( const ( // PullSecretSuffix is the suffix appended to the DynaKube name to n. - PullSecretSuffix = "-pull-secret" - ActiveGateTenantSecretSuffix = "-activegate-tenant-secret" - OneAgentTenantSecretSuffix = "-oneagent-tenant-secret" - AuthTokenSecretSuffix = "-activegate-authtoken-secret" - PodNameOsAgent = "oneagent" + PullSecretSuffix = "-pull-secret" + ActiveGateTenantSecretSuffix = "-activegate-tenant-secret" + OneAgentTenantSecretSuffix = "-oneagent-tenant-secret" + OneAgentConnectionInfoConfigMapSuffix = "-oneagent-connection-info" + ActiveGateConnectionInfoConfigMapSuffix = "-activegate-connection-info" + AuthTokenSecretSuffix = "-activegate-authtoken-secret" + PodNameOsAgent = "oneagent" defaultActiveGateImage = "/linux/activegate:latest" defaultStatsDImage = "/linux/dynatrace-datasource-statsd:latest" @@ -198,6 +200,14 @@ func (dk *DynaKube) ActiveGateAuthTokenSecret() string { return dk.Name + AuthTokenSecretSuffix } +func (dk *DynaKube) ActiveGateConnectionInfoConfigMapName() string { + return dk.Name + ActiveGateConnectionInfoConfigMapSuffix +} + +func (dk *DynaKube) OneAgentConnectionInfoConfigMapName() string { + return dk.Name + OneAgentConnectionInfoConfigMapSuffix +} + // PullSecret returns the name of the pull secret to be used for immutable images. func (dk *DynaKube) PullSecret() string { if dk.Spec.CustomPullSecret != "" { @@ -438,7 +448,8 @@ func (dk *DynaKube) CommunicationHosts() []dtclient.CommunicationHost { return communicationHosts } -func (dk *DynaKube) TenantUUID() (string, error) { +// TenantUUIDFromApiUrl gets the tenantUUID from the ApiUrl present in the struct, if the tenant is aliased then the alias will be returned +func (dk *DynaKube) TenantUUIDFromApiUrl() (string, error) { return tenantUUID(dk.Spec.APIURL) } diff --git a/src/builder/builder.go b/src/builder/builder.go index 2300064587..4c1dbeed8a 100644 --- a/src/builder/builder.go +++ b/src/builder/builder.go @@ -1,13 +1,13 @@ package builder type Builder[T any] interface { - Build() T + Build() (T, error) AddModifier(...Modifier[T]) Builder[T] } type Modifier[T any] interface { Enabled() bool - Modify(*T) + Modify(*T) error } type GenericBuilder[T any] struct { @@ -17,17 +17,20 @@ type GenericBuilder[T any] struct { var _ Builder[any] = (*GenericBuilder[any])(nil) -func (b GenericBuilder[T]) Build() T { +func (b GenericBuilder[T]) Build() (T, error) { + var data T if b.data == nil { - var data T b.data = &data } for _, m := range b.modifiers { if m.Enabled() { - m.Modify(b.data) + err := m.Modify(b.data) + if err != nil { + return *b.data, err + } } } - return *b.data + return *b.data, nil } func (b *GenericBuilder[T]) AddModifier(modifiers ...Modifier[T]) Builder[T] { diff --git a/src/builder/builder_test.go b/src/builder/builder_test.go index 93d6805585..bf179471ba 100644 --- a/src/builder/builder_test.go +++ b/src/builder/builder_test.go @@ -11,7 +11,8 @@ import ( func TestStatefulsetBuilder(t *testing.T) { t.Run("Simple, no modifiers", func(t *testing.T) { b := GenericBuilder[mocks.DataMock]{} - actual := b.Build() + actual, err := b.Build() + assert.NoError(t, err) expected := mocks.DataMock{} assert.Equal(t, expected, actual) }) @@ -19,10 +20,10 @@ func TestStatefulsetBuilder(t *testing.T) { b := GenericBuilder[mocks.DataMock]{} modifierMock := mocks.NewModifierMock[mocks.DataMock]() - modifierMock.On("Modify", mock.Anything).Return() + modifierMock.On("Modify", mock.Anything).Return(nil) modifierMock.On("Enabled").Return(true) - actual := b.AddModifier(modifierMock).Build() + actual, _ := b.AddModifier(modifierMock).Build() modifierMock.AssertNumberOfCalls(t, "Modify", 1) @@ -33,10 +34,10 @@ func TestStatefulsetBuilder(t *testing.T) { b := GenericBuilder[mocks.DataMock]{} modifierMock := mocks.NewModifierMock[mocks.DataMock]() - modifierMock.On("Modify", mock.Anything).Return() + modifierMock.On("Modify", mock.Anything).Return(nil) modifierMock.On("Enabled").Return(false) - actual := b.AddModifier(modifierMock).Build() + actual, _ := b.AddModifier(modifierMock).Build() modifierMock.AssertNumberOfCalls(t, "Modify", 0) @@ -47,13 +48,13 @@ func TestStatefulsetBuilder(t *testing.T) { b := GenericBuilder[mocks.DataMock]{} modifierMock0 := mocks.NewModifierMock[mocks.DataMock]() - modifierMock0.On("Modify", mock.Anything).Return() + modifierMock0.On("Modify", mock.Anything).Return(nil) modifierMock0.On("Enabled").Return(true) modifierMock1 := mocks.NewModifierMock[mocks.DataMock]() - modifierMock1.On("Modify", mock.Anything).Return() + modifierMock1.On("Modify", mock.Anything).Return(nil) modifierMock1.On("Enabled").Return(true) - actual := b.AddModifier(modifierMock0, modifierMock0, modifierMock1).Build() + actual, _ := b.AddModifier(modifierMock0, modifierMock0, modifierMock1).Build() modifierMock0.AssertNumberOfCalls(t, "Modify", 2) modifierMock1.AssertNumberOfCalls(t, "Modify", 1) @@ -65,13 +66,13 @@ func TestStatefulsetBuilder(t *testing.T) { b := GenericBuilder[mocks.DataMock]{} modifierMock0 := mocks.NewModifierMock[mocks.DataMock]() - modifierMock0.On("Modify", mock.Anything).Return() + modifierMock0.On("Modify", mock.Anything).Return(nil) modifierMock0.On("Enabled").Return(true) modifierMock1 := mocks.NewModifierMock[mocks.DataMock]() - modifierMock1.On("Modify", mock.Anything).Return() + modifierMock1.On("Modify", mock.Anything).Return(nil) modifierMock1.On("Enabled").Return(true) - actual := b.AddModifier(modifierMock0, modifierMock0).AddModifier(modifierMock1).Build() + actual, _ := b.AddModifier(modifierMock0, modifierMock0).AddModifier(modifierMock1).Build() modifierMock0.AssertNumberOfCalls(t, "Modify", 2) modifierMock1.AssertNumberOfCalls(t, "Modify", 1) diff --git a/src/builder/mocks/modifier.go b/src/builder/mocks/modifier.go index ddfaee5436..8aa48097d3 100644 --- a/src/builder/mocks/modifier.go +++ b/src/builder/mocks/modifier.go @@ -15,6 +15,7 @@ func (m *ModifierMock[T]) Enabled() bool { return args.Bool(0) } -func (m *ModifierMock[T]) Modify(data *T) { - m.Called(data) +func (m *ModifierMock[T]) Modify(data *T) error { + args := m.Called(data) + return args.Error(0) } diff --git a/src/controllers/csi/gc/reconciler.go b/src/controllers/csi/gc/reconciler.go index 7d5ca23a37..f3c6516c40 100644 --- a/src/controllers/csi/gc/reconciler.go +++ b/src/controllers/csi/gc/reconciler.go @@ -127,7 +127,7 @@ func getDynakubeFromRequest(ctx context.Context, apiReader client.Reader, reques } func collectGCInfo(dynakube dynatracev1beta1.DynaKube, dynakubeList *dynatracev1beta1.DynaKubeList) (*garbageCollectionInfo, error) { - tenantUUID, err := dynakube.TenantUUID() + tenantUUID, err := dynakube.TenantUUIDFromApiUrl() if err != nil { log.Info("failed to get tenantUUID of DynaKube, checking later") return nil, nil @@ -186,7 +186,7 @@ func isUpgrading(dkMetadata *metadata.Dynakube, filteredDynakubes map[string]dyn func getAllPinnedVersionsForTenantUUID(dynakubeList *dynatracev1beta1.DynaKubeList, tenantUUID string) (pinnedVersionSet, error) { pinnedVersions := make(pinnedVersionSet) for _, dynakube := range dynakubeList.Items { - uuid, err := dynakube.TenantUUID() + uuid, err := dynakube.TenantUUIDFromApiUrl() if err != nil { log.Error(err, "failed to get tenantUUID of DynaKube") continue diff --git a/src/controllers/csi/provisioner/agent.go b/src/controllers/csi/provisioner/agent.go index 8157a68bf9..e4f839e6b4 100644 --- a/src/controllers/csi/provisioner/agent.go +++ b/src/controllers/csi/provisioner/agent.go @@ -36,7 +36,7 @@ func newAgentUrlUpdater( recorder record.EventRecorder, dk *dynatracev1beta1.DynaKube) (*agentUpdater, error) { - tenantUUID, err := dk.TenantUUID() + tenantUUID, err := dk.TenantUUIDFromApiUrl() if err != nil { return nil, err } @@ -66,8 +66,7 @@ func newAgentImageUpdater( db metadata.Access, recorder record.EventRecorder, dk *dynatracev1beta1.DynaKube) (*agentUpdater, error) { - - tenantUUID, err := dk.TenantUUID() + tenantUUID, err := dk.TenantUUIDFromApiUrl() if err != nil { return nil, err } diff --git a/src/controllers/csi/provisioner/controller.go b/src/controllers/csi/provisioner/controller.go index 9e527d9129..b95d45046a 100644 --- a/src/controllers/csi/provisioner/controller.go +++ b/src/controllers/csi/provisioner/controller.go @@ -232,7 +232,7 @@ func (provisioner *OneAgentProvisioner) handleMetadata(ctx context.Context, dk * oldDynakubeMetadata = *dynakubeMetadata } - tenantUUID, err := dk.TenantUUID() + tenantUUID, err := dk.TenantUUIDFromApiUrl() if err != nil { return nil, metadata.Dynakube{}, err } @@ -295,13 +295,13 @@ func (provisioner *OneAgentProvisioner) getDynaKube(ctx context.Context, name ty return &dk, err } -func (provisioner *OneAgentProvisioner) createCSIDirectories(tenantUuid string) error { - tenantDir := provisioner.path.TenantDir(tenantUuid) +func (provisioner *OneAgentProvisioner) createCSIDirectories(tenantUUID string) error { + tenantDir := provisioner.path.TenantDir(tenantUUID) if err := provisioner.fs.MkdirAll(tenantDir, 0755); err != nil { return fmt.Errorf("failed to create directory %s: %w", tenantDir, err) } - agentBinaryDir := provisioner.path.AgentBinaryDir(tenantUuid) + agentBinaryDir := provisioner.path.AgentBinaryDir(tenantUUID) if err := provisioner.fs.MkdirAll(agentBinaryDir, 0755); err != nil { return fmt.Errorf("failed to create directory %s: %w", agentBinaryDir, err) } diff --git a/src/controllers/csi/provisioner/controller_test.go b/src/controllers/csi/provisioner/controller_test.go index f3de6760b9..364f44a3a8 100644 --- a/src/controllers/csi/provisioner/controller_test.go +++ b/src/controllers/csi/provisioner/controller_test.go @@ -52,7 +52,7 @@ func (fs *mkDirAllErrorFs) MkdirAll(_ string, _ os.FileMode) error { return fmt.Errorf(errorMsg) } -func TestOneAgentProvisioner_Reconcile(t *testing.T) { +func TestOneAgentProvisioner_Reconcile(t *testing.T) { //nolint:revive ctx := context.TODO() dynakubeName := "test-dk" @@ -170,29 +170,46 @@ func TestOneAgentProvisioner_Reconcile(t *testing.T) { require.Len(t, dynakubeMetadatas, 0) }) t.Run(`host monitoring used`, func(t *testing.T) { + fakeClient := fake.NewClient( + &dynatracev1beta1.DynaKube{ + ObjectMeta: metav1.ObjectMeta{ + Name: dkName, + }, + Spec: dynatracev1beta1.DynaKubeSpec{ + APIURL: testAPIURL, + OneAgent: dynatracev1beta1.OneAgentSpec{ + HostMonitoring: &dynatracev1beta1.HostInjectSpec{}, + }, + }, + }, + &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: dkName, + }, + Data: map[string][]byte{ + dtclient.DynatraceApiToken: []byte("api-token"), + }, + }, + ) + mockClient := &dtclient.MockDynatraceClient{} + mockDtcBuilder := &dynatraceclient.StubBuilder{ + DynatraceClient: mockClient, + } + gc := &CSIGarbageCollectorMock{} gc.On("Reconcile").Return(reconcile.Result{}, nil) db := metadata.FakeMemoryDB() + provisioner := &OneAgentProvisioner{ - apiReader: fake.NewClient( - &dynatracev1beta1.DynaKube{ - ObjectMeta: metav1.ObjectMeta{ - Name: dynakubeName, - }, - Spec: dynatracev1beta1.DynaKubeSpec{ - APIURL: testAPIURL, - OneAgent: dynatracev1beta1.OneAgentSpec{ - HostMonitoring: &dynatracev1beta1.HostInjectSpec{}, - }, - }, - }, - ), - fs: afero.NewMemMapFs(), - db: db, - gc: gc, - path: metadata.PathResolver{}, + apiReader: fakeClient, + client: fakeClient, + fs: afero.NewMemMapFs(), + db: db, + gc: gc, + path: metadata.PathResolver{}, + dynatraceClientBuilder: mockDtcBuilder, } - result, err := provisioner.Reconcile(context.TODO(), reconcile.Request{NamespacedName: types.NamespacedName{Name: dynakubeName}}) + result, err := provisioner.Reconcile(context.TODO(), reconcile.Request{NamespacedName: types.NamespacedName{Name: dkName}}) require.NoError(t, err) require.NotNil(t, result) diff --git a/src/controllers/dynakube/activegate/consts/consts.go b/src/controllers/dynakube/activegate/consts/consts.go index 0e3241dc5b..b501d7c8e7 100644 --- a/src/controllers/dynakube/activegate/consts/consts.go +++ b/src/controllers/dynakube/activegate/consts/consts.go @@ -29,6 +29,7 @@ const ( EnvDtServer = "DT_SERVER" EnvDtTenant = "DT_TENANT" + EnvDtCommunication = "DT_COMMUNICATION" EnvDtCapabilities = "DT_CAPABILITIES" EnvDtIdSeedNamespace = "DT_ID_SEED_NAMESPACE" EnvDtIdSeedClusterId = "DT_ID_SEED_K8S_CLUSTER_ID" diff --git a/src/controllers/dynakube/activegate/internal/statefulset/builder/builder_test.go b/src/controllers/dynakube/activegate/internal/statefulset/builder/builder_test.go index ebb16b846a..53bbe0f5f0 100644 --- a/src/controllers/dynakube/activegate/internal/statefulset/builder/builder_test.go +++ b/src/controllers/dynakube/activegate/internal/statefulset/builder/builder_test.go @@ -21,14 +21,16 @@ func (m *ModifierMock) Enabled() bool { return args.Bool(0) } -func (m *ModifierMock) Modify(sts *appsv1.StatefulSet) { - m.Called(sts) +func (m *ModifierMock) Modify(sts *appsv1.StatefulSet) error { + args := m.Called(sts) + return args.Error(0) } func TestBuilder(t *testing.T) { t.Run("Simple, no modifiers", func(t *testing.T) { b := Builder{} - actual := b.Build() + actual, err := b.Build() + assert.NoError(t, err) expected := appsv1.StatefulSet{} assert.Equal(t, expected, actual) }) @@ -36,10 +38,10 @@ func TestBuilder(t *testing.T) { b := Builder{} modifierMock := NewModifierMock() - modifierMock.On("Modify", mock.Anything).Return() + modifierMock.On("Modify", mock.Anything).Return(nil) modifierMock.On("Enabled").Return(true) - actual := b.AddModifier(modifierMock).Build() + actual, _ := b.AddModifier(modifierMock).Build() modifierMock.AssertNumberOfCalls(t, "Modify", 1) @@ -50,10 +52,10 @@ func TestBuilder(t *testing.T) { b := Builder{} modifierMock := NewModifierMock() - modifierMock.On("Modify", mock.Anything).Return() + modifierMock.On("Modify", mock.Anything).Return(nil) modifierMock.On("Enabled").Return(false) - actual := b.AddModifier(modifierMock).Build() + actual, _ := b.AddModifier(modifierMock).Build() modifierMock.AssertNumberOfCalls(t, "Modify", 0) @@ -65,12 +67,12 @@ func TestBuilder(t *testing.T) { modifierMock0 := NewModifierMock() modifierMock0.On("Enabled").Return(true) - modifierMock0.On("Modify", mock.Anything).Return() + modifierMock0.On("Modify", mock.Anything).Return(nil) modifierMock1 := NewModifierMock() modifierMock1.On("Enabled").Return(true) - modifierMock1.On("Modify", mock.Anything).Return() + modifierMock1.On("Modify", mock.Anything).Return(nil) - actual := b.AddModifier(modifierMock0, modifierMock0, modifierMock1).Build() + actual, _ := b.AddModifier(modifierMock0, modifierMock0, modifierMock1).Build() modifierMock0.AssertNumberOfCalls(t, "Modify", 2) modifierMock1.AssertNumberOfCalls(t, "Modify", 1) diff --git a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/authtoken.go b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/authtoken.go index 6f94983792..c0539e3da9 100644 --- a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/authtoken.go +++ b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/authtoken.go @@ -28,10 +28,11 @@ func (mod AuthTokenModifier) Enabled() bool { return mod.dynakube.UseActiveGateAuthToken() } -func (mod AuthTokenModifier) Modify(sts *appsv1.StatefulSet) { +func (mod AuthTokenModifier) Modify(sts *appsv1.StatefulSet) error { baseContainer := kubeobjects.FindContainerInPodSpec(&sts.Spec.Template.Spec, consts.ActiveGateContainerName) sts.Spec.Template.Spec.Volumes = append(sts.Spec.Template.Spec.Volumes, mod.getVolumes()...) baseContainer.VolumeMounts = append(baseContainer.VolumeMounts, mod.getVolumeMounts()...) + return nil } func (mod AuthTokenModifier) getVolumes() []corev1.Volume { diff --git a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/authtoken_test.go b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/authtoken_test.go index fb46fdc209..f26492275b 100644 --- a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/authtoken_test.go +++ b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/authtoken_test.go @@ -43,7 +43,7 @@ func TestAuthTokenModify(t *testing.T) { mod := NewAuthTokenModifier(dynakube) builder := createBuilderForTesting() - sts := builder.AddModifier(mod).Build() + sts, _ := builder.AddModifier(mod).Build() require.NotEmpty(t, sts) isSubset(t, mod.getVolumes(), sts.Spec.Template.Spec.Volumes) diff --git a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/certs.go b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/certs.go index 50839d07a8..8e266f9f0d 100644 --- a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/certs.go +++ b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/certs.go @@ -34,10 +34,11 @@ func (mod CertificatesModifier) Enabled() bool { return mod.dynakube.HasActiveGateCaCert() } -func (mod CertificatesModifier) Modify(sts *appsv1.StatefulSet) { +func (mod CertificatesModifier) Modify(sts *appsv1.StatefulSet) error { baseContainer := kubeobjects.FindContainerInPodSpec(&sts.Spec.Template.Spec, consts.ActiveGateContainerName) sts.Spec.Template.Spec.Volumes = append(sts.Spec.Template.Spec.Volumes, mod.getVolumes()...) baseContainer.VolumeMounts = append(baseContainer.VolumeMounts, mod.getVolumeMounts()...) + return nil } func (mod CertificatesModifier) getVolumes() []corev1.Volume { diff --git a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/certs_test.go b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/certs_test.go index 0b19d40ef6..040e66ef14 100644 --- a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/certs_test.go +++ b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/certs_test.go @@ -48,7 +48,7 @@ func TestCertModify(t *testing.T) { mod := NewCertificatesModifier(dynakube) builder := createBuilderForTesting() - sts := builder.AddModifier(mod).Build() + sts, _ := builder.AddModifier(mod).Build() require.NotEmpty(t, sts) isSubset(t, mod.getVolumes(), sts.Spec.Template.Spec.Volumes) diff --git a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/config_test.go b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/config_test.go index 852059829b..32d01b7860 100644 --- a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/config_test.go +++ b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/config_test.go @@ -90,7 +90,7 @@ func TestNoConflict(t *testing.T) { mods := GenerateAllModifiers(dynakube, multiCapability) builder := createBuilderForTesting() - sts := builder.AddModifier(mods...).Build() + sts, _ := builder.AddModifier(mods...).Build() require.NotEmpty(t, sts) for _, mod := range mods { diff --git a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/customprops.go b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/customprops.go index 93207c4eb2..0415e38473 100644 --- a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/customprops.go +++ b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/customprops.go @@ -33,10 +33,11 @@ func (mod CustomPropertiesModifier) Enabled() bool { return mod.hasCustomProperties() } -func (mod CustomPropertiesModifier) Modify(sts *appsv1.StatefulSet) { +func (mod CustomPropertiesModifier) Modify(sts *appsv1.StatefulSet) error { baseContainer := kubeobjects.FindContainerInPodSpec(&sts.Spec.Template.Spec, consts.ActiveGateContainerName) sts.Spec.Template.Spec.Volumes = append(sts.Spec.Template.Spec.Volumes, mod.getVolumes()...) baseContainer.VolumeMounts = append(baseContainer.VolumeMounts, mod.getVolumeMounts()...) + return nil } func (mod CustomPropertiesModifier) getVolumes() []corev1.Volume { diff --git a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/customprops_test.go b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/customprops_test.go index 4f79a48da3..62ef071ed4 100644 --- a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/customprops_test.go +++ b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/customprops_test.go @@ -54,7 +54,7 @@ func TestCustomPropertyModify(t *testing.T) { mod := NewCustomPropertiesModifier(dynakube, multiCapability) builder := createBuilderForTesting() - sts := builder.AddModifier(mod).Build() + sts, _ := builder.AddModifier(mod).Build() require.NotEmpty(t, sts) isSubset(t, mod.getVolumes(), sts.Spec.Template.Spec.Volumes) diff --git a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/eec.go b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/eec.go index cbfaa2cf8f..5c2bc7d0fc 100644 --- a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/eec.go +++ b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/eec.go @@ -51,13 +51,14 @@ func (eec ExtensionControllerModifier) Enabled() bool { return eec.dynakube.IsStatsdActiveGateEnabled() } -func (eec ExtensionControllerModifier) Modify(sts *appsv1.StatefulSet) { +func (eec ExtensionControllerModifier) Modify(sts *appsv1.StatefulSet) error { sts.Spec.Template.Spec.Containers = append(sts.Spec.Template.Spec.Containers, eec.buildContainer()) sts.Spec.Template.Spec.Volumes = append(sts.Spec.Template.Spec.Volumes, eec.getVolumes(sts.Spec.Template.Spec.Volumes)...) baseContainer := kubeobjects.FindContainerInPodSpec(&sts.Spec.Template.Spec, consts.ActiveGateContainerName) baseContainer.VolumeMounts = append(baseContainer.VolumeMounts, eec.getActiveGateVolumeMounts(baseContainer.VolumeMounts)...) + return nil } func (eec ExtensionControllerModifier) getActiveGateVolumeMounts(presentMounts []corev1.VolumeMount) []corev1.VolumeMount { @@ -193,7 +194,7 @@ func (eec ExtensionControllerModifier) buildVolumeMounts() []corev1.VolumeMount } func (eec ExtensionControllerModifier) buildEnvs() []corev1.EnvVar { - tenantId, err := eec.dynakube.TenantUUID() + tenantId, err := eec.dynakube.TenantUUIDFromApiUrl() if err != nil { log.Error(err, "Problem getting tenant id from api url") } diff --git a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/kubemon.go b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/kubemon.go index ec6eae6426..dc69905275 100644 --- a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/kubemon.go +++ b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/kubemon.go @@ -43,11 +43,12 @@ func (mod KubernetesMonitoringModifier) Enabled() bool { return mod.dynakube.IsKubernetesMonitoringActiveGateEnabled() } -func (mod KubernetesMonitoringModifier) Modify(sts *appsv1.StatefulSet) { +func (mod KubernetesMonitoringModifier) Modify(sts *appsv1.StatefulSet) error { baseContainer := kubeobjects.FindContainerInPodSpec(&sts.Spec.Template.Spec, consts.ActiveGateContainerName) sts.Spec.Template.Spec.Volumes = append(sts.Spec.Template.Spec.Volumes, mod.getVolumes()...) baseContainer.VolumeMounts = append(baseContainer.VolumeMounts, mod.getVolumeMounts()...) sts.Spec.Template.Spec.InitContainers = append(sts.Spec.Template.Spec.InitContainers, mod.getInitContainers()...) + return nil } func (mod KubernetesMonitoringModifier) getInitContainers() []corev1.Container { diff --git a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/kubemon_test.go b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/kubemon_test.go index bffa6a2401..d3e312ae53 100644 --- a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/kubemon_test.go +++ b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/kubemon_test.go @@ -48,7 +48,7 @@ func TestKubernetesMonitoringModify(t *testing.T) { expectedIniContainers := mod.getInitContainers() expectedVolumeMounts := mod.getVolumeMounts() - sts := builder.AddModifier(mod).Build() + sts, _ := builder.AddModifier(mod).Build() require.NotEmpty(t, sts) container := sts.Spec.Template.Spec.Containers[0] @@ -67,7 +67,7 @@ func TestKubernetesMonitoringModify(t *testing.T) { expectedIniContainers := mod.getInitContainers() expectedVolumeMounts := mod.getVolumeMounts() - sts := builder.AddModifier(mod).Build() + sts, _ := builder.AddModifier(mod).Build() require.NotEmpty(t, sts) container := sts.Spec.Template.Spec.Containers[0] diff --git a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/proxy.go b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/proxy.go index 24f50f27fa..a9415d1a2e 100644 --- a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/proxy.go +++ b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/proxy.go @@ -28,11 +28,11 @@ func (mod ProxyModifier) Enabled() bool { return mod.dynakube.NeedsActiveGateProxy() } -func (mod ProxyModifier) Modify(sts *appsv1.StatefulSet) { +func (mod ProxyModifier) Modify(sts *appsv1.StatefulSet) error { sts.Spec.Template.Spec.Volumes = append(sts.Spec.Template.Spec.Volumes, mod.getVolumes()...) baseContainer := kubeobjects.FindContainerInPodSpec(&sts.Spec.Template.Spec, consts.ActiveGateContainerName) baseContainer.VolumeMounts = append(baseContainer.VolumeMounts, mod.getVolumeMounts()...) - + return nil } func (mod ProxyModifier) getVolumes() []corev1.Volume { diff --git a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/proxy_test.go b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/proxy_test.go index 7d607e4dc9..728b3fc7d9 100644 --- a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/proxy_test.go +++ b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/proxy_test.go @@ -49,7 +49,7 @@ func TestProxyModify(t *testing.T) { mod := NewProxyModifier(dynakube) builder := createBuilderForTesting() - sts := builder.AddModifier(mod).Build() + sts, _ := builder.AddModifier(mod).Build() require.NotEmpty(t, sts) isSubset(t, mod.getVolumes(), sts.Spec.Template.Spec.Volumes) diff --git a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/rawimage.go b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/rawimage.go index 71a364f75c..89c83abefc 100644 --- a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/rawimage.go +++ b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/rawimage.go @@ -6,6 +6,7 @@ import ( "github.com/Dynatrace/dynatrace-operator/src/controllers/dynakube/activegate/internal/statefulset/builder" "github.com/Dynatrace/dynatrace-operator/src/controllers/dynakube/connectioninfo" "github.com/Dynatrace/dynatrace-operator/src/kubeobjects" + "github.com/Dynatrace/dynatrace-operator/src/kubeobjects/address" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" ) @@ -29,11 +30,12 @@ func (mod RawImageModifier) Enabled() bool { return !mod.dynakube.FeatureDisableActivegateRawImage() } -func (mod RawImageModifier) Modify(sts *appsv1.StatefulSet) { +func (mod RawImageModifier) Modify(sts *appsv1.StatefulSet) error { baseContainer := kubeobjects.FindContainerInPodSpec(&sts.Spec.Template.Spec, consts.ActiveGateContainerName) sts.Spec.Template.Spec.Volumes = append(sts.Spec.Template.Spec.Volumes, mod.getVolumes()...) baseContainer.VolumeMounts = append(baseContainer.VolumeMounts, mod.getVolumeMounts()...) baseContainer.Env = append(baseContainer.Env, mod.getEnvs()...) + return nil } func (mod RawImageModifier) getVolumes() []corev1.Volume { @@ -61,33 +63,30 @@ func (mod RawImageModifier) getVolumeMounts() []corev1.VolumeMount { } func (mod RawImageModifier) getEnvs() []corev1.EnvVar { - return []corev1.EnvVar{mod.tenantUUIDNameEnvVar(), mod.communicationEndpointEnvVar()} + return []corev1.EnvVar{mod.tenantUUIDEnvVar(), mod.communicationEndpointEnvVar()} } -func (mod RawImageModifier) tenantUUIDNameEnvVar() corev1.EnvVar { +func (mod RawImageModifier) tenantUUIDEnvVar() corev1.EnvVar { return corev1.EnvVar{ Name: consts.EnvDtTenant, - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: mod.dynakube.ActivegateTenantSecret(), - }, - Key: connectioninfo.TenantUuidName, + ValueFrom: &corev1.EnvVarSource{ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: mod.dynakube.ActiveGateConnectionInfoConfigMapName(), }, - }, - } + Key: connectioninfo.TenantUUIDName, + Optional: address.Of(false), + }}} } func (mod RawImageModifier) communicationEndpointEnvVar() corev1.EnvVar { return corev1.EnvVar{ Name: consts.EnvDtServer, - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: mod.dynakube.ActivegateTenantSecret(), - }, - Key: connectioninfo.CommunicationEndpointsName, + ValueFrom: &corev1.EnvVarSource{ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: mod.dynakube.ActiveGateConnectionInfoConfigMapName(), }, - }, + Key: connectioninfo.CommunicationEndpointsName, + Optional: address.Of(false), + }}, } } diff --git a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/rawimage_test.go b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/rawimage_test.go index b994a56fbc..08355680b9 100644 --- a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/rawimage_test.go +++ b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/rawimage_test.go @@ -43,7 +43,7 @@ func TestRawImageModify(t *testing.T) { mod := NewRawImageModifier(dynakube) builder := createBuilderForTesting() - sts := builder.AddModifier(mod).Build() + sts, _ := builder.AddModifier(mod).Build() require.NotEmpty(t, sts) isSubset(t, mod.getVolumes(), sts.Spec.Template.Spec.Volumes) diff --git a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/readonly.go b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/readonly.go index d4a0c854fd..067b23ba83 100644 --- a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/readonly.go +++ b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/readonly.go @@ -30,7 +30,7 @@ func (mod ReadOnlyModifier) Enabled() bool { return mod.dynakube.FeatureActiveGateReadOnlyFilesystem() } -func (mod ReadOnlyModifier) Modify(sts *appsv1.StatefulSet) { +func (mod ReadOnlyModifier) Modify(sts *appsv1.StatefulSet) error { mod.presentVolumes = sts.Spec.Template.Spec.Volumes sts.Spec.Template.Spec.Volumes = append(sts.Spec.Template.Spec.Volumes, mod.getVolumes()...) @@ -38,6 +38,8 @@ func (mod ReadOnlyModifier) Modify(sts *appsv1.StatefulSet) { baseContainer.SecurityContext.ReadOnlyRootFilesystem = address.Of(true) mod.presentMounts = baseContainer.VolumeMounts baseContainer.VolumeMounts = append(baseContainer.VolumeMounts, mod.getVolumeMounts()...) + + return nil } func (mod ReadOnlyModifier) getVolumes() []corev1.Volume { diff --git a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/readonly_test.go b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/readonly_test.go index 92366ec246..c1a25374ff 100644 --- a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/readonly_test.go +++ b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/readonly_test.go @@ -45,7 +45,7 @@ func TestReadOnlyModify(t *testing.T) { expectedVolumes := mod.getVolumes() expectedVolumeMounts := mod.getVolumeMounts() - sts := builder.AddModifier(mod).Build() + sts, _ := builder.AddModifier(mod).Build() require.NotEmpty(t, sts) isSubset(t, expectedVolumes, sts.Spec.Template.Spec.Volumes) diff --git a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/serviceport.go b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/serviceport.go index 6167d7588d..025ddc76cc 100644 --- a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/serviceport.go +++ b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/serviceport.go @@ -33,11 +33,12 @@ func (mod ServicePortModifier) Enabled() bool { return mod.dynakube.NeedsActiveGateServicePorts() } -func (mod ServicePortModifier) Modify(sts *appsv1.StatefulSet) { +func (mod ServicePortModifier) Modify(sts *appsv1.StatefulSet) error { baseContainer := kubeobjects.FindContainerInPodSpec(&sts.Spec.Template.Spec, consts.ActiveGateContainerName) baseContainer.ReadinessProbe.HTTPGet.Port = intstr.FromString(consts.HttpsServicePortName) baseContainer.Ports = append(baseContainer.Ports, mod.getPorts()...) baseContainer.Env = append(baseContainer.Env, mod.getEnvs()...) + return nil } func (mod ServicePortModifier) getPorts() []corev1.ContainerPort { diff --git a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/serviceport_test.go b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/serviceport_test.go index 83a6b1b7f5..766cd97435 100644 --- a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/serviceport_test.go +++ b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/serviceport_test.go @@ -48,7 +48,7 @@ func TestServicePortModify(t *testing.T) { expectedPorts := mod.getPorts() expectedEnv := mod.getEnvs() - sts := builder.AddModifier(mod).Build() + sts, _ := builder.AddModifier(mod).Build() require.NotEmpty(t, sts) container := sts.Spec.Template.Spec.Containers[0] diff --git a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/statsd.go b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/statsd.go index 5f3c580b11..fb8aeb68b6 100644 --- a/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/statsd.go +++ b/src/controllers/dynakube/activegate/internal/statefulset/builder/modifiers/statsd.go @@ -52,13 +52,14 @@ func (statsd StatsdModifier) Enabled() bool { return statsd.dynakube.IsStatsdActiveGateEnabled() } -func (statsd StatsdModifier) Modify(sts *appsv1.StatefulSet) { +func (statsd StatsdModifier) Modify(sts *appsv1.StatefulSet) error { sts.Spec.Template.Spec.Containers = append(sts.Spec.Template.Spec.Containers, statsd.buildContainer()) sts.Spec.Template.Spec.Volumes = append(sts.Spec.Template.Spec.Volumes, statsd.getVolumes(sts.Spec.Template.Spec.Volumes)...) baseContainer := kubeobjects.FindContainerInPodSpec(&sts.Spec.Template.Spec, consts.ActiveGateContainerName) baseContainer.VolumeMounts = append(baseContainer.VolumeMounts, statsd.getActiveGateVolumeMounts(baseContainer.VolumeMounts)...) + return nil } func (statsd *StatsdModifier) getActiveGateVolumeMounts(presentMounts []corev1.VolumeMount) []corev1.VolumeMount { diff --git a/src/controllers/dynakube/activegate/internal/statefulset/statefulset.go b/src/controllers/dynakube/activegate/internal/statefulset/statefulset.go index 454ea383de..4fe3cfa81e 100644 --- a/src/controllers/dynakube/activegate/internal/statefulset/statefulset.go +++ b/src/controllers/dynakube/activegate/internal/statefulset/statefulset.go @@ -38,7 +38,7 @@ func (statefulSetBuilder StatefulSetBuilder) CreateStatefulSet(mods []builder.Mo if len(mods) == 0 { mods = modifiers.GenerateAllModifiers(statefulSetBuilder.dynakube, statefulSetBuilder.capability) } - sts := activeGateBuilder.AddModifier(mods...).Build() + sts, _ := activeGateBuilder.AddModifier(mods...).Build() if err := setHash(&sts); err != nil { return nil, err @@ -176,6 +176,7 @@ func (statefulSetBuilder StatefulSetBuilder) buildCommonEnvs() []corev1.EnvVar { if statefulSetBuilder.dynakube.Spec.NetworkZone != "" { envs = append(envs, corev1.EnvVar{Name: consts.EnvDtNetworkZone, Value: statefulSetBuilder.dynakube.Spec.NetworkZone}) } + return envs } diff --git a/src/controllers/dynakube/connectioninfo/config.go b/src/controllers/dynakube/connectioninfo/config.go index 41ccb8e266..1da9b6839f 100644 --- a/src/controllers/dynakube/connectioninfo/config.go +++ b/src/controllers/dynakube/connectioninfo/config.go @@ -7,7 +7,7 @@ import ( const ( TenantTokenName = "tenant-token" CommunicationEndpointsName = "communication-endpoints" - TenantUuidName = "tenant-uuid" + TenantUUIDName = "tenant-uuid" TokenBasePath = "/var/lib/dynatrace/secrets/tokens" TenantTokenMountPoint = TokenBasePath + "/tenant-token" diff --git a/src/controllers/dynakube/connectioninfo/reconciler.go b/src/controllers/dynakube/connectioninfo/reconciler.go index ef3fc4c415..206ed2f2d2 100644 --- a/src/controllers/dynakube/connectioninfo/reconciler.go +++ b/src/controllers/dynakube/connectioninfo/reconciler.go @@ -27,36 +27,80 @@ func NewReconciler(ctx context.Context, clt client.Client, apiReader client.Read } } -func (r *Reconciler) Reconcile() (err error) { +func (r *Reconciler) Reconcile() error { if !r.dynakube.FeatureDisableActivegateRawImage() { - activeGateConnectionInfo, err := r.dtc.GetActiveGateConnectionInfo() + err := r.reconcileActiveGateConnectionInfo() if err != nil { - log.Info("failed to get activegate connection info") return err } + } - err = r.createOrUpdateSecret(r.dynakube.ActivegateTenantSecret(), activeGateConnectionInfo.ConnectionInfo) - if err != nil { - return err - } + err := r.reconcileOneAgentConnectionInfo() + if err != nil { + return err } + + return nil +} + +func (r *Reconciler) reconcileOneAgentConnectionInfo() error { oneAgentConnectionInfo, err := r.dtc.GetOneAgentConnectionInfo() if err != nil { log.Info("failed to get oneagent connection info") return err } - err = r.createOrUpdateSecret(r.dynakube.OneagentTenantSecret(), oneAgentConnectionInfo.ConnectionInfo) + err = r.maintainConnectionInfoObjects(r.dynakube.OneagentTenantSecret(), r.dynakube.OneAgentConnectionInfoConfigMapName(), oneAgentConnectionInfo.ConnectionInfo) if err != nil { return err } + return nil +} + +func (r *Reconciler) reconcileActiveGateConnectionInfo() error { + activeGateConnectionInfo, err := r.dtc.GetActiveGateConnectionInfo() + if err != nil { + log.Info("failed to get activegate connection info") + return err + } + err = r.maintainConnectionInfoObjects(r.dynakube.ActivegateTenantSecret(), r.dynakube.ActiveGateConnectionInfoConfigMapName(), activeGateConnectionInfo.ConnectionInfo) + if err != nil { + return err + } return nil } -func (r *Reconciler) createOrUpdateSecret(secretName string, connectionInfo dtclient.ConnectionInfo) error { - data := buildConnectionInfoSecret(connectionInfo) - secret := kubeobjects.NewSecret(secretName, r.dynakube.Namespace, data) +func (r *Reconciler) maintainConnectionInfoObjects(secretName string, configMapName string, connectionInfo dtclient.ConnectionInfo) error { + err := r.createTenantTokenSecret(secretName, connectionInfo) + if err != nil { + return err + } + + err = r.createTenantConnectionInfoConfigMap(configMapName, connectionInfo) + if err != nil { + return err + } + + return nil +} + +func (r *Reconciler) createTenantConnectionInfoConfigMap(secretName string, connectionInfo dtclient.ConnectionInfo) error { + configMapData := extractPublicData(connectionInfo) + configMap := kubeobjects.NewConfigMap(secretName, r.dynakube.Namespace, configMapData) + + query := kubeobjects.NewConfigMapQuery(r.context, r.client, r.apiReader, log) + err := query.CreateOrUpdate(*configMap) + if err != nil { + log.Info("could not create or update configMap for connection info", "name", configMap.Name) + return err + } + return nil +} + +func (r *Reconciler) createTenantTokenSecret(secretName string, connectionInfo dtclient.ConnectionInfo) error { + secretData := extractSensitiveData(connectionInfo) + secret := kubeobjects.NewSecret(secretName, r.dynakube.Namespace, secretData) query := kubeobjects.NewSecretQuery(r.context, r.client, r.apiReader, log) err := query.CreateOrUpdate(*secret) @@ -67,16 +111,22 @@ func (r *Reconciler) createOrUpdateSecret(secretName string, connectionInfo dtcl return nil } -func buildConnectionInfoSecret(connectionInfo dtclient.ConnectionInfo) map[string][]byte { +func extractSensitiveData(connectionInfo dtclient.ConnectionInfo) map[string][]byte { data := map[string][]byte{ TenantTokenName: []byte(connectionInfo.TenantToken), } + return data +} + +func extractPublicData(connectionInfo dtclient.ConnectionInfo) map[string]string { + data := map[string]string{} + if connectionInfo.TenantUUID != "" { - data[TenantUuidName] = []byte(connectionInfo.TenantUUID) + data[TenantUUIDName] = connectionInfo.TenantUUID } if connectionInfo.Endpoints != "" { - data[CommunicationEndpointsName] = []byte(connectionInfo.Endpoints) + data[CommunicationEndpointsName] = connectionInfo.Endpoints } return data diff --git a/src/controllers/dynakube/connectioninfo/reconciler_test.go b/src/controllers/dynakube/connectioninfo/reconciler_test.go index d59afd6b48..8ceed284c6 100644 --- a/src/controllers/dynakube/connectioninfo/reconciler_test.go +++ b/src/controllers/dynakube/connectioninfo/reconciler_test.go @@ -18,7 +18,7 @@ const ( testName = "test-name" testNamespace = "test-namespace" testTenantToken = "test-token" - testTenantUuid = "test-uuid" + testTenantUUID = "test-uuid" testTenantEndpoints = "test-endpoints" ) @@ -43,8 +43,6 @@ func TestReconcile_ActivegateSecret(t *testing.T) { err = fakeClient.Get(context.TODO(), client.ObjectKey{Name: dynakube.ActivegateTenantSecret(), Namespace: testNamespace}, &actualSecret) require.NoError(t, err) assert.Equal(t, []byte(testTenantToken), actualSecret.Data[TenantTokenName]) - assert.Equal(t, []byte(testTenantUuid), actualSecret.Data[TenantUuidName]) - assert.Equal(t, []byte(testTenantEndpoints), actualSecret.Data[CommunicationEndpointsName]) }) t.Run(`update activegate secret`, func(t *testing.T) { fakeClient := fake.NewClientBuilder().WithObjects( @@ -54,9 +52,7 @@ func TestReconcile_ActivegateSecret(t *testing.T) { Namespace: testNamespace, }, Data: map[string][]byte{ - TenantTokenName: []byte("outdated"), - TenantUuidName: []byte("outdated"), - CommunicationEndpointsName: []byte("outdated"), + TenantTokenName: []byte("outdated"), }, }, ).Build() @@ -68,8 +64,6 @@ func TestReconcile_ActivegateSecret(t *testing.T) { err = fakeClient.Get(context.TODO(), client.ObjectKey{Name: dynakube.ActivegateTenantSecret(), Namespace: testNamespace}, &actualSecret) require.NoError(t, err) assert.Equal(t, []byte(testTenantToken), actualSecret.Data[TenantTokenName]) - assert.Equal(t, []byte(testTenantUuid), actualSecret.Data[TenantUuidName]) - assert.Equal(t, []byte(testTenantEndpoints), actualSecret.Data[CommunicationEndpointsName]) }) t.Run(`up to date activegate secret`, func(t *testing.T) { fakeClient := fake.NewClientBuilder().WithObjects( @@ -79,9 +73,73 @@ func TestReconcile_ActivegateSecret(t *testing.T) { Namespace: testNamespace, }, Data: map[string][]byte{ - TenantTokenName: []byte(testTenantToken), - TenantUuidName: []byte(testTenantUuid), - CommunicationEndpointsName: []byte(testTenantEndpoints), + TenantTokenName: []byte(testTenantToken), + }, + }, + ).Build() + + r := NewReconciler(context.TODO(), fakeClient, fakeClient, dynakube, dtc) + err := r.Reconcile() + require.NoError(t, err) + }) +} + +func TestReconcile_ActivegateConfigMap(t *testing.T) { + dynakube := &dynatracev1beta1.DynaKube{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: testNamespace, + Name: testName, + }} + + dtc := &dtclient.MockDynatraceClient{} + dtc.On("GetActiveGateConnectionInfo").Return(getTestActiveGateConnectionInfo(), nil) + dtc.On("GetOneAgentConnectionInfo").Return(getTestOneAgentConnectionInfo(), nil) + + t.Run(`create activegate ConfigMap`, func(t *testing.T) { + fakeClient := fake.NewClientBuilder().Build() + r := NewReconciler(context.TODO(), fakeClient, fakeClient, dynakube, dtc) + err := r.Reconcile() + require.NoError(t, err) + + var actual corev1.ConfigMap + err = fakeClient.Get(context.TODO(), client.ObjectKey{Name: dynakube.ActiveGateConnectionInfoConfigMapName(), Namespace: testNamespace}, &actual) + require.NoError(t, err) + assert.Equal(t, testTenantUUID, actual.Data[TenantUUIDName]) + assert.Equal(t, testTenantEndpoints, actual.Data[CommunicationEndpointsName]) + }) + t.Run(`update activegate ConfigMap`, func(t *testing.T) { + fakeClient := fake.NewClientBuilder().WithObjects( + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: dynakube.ActiveGateConnectionInfoConfigMapName(), + Namespace: testNamespace, + }, + Data: map[string]string{ + TenantUUIDName: "outdated", + CommunicationEndpointsName: "outdated", + }, + }, + ).Build() + r := NewReconciler(context.TODO(), fakeClient, fakeClient, dynakube, dtc) + err := r.Reconcile() + require.NoError(t, err) + + var actual corev1.ConfigMap + err = fakeClient.Get(context.TODO(), client.ObjectKey{Name: dynakube.ActiveGateConnectionInfoConfigMapName(), Namespace: testNamespace}, &actual) + require.NoError(t, err) + assert.Equal(t, testTenantUUID, actual.Data[TenantUUIDName]) + assert.Equal(t, testTenantEndpoints, actual.Data[CommunicationEndpointsName]) + }) + t.Run(`up to date activegate secret`, func(t *testing.T) { + fakeClient := fake.NewClientBuilder().WithObjects( + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: dynakube.ActiveGateConnectionInfoConfigMapName(), + Namespace: testNamespace, + }, + Data: map[string]string{ + TenantUUIDName: testTenantUUID, + CommunicationEndpointsName: testTenantEndpoints, }, }, ).Build() @@ -116,8 +174,6 @@ func TestReconcile_OneagentSecret(t *testing.T) { err = fakeClient.Get(context.TODO(), client.ObjectKey{Name: dynakube.OneagentTenantSecret(), Namespace: testNamespace}, &actualSecret) require.NoError(t, err) assert.Equal(t, []byte(testTenantToken), actualSecret.Data[TenantTokenName]) - assert.Equal(t, []byte(testTenantUuid), actualSecret.Data[TenantUuidName]) - assert.Equal(t, []byte(testTenantEndpoints), actualSecret.Data[CommunicationEndpointsName]) }) t.Run(`update oneagent secret`, func(t *testing.T) { fakeClient := fake.NewClientBuilder().WithObjects( @@ -127,9 +183,7 @@ func TestReconcile_OneagentSecret(t *testing.T) { Namespace: testNamespace, }, Data: map[string][]byte{ - TenantTokenName: []byte("outdated"), - TenantUuidName: []byte("outdated"), - CommunicationEndpointsName: []byte("outdated"), + TenantTokenName: []byte("outdated"), }, }, ).Build() @@ -142,8 +196,6 @@ func TestReconcile_OneagentSecret(t *testing.T) { err = fakeClient.Get(context.TODO(), client.ObjectKey{Name: dynakube.OneagentTenantSecret(), Namespace: testNamespace}, &actualSecret) require.NoError(t, err) assert.Equal(t, []byte(testTenantToken), actualSecret.Data[TenantTokenName]) - assert.Equal(t, []byte(testTenantUuid), actualSecret.Data[TenantUuidName]) - assert.Equal(t, []byte(testTenantEndpoints), actualSecret.Data[CommunicationEndpointsName]) }) t.Run(`up to date oneagent secret`, func(t *testing.T) { fakeClient := fake.NewClientBuilder().WithObjects( @@ -153,9 +205,77 @@ func TestReconcile_OneagentSecret(t *testing.T) { Namespace: testNamespace, }, Data: map[string][]byte{ - TenantTokenName: []byte(testTenantToken), - TenantUuidName: []byte(testTenantUuid), - CommunicationEndpointsName: []byte(testTenantEndpoints), + TenantTokenName: []byte(testTenantToken), + }, + }, + ).Build() + + r := NewReconciler(context.TODO(), fakeClient, fakeClient, dynakube, dtc) + err := r.Reconcile() + require.NoError(t, err) + }) +} + +func TestReconcile_OneagentConfigMap(t *testing.T) { + dynakube := &dynatracev1beta1.DynaKube{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: testNamespace, + Name: testName, + Annotations: map[string]string{ + dynatracev1beta1.AnnotationFeatureActiveGateRawImage: "false", + }, + }} + + dtc := &dtclient.MockDynatraceClient{} + dtc.On("GetOneAgentConnectionInfo").Return(getTestOneAgentConnectionInfo(), nil) + + t.Run(`create oneagent ConfigMap`, func(t *testing.T) { + fakeClient := fake.NewClientBuilder().Build() + + r := NewReconciler(context.TODO(), fakeClient, fakeClient, dynakube, dtc) + err := r.Reconcile() + require.NoError(t, err) + + var actual corev1.ConfigMap + err = fakeClient.Get(context.TODO(), client.ObjectKey{Name: dynakube.OneAgentConnectionInfoConfigMapName(), Namespace: testNamespace}, &actual) + require.NoError(t, err) + assert.Equal(t, testTenantUUID, actual.Data[TenantUUIDName]) + assert.Equal(t, testTenantEndpoints, actual.Data[CommunicationEndpointsName]) + }) + t.Run(`update oneagent ConfigMap`, func(t *testing.T) { + fakeClient := fake.NewClientBuilder().WithObjects( + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: dynakube.OneAgentConnectionInfoConfigMapName(), + Namespace: testNamespace, + }, + Data: map[string]string{ + TenantUUIDName: "outdated", + CommunicationEndpointsName: "outdated", + }, + }, + ).Build() + + r := NewReconciler(context.TODO(), fakeClient, fakeClient, dynakube, dtc) + err := r.Reconcile() + require.NoError(t, err) + + var actual corev1.ConfigMap + err = fakeClient.Get(context.TODO(), client.ObjectKey{Name: dynakube.OneAgentConnectionInfoConfigMapName(), Namespace: testNamespace}, &actual) + require.NoError(t, err) + assert.Equal(t, testTenantUUID, actual.Data[TenantUUIDName]) + assert.Equal(t, testTenantEndpoints, actual.Data[CommunicationEndpointsName]) + }) + t.Run(`up to date oneagent ConfigMap`, func(t *testing.T) { + fakeClient := fake.NewClientBuilder().WithObjects( + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: dynakube.OneAgentConnectionInfoConfigMapName(), + Namespace: testNamespace, + }, + Data: map[string]string{ + TenantUUIDName: testTenantUUID, + CommunicationEndpointsName: testTenantEndpoints, }, }, ).Build() @@ -169,7 +289,7 @@ func TestReconcile_OneagentSecret(t *testing.T) { func getTestOneAgentConnectionInfo() dtclient.OneAgentConnectionInfo { return dtclient.OneAgentConnectionInfo{ ConnectionInfo: dtclient.ConnectionInfo{ - TenantUUID: testTenantUuid, + TenantUUID: testTenantUUID, TenantToken: testTenantToken, Endpoints: testTenantEndpoints, }, @@ -179,7 +299,7 @@ func getTestOneAgentConnectionInfo() dtclient.OneAgentConnectionInfo { func getTestActiveGateConnectionInfo() *dtclient.ActiveGateConnectionInfo { return &dtclient.ActiveGateConnectionInfo{ ConnectionInfo: dtclient.ConnectionInfo{ - TenantUUID: testTenantUuid, + TenantUUID: testTenantUUID, TenantToken: testTenantToken, Endpoints: testTenantEndpoints, }, diff --git a/src/controllers/dynakube/oneagent/daemonset/arguments.go b/src/controllers/dynakube/oneagent/daemonset/arguments.go index 19a5a04dfd..89bebd7b05 100644 --- a/src/controllers/dynakube/oneagent/daemonset/arguments.go +++ b/src/controllers/dynakube/oneagent/daemonset/arguments.go @@ -3,6 +3,7 @@ package daemonset import ( "fmt" + "github.com/Dynatrace/dynatrace-operator/src/controllers/dynakube/activegate/consts" "github.com/Dynatrace/dynatrace-operator/src/deploymentmetadata" "github.com/Dynatrace/dynatrace-operator/src/version" ) @@ -29,8 +30,8 @@ func (dsInfo *builderInfo) arguments() []string { } func (dsInfo *builderInfo) appendImmutableImageArgs(args []string) []string { - args = append(args, fmt.Sprintf("--set-tenant=%s", dsInfo.instance.Status.ConnectionInfo.TenantUUID)) - args = append(args, fmt.Sprintf("--set-server={%s}", dsInfo.instance.Status.ConnectionInfo.FormattedCommunicationEndpoints)) + args = append(args, fmt.Sprintf("--set-tenant=$(%s)", consts.EnvDtTenant)) + args = append(args, fmt.Sprintf("--set-server={$(%s)}", consts.EnvDtServer)) return args } diff --git a/src/controllers/dynakube/oneagent/daemonset/arguments_test.go b/src/controllers/dynakube/oneagent/daemonset/arguments_test.go index c38c3a2127..9084ebbc07 100644 --- a/src/controllers/dynakube/oneagent/daemonset/arguments_test.go +++ b/src/controllers/dynakube/oneagent/daemonset/arguments_test.go @@ -5,6 +5,7 @@ import ( "testing" dynatracev1beta1 "github.com/Dynatrace/dynatrace-operator/src/api/v1beta1" + "github.com/Dynatrace/dynatrace-operator/src/controllers/dynakube/activegate/consts" "github.com/Dynatrace/dynatrace-operator/src/deploymentmetadata" "github.com/Dynatrace/dynatrace-operator/src/version" "github.com/stretchr/testify/assert" @@ -126,8 +127,8 @@ func TestPodSpec_Arguments(t *testing.T) { }) t.Run(`feature flag immutable image is enabled`, func(t *testing.T) { podSpecs = dsInfo.podSpec() - assert.Contains(t, podSpecs.Containers[0].Args, "--set-tenant="+testTenantUUID) - assert.Contains(t, podSpecs.Containers[0].Args, fmt.Sprintf("--set-server={%s}", testFormattedCommunicationHosts)) + assert.Contains(t, podSpecs.Containers[0].Args, "--set-tenant=$("+consts.EnvDtTenant+")") + assert.Contains(t, podSpecs.Containers[0].Args, fmt.Sprintf("--set-server={$(%s)}", consts.EnvDtServer)) }) t.Run(`has network zone arg`, func(t *testing.T) { instance.Spec.NetworkZone = testValue diff --git a/src/controllers/dynakube/oneagent/daemonset/env_vars.go b/src/controllers/dynakube/oneagent/daemonset/env_vars.go index 84e9df1bef..8e14fbf669 100644 --- a/src/controllers/dynakube/oneagent/daemonset/env_vars.go +++ b/src/controllers/dynakube/oneagent/daemonset/env_vars.go @@ -3,13 +3,17 @@ package daemonset import ( "sort" + "github.com/Dynatrace/dynatrace-operator/src/controllers/dynakube/activegate/consts" + "github.com/Dynatrace/dynatrace-operator/src/controllers/dynakube/connectioninfo" + "github.com/Dynatrace/dynatrace-operator/src/kubeobjects/address" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" ) const ( - dtNodeName = "DT_K8S_NODE_NAME" - dtClusterId = "DT_K8S_CLUSTER_ID" + dtNodeName = "DT_K8S_NODE_NAME" + dtClusterId = "DT_K8S_CLUSTER_ID" + dtCommunication = "DT_COMMUNICATION" oneagentDisableContainerInjection = "ONEAGENT_DISABLE_CONTAINER_INJECTION" oneagentReadOnlyMode = "ONEAGENT_READ_ONLY_MODE" @@ -27,6 +31,20 @@ func (dsInfo *builderInfo) environmentVariables() []corev1.EnvVar { envVarMap := envVarsToMap(environmentVariables) envVarMap = setDefaultValueSource(envVarMap, dtNodeName, &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{FieldPath: "spec.nodeName"}}) envVarMap = setDefaultValue(envVarMap, dtClusterId, dsInfo.clusterId) + envVarMap = setDefaultValueSource(envVarMap, consts.EnvDtTenant, &corev1.EnvVarSource{ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: dsInfo.instance.OneAgentConnectionInfoConfigMapName(), + }, + Key: connectioninfo.TenantUUIDName, + Optional: address.Of(false), + }}) + envVarMap = setDefaultValueSource(envVarMap, consts.EnvDtServer, &corev1.EnvVarSource{ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: dsInfo.instance.OneAgentConnectionInfoConfigMapName(), + }, + Key: connectioninfo.CommunicationEndpointsName, + Optional: address.Of(false), + }}) if dsInfo.hasProxy() { envVarMap = dsInfo.setDefaultProxy(envVarMap) diff --git a/src/controllers/dynakube/oneagent/daemonset/env_vars_test.go b/src/controllers/dynakube/oneagent/daemonset/env_vars_test.go index 6640a8b4f0..b48923b43d 100644 --- a/src/controllers/dynakube/oneagent/daemonset/env_vars_test.go +++ b/src/controllers/dynakube/oneagent/daemonset/env_vars_test.go @@ -3,6 +3,7 @@ package daemonset import ( "testing" + dynatracev1beta1 "github.com/Dynatrace/dynatrace-operator/src/api/v1beta1" "github.com/Dynatrace/dynatrace-operator/src/kubeobjects" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" @@ -10,7 +11,9 @@ import ( func TestEnvironmentVariables(t *testing.T) { t.Run("returns default values when members are nil", func(t *testing.T) { - dsInfo := builderInfo{} + dsInfo := builderInfo{ + instance: &dynatracev1beta1.DynaKube{}, + } envVars := dsInfo.environmentVariables() assert.Contains(t, envVars, corev1.EnvVar{Name: dtClusterId, ValueFrom: nil}) diff --git a/src/dtclient/connection_info.go b/src/dtclient/connection_info.go index 4987f701eb..85dd80448e 100644 --- a/src/dtclient/connection_info.go +++ b/src/dtclient/connection_info.go @@ -118,7 +118,7 @@ func (dtc *dynatraceClient) readResponseForOneAgentConnectionInfo(response []byt return OneAgentConnectionInfo{}, err } - tenantUuid := resp.TenantUUID + tenantUUID := resp.TenantUUID tenantToken := resp.TenantToken communicationHosts := make([]CommunicationHost, 0, len(resp.CommunicationEndpoints)) formattedCommunicationEndpoints := resp.FormattedCommunicationEndpoints @@ -139,7 +139,7 @@ func (dtc *dynatraceClient) readResponseForOneAgentConnectionInfo(response []byt ci := OneAgentConnectionInfo{ CommunicationHosts: communicationHosts, ConnectionInfo: ConnectionInfo{ - TenantUUID: tenantUuid, + TenantUUID: tenantUUID, TenantToken: tenantToken, Endpoints: formattedCommunicationEndpoints, }, diff --git a/src/dtclient/connection_info_test.go b/src/dtclient/connection_info_test.go index c1c906d70a..2fde0359ee 100644 --- a/src/dtclient/connection_info_test.go +++ b/src/dtclient/connection_info_test.go @@ -11,20 +11,20 @@ import ( const ( activeGateConnectionInfoEndpoint = "/v1/deployment/installer/gateway/connectioninfo" - testTenantUuid = "1234" + testTenantUUID = "1234" testTenantToken = "abcd" testEndpoint = "/some/url" ) func Test_GetActiveGateConnectionInfo(t *testing.T) { activegateJsonResponse := &activeGateConnectionInfoJsonResponse{ - TenantUUID: testTenantUuid, + TenantUUID: testTenantUUID, TenantToken: testTenantToken, CommunicationEndpoints: testEndpoint, } expectedActivegateConnectionInfo := &ActiveGateConnectionInfo{ ConnectionInfo: ConnectionInfo{ - TenantUUID: testTenantUuid, + TenantUUID: testTenantUUID, TenantToken: testTenantToken, Endpoints: testEndpoint, }, diff --git a/src/ingestendpoint/secret.go b/src/ingestendpoint/secret.go index 953b221858..3c5686f509 100644 --- a/src/ingestendpoint/secret.go +++ b/src/ingestendpoint/secret.go @@ -209,7 +209,7 @@ func metricsIngestUrlForDynatraceActiveGate(dk *dynatracev1beta1.DynaKube) (stri } func metricsIngestUrlForClusterActiveGate(dk *dynatracev1beta1.DynaKube) (string, error) { - tenant, err := dk.TenantUUID() + tenant, err := dk.TenantUUIDFromApiUrl() if err != nil { return "", err } diff --git a/src/kubeobjects/configmap.go b/src/kubeobjects/configmap.go new file mode 100644 index 0000000000..d88f7aad5e --- /dev/null +++ b/src/kubeobjects/configmap.go @@ -0,0 +1,86 @@ +package kubeobjects + +import ( + "context" + "reflect" + + "github.com/go-logr/logr" + "github.com/pkg/errors" + corev1 "k8s.io/api/core/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type ConfigMapQuery struct { + kubeQuery +} + +func NewConfigMapQuery(ctx context.Context, kubeClient client.Client, kubeReader client.Reader, log logr.Logger) ConfigMapQuery { + return ConfigMapQuery{ + newKubeQuery(ctx, kubeClient, kubeReader, log), + } +} + +func (query ConfigMapQuery) Get(objectKey client.ObjectKey) (corev1.ConfigMap, error) { + var configMap corev1.ConfigMap + err := query.kubeReader.Get(query.ctx, objectKey, &configMap) + + return configMap, errors.WithStack(err) +} + +func (query ConfigMapQuery) Create(configMap corev1.ConfigMap) error { + query.log.Info("creating configMap", "name", configMap.Name, "namespace", configMap.Namespace) + + return errors.WithStack(query.kubeClient.Create(query.ctx, &configMap)) +} + +func (query ConfigMapQuery) Update(configMap corev1.ConfigMap) error { + query.log.Info("updating configMap", "name", configMap.Name, "namespace", configMap.Namespace) + + return errors.WithStack(query.kubeClient.Update(query.ctx, &configMap)) +} + +func (query ConfigMapQuery) CreateOrUpdate(configMap corev1.ConfigMap) error { + currentConfigMap, err := query.Get(types.NamespacedName{Name: configMap.Name, Namespace: configMap.Namespace}) + if err != nil { + if k8serrors.IsNotFound(err) { + err = query.Create(configMap) + if err != nil { + return errors.WithStack(err) + } + return nil + } + return errors.WithStack(err) + } + + if AreConfigMapsEqual(configMap, currentConfigMap) { + query.log.Info("configMap unchanged", "name", configMap.Name, "namespace", configMap.Namespace) + return nil + } + + err = query.Update(configMap) + if err != nil { + return errors.WithStack(err) + } + return nil +} + +func AreConfigMapsEqual(configMap corev1.ConfigMap, other corev1.ConfigMap) bool { + return reflect.DeepEqual(configMap.Data, other.Data) && reflect.DeepEqual(configMap.Labels, other.Labels) +} + +func NewConfigMap(name string, namespace string, data map[string]string) *corev1.ConfigMap { + return &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Data: data, + } +} + +func IsConfigMapDataEqual(currentConfigMap *corev1.ConfigMap, desired map[string]string) bool { + return reflect.DeepEqual(desired, currentConfigMap.Data) +} diff --git a/src/kubeobjects/configmap_test.go b/src/kubeobjects/configmap_test.go new file mode 100644 index 0000000000..a07a337e0a --- /dev/null +++ b/src/kubeobjects/configmap_test.go @@ -0,0 +1,252 @@ +package kubeobjects + +import ( + "context" + "reflect" + "testing" + + "github.com/Dynatrace/dynatrace-operator/src/logger" + "github.com/Dynatrace/dynatrace-operator/src/scheme/fake" + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +var log = logger.Factory.GetLogger("test-configMap") + +func TestConfigMapQuery(t *testing.T) { + t.Run(`Get configMap`, testGetConfigMap) + t.Run(`Create configMap`, testCreateConfigMap) + t.Run(`Update configMap`, testUpdateConfigMap) + t.Run(`Create or update configMap`, testCreateOrUpdateConfigMap) + t.Run(`Identical configMap is not updated`, testIdenticalConfigMapIsNotUpdated) + t.Run(`Update configMap when data has changed`, testUpdateConfigMapWhenDataChanged) + t.Run(`Update configMap when labels have changed`, testUpdateConfigMapWhenLabelsChanged) + t.Run(`Create configMap in target namespace`, testCreateConfigMapInTargetNamespace) +} + +func testGetConfigMap(t *testing.T) { + configMap := corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: testConfigMapName, + Namespace: testNamespace, + }, + Data: map[string]string{testKey1: testConfigMapValue}, + } + fakeClient := fake.NewClient(&configMap) + configMapQuery := NewConfigMapQuery(context.TODO(), fakeClient, fakeClient, log) + + foundConfigMap, err := configMapQuery.Get(client.ObjectKey{Name: testConfigMapName, Namespace: testNamespace}) + + assert.NoError(t, err) + assert.True(t, AreConfigMapsEqual(configMap, foundConfigMap)) +} + +func testCreateConfigMap(t *testing.T) { + fakeClient := fake.NewClient() + configMapQuery := NewConfigMapQuery(context.TODO(), fakeClient, fakeClient, log) + configMap := corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: testConfigMapName, + Namespace: testNamespace, + }, + Data: map[string]string{testKey1: testConfigMapValue}, + } + + err := configMapQuery.Create(configMap) + + assert.NoError(t, err) + + var actualConfigMap corev1.ConfigMap + err = fakeClient.Get(context.TODO(), client.ObjectKey{Name: testConfigMapName, Namespace: testNamespace}, &actualConfigMap) + + assert.NoError(t, err) + assert.True(t, AreConfigMapsEqual(configMap, actualConfigMap)) +} + +func testUpdateConfigMap(t *testing.T) { + configMap := corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: testConfigMapName, + Namespace: testNamespace, + }, + Data: map[string]string{testKey1: testConfigMapValue}, + } + fakeClient := fake.NewClient() + configMapQuery := NewConfigMapQuery(context.TODO(), fakeClient, fakeClient, log) + + err := configMapQuery.Update(configMap) + + assert.Error(t, err) + + configMap.Data = nil + fakeClient = fake.NewClient(&configMap) + configMapQuery.kubeClient = fakeClient + + err = configMapQuery.Update(configMap) + + assert.NoError(t, err) + + var updatedConfigMap corev1.ConfigMap + err = fakeClient.Get(context.TODO(), client.ObjectKey{Name: configMap.Name, Namespace: configMap.Namespace}, &updatedConfigMap) + + assert.NoError(t, err) + assert.True(t, AreConfigMapsEqual(configMap, updatedConfigMap)) +} + +func testCreateOrUpdateConfigMap(t *testing.T) { + configMap := corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: testConfigMapName, + Namespace: testNamespace, + }, + Data: map[string]string{testKey1: testConfigMapValue}, + } + fakeClient := fake.NewClient() + configMapQuery := NewConfigMapQuery(context.TODO(), fakeClient, fakeClient, log) + + err := configMapQuery.CreateOrUpdate(configMap) + assert.NoError(t, err) + + var createdConfigMap corev1.ConfigMap + err = fakeClient.Get(context.TODO(), client.ObjectKey{Name: configMap.Name, Namespace: configMap.Namespace}, &createdConfigMap) + + assert.NoError(t, err) + assert.True(t, AreConfigMapsEqual(configMap, createdConfigMap)) + + fakeClient = fake.NewClient(&configMap) + configMap = corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: testConfigMapName, + Namespace: testNamespace, + }, + Data: nil, + } + configMapQuery.kubeClient = fakeClient + + err = configMapQuery.CreateOrUpdate(configMap) + + assert.NoError(t, err) + + var updatedConfigMap corev1.ConfigMap + err = fakeClient.Get(context.TODO(), client.ObjectKey{Name: configMap.Name, Namespace: configMap.Namespace}, &updatedConfigMap) + + assert.NoError(t, err) + assert.True(t, AreConfigMapsEqual(configMap, updatedConfigMap)) +} + +func testIdenticalConfigMapIsNotUpdated(t *testing.T) { + data := map[string]string{testKey1: string(testValue1)} + labels := map[string]string{ + "label": "test", + } + fakeClient := fake.NewClient(&corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: testConfigMapName, + Namespace: testNamespace, + Labels: labels, + }, + Data: data, + }) + configMap := createTestConfigMap(labels, data) + configMapQuery := NewConfigMapQuery(context.TODO(), fakeClient, fakeClient, log) + + err := configMapQuery.CreateOrUpdate(*configMap) + assert.NoError(t, err) +} + +func testUpdateConfigMapWhenDataChanged(t *testing.T) { + data := map[string]string{testKey1: string(testValue1)} + labels := map[string]string{ + "label": "test", + } + fakeClient := fake.NewClient(&corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: testConfigMapName, + Namespace: testNamespace, + Labels: labels, + }, + Data: map[string]string{}, + }) + configMap := createTestConfigMap(labels, data) + configMapQuery := NewConfigMapQuery(context.TODO(), fakeClient, fakeClient, log) + + err := configMapQuery.CreateOrUpdate(*configMap) + assert.NoError(t, err) + + var updatedConfigMap corev1.ConfigMap + err = fakeClient.Get(context.TODO(), types.NamespacedName{Name: testConfigMapName, Namespace: testNamespace}, &updatedConfigMap) + + assert.NoError(t, err) + assert.True(t, reflect.DeepEqual(data, updatedConfigMap.Data)) +} + +func testUpdateConfigMapWhenLabelsChanged(t *testing.T) { + data := map[string]string{testKey1: string(testValue1)} + labels := map[string]string{ + "label": "test", + } + fakeClient := fake.NewClient(&corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: testConfigMapName, + Namespace: testNamespace, + Labels: map[string]string{}, + }, + Data: data, + }) + configMap := createTestConfigMap(labels, data) + configMapQuery := NewConfigMapQuery(context.TODO(), fakeClient, fakeClient, log) + + err := configMapQuery.CreateOrUpdate(*configMap) + assert.NoError(t, err) + + var updatedConfigMap corev1.ConfigMap + err = fakeClient.Get(context.TODO(), types.NamespacedName{Name: testConfigMapName, Namespace: testNamespace}, &updatedConfigMap) + + assert.NoError(t, err) + assert.True(t, reflect.DeepEqual(labels, updatedConfigMap.Labels)) +} + +func testCreateConfigMapInTargetNamespace(t *testing.T) { + data := map[string]string{testKey1: string(testValue1)} + labels := map[string]string{ + "label": "test", + } + fakeClient := fake.NewClient(&corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: testConfigMapName, + Namespace: "other", + }, + Data: map[string]string{}, + }) + configMap := createTestConfigMap(labels, data) + configMapQuery := NewConfigMapQuery(context.TODO(), fakeClient, fakeClient, log) + + err := configMapQuery.CreateOrUpdate(*configMap) + + assert.NoError(t, err) + + var newConfigMap corev1.ConfigMap + err = fakeClient.Get(context.TODO(), types.NamespacedName{Name: testConfigMapName, Namespace: testNamespace}, &newConfigMap) + + assert.NoError(t, err) + assert.True(t, reflect.DeepEqual(data, newConfigMap.Data)) + assert.True(t, reflect.DeepEqual(labels, newConfigMap.Labels)) + assert.Equal(t, testConfigMapName, newConfigMap.Name) + assert.Equal(t, testNamespace, newConfigMap.Namespace) +} + +func createTestConfigMap(labels map[string]string, data map[string]string) *corev1.ConfigMap { + configMap := &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: testConfigMapName, + Namespace: testNamespace, + Labels: labels, + }, + Data: data, + } + return configMap +} diff --git a/src/kubeobjects/secret_test.go b/src/kubeobjects/secret_test.go deleted file mode 100644 index 7e606c09d7..0000000000 --- a/src/kubeobjects/secret_test.go +++ /dev/null @@ -1,361 +0,0 @@ -package kubeobjects - -import ( - "context" - "reflect" - "testing" - - "github.com/Dynatrace/dynatrace-operator/src/dtclient" - "github.com/Dynatrace/dynatrace-operator/src/logger" - "github.com/Dynatrace/dynatrace-operator/src/scheme/fake" - "github.com/stretchr/testify/assert" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -var log = logger.Factory.GetLogger("test-secret") - -func TestSecretQuery(t *testing.T) { - t.Run(`Get secret`, testGetSecret) - t.Run(`Create secret`, testCreateSecret) - t.Run(`Update secret`, testUpdateSecret) - t.Run(`Create or update secret`, testCreateOrUpdateSecret) - t.Run(`Identical secret is not updated`, testIdenticalSecretIsNotUpdated) - t.Run(`Update secret when data has changed`, testUpdateSecretWhenDataChanged) - t.Run(`Update secret when labels have changed`, testUpdateSecretWhenLabelsChanged) - t.Run(`Create secret in target namespace`, testCreateSecretInTargetNamespace) -} - -func testGetSecret(t *testing.T) { - secret := corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: testSecretName, - Namespace: testNamespace, - }, - Data: map[string][]byte{testKey1: []byte(testSecretValue)}, - } - fakeClient := fake.NewClient(&secret) - secretQuery := NewSecretQuery(context.TODO(), fakeClient, fakeClient, log) - - foundSecret, err := secretQuery.Get(client.ObjectKey{Name: testSecretName, Namespace: testNamespace}) - - assert.NoError(t, err) - assert.True(t, AreSecretsEqual(secret, foundSecret)) -} - -func testCreateSecret(t *testing.T) { - fakeClient := fake.NewClient() - secretQuery := NewSecretQuery(context.TODO(), fakeClient, fakeClient, log) - secret := corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: testSecretName, - Namespace: testNamespace, - }, - Data: map[string][]byte{testKey1: []byte(testSecretValue)}, - } - - err := secretQuery.Create(secret) - - assert.NoError(t, err) - - var actualSecret corev1.Secret - err = fakeClient.Get(context.TODO(), client.ObjectKey{Name: testSecretName, Namespace: testNamespace}, &actualSecret) - - assert.NoError(t, err) - assert.True(t, AreSecretsEqual(secret, actualSecret)) -} - -func testUpdateSecret(t *testing.T) { - secret := corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: testSecretName, - Namespace: testNamespace, - }, - Data: map[string][]byte{testKey1: []byte(testSecretValue)}, - } - fakeClient := fake.NewClient() - secretQuery := NewSecretQuery(context.TODO(), fakeClient, fakeClient, log) - - err := secretQuery.Update(secret) - - assert.Error(t, err) - - secret.Data = nil - fakeClient = fake.NewClient(&secret) - secretQuery.kubeClient = fakeClient - - err = secretQuery.Update(secret) - - assert.NoError(t, err) - - var updatedSecret corev1.Secret - err = fakeClient.Get(context.TODO(), client.ObjectKey{Name: secret.Name, Namespace: secret.Namespace}, &updatedSecret) - - assert.NoError(t, err) - assert.True(t, AreSecretsEqual(secret, updatedSecret)) -} - -func testCreateOrUpdateSecret(t *testing.T) { - secret := corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: testSecretName, - Namespace: testNamespace, - }, - Data: map[string][]byte{testKey1: []byte(testSecretValue)}, - } - fakeClient := fake.NewClient() - secretQuery := NewSecretQuery(context.TODO(), fakeClient, fakeClient, log) - - err := secretQuery.CreateOrUpdate(secret) - assert.NoError(t, err) - - var createdSecret corev1.Secret - err = fakeClient.Get(context.TODO(), client.ObjectKey{Name: secret.Name, Namespace: secret.Namespace}, &createdSecret) - - assert.NoError(t, err) - assert.True(t, AreSecretsEqual(secret, createdSecret)) - - fakeClient = fake.NewClient(&secret) - secret = corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: testSecretName, - Namespace: testNamespace, - }, - Data: nil, - } - secretQuery.kubeClient = fakeClient - - err = secretQuery.CreateOrUpdate(secret) - - assert.NoError(t, err) - - var updatedSecret corev1.Secret - err = fakeClient.Get(context.TODO(), client.ObjectKey{Name: secret.Name, Namespace: secret.Namespace}, &updatedSecret) - - assert.NoError(t, err) - assert.True(t, AreSecretsEqual(secret, updatedSecret)) -} - -func testIdenticalSecretIsNotUpdated(t *testing.T) { - data := map[string][]byte{testKey1: []byte(testValue1)} - labels := map[string]string{ - "label": "test", - } - fakeClient := fake.NewClient(&corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: testSecretName, - Namespace: testNamespace, - Labels: labels, - }, - Data: data, - }) - secret := createTestSecret(labels, data) - secretQuery := NewSecretQuery(context.TODO(), fakeClient, fakeClient, log) - - err := secretQuery.CreateOrUpdate(*secret) - assert.NoError(t, err) -} - -func testUpdateSecretWhenDataChanged(t *testing.T) { - data := map[string][]byte{testKey1: []byte(testValue1)} - labels := map[string]string{ - "label": "test", - } - fakeClient := fake.NewClient(&corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: testSecretName, - Namespace: testNamespace, - Labels: labels, - }, - Data: map[string][]byte{}, - }) - secret := createTestSecret(labels, data) - secretQuery := NewSecretQuery(context.TODO(), fakeClient, fakeClient, log) - - err := secretQuery.CreateOrUpdate(*secret) - assert.NoError(t, err) - - var updatedSecret corev1.Secret - err = fakeClient.Get(context.TODO(), types.NamespacedName{Name: testSecretName, Namespace: testNamespace}, &updatedSecret) - - assert.NoError(t, err) - assert.True(t, reflect.DeepEqual(data, updatedSecret.Data)) -} - -func testUpdateSecretWhenLabelsChanged(t *testing.T) { - data := map[string][]byte{testKey1: []byte(testValue1)} - labels := map[string]string{ - "label": "test", - } - fakeClient := fake.NewClient(&corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: testSecretName, - Namespace: testNamespace, - Labels: map[string]string{}, - }, - Data: data, - }) - secret := createTestSecret(labels, data) - secretQuery := NewSecretQuery(context.TODO(), fakeClient, fakeClient, log) - - err := secretQuery.CreateOrUpdate(*secret) - assert.NoError(t, err) - - var updatedSecret corev1.Secret - err = fakeClient.Get(context.TODO(), types.NamespacedName{Name: testSecretName, Namespace: testNamespace}, &updatedSecret) - - assert.NoError(t, err) - assert.True(t, reflect.DeepEqual(labels, updatedSecret.Labels)) -} - -func testCreateSecretInTargetNamespace(t *testing.T) { - data := map[string][]byte{testKey1: []byte(testValue1)} - labels := map[string]string{ - "label": "test", - } - fakeClient := fake.NewClient(&corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: testSecretName, - Namespace: "other", - }, - Data: map[string][]byte{}, - }) - secret := createTestSecret(labels, data) - secretQuery := NewSecretQuery(context.TODO(), fakeClient, fakeClient, log) - - err := secretQuery.CreateOrUpdate(*secret) - - assert.NoError(t, err) - - var newSecret corev1.Secret - err = fakeClient.Get(context.TODO(), types.NamespacedName{Name: testSecretName, Namespace: testNamespace}, &newSecret) - - assert.NoError(t, err) - assert.True(t, reflect.DeepEqual(data, newSecret.Data)) - assert.True(t, reflect.DeepEqual(labels, newSecret.Labels)) - assert.Equal(t, testSecretName, newSecret.Name) - assert.Equal(t, testNamespace, newSecret.Namespace) - assert.Equal(t, corev1.SecretTypeOpaque, newSecret.Type) -} - -func TestNewTokens(t *testing.T) { - t.Run(`NewTokens extracts api and paas token from secret`, func(t *testing.T) { - secret := corev1.Secret{ - Data: map[string][]byte{ - dtclient.DynatraceApiToken: []byte(testValue1), - dtclient.DynatracePaasToken: []byte(testValue2), - }} - tokens, err := NewTokens(&secret) - - assert.NoError(t, err) - assert.NotNil(t, tokens) - assert.Equal(t, testValue1, tokens.ApiToken) - assert.Equal(t, testValue2, tokens.PaasToken) - }) - t.Run(`NewTokens handles missing api or paas token`, func(t *testing.T) { - secret := corev1.Secret{ - Data: map[string][]byte{ - dtclient.DynatraceApiToken: []byte(testValue1), - }} - tokens, err := NewTokens(&secret) - - assert.NoError(t, err) - assert.NotNil(t, tokens) - assert.Equal(t, testValue1, tokens.ApiToken) - assert.Equal(t, testValue1, tokens.PaasToken) - - secret = corev1.Secret{ - Data: map[string][]byte{ - dtclient.DynatracePaasToken: []byte(testValue2), - }} - tokens, err = NewTokens(&secret) - - assert.Error(t, err) - assert.Nil(t, tokens) - assert.Contains(t, err.Error(), dtclient.DynatraceApiToken) - - secret = corev1.Secret{ - Data: map[string][]byte{}} - tokens, err = NewTokens(&secret) - - assert.Error(t, err) - assert.Nil(t, tokens) - assert.Contains(t, err.Error(), dtclient.DynatraceApiToken) - }) - t.Run(`NewTokens handles nil secret`, func(t *testing.T) { - tokens, err := NewTokens(nil) - - assert.Error(t, err) - assert.Nil(t, tokens) - }) -} - -func TestExtractToken(t *testing.T) { - t.Run(`ExtractToken returns value from secret`, func(t *testing.T) { - secret := corev1.Secret{ - Data: map[string][]byte{ - testKey1: []byte(testValue1), - testKey2: []byte(testValue2), - }} - - value, err := ExtractToken(&secret, testKey1) - - assert.NoError(t, err) - assert.Equal(t, value, testValue1) - - value, err = ExtractToken(&secret, testKey2) - - assert.NoError(t, err) - assert.Equal(t, value, testValue2) - }) - t.Run(`ExtractToken handles missing key`, func(t *testing.T) { - secret := corev1.Secret{ - Data: map[string][]byte{}} - - value, err := ExtractToken(&secret, testKey1) - - assert.Error(t, err) - assert.Empty(t, value) - }) -} - -func TestGetDataFromSecretName(t *testing.T) { - t.Run(`GetDataFromSecret returns value from secret`, func(t *testing.T) { - client := fake.NewClient(&corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: testSecretName, - Namespace: testNamespace, - Labels: map[string]string{}, - }, - Data: map[string][]byte{ - testKey1: []byte(testValue1), - }, - }) - - value, err := GetDataFromSecretName(client, types.NamespacedName{Name: testSecretName, Namespace: testNamespace}, testKey1, log) - - assert.NoError(t, err) - assert.Equal(t, value, testValue1) - }) - t.Run(`ExtractToken handles missing key`, func(t *testing.T) { - value, err := GetDataFromSecretName(fake.NewClient(), types.NamespacedName{Name: testSecretName, Namespace: testNamespace}, testKey1, log) - assert.Error(t, err) - assert.Empty(t, value) - }) -} - -func createTestSecret(labels map[string]string, data map[string][]byte) *corev1.Secret { - secret := &corev1.Secret{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Name: testSecretName, - Namespace: testNamespace, - Labels: labels, - }, - Data: data, - Type: corev1.SecretTypeOpaque, - } - return secret -}