Skip to content
Snippets Groups Projects
Commit 3698957e authored by OZGCloud's avatar OZGCloud
Browse files

OZG-4453 create certificate secret

parent eb680262
No related branches found
No related tags found
No related merge requests found
Showing
with 221 additions and 27 deletions
...@@ -32,6 +32,7 @@ public class ElasticsearchReconciler implements Reconciler<OzgCloudElasticsearch ...@@ -32,6 +32,7 @@ public class ElasticsearchReconciler implements Reconciler<OzgCloudElasticsearch
service.createIndexIfMissing(namespace); service.createIndexIfMissing(namespace);
service.createSecurityRoleIfMissing(namespace); service.createSecurityRoleIfMissing(namespace);
service.createSecurityUserIfMissing(namespace, getPassword(secret)); service.createSecurityUserIfMissing(namespace, getPassword(secret));
service.createCertificateIfMissing(namespace);
log.info("Reconcile user successful."); log.info("Reconcile user successful.");
return OzgCloudElasticsearchUpdateControlBuilder.fromResource(resource).withStatus(CustomResourceStatus.OK).build(); return OzgCloudElasticsearchUpdateControlBuilder.fromResource(resource).withStatus(CustomResourceStatus.OK).build();
} catch (Exception e) { } catch (Exception e) {
...@@ -41,7 +42,7 @@ public class ElasticsearchReconciler implements Reconciler<OzgCloudElasticsearch ...@@ -41,7 +42,7 @@ public class ElasticsearchReconciler implements Reconciler<OzgCloudElasticsearch
} }
private String getPassword(Secret secret) { private String getPassword(Secret secret) {
return MapUtils.getString(secret.getStringData(), OzgCloudElasticsearchSecretHelper.SECRET_PASSWORD_FIELD); return MapUtils.getString(secret.getStringData(), OzgCloudElasticsearchSecretHelper.CREDENTIAL_SECRET_PASSWORD_FIELD);
} }
UpdateControl<OzgCloudElasticsearchCustomResource> buildExceptionUpdateControl(OzgCloudElasticsearchCustomResource resource, Exception e) { UpdateControl<OzgCloudElasticsearchCustomResource> buildExceptionUpdateControl(OzgCloudElasticsearchCustomResource resource, Exception e) {
......
...@@ -28,5 +28,7 @@ public class OzgCloudElasticsearchProperties { ...@@ -28,5 +28,7 @@ public class OzgCloudElasticsearchProperties {
private String host; private String host;
private int port; private int port;
private String scheme; private String scheme;
private String certificateSecretName;
private String certificateSecretDataKey;
} }
} }
\ No newline at end of file
...@@ -13,10 +13,13 @@ import lombok.RequiredArgsConstructor; ...@@ -13,10 +13,13 @@ import lombok.RequiredArgsConstructor;
public class OzgCloudElasticsearchSecretHelper { public class OzgCloudElasticsearchSecretHelper {
static final String SECRET_TYPE = "Opaque"; static final String SECRET_TYPE = "Opaque";
static final String SECRET_ADDRESS_FIELD = "address";
static final String SECRET_INDEX_FIELD = "index"; static final String CREDENTIAL_SECRET_ADDRESS_FIELD = "address";
static final String SECRET_PASSWORD_FIELD = "password"; static final String CREDENTIAL_SECRET_INDEX_FIELD = "index";
static final String SECRET_USERNAME_FIELD = "username"; 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; static final int PASSWORD_LENGTH = 15;
...@@ -26,10 +29,10 @@ public class OzgCloudElasticsearchSecretHelper { ...@@ -26,10 +29,10 @@ public class OzgCloudElasticsearchSecretHelper {
return new SecretBuilder() return new SecretBuilder()
.withType(SECRET_TYPE) .withType(SECRET_TYPE)
.withMetadata(createMetaData(name, namespace)) .withMetadata(createMetaData(name, namespace))
.addToStringData(SECRET_ADDRESS_FIELD, buildSecretAddress()) .addToStringData(CREDENTIAL_SECRET_ADDRESS_FIELD, buildSecretAddress())
.addToStringData(SECRET_INDEX_FIELD, namespace) .addToStringData(CREDENTIAL_SECRET_INDEX_FIELD, namespace)
.addToStringData(SECRET_PASSWORD_FIELD, generatePassword()) .addToStringData(CREDENTIAL_SECRET_PASSWORD_FIELD, generatePassword())
.addToStringData(SECRET_USERNAME_FIELD, namespace) .addToStringData(CREDENTIAL_SECRET_USERNAME_FIELD, namespace)
.build(); .build();
} }
...@@ -41,6 +44,14 @@ public class OzgCloudElasticsearchSecretHelper { ...@@ -41,6 +44,14 @@ public class OzgCloudElasticsearchSecretHelper {
return RandomStringUtils.randomAlphabetic(PASSWORD_LENGTH); return RandomStringUtils.randomAlphabetic(PASSWORD_LENGTH);
} }
public Secret buildCertificateSecret(String namespace, String data) {
return new SecretBuilder()
.withType(SECRET_TYPE)
.withMetadata(createMetaData(properties.getCertificateSecretName(), namespace))
.addToStringData(CERTIFICATE_SECRET_DATA_KEY, data)
.build();
}
private ObjectMeta createMetaData(String name, String namespace) { private ObjectMeta createMetaData(String name, String namespace) {
var metadata = new ObjectMeta(); var metadata = new ObjectMeta();
metadata.setName(name); metadata.setName(name);
......
...@@ -2,6 +2,7 @@ package de.ozgcloud.operator; ...@@ -2,6 +2,7 @@ package de.ozgcloud.operator;
import java.util.Objects; import java.util.Objects;
import org.apache.commons.collections.MapUtils;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import de.ozgcloud.operator.PutRoleRequestData.IndicesPrivilegesData; import de.ozgcloud.operator.PutRoleRequestData.IndicesPrivilegesData;
...@@ -37,7 +38,6 @@ public class OzgCloudElasticsearchService { ...@@ -37,7 +38,6 @@ public class OzgCloudElasticsearchService {
createCredentialSecret(secretResource, namespace); createCredentialSecret(secretResource, namespace);
log.info("Secret creation successful."); log.info("Secret creation successful.");
} }
log.info("Secret already exists, return existing.");
return secretResource.get(); return secretResource.get();
} catch (Exception e) { } catch (Exception e) {
log.info("Secret creation failed: " + e); log.info("Secret creation failed: " + e);
...@@ -53,10 +53,6 @@ public class OzgCloudElasticsearchService { ...@@ -53,10 +53,6 @@ public class OzgCloudElasticsearchService {
createAdapter(resource).create(secretHelper.buildCredentialSecret(namespace, properties.getSecretCredentialsName())); createAdapter(resource).create(secretHelper.buildCredentialSecret(namespace, properties.getSecretCredentialsName()));
} }
ResourceAdapter<Secret> createAdapter(Resource<Secret> resource) {
return new ResourceAdapter<Secret>(resource);
}
// curl -k -X PUT -u elastic:$ELASTICSEARCH_PASSWORD -H 'Content-Type: application/json' 'https://ozg-search-cluster-es-http:9200/'$ES_NS_USER // curl -k -X PUT -u elastic:$ELASTICSEARCH_PASSWORD -H 'Content-Type: application/json' 'https://ozg-search-cluster-es-http:9200/'$ES_NS_USER
public void createIndexIfMissing(String name) throws Exception { public void createIndexIfMissing(String name) throws Exception {
log.info("Check index..."); log.info("Check index...");
...@@ -121,4 +117,34 @@ public class OzgCloudElasticsearchService { ...@@ -121,4 +117,34 @@ public class OzgCloudElasticsearchService {
} }
log.info("Delete index role successful."); log.info("Delete index role successful.");
} }
public void createCertificateIfMissing(String namespace) {
try {
log.info("Create certificate secret if missing...");
var secretResource = kubernetesService.getSecretResource(namespace, properties.getCertificateSecretName());
if(Objects.isNull(secretResource.get())) {
log.info("create...");
createCredentialSecret(namespace, secretResource);
log.info("create successful.");
}
} catch(Exception e) {
log.info("Certificate secret creation failed: " + e);
throw e;
}
}
void createCredentialSecret(String namespace, Resource<Secret> secretResource) {
var serverSecretResource = kubernetesService.getSecretResource(properties.getServerProperties().getNamespace(), properties.getServerProperties().getCertificateSecretName());
createAdapter(secretResource).create(secretHelper.buildCertificateSecret(namespace, getSecretData(serverSecretResource.get())));
}
private String getSecretData(Secret secret) {
return MapUtils.getString(secret.getStringData(), properties.getServerProperties().getCertificateSecretDataKey());
}
ResourceAdapter<Secret> createAdapter(Resource<Secret> resource) {
return new ResourceAdapter<Secret>(resource);
}
} }
\ No newline at end of file
...@@ -10,6 +10,7 @@ ozgcloud: ...@@ -10,6 +10,7 @@ ozgcloud:
port: 9200 port: 9200
scheme: https scheme: https
certificateSecretName: elasticsearch-certificate certificateSecretName: elasticsearch-certificate
certificateSecretDataKey: tlsCrt
management: management:
server: server:
......
...@@ -37,7 +37,7 @@ class ElasticsearchReconcilerTest { ...@@ -37,7 +37,7 @@ class ElasticsearchReconcilerTest {
private final OzgCloudElasticsearchCustomResource resource = ElasticsearchCustomResourceTestFactory.create(); private final OzgCloudElasticsearchCustomResource resource = ElasticsearchCustomResourceTestFactory.create();
private final static String PASSWORD = "dummyPassword"; private final static String PASSWORD = "dummyPassword";
private final Secret secret = SecretTestFactory.createBuilder().addToStringData(OzgCloudElasticsearchSecretHelper.SECRET_PASSWORD_FIELD, PASSWORD).build(); private final Secret secret = SecretTestFactory.createBuilder().addToStringData(OzgCloudElasticsearchSecretHelper.CREDENTIAL_SECRET_PASSWORD_FIELD, PASSWORD).build();
@DisplayName("process flow") @DisplayName("process flow")
@Nested @Nested
...@@ -57,7 +57,7 @@ class ElasticsearchReconcilerTest { ...@@ -57,7 +57,7 @@ class ElasticsearchReconcilerTest {
@SneakyThrows @SneakyThrows
@Test @Test
void shouldCheckIndex() { void shouldCreateIndexIfMissing() {
reconcile(); reconcile();
verify(service).createIndexIfMissing(NamespaceTestFactory.NAMESPACE); verify(service).createIndexIfMissing(NamespaceTestFactory.NAMESPACE);
...@@ -65,7 +65,7 @@ class ElasticsearchReconcilerTest { ...@@ -65,7 +65,7 @@ class ElasticsearchReconcilerTest {
@SneakyThrows @SneakyThrows
@Test @Test
void shouldCheckSecurityRole() { void shouldCreateecurityRoleIfMissing() {
reconcile(); reconcile();
verify(service).createSecurityRoleIfMissing(NamespaceTestFactory.NAMESPACE); verify(service).createSecurityRoleIfMissing(NamespaceTestFactory.NAMESPACE);
...@@ -73,11 +73,19 @@ class ElasticsearchReconcilerTest { ...@@ -73,11 +73,19 @@ class ElasticsearchReconcilerTest {
@SneakyThrows @SneakyThrows
@Test @Test
void shouldCheckSecurityUser() { void shouldCallCreateSecurityUserIfMissing() {
reconcile(); reconcile();
verify(service).createSecurityUserIfMissing(NamespaceTestFactory.NAMESPACE, PASSWORD); verify(service).createSecurityUserIfMissing(NamespaceTestFactory.NAMESPACE, PASSWORD);
} }
@SneakyThrows
@Test
void shouldCallCreateCertificateIfMissing() {
reconcile();
verify(service).createCertificateIfMissing(NamespaceTestFactory.NAMESPACE);
}
} }
@DisplayName("on exception") @DisplayName("on exception")
......
...@@ -3,6 +3,8 @@ package de.ozgcloud.operator; ...@@ -3,6 +3,8 @@ package de.ozgcloud.operator;
import static org.assertj.core.api.Assertions.*; import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
import java.util.Map;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Nested;
...@@ -16,7 +18,7 @@ import de.ozgcloud.operator.common.kubernetes.NamespaceTestFactory; ...@@ -16,7 +18,7 @@ import de.ozgcloud.operator.common.kubernetes.NamespaceTestFactory;
import de.ozgcloud.operator.common.kubernetes.SecretTestFactory; import de.ozgcloud.operator.common.kubernetes.SecretTestFactory;
import io.fabric8.kubernetes.api.model.Secret; import io.fabric8.kubernetes.api.model.Secret;
class ElasticsearchSecretBuilderTest { class OzgCloudElasticsearchSecretHelperTest {
@Spy @Spy
@InjectMocks @InjectMocks
...@@ -75,7 +77,7 @@ class ElasticsearchSecretBuilderTest { ...@@ -75,7 +77,7 @@ class ElasticsearchSecretBuilderTest {
void shouldBeSet() { void shouldBeSet() {
var secret = buildCredentialSecret(); var secret = buildCredentialSecret();
assertThat(secret.getStringData()).containsEntry(OzgCloudElasticsearchSecretHelper.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 @Test
...@@ -97,26 +99,73 @@ class ElasticsearchSecretBuilderTest { ...@@ -97,26 +99,73 @@ class ElasticsearchSecretBuilderTest {
void shouldContainIndex() { void shouldContainIndex() {
var secret = buildCredentialSecret(); var secret = buildCredentialSecret();
assertThat(secret.getStringData()).containsEntry(OzgCloudElasticsearchSecretHelper.SECRET_INDEX_FIELD, NamespaceTestFactory.NAMESPACE); assertThat(secret.getStringData()).containsEntry(OzgCloudElasticsearchSecretHelper.CREDENTIAL_SECRET_INDEX_FIELD, NamespaceTestFactory.NAMESPACE);
} }
@Test @Test
void shouldContainPassword() { void shouldContainPassword() {
var secret = buildCredentialSecret(); var secret = buildCredentialSecret();
assertThat(secret.getStringData()).containsKey(OzgCloudElasticsearchSecretHelper.SECRET_PASSWORD_FIELD); assertThat(secret.getStringData()).containsKey(OzgCloudElasticsearchSecretHelper.CREDENTIAL_SECRET_PASSWORD_FIELD);
assertThat(secret.getStringData().get(OzgCloudElasticsearchSecretHelper.SECRET_PASSWORD_FIELD)).isNotNull(); assertThat(secret.getStringData().get(OzgCloudElasticsearchSecretHelper.CREDENTIAL_SECRET_PASSWORD_FIELD)).isNotNull();
} }
@Test @Test
void shouldContainUsername() { void shouldContainUsername() {
var secret = buildCredentialSecret(); var secret = buildCredentialSecret();
assertThat(secret.getStringData()).containsEntry(OzgCloudElasticsearchSecretHelper.SECRET_USERNAME_FIELD, NamespaceTestFactory.NAMESPACE); assertThat(secret.getStringData()).containsEntry(OzgCloudElasticsearchSecretHelper.CREDENTIAL_SECRET_USERNAME_FIELD, NamespaceTestFactory.NAMESPACE);
} }
private Secret buildCredentialSecret() { private Secret buildCredentialSecret() {
return builder.buildCredentialSecret(NamespaceTestFactory.NAMESPACE, SecretTestFactory.NAME); 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.getStringData()).containsExactly(Map.entry(OzgCloudElasticsearchSecretHelper.CERTIFICATE_SECRET_DATA_KEY, DATA));
}
}
} }
...@@ -13,6 +13,7 @@ import org.mockito.InjectMocks; ...@@ -13,6 +13,7 @@ import org.mockito.InjectMocks;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.Spy; import org.mockito.Spy;
import de.ozgcloud.operator.OzgCloudElasticsearchProperties.OzgCloudElasticsearchServerProperties;
import de.ozgcloud.operator.common.elasticsearch.ElasticsearchRemoteService; import de.ozgcloud.operator.common.elasticsearch.ElasticsearchRemoteService;
import de.ozgcloud.operator.common.kubernetes.KubernetesRemoteService; import de.ozgcloud.operator.common.kubernetes.KubernetesRemoteService;
import de.ozgcloud.operator.common.kubernetes.NamespaceTestFactory; import de.ozgcloud.operator.common.kubernetes.NamespaceTestFactory;
...@@ -297,7 +298,7 @@ class OzgCloudElasticsearchServiceTest { ...@@ -297,7 +298,7 @@ class OzgCloudElasticsearchServiceTest {
@Nested @Nested
class TestDeleteIndexIfExists { class TestDeleteIndexIfExists {
private static final String INDEX_NAME = NamespaceTestFactory.NAMESPACE; private static final String INDEX_NAME = NAMESPACE;
@SneakyThrows @SneakyThrows
@Test @Test
...@@ -317,4 +318,99 @@ class OzgCloudElasticsearchServiceTest { ...@@ -317,4 +318,99 @@ class OzgCloudElasticsearchServiceTest {
verify(remoteService).deleteIndex(INDEX_NAME); verify(remoteService).deleteIndex(INDEX_NAME);
} }
} }
@DisplayName("Create certificate if missing")
@Nested
class TestCreateCertificateIfMissing {
private static final String CERTIFICATE_NAME = "dummySecretName";
@Mock
private Resource<Secret> secretResource;
@DisplayName("process flow")
@Nested
class TestProcessFlow {
@Mock
private OzgCloudElasticsearchServerProperties serverProperties;
@BeforeEach
void mock() {
when(kubernetesService.getSecretResource(any(), any())).thenReturn(secretResource);
when(properties.getCertificateSecretName()).thenReturn(CERTIFICATE_NAME);
}
@Test
void shouldGetCertificateSecret() {
when(secretResource.get()).thenReturn(SecretTestFactory.create());
service.createCertificateIfMissing(NAMESPACE);
verify(kubernetesService).getSecretResource(NAMESPACE, CERTIFICATE_NAME);
}
@Test
void shouldCreateIfMissing() {
when(secretResource.get()).thenReturn(null);
doNothing().when(service).createCredentialSecret(any(), any());
service.createCertificateIfMissing(NAMESPACE);
verify(service).createCredentialSecret(NAMESPACE, secretResource);
}
}
@DisplayName("create credential secret")
@Nested
class TestCreateCredentialSecret {
private static final String SERVER_NAMESPACE = "";
private static final String SERVER_CERTIFICATE_SECRET_NAME = "";
private static final String SERVER_CERTIFICATE_SECRET_DATA_KEY = "fesfdsfd";
private static final String SERVER_CERTIFICATE_SECRET_DATA = "dsadwadas";
private static final Secret SERVER_CERTIFICATE_SECRET = SecretTestFactory.createBuilder()
.addToStringData(SERVER_CERTIFICATE_SECRET_DATA_KEY, SERVER_CERTIFICATE_SECRET_DATA)
.build();
private static final Secret CREDENTIAL_SECRET = SecretTestFactory.create();
@Mock
private OzgCloudElasticsearchServerProperties serverProperties;
@Mock
private ResourceAdapter<Secret> resourceAdapter;
@BeforeEach
void mock() {
when(properties.getServerProperties()).thenReturn(serverProperties);
when(serverProperties.getNamespace()).thenReturn(SERVER_NAMESPACE);
when(serverProperties.getCertificateSecretName()).thenReturn(SERVER_CERTIFICATE_SECRET_NAME);
when(serverProperties.getCertificateSecretDataKey()).thenReturn(SERVER_CERTIFICATE_SECRET_DATA_KEY);
when(kubernetesService.getSecretResource(any(), any())).thenReturn(secretResource);
when(secretResource.get()).thenReturn(SERVER_CERTIFICATE_SECRET);
doReturn(resourceAdapter).when(service).createAdapter(any());
when(secretHelper.buildCertificateSecret(any(), any())).thenReturn(CREDENTIAL_SECRET);
}
@Test
void shouldGetServerSecret() {
service.createCredentialSecret(NAMESPACE, secretResource);
verify(kubernetesService).getSecretResource(SERVER_NAMESPACE, SERVER_CERTIFICATE_SECRET_NAME);
}
@Test
void shouldBuildSecret() {
service.createCredentialSecret(NAMESPACE, secretResource);
verify(secretHelper).buildCertificateSecret(NAMESPACE, SERVER_CERTIFICATE_SECRET_DATA);
}
@Test
void shouldCreate() {
service.createCredentialSecret(NAMESPACE, secretResource);
verify(resourceAdapter).create(CREDENTIAL_SECRET);
}
}
}
} }
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment