From 69890d5cf405a22e1522d17fe756ff772e44283e Mon Sep 17 00:00:00 2001 From: OZGCloud <ozgcloud@mgm-tp.com> Date: Thu, 4 Jan 2024 14:02:06 +0100 Subject: [PATCH] OZG-4453 revert stringData -> data; using data for encoding and string for plane text --- .../operator/ElasticsearchReconciler.java | 29 ++-- .../OzgCloudElasticsearchSecretHelper.java | 24 ++-- .../operator/ElasticsearchReconcilerTest.java | 129 +++++++++++------- ...OzgCloudElasticsearchSecretHelperTest.java | 89 ++++++------ 4 files changed, 160 insertions(+), 111 deletions(-) diff --git a/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/ElasticsearchReconciler.java b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/ElasticsearchReconciler.java index 4874388..613b763 100644 --- a/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/ElasticsearchReconciler.java +++ b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/ElasticsearchReconciler.java @@ -1,5 +1,6 @@ package de.ozgcloud.operator; +import java.util.Base64; import java.util.logging.Level; import org.apache.commons.collections.MapUtils; @@ -19,12 +20,13 @@ import lombok.extern.java.Log; @RequiredArgsConstructor @ControllerConfiguration @Component -public class ElasticsearchReconciler implements Reconciler<OzgCloudElasticsearchCustomResource>, Cleaner<OzgCloudElasticsearchCustomResource> { +public class ElasticsearchReconciler implements Reconciler<OzgCloudElasticsearchCustomResource>, Cleaner<OzgCloudElasticsearchCustomResource> { private final OzgCloudElasticsearchService service; @Override - public UpdateControl<OzgCloudElasticsearchCustomResource> reconcile(OzgCloudElasticsearchCustomResource resource, Context<OzgCloudElasticsearchCustomResource> context) { + public UpdateControl<OzgCloudElasticsearchCustomResource> reconcile(OzgCloudElasticsearchCustomResource resource, + Context<OzgCloudElasticsearchCustomResource> context) { try { log.info("Reconcile user " + resource.getCRDName() + "..."); var namespace = resource.getMetadata().getNamespace(); @@ -40,15 +42,24 @@ public class ElasticsearchReconciler implements Reconciler<OzgCloudElasticsearch return buildExceptionUpdateControl(resource, exception); } } - - private String getPassword(Secret secret) { + + String getPassword(Secret secret) { log.info("get password for elastic user..."); log.info(String.format("from secret string map: %s", secret.getStringData())); log.info(String.format("from secret data map: %s", secret.getData())); - return MapUtils.getString(secret.getData(), OzgCloudElasticsearchSecretHelper.CREDENTIAL_SECRET_PASSWORD_FIELD); + return decode(MapUtils.getString(secret.getData(), OzgCloudElasticsearchSecretHelper.CREDENTIAL_SECRET_PASSWORD_FIELD)); } - - UpdateControl<OzgCloudElasticsearchCustomResource> buildExceptionUpdateControl(OzgCloudElasticsearchCustomResource resource, Exception exception) { + + private String decode(String encodedPassword) { + try { + return new String(Base64.getDecoder().decode(encodedPassword)); + } catch (Exception e) { + throw new RuntimeException("Could not decode password from secret."); + } + } + + UpdateControl<OzgCloudElasticsearchCustomResource> buildExceptionUpdateControl(OzgCloudElasticsearchCustomResource resource, + Exception exception) { return OzgCloudElasticsearchUpdateControlBuilder .fromResource(resource) .withStatus(CustomResourceStatus.ERROR) @@ -56,7 +67,7 @@ public class ElasticsearchReconciler implements Reconciler<OzgCloudElasticsearch .withMessage(exception.getMessage()) .build(); } - + @Override public DeleteControl cleanup(OzgCloudElasticsearchCustomResource resource, Context<OzgCloudElasticsearchCustomResource> context) { var namespace = resource.getMetadata().getNamespace(); @@ -65,7 +76,7 @@ public class ElasticsearchReconciler implements Reconciler<OzgCloudElasticsearch service.deleteSecurityRoleIfExists(namespace); service.deleteIndexIfExists(namespace); return DeleteControl.defaultDelete(); - } catch(Exception e) { + } catch (Exception e) { log.log(Level.INFO, String.format("Could not cleanup elasticsearch resource in namespace %s. %n %s", namespace, e)); return DeleteControl.defaultDelete(); } diff --git a/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/OzgCloudElasticsearchSecretHelper.java b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/OzgCloudElasticsearchSecretHelper.java index 6fbe2c3..b6284d9 100644 --- a/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/OzgCloudElasticsearchSecretHelper.java +++ b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/OzgCloudElasticsearchSecretHelper.java @@ -13,37 +13,37 @@ import lombok.RequiredArgsConstructor; public class OzgCloudElasticsearchSecretHelper { static final String SECRET_TYPE = "Opaque"; - + static final String CREDENTIAL_SECRET_ADDRESS_FIELD = "address"; static final String CREDENTIAL_SECRET_INDEX_FIELD = "index"; static final String CREDENTIAL_SECRET_PASSWORD_FIELD = "password"; static final String CREDENTIAL_SECRET_USERNAME_FIELD = "username"; - + static final String CERTIFICATE_SECRET_DATA_KEY = "ca.crt"; - + static final int PASSWORD_LENGTH = 15; - + private final OzgCloudElasticsearchProperties properties; public Secret buildCredentialSecret(String namespace, String name) { return new SecretBuilder() .withType(SECRET_TYPE) .withMetadata(createMetaData(name, namespace)) - .addToData(CREDENTIAL_SECRET_ADDRESS_FIELD, buildSecretAddress()) - .addToData(CREDENTIAL_SECRET_INDEX_FIELD, namespace) - .addToData(CREDENTIAL_SECRET_PASSWORD_FIELD, generatePassword()) - .addToData(CREDENTIAL_SECRET_USERNAME_FIELD, namespace) + .addToStringData(CREDENTIAL_SECRET_ADDRESS_FIELD, buildSecretAddress()) + .addToStringData(CREDENTIAL_SECRET_INDEX_FIELD, namespace) + .addToStringData(CREDENTIAL_SECRET_PASSWORD_FIELD, generatePassword()) + .addToStringData(CREDENTIAL_SECRET_USERNAME_FIELD, namespace) .build(); } - + private String buildSecretAddress() { return String.format("%s:%s", properties.getServer().getHost(), properties.getServer().getPort()); } - + private String generatePassword() { return RandomStringUtils.randomAlphabetic(PASSWORD_LENGTH); } - + public Secret buildCertificateSecret(String namespace, String data) { return new SecretBuilder() .withType(SECRET_TYPE) @@ -51,7 +51,7 @@ public class OzgCloudElasticsearchSecretHelper { .addToData(CERTIFICATE_SECRET_DATA_KEY, data) .build(); } - + private ObjectMeta createMetaData(String name, String namespace) { var metadata = new ObjectMeta(); metadata.setName(name); diff --git a/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/ElasticsearchReconcilerTest.java b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/ElasticsearchReconcilerTest.java index d75e012..163b26d 100644 --- a/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/ElasticsearchReconcilerTest.java +++ b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/ElasticsearchReconcilerTest.java @@ -4,6 +4,9 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +import java.util.Base64; +import java.util.Map; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -12,9 +15,12 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; +import com.thedeanda.lorem.LoremIpsum; + import de.ozgcloud.operator.common.kubernetes.NamespaceTestFactory; import de.ozgcloud.operator.common.kubernetes.SecretTestFactory; import io.fabric8.kubernetes.api.model.Secret; +import io.fabric8.kubernetes.api.model.SecretBuilder; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; import lombok.SneakyThrows; @@ -30,167 +36,196 @@ class ElasticsearchReconcilerTest { @DisplayName("Reconcile") @Nested class TestReconcile { - + @Mock private Context<OzgCloudElasticsearchCustomResource> context; - + private final OzgCloudElasticsearchCustomResource resource = ElasticsearchCustomResourceTestFactory.create(); - - private final static String PASSWORD = "dummyPassword"; - private final Secret secret = SecretTestFactory.createBuilder().addToData(OzgCloudElasticsearchSecretHelper.CREDENTIAL_SECRET_PASSWORD_FIELD, PASSWORD).build(); - + + private final static String PASSWORD = new String(Base64.getEncoder().encode("dummyPassword".getBytes())); + private final static String DECODED_PASSWORD = new String(Base64.getDecoder().decode(PASSWORD)); + private final Secret secret = SecretTestFactory.createBuilder() + .addToData(OzgCloudElasticsearchSecretHelper.CREDENTIAL_SECRET_PASSWORD_FIELD, PASSWORD).build(); + @DisplayName("process flow") @Nested class TestProcessFlow { - + @BeforeEach void mockCredentialSecret() { when(service.getOrCreateCredentialSecret(any(), any())).thenReturn(secret); } - + @Test void shouldGetCredentialSecret() { reconcile(); - + verify(service).getOrCreateCredentialSecret(resource, context); } - + @SneakyThrows @Test void shouldCreateIndexIfMissing() { reconcile(); - + verify(service).createIndexIfMissing(NamespaceTestFactory.NAMESPACE); } - + @SneakyThrows @Test void shouldCreateecurityRoleIfMissing() { reconcile(); - + verify(service).createSecurityRoleIfMissing(NamespaceTestFactory.NAMESPACE); } - + @SneakyThrows @Test void shouldCallCreateSecurityUserIfMissing() { reconcile(); - - verify(service).createSecurityUserIfMissing(NamespaceTestFactory.NAMESPACE, PASSWORD); + + verify(service).createSecurityUserIfMissing(NamespaceTestFactory.NAMESPACE, DECODED_PASSWORD); } - + @SneakyThrows @Test void shouldCallCreateCertificateIfMissing() { reconcile(); - + verify(service).createCertificateIfMissing(NamespaceTestFactory.NAMESPACE); } } - + @DisplayName("on exception") @Nested class TestOnException { - + private final Exception exception = new RuntimeException(); - + @BeforeEach void mock() { when(service.getOrCreateCredentialSecret(any(), any())).thenThrow(exception); } - + @Test void shouldBuildExceptionUpdateControl() { reconcile(); - + verify(reconciler).buildExceptionUpdateControl(resource, exception); } } - + @SneakyThrows private UpdateControl<OzgCloudElasticsearchCustomResource> reconcile() { return reconciler.reconcile(resource, context); } - + @DisplayName("build exception update control") @Nested class TestBuildExceptionUpdateControl { - + private final OzgCloudElasticsearchCustomResource resource = ElasticsearchCustomResourceTestFactory.create(); private final static String EXCEPTION_MESSAGE = "ExeptionMessage"; private final Exception exception = new RuntimeException(EXCEPTION_MESSAGE); - + @Test void shouldContainResource() { var updateControl = buildExceptionUpdateControl(); - + assertThat(updateControl.getResource()).isEqualTo(resource); } - + @Test void shouldContainUpdateStatus() { var updateControl = buildExceptionUpdateControl(); - + assertThat(updateControl.getResource().getStatus().getStatus()).isEqualTo(CustomResourceStatus.ERROR); } - + @Test void shouldContainReschedule() { var updateControl = buildExceptionUpdateControl(); - + assertThat(updateControl.getScheduleDelay()).hasValue(60000L); } - + @Test void shouldContainMessage() { var updateControl = buildExceptionUpdateControl(); - + assertThat(updateControl.getResource().getStatus().getMessage()).isEqualTo(EXCEPTION_MESSAGE); } - + private UpdateControl<OzgCloudElasticsearchCustomResource> buildExceptionUpdateControl() { return reconciler.buildExceptionUpdateControl(resource, exception); } } + + @DisplayName("get password") + @Nested + class TestGetPassword { + + private static final String SECRET_PASSWORD = LoremIpsum.getInstance().getWords(1); + + @Test + void shouldReturnPasssowrd() { + var secret = buildSecret(); + + var password = reconciler.getPassword(secret); + + assertThat(password).isEqualTo(SECRET_PASSWORD); + } + + private Secret buildSecret() { + return new SecretBuilder() + .addToData(Map.of(OzgCloudElasticsearchSecretHelper.CREDENTIAL_SECRET_PASSWORD_FIELD, + encodeStringBase64(SECRET_PASSWORD))) + .build(); + } + + private String encodeStringBase64(String string) { + return Base64.getEncoder().encodeToString(string.getBytes()); + } + } } - + @DisplayName("Cleanup") @Nested class TestCleanup { - + @Mock private Context<OzgCloudElasticsearchCustomResource> context; - + private final OzgCloudElasticsearchCustomResource resource = ElasticsearchCustomResourceTestFactory.create(); - + @SneakyThrows @Test void shouldDeleteSecurityUser() { reconciler.cleanup(resource, context); - + verify(service).deleteSecurityUserIfExists(NamespaceTestFactory.NAMESPACE); } - + @SneakyThrows @Test void shouldDeleteSecurityRole() { reconciler.cleanup(resource, context); - + verify(service).deleteSecurityRoleIfExists(NamespaceTestFactory.NAMESPACE); } - + @SneakyThrows @Test void shouldDeleteIndex() { reconciler.cleanup(resource, context); - + verify(service).deleteIndexIfExists(NamespaceTestFactory.NAMESPACE); } - + @Test - void shouldReturnDeleteControl(){ + void shouldReturnDeleteControl() { var deleteControl = reconciler.cleanup(resource, context); - + assertThat(deleteControl).isNotNull(); } } diff --git a/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/OzgCloudElasticsearchSecretHelperTest.java b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/OzgCloudElasticsearchSecretHelperTest.java index 3f2a3de..5cba01b 100644 --- a/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/OzgCloudElasticsearchSecretHelperTest.java +++ b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/OzgCloudElasticsearchSecretHelperTest.java @@ -25,146 +25,149 @@ class OzgCloudElasticsearchSecretHelperTest { private OzgCloudElasticsearchSecretHelper builder; @Mock private OzgCloudElasticsearchProperties properties; - + @DisplayName("Build credential secret") @Nested class TestBuildCredentialsSecret { - + private final static String HOST = "dummyHost"; private final static int PORT = 42; - + @Mock private OzgCloudElasticsearchServerProperties serverProperties; - + @BeforeEach void mockProperties() { when(properties.getServer()).thenReturn(serverProperties); when(serverProperties.getHost()).thenReturn(HOST); when(serverProperties.getPort()).thenReturn(PORT); } - + @Test void shouldContainType() { var secret = buildCredentialSecret(); - + assertThat(secret.getType()).isEqualTo(OzgCloudElasticsearchSecretHelper.SECRET_TYPE); } - + @DisplayName("metadata") @Nested class TestMetadata { - + @Test void shouldContainName() { var secret = buildCredentialSecret(); - + assertThat(secret.getMetadata().getName()).isEqualTo(SecretTestFactory.NAME); } - + @Test void shouldContainNamespace() { var secret = buildCredentialSecret(); - + assertThat(secret.getMetadata().getNamespace()).isEqualTo(NamespaceTestFactory.NAMESPACE); } } - + @DisplayName("address") @Nested class TestAddress { - + @Test void shouldBeSet() { var secret = buildCredentialSecret(); - - assertThat(secret.getData()).containsEntry(OzgCloudElasticsearchSecretHelper.CREDENTIAL_SECRET_ADDRESS_FIELD, String.format("%s:%s", HOST, PORT)); + + assertThat(secret.getStringData()).containsEntry(OzgCloudElasticsearchSecretHelper.CREDENTIAL_SECRET_ADDRESS_FIELD, + String.format("%s:%s", HOST, PORT)); } - + @Test void shouldGetHostFromProperties() { buildCredentialSecret(); - + verify(serverProperties).getHost(); } - + @Test void shouldGetPortFromPorperties() { buildCredentialSecret(); - + verify(serverProperties).getPort(); } } - + @Test void shouldContainIndex() { var secret = buildCredentialSecret(); - - assertThat(secret.getData()).containsEntry(OzgCloudElasticsearchSecretHelper.CREDENTIAL_SECRET_INDEX_FIELD, NamespaceTestFactory.NAMESPACE); + + assertThat(secret.getStringData()).containsEntry(OzgCloudElasticsearchSecretHelper.CREDENTIAL_SECRET_INDEX_FIELD, + NamespaceTestFactory.NAMESPACE); } - + @Test void shouldContainPassword() { var secret = buildCredentialSecret(); - - assertThat(secret.getData()).containsKey(OzgCloudElasticsearchSecretHelper.CREDENTIAL_SECRET_PASSWORD_FIELD); - assertThat(secret.getData().get(OzgCloudElasticsearchSecretHelper.CREDENTIAL_SECRET_PASSWORD_FIELD)).isNotNull(); + + assertThat(secret.getStringData()).containsKey(OzgCloudElasticsearchSecretHelper.CREDENTIAL_SECRET_PASSWORD_FIELD); + assertThat(secret.getStringData().get(OzgCloudElasticsearchSecretHelper.CREDENTIAL_SECRET_PASSWORD_FIELD)).isNotNull(); } - + @Test void shouldContainUsername() { var secret = buildCredentialSecret(); - - assertThat(secret.getData()).containsEntry(OzgCloudElasticsearchSecretHelper.CREDENTIAL_SECRET_USERNAME_FIELD, NamespaceTestFactory.NAMESPACE); + + assertThat(secret.getStringData()).containsEntry(OzgCloudElasticsearchSecretHelper.CREDENTIAL_SECRET_USERNAME_FIELD, + NamespaceTestFactory.NAMESPACE); } - + private Secret buildCredentialSecret() { return builder.buildCredentialSecret(NamespaceTestFactory.NAMESPACE, SecretTestFactory.NAME); } } - + @DisplayName("Build certificate secret") @Nested class TestBuildCertificatSecret { - + private static final String DATA = "fgdrgsgreg"; - + @Test void shouldHaveType() { var secret = builder.buildCertificateSecret(NamespaceTestFactory.NAMESPACE, DATA); - + assertThat(secret.getType()).isEqualTo(OzgCloudElasticsearchSecretHelper.SECRET_TYPE); } - + @DisplayName("metadata") @Nested class TestMetadata { - + private static final String CERTIFICATE_SECRET_NAME = "rfgsgrgsr"; - + @BeforeEach void mock() { when(properties.getCertificateSecretName()).thenReturn(CERTIFICATE_SECRET_NAME); } - + @Test void shouldContainName() { var secret = builder.buildCertificateSecret(NamespaceTestFactory.NAMESPACE, DATA); - + assertThat(secret.getMetadata().getName()).isEqualTo(CERTIFICATE_SECRET_NAME); } - + @Test void shouldContainNamespace() { var secret = builder.buildCertificateSecret(NamespaceTestFactory.NAMESPACE, DATA); - + assertThat(secret.getMetadata().getNamespace()).isEqualTo(NamespaceTestFactory.NAMESPACE); } } - + @Test void shouldHaveCaCrtData() { var secret = builder.buildCertificateSecret(NamespaceTestFactory.NAMESPACE, DATA); - + assertThat(secret.getData()).containsExactly(Map.entry(OzgCloudElasticsearchSecretHelper.CERTIFICATE_SECRET_DATA_KEY, DATA)); } } -- GitLab