diff --git a/Jenkinsfile b/Jenkinsfile index 2319a3d6d0722fa67ed3a5e5865d53a689b6a715..9a56d69ab399a94a61a9b6e5917695248a9203c2 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -6,17 +6,19 @@ pipeline { } environment { - BLUE_OCEAN_URL = "https://jenkins.infra.ozg-cloud.systems/job/ozgcloud-keycloak-operator/job/${env.BRANCH_NAME}/${env.BUILD_NUMBER}/" RELEASE_REGEX = /\d+.\d+.\d+/ SNAPSHOT_REGEX = /\d+.\d+.\d+-SNAPSHOT/ FAILED_STAGE = "" SH_SUCCESS_STATUS_CODE = 0 + + KEYCLOAK_OPERATOR_NAME = 'ozgcloud-keycloak-operator' + ELASTICSEARCH_OPERATOR_NAME = 'ozgcloud-elasticsearch-operator' } options { timeout(time: 1, unit: 'HOURS') disableConcurrentBuilds() - buildDiscarder(logRotator(numToKeepStr: '5')) + buildDiscarder(logRotator(numToKeepStr: '10')) } stages { @@ -27,8 +29,7 @@ pipeline { def rootPom = readMavenPom file: 'pom.xml' def rootVersion = rootPom.version - - if(env.BRANCH_NAME == 'release'){ + if(isReleaseBranch()){ if ( !isReleaseVersion([rootVersion])) { error("Keine Release Version für Branch ${env.BRANCH_NAME}.") } @@ -40,7 +41,7 @@ pipeline { } } } - stage('Build OzgCloud Keycloak Operator') { + stage('Build OzgCloud Operator') { steps { script { FAILED_STAGE=env.STAGE_NAME @@ -93,45 +94,50 @@ pipeline { } configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) { - sh 'mvn -s $MAVEN_SETTINGS spring-boot:build-image -DskipTests -Dmaven.wagon.http.retryHandler.count=3' + sh 'mvn -s $MAVEN_SETTINGS spring-boot:build-image -DskipTests -Dmaven.wagon.http.retryHandler.count=3' } - } + } } - stage('Tag and Push Docker image') { steps { script { FAILED_STAGE=env.STAGE_NAME IMAGE_TAG = generateImageTag() - tagAndPushDockerImage('ozgcloud-keycloak-operator', IMAGE_TAG) + tagAndPushDockerImage(KEYCLOAK_OPERATOR_NAME, IMAGE_TAG) + tagAndPushDockerImage(ELASTICSEARCH_OPERATOR_NAME, IMAGE_TAG) - if (env.BRANCH_NAME == 'master') { - tagAndPushDockerImage('ozgcloud-keycloak-operator', 'snapshot-latest') + if (isMasterBranch()) { + tagAndPushDockerImage(KEYCLOAK_OPERATOR_NAME, 'snapshot-latest') + tagAndPushDockerImage(ELASTICSEARCH_OPERATOR_NAME, 'snapshot-latest') } - else if (env.BRANCH_NAME == 'release') { - tagAndPushDockerImage('ozgcloud-keycloak-operator', 'latest') + else if (isReleaseBranch()) { + tagAndPushDockerImage(KEYCLOAK_OPERATOR_NAME, 'latest') + tagAndPushDockerImage(ELASTICSEARCH_OPERATOR_NAME, 'latest') } } } } - stage('Test, build and deploy Helm Chart') { + stage('Test, build and deploy Keycloak-Operator Helm Chart') { steps { script { FAILED_STAGE=env.STAGE_NAME HELM_CHART_VERSION = generateHelmChartVersion() - dir('src/main/helm') { - sh "helm lint -f ../../test/helm/linter_values.yaml" - - sh "helm unittest --helm3 -f '../../test/helm/*.yaml' -f '../../test/helm/*/*.yaml' ." - - sh "helm package --version=${HELM_CHART_VERSION} ." + testAndDeployKeycloakHelmChart(HELM_CHART_VERSION) + } + } + } + + stage('Test, build and deploy Elasticsearch-Operator Helm Chart') { + steps { + script { + FAILED_STAGE=env.STAGE_NAME + HELM_CHART_VERSION = generateHelmChartVersion() - deployHelmChart("ozgcloud-keycloak-operator", HELM_CHART_VERSION) - } + testAndDeployElasticsearchHelmChart(HELM_CHART_VERSION) } } } @@ -144,11 +150,7 @@ pipeline { script { FAILED_STAGE = env.STAGE_NAME - cloneGitopsRepo() - - setNewDevVersion() - - pushNewDevVersion() + doDevRollout() } } } @@ -161,68 +163,58 @@ pipeline { script { FAILED_STAGE = env.STAGE_NAME - cloneGitopsRepo() - - setNewTestVersion() - - pushNewTestVersion() + doTestRollout() } } } stage ('OWASP Dependency-Check Vulnerabilities') { steps { - dependencyCheck additionalArguments: ''' - -o "./" - -s "./" - -f "ALL" - -d /dependency-check-data - --suppression dependency-check-supressions.xml - --disableKnownExploited - --noupdate - --disableArchive - --prettyPrint''', odcInstallation: 'dependency-check-owasp' - - dependencyCheckPublisher( - pattern: 'dependency-check-report.xml' , - //unstableNewCritical: 999, - //unstableNewHigh: 999, - //unstableNewMedium: 999, - //unstableNewLow: 999, - //unstableTotalCritical: 999, - //unstableTotalHigh: 999, - //unstableTotalMedium: 999, - //unstableTotalLow: 999, - //failedNewCritical: 999, - //failedNewHigh: 999, - //failedNewMedium: 999, - //failedNewLow: 999, - //failedTotalCritical: 999, - //failedTotalHigh: 999, - //failedTotalMedium: 999, - //failedTotalLow: 999 - ) } - } - } - post { - failure { - script { - if (env.BRANCH_NAME == 'master' || env.BRANCH_NAME == 'release') { - //sendFailureMessage() - } + dependencyCheck additionalArguments: ''' + -o "./" + -s "./" + -f "ALL" + -d /dependency-check-data + --suppression dependency-check-supressions.xml + --disableKnownExploited + --noupdate + --disableArchive + --prettyPrint''', odcInstallation: 'dependency-check-owasp' + + dependencyCheckPublisher( + pattern: 'dependency-check-report.xml' + ) } } } } + +Void testAndDeployKeycloakHelmChart(String helmChartVersion){ + dir("${KEYCLOAK_OPERATOR_NAME}/src/main/helm") { + runHelmTests() + deployHelmChart(KEYCLOAK_OPERATOR_NAME, helmChartVersion) + } +} + +Void testAndDeployElasticsearchHelmChart(String helmChartVersion){ + dir("${ELASTICSEARCH_OPERATOR_NAME}/src/main/helm") { + runHelmTests() + deployHelmChart(ELASTICSEARCH_OPERATOR_NAME, helmChartVersion) + } +} + +Void runHelmTests(){ + sh 'helm lint -f ../../test/helm/linter_values.yaml' + sh "helm unittest --helm3 -f '../../test/helm/*.yaml' -f '../../test/helm/*/*.yaml' ." + sh "helm package --version=${HELM_CHART_VERSION} ." +} + Void deployHelmChart(String helmChartName, String helmChartVersion) { withCredentials([usernamePassword(credentialsId: 'jenkins-nexus-login', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]){ - if (env.BRANCH_NAME == 'release') { - result = sh script: '''curl -u $USERNAME:$PASSWORD https://nexus.ozg-sh.de/service/rest/v1/components?repository=ozg-base-apps -F file=@'''+helmChartName+'''-'''+helmChartVersion+'''.tgz''', returnStdout: true - } - else { - result = sh script: '''curl -u $USERNAME:$PASSWORD https://nexus.ozg-sh.de/service/rest/v1/components?repository=ozg-base-apps-snapshot -F file=@'''+helmChartName+'''-'''+helmChartVersion+'''.tgz''', returnStdout: true - } + def url = getHelmRepoUrl() + echo "Url: ${url}" + def result = sh script: '''curl -u $USERNAME:$PASSWORD ''' + url + ''' -F file=@'''+helmChartName+'''-'''+helmChartVersion+'''.tgz''', returnStdout: true if (result != '') { error(result) @@ -230,34 +222,32 @@ Void deployHelmChart(String helmChartName, String helmChartVersion) { } } +String getHelmRepoUrl(){ + if (isReleaseBranch()) { + return "https://nexus.ozg-sh.de/service/rest/v1/components?repository=ozg-base-apps" + } + return "https://nexus.ozg-sh.de/service/rest/v1/components?repository=ozg-base-apps-snapshot" +} + String generateHelmChartVersion() { def chartVersion = getPomVersion('pom.xml') - if (env.BRANCH_NAME == 'master') { + if (isMasterBranch()) { chartVersion += "-${env.GIT_COMMIT.take(7)}" } - else if (env.BRANCH_NAME != 'release') { + else if (!isReleaseBranch()) { chartVersion += "-${env.BRANCH_NAME}" } - return chartVersion.replaceAll("_", "-") + return chartVersion.replaceAll('_', '-') } -Void sendFailureMessage() { - def room = '' - def data = """{"msgtype":"m.text", \ - "body":"OzgCloud-Keycloak-Operator: Build Failed. Stage: ${FAILED_STAGE} Build-ID: ${env.BUILD_NUMBER} Link: ${BLUE_OCEAN_URL}", \ - "format": "org.matrix.custom.html", \ - "formatted_body":"OzgCloud-Keycloak-Operator: Build Failed. Stage: ${FAILED_STAGE} Build-ID: <a href='${BLUE_OCEAN_URL}'>${env.BUILD_NUMBER}</a>"}""" - - if (env.BRANCH_NAME == 'master') { - room = "!iQPAvQIiRwRpNOszjw:matrix.ozg-sh.de" - } - else if (env.BRANCH_NAME == 'release') { - room = "!oWZpUGTFsxkJIYNfYg:matrix.ozg-sh.de" - } +Boolean isMasterBranch() { + return env.BRANCH_NAME == 'master' +} - sh "curl -XPOST -H 'authorization: Bearer ${getElementAccessToken()}' -d '${data}' https://matrix.ozg-sh.de/_matrix/client/v3/rooms/$room/send/m.room.message" +Boolean isReleaseBranch() { + return env.BRANCH_NAME == 'release' } String getElementAccessToken() { @@ -266,32 +256,47 @@ String getElementAccessToken() { } } -Void setNewDevVersion() { - setNewOzgOperatorVersion('dev') +Void doDevRollout() { + cloneGitopsRepo() + setNewOperatorVersion('dev') + pushNewGitopsVersion('dev') } -Void setNewTestVersion() { - setNewOzgOperatorVersion('test') +Void doTestRollout() { + cloneGitopsRepo() + setNewOperatorVersion('test') + pushNewGitopsVersion('test') } -Void setNewOzgOperatorVersion(String environment) { - dir("gitops") { - def envFile = "${environment}/application/values/ozgcloud-keycloak-operator-values.yaml" - def envVersions = readYaml file: envFile +Void setNewOperatorVersion(String environment) { + dir('gitops') { + updateKeycloakOperatorVersions() + updateElasticsearchOperatorVersions() + } +} - envVersions.ozgcloud_keycloak_operator.image.tag = IMAGE_TAG - envVersions.ozgcloud_keycloak_operator.helm.version = HELM_CHART_VERSION +Void updateKeycloakOperatorVersions(String environment){ + def valuesFile = getApplicationValues(environment, KEYCLOAK_OPERATOR_NAME) + def envVersions = readYaml file: envFile - writeYaml file: envFile, data: envVersions, overwrite: true - } + envVersions.ozgcloud_keycloak_operator.image.tag = IMAGE_TAG + envVersions.ozgcloud_keycloak_operator.helm.version = HELM_CHART_VERSION + + writeYaml file: envFile, data: envVersions, overwrite: true } -Void pushNewDevVersion() { - pushNewGitopsVersion('dev') +Void updateElasticsearchOperatorVersions(String environment){ + def valuesFile = getApplicationValues(environment, ELASTICSEARCH_OPERATOR_NAME) + def envVersions = readYaml file: envFile + + envVersions.ozgcloud_elasticsearch_operator.image.tag = IMAGE_TAG + envVersions.ozgcloud_elasticsearch_operator.helm.version = HELM_CHART_VERSION + + writeYaml file: envFile, data: envVersions, overwrite: true } -Void pushNewTestVersion() { - pushNewGitopsVersion('test') +String getApplicationValues(String environment, String valuesFileName) { + return "${environment}/application/values/${valuesFileName}-values.yaml" } Void pushNewGitopsVersion(String environment) { diff --git a/build.sh b/build.sh deleted file mode 100755 index 73364f7380a8fc8d130f175a93484d592b87ef0e..0000000000000000000000000000000000000000 --- a/build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -mvn -Pnative spring-boot:build-image diff --git a/lombok.config b/lombok.config new file mode 100644 index 0000000000000000000000000000000000000000..1176e6c790024968252f068bcf3c71091da20f91 --- /dev/null +++ b/lombok.config @@ -0,0 +1,7 @@ + +lombok.log.fieldName=LOG +lombok.log.slf4j.flagUsage = ERROR +lombok.log.log4j.flagUsage = ERROR +lombok.data.flagUsage = ERROR +lombok.nonNull.exceptionType = IllegalArgumentException +lombok.addLombokGeneratedAnnotation = true \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/pom.xml b/ozgcloud-elasticsearch-operator/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..1814ae1d749e70488a71c85bb8a16eda4fc88c2f --- /dev/null +++ b/ozgcloud-elasticsearch-operator/pom.xml @@ -0,0 +1,79 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>de.ozgcloud</groupId> + <artifactId>ozgcloud-operator-parent</artifactId> + <version>2.1.0-SNAPSHOT</version> + <relativePath>../</relativePath> + </parent> + + <artifactId>ozgcloud-elasticsearch-operator</artifactId> + <packaging>jar</packaging> + + <name>OZG-Cloud Elasticsearch Operator</name> + <description>OZG-Cloud Elasticsearch Operator</description> + + <properties> + <spring-boot.build-image.imageName>docker.ozg-sh.de/ozgcloud-elasticsearch-operator:build-latest</spring-boot.build-image.imageName> + </properties> + + <dependencies> + <dependency> + <groupId>co.elastic.clients</groupId> + <artifactId>elasticsearch-java</artifactId> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-web</artifactId> + </dependency> + + <!-- test --> + <dependency> + <groupId>org.testcontainers</groupId> + <artifactId>elasticsearch</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>io.javaoperatorsdk</groupId> + <artifactId>jenvtest-fabric8-client-support</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>io.javaoperatorsdk</groupId> + <artifactId>jenvtest</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <fork>true</fork> + <annotationProcessorPaths> + <path> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + <version>${lombok.version}</version> + </path> + </annotationProcessorPaths> + <showWarnings>true</showWarnings> + </configuration> + </plugin> + </plugins> + </build> + +</project> \ No newline at end of file diff --git a/run_helm_test.sh b/ozgcloud-elasticsearch-operator/run_helm_test.sh similarity index 100% rename from run_helm_test.sh rename to ozgcloud-elasticsearch-operator/run_helm_test.sh diff --git a/ozgcloud-elasticsearch-operator/src/main/helm/Chart.yaml b/ozgcloud-elasticsearch-operator/src/main/helm/Chart.yaml new file mode 100644 index 0000000000000000000000000000000000000000..ae8cad725089582a3f9dd8f21d8514b1f26b2e2f --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/main/helm/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: ozgcloud-elasticsearch-operator +description: OZG-Cloud Elasticsearch Operator +type: application +version: 0.0.0-MANAGED-BY-JENKINS +appVersion: "0.0.0-MANAGED-BY-JENKINS" +icon: https://simpleicons.org/icons/helm.svg \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/main/helm/templates/_helpers.tpl b/ozgcloud-elasticsearch-operator/src/main/helm/templates/_helpers.tpl new file mode 100644 index 0000000000000000000000000000000000000000..5b08fa932ccbeb01edc1aea23eb199f904d2c8c9 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/main/helm/templates/_helpers.tpl @@ -0,0 +1,7 @@ + + + +{{- define "app.matchLabels" }} +app.kubernetes.io/name: {{ .Release.Name }} +app.kubernetes.io/namespace: {{ .Release.Namespace }} +{{- end -}} \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/main/helm/templates/configmap_bindings_type.yaml b/ozgcloud-elasticsearch-operator/src/main/helm/templates/configmap_bindings_type.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d2ec843e542a4c8f2f215d9bd9d222522122bc44 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/main/helm/templates/configmap_bindings_type.yaml @@ -0,0 +1,32 @@ +# +# Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +apiVersion: v1 +kind: ConfigMap +metadata: + name: bindings-type + namespace: {{ .Release.Namespace }} +data: + type: | + ca-certificates \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/main/helm/templates/crd/operator.ozgcloud.de_OzgCloudElasticsearch.yaml b/ozgcloud-elasticsearch-operator/src/main/helm/templates/crd/operator.ozgcloud.de_OzgCloudElasticsearch.yaml new file mode 100644 index 0000000000000000000000000000000000000000..bbb564af71b8d78cf0d1400354d10026cc40fddb --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/main/helm/templates/crd/operator.ozgcloud.de_OzgCloudElasticsearch.yaml @@ -0,0 +1,43 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: ozgcloudelasticsearchs.operator.ozgcloud.de +spec: + group: operator.ozgcloud.de + names: + kind: OzgCloudElasticsearch + listKind: OzgCloudElasticsearchList + plural: ozgcloudelasticsearchs + singular: ozgcloudelasticsearch + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + type: object + description: OzgCloudElasticsearch is the Schema for the elasticsearch API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + type: string + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + metadata: + type: object + spec: + description: Spec defines the desired state of Elasticsearch + type: object + x-kubernetes-preserve-unknown-fields: true + status: + description: Status defines the observed state of Elasticsearch + type: object + x-kubernetes-preserve-unknown-fields: true + served: true + storage: true + subresources: + status: {} \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/main/helm/templates/deployment.yaml b/ozgcloud-elasticsearch-operator/src/main/helm/templates/deployment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..549b8979f9eb3d62c6008b29c468926edd1b2cad --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/main/helm/templates/deployment.yaml @@ -0,0 +1,112 @@ +# +# Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }} + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/name: {{ .Release.Name }} + app.kubernetes.io/namespace: {{ .Release.Namespace }} + app.kubernetes.io/part-of: ozg + app.kubernetes.io/version: {{ .Chart.AppVersion }} + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} +spec: + selector: + matchLabels: + {{- include "app.matchLabels" . | indent 6 }} + template: + metadata: + labels: + {{- include "app.matchLabels" . | indent 8 }} + spec: + serviceAccountName: ozgcloud-elasticsearch-operator-serviceaccount + containers: + - name: ozgcloud-elasticsearch-operator + image: "{{ required "image.repo must be set" (.Values.image).repo }}/{{ required "image.name must be set" (.Values.image).name }}:{{ required "image.tag must be set" (.Values.image).tag }}" + env: + {{- with (.Values.env).customList }} +{{ toYaml . | indent 8 }} + {{- end }} + - name: SERVICE_BINDING_ROOT + value: "/bindings" + imagePullPolicy: Always + readinessProbe: + failureThreshold: 3 + httpGet: + path: /actuator/health/readiness + port: 8081 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 3 + startupProbe: + failureThreshold: 10 + httpGet: + path: /actuator/health/readiness + port: 8081 + scheme: HTTP + initialDelaySeconds: 30 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 5 + resources: + {{- with .Values.resources }} +{{ toYaml . | indent 10 }} + {{- end }} + securityContext: + allowPrivilegeEscalation: false + privileged: false + readOnlyRootFilesystem: false + runAsNonRoot: true + stdin: true + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + tty: true + volumeMounts: + - name: bindings + mountPath: "/bindings/ca-certificates/type" + subPath: type + readOnly: true + - name: elasticsearch-certificate + mountPath: "/bindings/ca-certificates/es-root-ca.pem" + subPath: ca.crt + readOnly: true + volumes: + - name: bindings + configMap: + name: bindings-type + - name: elasticsearch-certificate + secret: + secretName: {{ required "elasticsearch.certificateSecretName must be set" (.Values.elasticsearch).certificateSecretName }} + optional: false + dnsConfig: {} + dnsPolicy: ClusterFirst + imagePullSecrets: + - name: {{ required "imagePullSecret must be set" .Values.imagePullSecret }} + restartPolicy: Always + diff --git a/ozgcloud-elasticsearch-operator/src/main/helm/templates/rbac/ozgcloud_elasticsearch_operator_admin_secret_view_role.yaml b/ozgcloud-elasticsearch-operator/src/main/helm/templates/rbac/ozgcloud_elasticsearch_operator_admin_secret_view_role.yaml new file mode 100644 index 0000000000000000000000000000000000000000..7b92028c82b9571f7018ac6e5ee99646649b319e --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/main/helm/templates/rbac/ozgcloud_elasticsearch_operator_admin_secret_view_role.yaml @@ -0,0 +1,39 @@ +# +# Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: ozgcloud-elasticsearch-operator-admin-secret-view-role + namespace: {{ required "elasticsearch.namespace must be set" (.Values.elasticsearch).namespace }} +rules: + - apiGroups: + - "" + resourceNames: + - {{ required "elasticsearch.adminSecretName must be set" (.Values.elasticsearch).adminSecretName }} + - {{ required "elasticsearch.certificateSecretName must be set" (.Values.elasticsearch).certificateSecretName }} + resources: + - secrets + verbs: + - get diff --git a/ozgcloud-elasticsearch-operator/src/main/helm/templates/rbac/ozgcloud_elasticsearch_operator_admin_secret_view_rolebinding.yaml b/ozgcloud-elasticsearch-operator/src/main/helm/templates/rbac/ozgcloud_elasticsearch_operator_admin_secret_view_rolebinding.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f17704d5bd024797b643bcbf6e06d095ded6fa77 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/main/helm/templates/rbac/ozgcloud_elasticsearch_operator_admin_secret_view_rolebinding.yaml @@ -0,0 +1,37 @@ +# +# Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: ozgcloud-elasticsearch-operator-admin-secret-view-role-binding + namespace: {{ required "elasticsearch.namespace must be set" (.Values.elasticsearch).namespace }} +subjects: + - kind: ServiceAccount + name: ozgcloud-elasticsearch-operator-serviceaccount + namespace: {{ .Release.Namespace }} +roleRef: + kind: Role + name: ozgcloud-elasticsearch-operator-admin-secret-view-role + apiGroup: rbac.authorization.k8s.io diff --git a/ozgcloud-elasticsearch-operator/src/main/helm/templates/rbac/ozgcloud_elasticsearch_operator_edit_role.yaml b/ozgcloud-elasticsearch-operator/src/main/helm/templates/rbac/ozgcloud_elasticsearch_operator_edit_role.yaml new file mode 100644 index 0000000000000000000000000000000000000000..6d1374db245847f4616041d850d657f9b3f7bca0 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/main/helm/templates/rbac/ozgcloud_elasticsearch_operator_edit_role.yaml @@ -0,0 +1,18 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: ozgcloud-elasticsearch-operator-edit-role + labels: + app.kubernetes.io/name: {{ .Release.Name }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: ozgcloud-elasticsearch-operator +rules: +- apiGroups: + - operator.ozgcloud.de + resources: + - ozgcloudelasticsearchs + - ozgcloudelasticsearchs/status + - ozgcloudelasticsearchs/finalizers + verbs: + - patch + - update diff --git a/ozgcloud-elasticsearch-operator/src/main/helm/templates/rbac/ozgcloud_elasticsearch_operator_edit_rolebinding.yaml b/ozgcloud-elasticsearch-operator/src/main/helm/templates/rbac/ozgcloud_elasticsearch_operator_edit_rolebinding.yaml new file mode 100644 index 0000000000000000000000000000000000000000..856aa637f4164e537213ebe1415dbee0e1137e61 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/main/helm/templates/rbac/ozgcloud_elasticsearch_operator_edit_rolebinding.yaml @@ -0,0 +1,13 @@ + +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: ozgcloud-elasticsearch-operator-edit-role-binding +subjects: + - kind: ServiceAccount + name: ozgcloud-elasticsearch-operator-serviceaccount + namespace: {{ .Release.Namespace }} +roleRef: + kind: ClusterRole + name: ozgcloud-elasticsearch-operator-edit-role + apiGroup: rbac.authorization.k8s.io \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/main/helm/templates/rbac/ozgcloud_elasticsearch_operator_serviceaccount.yaml b/ozgcloud-elasticsearch-operator/src/main/helm/templates/rbac/ozgcloud_elasticsearch_operator_serviceaccount.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a1441499d95af95ada1da6ab84ec0e3584abc095 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/main/helm/templates/rbac/ozgcloud_elasticsearch_operator_serviceaccount.yaml @@ -0,0 +1,28 @@ +# +# Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# +apiVersion: v1 +kind: ServiceAccount +metadata: + name: ozgcloud-elasticsearch-operator-serviceaccount + namespace: {{ .Release.Namespace }} \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/main/helm/templates/rbac/ozgcloud_elasticsearch_operator_view_role.yaml b/ozgcloud-elasticsearch-operator/src/main/helm/templates/rbac/ozgcloud_elasticsearch_operator_view_role.yaml new file mode 100644 index 0000000000000000000000000000000000000000..9e0698765648fa7e4d9e15c1256a8b1853833c68 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/main/helm/templates/rbac/ozgcloud_elasticsearch_operator_view_role.yaml @@ -0,0 +1,20 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: ozgcloud-elasticsearch-operator-view-role + labels: + app.kubernetes.io/name: {{ .Release.Name }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: ozgcloud-elasticsearch-operator +rules: +- apiGroups: + - operator.ozgcloud.de + resources: + - secrets + - ozgcloudelasticsearchs + - ozgcloudelasticsearchs/status + - ozgcloudelasticsearchs/finalizers + verbs: + - get + - list + - watch diff --git a/ozgcloud-elasticsearch-operator/src/main/helm/templates/rbac/ozgcloud_elasticsearch_operator_view_rolebinding.yaml b/ozgcloud-elasticsearch-operator/src/main/helm/templates/rbac/ozgcloud_elasticsearch_operator_view_rolebinding.yaml new file mode 100644 index 0000000000000000000000000000000000000000..150d7509b3921c1e5815e3a83e9eaf44d4f2258f --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/main/helm/templates/rbac/ozgcloud_elasticsearch_operator_view_rolebinding.yaml @@ -0,0 +1,13 @@ + +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: ozgcloud-elasticsearch-operator-view-role-binding +subjects: + - kind: ServiceAccount + name: ozgcloud-elasticsearch-operator-serviceaccount + namespace: {{ .Release.Namespace }} +roleRef: + kind: ClusterRole + name: ozgcloud-elasticsearch-operator-view-role + apiGroup: rbac.authorization.k8s.io \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/main/helm/values.yaml b/ozgcloud-elasticsearch-operator/src/main/helm/values.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e4b3820a25467fb8faeec61a5b75be8d5ef06c99 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/main/helm/values.yaml @@ -0,0 +1,8 @@ + +image: + repo: docker.ozg-sh.de + +elasticsearch: + namespace: elastic-system + adminSecretName: ozg-search-cluster-es-elastic-user + certificateSecretName: ozg-search-cluster-es-http-ca-internal diff --git a/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/CustomResourceStatus.java b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/CustomResourceStatus.java new file mode 100644 index 0000000000000000000000000000000000000000..7ef66d5c501cc50d1d8dbf0e3ac323f0df118e99 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/CustomResourceStatus.java @@ -0,0 +1,5 @@ +package de.ozgcloud.operator; + +public enum CustomResourceStatus { + OK, IN_PROGRESS, ERROR; +} diff --git a/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/ElasticsearchOperatorApplication.java b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/ElasticsearchOperatorApplication.java new file mode 100644 index 0000000000000000000000000000000000000000..dde6f8261a674f6afd0d21ce3b395116dfa5acdd --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/ElasticsearchOperatorApplication.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.operator; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class ElasticsearchOperatorApplication { + + public static void main(String[] args) { + SpringApplication.run(ElasticsearchOperatorApplication.class, args); + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000000000000000000000000000000000..24899d8fd8ce95377de40d38e4d0c676b7e1226b --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/ElasticsearchReconciler.java @@ -0,0 +1,80 @@ +package de.ozgcloud.operator; + +import java.util.Base64; + +import org.apache.commons.collections.MapUtils; +import org.springframework.stereotype.Component; + +import io.fabric8.kubernetes.api.model.Secret; +import io.javaoperatorsdk.operator.api.reconciler.Cleaner; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; +import io.javaoperatorsdk.operator.api.reconciler.DeleteControl; +import io.javaoperatorsdk.operator.api.reconciler.Reconciler; +import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; + +@Log4j2 +@RequiredArgsConstructor +@ControllerConfiguration +@Component +public class ElasticsearchReconciler implements Reconciler<OzgCloudElasticsearchCustomResource>, Cleaner<OzgCloudElasticsearchCustomResource> { + + private final OzgCloudElasticsearchService service; + + @Override + public UpdateControl<OzgCloudElasticsearchCustomResource> reconcile(OzgCloudElasticsearchCustomResource resource, + Context<OzgCloudElasticsearchCustomResource> context) { + try { + var namespace = resource.getMetadata().getNamespace(); + LOG.info("{}: Reconcile user", namespace); + var secret = service.getOrCreateCredentialSecret(resource, context); + service.createIndexIfMissing(namespace); + service.createSecurityRoleIfMissing(namespace); + service.createSecurityUserIfMissing(namespace, getPassword(secret)); + service.createCertificateIfMissing(namespace); + LOG.info("{}: Reconcile user successful.", namespace); + return OzgCloudElasticsearchUpdateControlBuilder.fromResource(resource).withStatus(CustomResourceStatus.OK).build(); + } catch (Exception exception) { + LOG.warn(resource.getMetadata().getNamespace() + ": Reconcile user failed.", exception); + return buildExceptionUpdateControl(resource, exception); + } + } + + String getPassword(Secret secret) { + return decode(MapUtils.getString(secret.getData(), OzgCloudElasticsearchSecretHelper.CREDENTIAL_SECRET_PASSWORD_FIELD)); + } + + 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) + .withReschedule(OperatorConfig.RECONCILER_RETRY_SECONDS_ON_ERROR) + .withMessage(exception.getMessage()) + .build(); + } + + @Override + public DeleteControl cleanup(OzgCloudElasticsearchCustomResource resource, Context<OzgCloudElasticsearchCustomResource> context) { + var namespace = resource.getMetadata().getNamespace(); + try { + service.deleteSecurityUserIfExists(namespace); + service.deleteSecurityRoleIfExists(namespace); + service.deleteIndexIfExists(namespace); + return DeleteControl.defaultDelete(); + } catch (Exception e) { + LOG.warn(resource.getMetadata().getNamespace() + ": Could not cleanup elasticsearch resource.", e); + return DeleteControl.defaultDelete(); + } + } +} \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/IndicesPrivilege.java b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/IndicesPrivilege.java new file mode 100644 index 0000000000000000000000000000000000000000..d7312ee608e05021f9892da996f20fb4e4fe1f8c --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/IndicesPrivilege.java @@ -0,0 +1,14 @@ +package de.ozgcloud.operator; + +import lombok.Getter; + +public enum IndicesPrivilege { + ALL("all"); + + @Getter + private String value; + + private IndicesPrivilege(String value) { + this.value = value; + } +} diff --git a/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/OperatorConfig.java b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/OperatorConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..02d81e473448151ee2cbbb9cd0e6050107042dbf --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/OperatorConfig.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.operator; + +import java.time.Duration; +import java.util.List; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import io.javaoperatorsdk.operator.Operator; +import io.javaoperatorsdk.operator.api.reconciler.Reconciler; + +@Configuration +public class OperatorConfig { + + public static final Duration RECONCILER_RETRY_SECONDS = Duration.ofSeconds(20); + public static final Duration RECONCILER_RETRY_SECONDS_ON_ERROR = Duration.ofSeconds(60); + + @Bean(initMethod = "start", destroyMethod = "stop") + @SuppressWarnings("rawtypes") + Operator operator(List<Reconciler> controllers) { + var operator = new Operator(); + controllers.forEach(operator::register); + return operator; + } +} \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/OzgCloudElasticsearchCustomResource.java b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/OzgCloudElasticsearchCustomResource.java new file mode 100644 index 0000000000000000000000000000000000000000..933d76e0263fc49587c1a0a12b903eb2d54978e5 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/OzgCloudElasticsearchCustomResource.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.operator; + +import io.fabric8.kubernetes.api.model.Namespaced; +import io.fabric8.kubernetes.client.CustomResource; +import io.fabric8.kubernetes.model.annotation.Group; +import io.fabric8.kubernetes.model.annotation.Kind; +import io.fabric8.kubernetes.model.annotation.Plural; +import io.fabric8.kubernetes.model.annotation.Singular; +import io.fabric8.kubernetes.model.annotation.Version; + +@Kind("OzgCloudElasticsearch") +@Group("operator.ozgcloud.de") +@Version("v1") +@Singular("ozgcloudelasticsearch") +@Plural("ozgcloudelasticsearchs") +@SuppressWarnings("serial") +class OzgCloudElasticsearchCustomResource extends CustomResource<OzgCloudElasticsearchSpec, OzgCloudElasticsearchCustomResourceStatus> implements Namespaced { +} diff --git a/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/OzgCloudElasticsearchCustomResourceStatus.java b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/OzgCloudElasticsearchCustomResourceStatus.java new file mode 100644 index 0000000000000000000000000000000000000000..39b7f8f38d0e608870556f3f9f4e7aaeadba7c42 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/OzgCloudElasticsearchCustomResourceStatus.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.operator; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +import io.javaoperatorsdk.operator.api.ObservedGenerationAwareStatus; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class OzgCloudElasticsearchCustomResourceStatus extends ObservedGenerationAwareStatus { + + private CustomResourceStatus status; + + private String message; +} diff --git a/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/OzgCloudElasticsearchProperties.java b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/OzgCloudElasticsearchProperties.java new file mode 100644 index 0000000000000000000000000000000000000000..6cb1e7326a03d09eb88758d8da976c064599eac0 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/OzgCloudElasticsearchProperties.java @@ -0,0 +1,35 @@ +package de.ozgcloud.operator; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@ConfigurationProperties("ozgcloud.elasticsearch") +@Configuration +public class OzgCloudElasticsearchProperties { + + private String secretCredentialsName; + private String certificateSecretName; + + private OzgCloudElasticsearchServerProperties server; + + @Getter + @Setter + public static class OzgCloudElasticsearchServerProperties { + + private String namespace; + private String secretName; + private String secretDataKey; + private String secretCredentialsName; + private String host; + private int port; + private String scheme; + private String certificateNamespace; + private String certificateSecretName; + private String certificateSecretDataKey; + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000000000000000000000000000000000..18e2301050842f9615feac1ee7541ce52b7811b4 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/OzgCloudElasticsearchSecretHelper.java @@ -0,0 +1,62 @@ +package de.ozgcloud.operator; + +import org.apache.commons.lang3.RandomStringUtils; +import org.springframework.stereotype.Component; + +import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.fabric8.kubernetes.api.model.Secret; +import io.fabric8.kubernetes.api.model.SecretBuilder; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Component +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)) + .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) + .withMetadata(createMetaData(properties.getCertificateSecretName(), namespace)) + .addToData(CERTIFICATE_SECRET_DATA_KEY, data) + .build(); + } + + private ObjectMeta createMetaData(String name, String namespace) { + var metadata = new ObjectMeta(); + metadata.setName(name); + metadata.setNamespace(namespace); + + return metadata; + } +} diff --git a/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/OzgCloudElasticsearchService.java b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/OzgCloudElasticsearchService.java new file mode 100644 index 0000000000000000000000000000000000000000..d7207e433215e4bbdbf7a5254b0aa9d16dfbee09 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/OzgCloudElasticsearchService.java @@ -0,0 +1,138 @@ +package de.ozgcloud.operator; + +import java.util.Objects; + +import org.apache.commons.collections.MapUtils; +import org.springframework.stereotype.Component; + +import de.ozgcloud.operator.PutRoleRequestData.IndicesPrivilegesData; +import de.ozgcloud.operator.common.elasticsearch.ElasticsearchRemoteService; +import de.ozgcloud.operator.common.kubernetes.KubernetesRemoteService; +import io.fabric8.kubernetes.api.model.Secret; +import io.fabric8.kubernetes.client.dsl.Resource; +import io.fabric8.kubernetes.client.extension.ResourceAdapter; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; + +@Log4j2 +@RequiredArgsConstructor +@Component +public class OzgCloudElasticsearchService { + + private final OzgCloudElasticsearchSecretHelper secretHelper; + private final OzgCloudElasticsearchProperties properties; + + private final ElasticsearchRemoteService remoteService; + + private final KubernetesRemoteService kubernetesService; + + public Secret getOrCreateCredentialSecret(OzgCloudElasticsearchCustomResource resource, Context<OzgCloudElasticsearchCustomResource> context) { + try { + LOG.debug("{}: Get or create secret.", resource.getMetadata().getNamespace()); + var namespace = resource.getMetadata().getNamespace(); + var secretResource = getCredentialsSecretResource(namespace); + + if (Objects.isNull(secretResource.get())) { + LOG.info("{}: Secret not exists, create one ...", resource.getMetadata().getNamespace()); + createCredentialSecret(secretResource, namespace); + LOG.info("{}: Secret creation successful.", resource.getMetadata().getNamespace()); + } + return secretResource.get(); + } catch (Exception e) { + LOG.warn(resource.getMetadata().getNamespace() + ": Secret creation failed: ", e); + throw e; + } + } + + private Resource<Secret> getCredentialsSecretResource(String namespace) { + return kubernetesService.getSecretResource(namespace, properties.getSecretCredentialsName()); + } + + private void createCredentialSecret(Resource<Secret> resource, String namespace) { + createAdapter(resource).create(secretHelper.buildCredentialSecret(namespace, properties.getSecretCredentialsName())); + } + + public void createIndexIfMissing(String name) throws Exception { + LOG.debug("{}: Check elasticsearch index...", name); + if (!remoteService.existsIndex(name)) { + remoteService.createIndex(name); + } + } + + public void createSecurityRoleIfMissing(String roleName) throws Exception { + LOG.debug("{}: Check elasticsearch role...", roleName); + if (!remoteService.existsSecurityRole(roleName)) { + remoteService.createSecurityRole(buildPutRoleRequestData(roleName)); + } + } + + PutRoleRequestData buildPutRoleRequestData(String roleName) { + return PutRoleRequestData.builder().name(roleName).indivesPrivilegesData(buildIndicesPrivilegesData(roleName)).build(); + } + + private IndicesPrivilegesData buildIndicesPrivilegesData(String roleName) { + return IndicesPrivilegesData.builder().names(roleName).privileges(IndicesPrivilege.ALL.getValue()).build(); + } + + public void createSecurityUserIfMissing(String namespace, String password) throws Exception { + LOG.debug("{}: Check elasticsearch user...", namespace); + if (!remoteService.existsSecurityUser(namespace)) { + remoteService.createSecurityUser(buildPutUserRequestData(namespace, password)); + } + } + + PutUserRequestData buildPutUserRequestData(String namespace, String password) { + return PutUserRequestData.builder().username(namespace).roles(namespace).password(password).build(); + } + + public void deleteSecurityUserIfExists(String userName) throws Exception { + LOG.debug("{}: Check delete elasticsearch user...", userName); + if (remoteService.existsSecurityUser(userName)) { + remoteService.deleteSecurityUser(userName); + } + } + + public void deleteSecurityRoleIfExists(String roleName) throws Exception { + LOG.debug("{}: Check delete elasticsearch role...", roleName); + if (remoteService.existsSecurityRole(roleName)) { + remoteService.deleteSecurityRole(roleName); + } + } + + public void deleteIndexIfExists(String indexName) throws Exception { + LOG.debug("{}: Check delete elasticsearch index ...", indexName); + if (remoteService.existsIndex(indexName)) { + remoteService.deleteIndex(indexName); + } + } + + public void createCertificateIfMissing(String namespace) { + try { + LOG.debug("{}: Create certificate secret if missing...", namespace); + var secretResource = kubernetesService.getSecretResource(namespace, properties.getCertificateSecretName()); + + if (Objects.isNull(secretResource.get())) { + LOG.info("{}: Create certificate secret", namespace); + createCredentialSecret(namespace, secretResource); + } + } catch (Exception e) { + throw new RuntimeException("Certificate secret creation failed " + namespace); + } + } + + void createCredentialSecret(String namespace, Resource<Secret> secretResource) { + var serverSecretResource = kubernetesService.getSecretResource(properties.getServer().getCertificateNamespace(), + properties.getServer().getCertificateSecretName()); + + createAdapter(secretResource).create(secretHelper.buildCertificateSecret(namespace, getSecretData(serverSecretResource.get()))); + } + + private String getSecretData(Secret secret) { + return MapUtils.getString(secret.getData(), properties.getServer().getCertificateSecretDataKey()); + } + + ResourceAdapter<Secret> createAdapter(Resource<Secret> resource) { + return new ResourceAdapter<>(resource); + } +} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/operator/keycloak/KeycloakClientTest.java b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/OzgCloudElasticsearchSpec.java similarity index 73% rename from src/test/java/de/ozgcloud/operator/keycloak/KeycloakClientTest.java rename to ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/OzgCloudElasticsearchSpec.java index 9ac65f4fc0d630c18cf5c33d2040da38b78e3984..dd5d0ac27dc8922e049a9a2f469969a003e56840 100644 --- a/src/test/java/de/ozgcloud/operator/keycloak/KeycloakClientTest.java +++ b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/OzgCloudElasticsearchSpec.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den * Ministerpräsidenten des Landes Schleswig-Holstein * Staatskanzlei * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung @@ -21,14 +21,18 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -package de.ozgcloud.operator.keycloak; +package de.ozgcloud.operator; -import org.junit.jupiter.api.Test; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -class KeycloakClientTest { +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Builder +@JsonIgnoreProperties(ignoreUnknown = true) +class OzgCloudElasticsearchSpec { - @Test - void shouldInitKeycloakClient() { - new KeycloakClient(); - } } diff --git a/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/OzgCloudElasticsearchUpdateControlBuilder.java b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/OzgCloudElasticsearchUpdateControlBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..22702728bc06914e35e1ba171b8194cbc1aedc78 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/OzgCloudElasticsearchUpdateControlBuilder.java @@ -0,0 +1,61 @@ +package de.ozgcloud.operator; + +import java.time.Duration; +import java.util.Optional; + +import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; + +class OzgCloudElasticsearchUpdateControlBuilder { + + private OzgCloudElasticsearchCustomResource resource; + + private CustomResourceStatus status; + private Optional<String> message = Optional.empty(); + + private boolean reschedule = false; + private Duration scheduleDuration; + + public OzgCloudElasticsearchUpdateControlBuilder(OzgCloudElasticsearchCustomResource resource) { + this.resource = resource; + } + + public static OzgCloudElasticsearchUpdateControlBuilder fromResource(OzgCloudElasticsearchCustomResource resource) { + return new OzgCloudElasticsearchUpdateControlBuilder(resource); + } + + public OzgCloudElasticsearchUpdateControlBuilder withStatus(CustomResourceStatus status) { + this.status = status; + return this; + } + + public OzgCloudElasticsearchUpdateControlBuilder withMessage(String message) { + this.message = Optional.ofNullable(message); + return this; + } + + public OzgCloudElasticsearchUpdateControlBuilder withReschedule(Duration duration) { + this.reschedule = true; + this.scheduleDuration = duration; + return this; + } + + public UpdateControl<OzgCloudElasticsearchCustomResource> build() { + resource.setStatus(buildElasticCustomResourceStatus()); + + return buildUpdateControl(); + } + + private OzgCloudElasticsearchCustomResourceStatus buildElasticCustomResourceStatus() { + var userStatus = OzgCloudElasticsearchCustomResourceStatus.builder().status(status); + message.ifPresent(userStatus::message); + + return userStatus.build(); + } + + private UpdateControl<OzgCloudElasticsearchCustomResource> buildUpdateControl() { + if (reschedule) { + return UpdateControl.updateStatus(resource).rescheduleAfter(scheduleDuration); + } + return UpdateControl.updateStatus(resource); + } +} diff --git a/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/PutRoleRequestData.java b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/PutRoleRequestData.java new file mode 100644 index 0000000000000000000000000000000000000000..276a2c0ba05d7fe17b450f9fe98e157b08045559 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/PutRoleRequestData.java @@ -0,0 +1,23 @@ +package de.ozgcloud.operator; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Builder +@Getter +@Setter +public class PutRoleRequestData { + + private String name; + private IndicesPrivilegesData indivesPrivilegesData; + + @Builder + @Getter + @Setter + public static class IndicesPrivilegesData { + + private String names; + private String privileges; + } +} \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/PutUserRequestData.java b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/PutUserRequestData.java new file mode 100644 index 0000000000000000000000000000000000000000..2ca925bd250d71679f88da96e565e5667cec9061 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/PutUserRequestData.java @@ -0,0 +1,15 @@ +package de.ozgcloud.operator; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Builder +@Getter +@Setter +public class PutUserRequestData { + + private String username; + private String roles; + private String password; +} diff --git a/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/common/elasticsearch/ElasticsearchClientConfiguration.java b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/common/elasticsearch/ElasticsearchClientConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..aa25d5e3454e672c0d6f7fee59472a714f546d0d --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/common/elasticsearch/ElasticsearchClientConfiguration.java @@ -0,0 +1,88 @@ +package de.ozgcloud.operator.common.elasticsearch; + +import java.util.Base64; + +import org.apache.commons.collections.MapUtils; +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.elasticsearch.client.RestClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Scope; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.json.jackson.JacksonJsonpMapper; +import co.elastic.clients.transport.rest_client.RestClientTransport; +import de.ozgcloud.operator.OzgCloudElasticsearchProperties; +import de.ozgcloud.operator.common.kubernetes.KubernetesRemoteService; +import io.fabric8.kubernetes.api.model.Secret; +import lombok.extern.log4j.Log4j2; + +@Log4j2 +@Configuration +public class ElasticsearchClientConfiguration { + + @Autowired + private KubernetesRemoteService kubernetesService; + @Autowired + private OzgCloudElasticsearchProperties elasticSearchProperties; + + @Bean + @Scope("singleton") + ElasticsearchClient createElasticsearchClient() { + LOG.info("Create elasticsearch client..."); + var credentialsProvider = createCredentialsProvider(elasticSearchProperties.getServer().getSecretDataKey(), getPassword()); + var restClient = buildRestClient(credentialsProvider); + var transport = createRestClientTransport(restClient); + return new ElasticsearchClient(transport); + } + + private BasicCredentialsProvider createCredentialsProvider(String userName, String password) { + var credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(userName, password)); + return credentialsProvider; + } + + private RestClient buildRestClient(BasicCredentialsProvider credentialsProvider) { + return RestClient.builder(createHttpHost()) + .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)) + .build(); + } + + private HttpHost createHttpHost() { + LOG.info(String.format("ElasticSearch config: host: %s with port: %s and scheme: %s", elasticSearchProperties.getServer().getHost(), + elasticSearchProperties.getServer().getPort(), elasticSearchProperties.getServer().getScheme())); + return new HttpHost(elasticSearchProperties.getServer().getHost(), elasticSearchProperties.getServer().getPort(), + elasticSearchProperties.getServer().getScheme()); + } + + private RestClientTransport createRestClientTransport(RestClient restClient) { + return new RestClientTransport(restClient, new JacksonJsonpMapper()); + } + + String getPassword() { + LOG.debug(String.format("get password from secret: %s in namespace %s", elasticSearchProperties.getServer().getSecretName(), + elasticSearchProperties.getServer().getNamespace())); + var secret = getCredentialsSecret(); + var password = getPasswordFromSecret(secret); + return password; + } + + private Secret getCredentialsSecret() { + return kubernetesService + .getSecretResource(elasticSearchProperties.getServer().getNamespace(), elasticSearchProperties.getServer().getSecretName()).get(); + } + + private String getPasswordFromSecret(Secret secret) { + var encodedPassword = MapUtils.getString(secret.getData(), elasticSearchProperties.getServer().getSecretDataKey()); + return decode(encodedPassword, secret); + } + + private String decode(String encodedPassword, Secret secret) { + return new String(Base64.getDecoder().decode(encodedPassword)); + } + +} \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/common/elasticsearch/ElasticsearchRemoteService.java b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/common/elasticsearch/ElasticsearchRemoteService.java new file mode 100644 index 0000000000000000000000000000000000000000..12c4c5db85dff6deeb1d815386f13b54823d6b38 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/common/elasticsearch/ElasticsearchRemoteService.java @@ -0,0 +1,132 @@ +package de.ozgcloud.operator.common.elasticsearch; + +import java.io.IOException; + +import org.springframework.stereotype.Component; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch._types.ElasticsearchException; +import co.elastic.clients.elasticsearch.security.IndicesPrivileges; +import co.elastic.clients.elasticsearch.security.PutRoleRequest; +import co.elastic.clients.elasticsearch.security.PutUserRequest; +import de.ozgcloud.operator.PutRoleRequestData; +import de.ozgcloud.operator.PutUserRequestData; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; + +@Log4j2 +@RequiredArgsConstructor +@Component +public class ElasticsearchRemoteService { + + private final ElasticsearchClient client; + + public boolean existsIndex(String index) throws Exception { + try { + LOG.debug("{}: Test if elasticsearch index exits.", index); + var exists = client.indices().exists(builder -> builder.index(index)).value(); + LOG.debug("{}: Elasticsearch index exists: {}", index, exists); + return exists; + } catch (ElasticsearchException | IOException e) { + throw new RuntimeException("Error checking index '" + index, e); + } + } + + public void createIndex(String indexName) throws Exception { + try { + LOG.info("{}: Create elasticsearch index", indexName); + client.indices().create(builder -> builder.index(indexName)); + LOG.info("{}: Create elasticsearch index successful", indexName); + } catch (Exception e) { + throw new RuntimeException("Create elasticsearch index " + indexName + "failed.", e); + } + } + + public boolean existsSecurityRole(String roleName) throws Exception { + return !client.security().getRole(builder -> builder.name(roleName)).result().isEmpty(); + } + + public void createSecurityRole(PutRoleRequestData requestData) throws Exception { + try { + LOG.info("{}: Create elasticsearch role ", requestData.getName()); + client.security().putRole(createPutRoleRequest(requestData)); + LOG.info("{}: Create elasticsearch role successful", requestData.getName()); + } catch (Exception e) { + throw new RuntimeException("Create elasticsearch role " + requestData.getName() + "failed.", e); + } + } + + PutRoleRequest createPutRoleRequest(PutRoleRequestData requestData) { + return PutRoleRequest.of(requestBuilder -> buildRequest(requestBuilder, requestData)); + } + + private PutRoleRequest.Builder buildRequest(PutRoleRequest.Builder requestBuilder, PutRoleRequestData requestData) { + requestBuilder.name(requestData.getName()); + requestBuilder.indices(builder -> buildIndicesPrivilegesRequest(builder, requestData)); + + return requestBuilder; + } + + private IndicesPrivileges.Builder buildIndicesPrivilegesRequest(IndicesPrivileges.Builder builder, PutRoleRequestData requestData) { + builder.names(requestData.getIndivesPrivilegesData().getNames()); + builder.privileges(requestData.getIndivesPrivilegesData().getPrivileges()); + + return builder; + } + + public boolean existsSecurityUser(String userName) throws Exception { + return !client.security().getUser(builder -> builder.username(userName)).result().isEmpty(); + } + + public void createSecurityUser(PutUserRequestData requestData) throws Exception { + try { + LOG.info("{}: Create elasticsearch user", requestData.getUsername()); + client.security().putUser(createPutUserRequest(requestData)); + LOG.info("{}: Create elasticsearch user successful", requestData.getUsername()); + } catch (Exception e) { + throw new RuntimeException("Create elasticsearch user " + requestData.getUsername() + "failed.", e); + } + } + + PutUserRequest createPutUserRequest(PutUserRequestData requestData) { + return PutUserRequest.of(requestBuilder -> buildPutUserRequest(requestBuilder, requestData)); + } + + private PutUserRequest.Builder buildPutUserRequest(PutUserRequest.Builder builder, PutUserRequestData requestData) { + builder.username(requestData.getUsername()); + builder.roles(requestData.getRoles()); + builder.password(requestData.getPassword()); + + return builder; + } + + public void deleteIndex(String indexName) throws Exception { + try { + LOG.info("{}: Delete elasticsearch index", indexName); + client.indices().delete(builder -> builder.index(indexName)); + LOG.info("{}: Delete elasticsearch index successful", indexName); + } catch (Exception e) { + throw new RuntimeException("Delete elasticsearch index " + indexName + "failed.", e); + } + } + + public void deleteSecurityRole(String roleName) throws Exception { + try { + LOG.info("{}: Delete elasticsearch role", roleName); + client.security().deleteRole(builder -> builder.name(roleName)); + LOG.info("{}: Delete elasticsearch role successful", roleName); + } catch (Exception e) { + throw new RuntimeException("Delete elasticsearch role " + roleName + "failed.", e); + } + } + + public void deleteSecurityUser(String userName) throws Exception { + try { + LOG.info("{}: Delete elasticsearch user", userName); + client.security().deleteUser(builder -> builder.username(userName)); + LOG.info("{}: Delete elasticsearch user successful", userName); + } catch (Exception e) { + throw new RuntimeException("Delete elasticsearch user " + userName + "failed.", e); + } + } +} \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/common/kubernetes/KubernetesRemoteService.java b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/common/kubernetes/KubernetesRemoteService.java new file mode 100644 index 0000000000000000000000000000000000000000..c8d4bd2e85fc7f380f650bc974775984809b9b80 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/main/java/de/ozgcloud/operator/common/kubernetes/KubernetesRemoteService.java @@ -0,0 +1,19 @@ +package de.ozgcloud.operator.common.kubernetes; + +import org.springframework.stereotype.Component; + +import io.fabric8.kubernetes.api.model.Secret; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.dsl.Resource; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Component +public class KubernetesRemoteService { + + private final KubernetesClient client; + + public Resource<Secret> getSecretResource(String namespace, String name) { + return client.secrets().inNamespace(namespace).withName(name); + } +} \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/main/resources/application.yml b/ozgcloud-elasticsearch-operator/src/main/resources/application.yml new file mode 100644 index 0000000000000000000000000000000000000000..8fe6660834d7b720fc93b6aad55cbdc3f8b120f6 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/main/resources/application.yml @@ -0,0 +1,37 @@ +ozgcloud: + elasticsearch: + secretCredentialsName: elasticsearch-credentials + certificateSecretName: elasticsearch-certificate + server: + namespace: elastic-system + secretName: ozg-search-cluster-es-elastic-user + secretDataKey: elastic + host: ozg-search-cluster-es-http.${ozgcloud.elasticsearch.server.namespace} + port: 9200 + scheme: https + certificateNamespace: ozgcloud-elasticsearch-operator + certificateSecretName: ozg-search-cluster-es-http-ca-internal + certificateSecretDataKey: ca.crt + +management: + server: + port: 8081 + health: + livenessState: + enabled: true + readinessState: + enabled: true + endpoint: + health: + group: + exploratory: + include: livenessState,readinessState,ping + show-details: always + probes: + enabled: true + prometheus: + enabled: true + endpoints: + web: + exposure: + include: "*" diff --git a/ozgcloud-elasticsearch-operator/src/test/helm/configmap_bindings_type_test.yaml b/ozgcloud-elasticsearch-operator/src/test/helm/configmap_bindings_type_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..83ece1c08bd44bc06afaa631215dcf699fd33239 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/helm/configmap_bindings_type_test.yaml @@ -0,0 +1,45 @@ +# +# Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +suite: Certificate ConfigMap Binding +release: + namespace: sh-helm-test +templates: + - templates/configmap_bindings_type.yaml +tests: + - it: validate configMap values + asserts: + - isKind: + of: ConfigMap + - equal: + path: metadata.name + value: bindings-type + - equal: + path: metadata.namespace + value: sh-helm-test + - equal: + path: data + value: + type: | + ca-certificates \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/test/helm/crd/operator.ozgcloud.de_OzgCloudElasticsearch_test.yaml b/ozgcloud-elasticsearch-operator/src/test/helm/crd/operator.ozgcloud.de_OzgCloudElasticsearch_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..6efab0f76873510049e19e5804665d807cf26965 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/helm/crd/operator.ozgcloud.de_OzgCloudElasticsearch_test.yaml @@ -0,0 +1,51 @@ +suite: operator.ozgcloud.de_OzgCloudElasticsearch test +templates: + - templates/crd/operator.ozgcloud.de_OzgCloudElasticsearch.yaml +tests: + - it: should have apiVersion + asserts: + - equal: + path: apiVersion + value: apiextensions.k8s.io/v1 + - it: should have isKind of + asserts: + - isKind: + of: CustomResourceDefinition + + - it: should have metadata name + asserts: + - equal: + path: metadata.name + value: ozgcloudelasticsearchs.operator.ozgcloud.de + + - it: should have spec group + asserts: + - equal: + path: spec.group + value: operator.ozgcloud.de + - it: should have spec names kind + asserts: + - equal: + path: spec.names.kind + value: OzgCloudElasticsearch + - it: should have spec names listKind + asserts: + - equal: + path: spec.names.listKind + value: OzgCloudElasticsearchList + - it: should have spec names plural + asserts: + - equal: + path: spec.names.plural + value: ozgcloudelasticsearchs + - it: should have spec names singular + asserts: + - equal: + path: spec.names.singular + value: ozgcloudelasticsearch + + - it: should have spec scope + asserts: + - equal: + path: spec.scope + value: Namespaced \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/test/helm/crd/operator.ozgcloud.de_OzgCloudElasticsearch_versions_v1_test.yaml b/ozgcloud-elasticsearch-operator/src/test/helm/crd/operator.ozgcloud.de_OzgCloudElasticsearch_versions_v1_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8e66bce6f29772a0e7051e326aa106a396d182b6 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/helm/crd/operator.ozgcloud.de_OzgCloudElasticsearch_versions_v1_test.yaml @@ -0,0 +1,99 @@ +suite: operator.ozgcloud.de_OzgCloudElasticsearch versions v1 test +templates: + - templates/crd/operator.ozgcloud.de_OzgCloudElasticsearch.yaml +tests: + - it: should have versions name + asserts: + - equal: + path: spec.versions[0].name + value: v1 + - it: should have versions schema description + asserts: + - equal: + path: spec.versions[0].schema.openAPIV3Schema.description + value: OzgCloudElasticsearch is the Schema for the elasticsearch API + - it: should have versions schema type + asserts: + - equal: + path: spec.versions[0].schema.openAPIV3Schema.type + value: object + + - it: should have versions schema apiVersion property type + asserts: + - equal: + path: spec.versions[0].schema.openAPIV3Schema.properties.apiVersion.type + value: string + - it: should have versions schema apiVersion property description + asserts: + - equal: + path: spec.versions[0].schema.openAPIV3Schema.properties.apiVersion.description + value: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + + - it: should have versions schema kind property type + asserts: + - equal: + path: spec.versions[0].schema.openAPIV3Schema.properties.kind.type + value: string + - it: should have versions schema kind property description + asserts: + - equal: + path: spec.versions[0].schema.openAPIV3Schema.properties.kind.description + value: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + + - it: should have versions schema metadata + asserts: + - equal: + path: spec.versions[0].schema.openAPIV3Schema.properties.metadata.type + value: object + + - it: should have versions schema spec description + asserts: + - equal: + path: spec.versions[0].schema.openAPIV3Schema.properties.spec.description + value: Spec defines the desired state of Elasticsearch + - it: should have versions schema spec type + asserts: + - equal: + path: spec.versions[0].schema.openAPIV3Schema.properties.spec.type + value: object + - it: should have versions schema spec preserve unknown fields + asserts: + - equal: + path: spec.versions[0].schema.openAPIV3Schema.properties.spec.x-kubernetes-preserve-unknown-fields + value: true + + - it: should have versions schema status description + asserts: + - equal: + path: spec.versions[0].schema.openAPIV3Schema.properties.status.description + value: Status defines the observed state of Elasticsearch + - it: should have versions schema status type + asserts: + - equal: + path: spec.versions[0].schema.openAPIV3Schema.properties.status.type + value: object + - it: should have versions schema status preserve unknown fields + asserts: + - equal: + path: spec.versions[0].schema.openAPIV3Schema.properties.status.x-kubernetes-preserve-unknown-fields + value: true + + - it: should have versions served + asserts: + - equal: + path: spec.versions[0].served + value: true + - it: should have versions storage + asserts: + - equal: + path: spec.versions[0].storage + value: true + - it: should have versions subresources statis + asserts: + - equal: + path: spec.versions[0].subresources.status + value: {} \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/test/helm/deployment_container_test.yaml b/ozgcloud-elasticsearch-operator/src/test/helm/deployment_container_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..462e66f312dc001e037e1684545c168375caead4 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/helm/deployment_container_test.yaml @@ -0,0 +1,93 @@ +# +# Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +suite: deployment container test +release: + name: elastic-test-operator + namespace: sh-helm-test +templates: + - deployment.yaml +tests: + - it: validate image type and container image + set: + image: + name: hase + tag: latest + imagePullSecret: imagePullSecret + asserts: + - equal: + path: spec.template.spec.containers[0].name + value: ozgcloud-elasticsearch-operator + - equal: + path: spec.template.spec.containers[0].image + value: docker.ozg-sh.de/hase:latest + - equal: + path: spec.template.spec.containers[0].imagePullPolicy + value: Always + + - it: validate health checks + set: + image: + name: hase + tag: latest + imagePullSecret: imagePullSecret + asserts: + - equal: + path: spec.template.spec.containers[0].readinessProbe + value: + failureThreshold: 3 + httpGet: + path: /actuator/health/readiness + port: 8081 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 3 + - equal: + path: spec.template.spec.containers[0].startupProbe + value: + failureThreshold: 10 + httpGet: + path: /actuator/health/readiness + port: 8081 + scheme: HTTP + initialDelaySeconds: 30 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 5 + + - it: validate security context + set: + image: + name: hase + tag: latest + imagePullSecret: imagePullSecret + asserts: + - equal: + path: spec.template.spec.containers[0].securityContext + value: + allowPrivilegeEscalation: false + privileged: false + readOnlyRootFilesystem: false + runAsNonRoot: true diff --git a/ozgcloud-elasticsearch-operator/src/test/helm/deployment_env_test.yaml b/ozgcloud-elasticsearch-operator/src/test/helm/deployment_env_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..0179c95b140f6f2cbdf14fb0ebb7a0576da5f807 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/helm/deployment_env_test.yaml @@ -0,0 +1,45 @@ +# +# Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +suite: test environments +templates: + - templates/deployment.yaml +tests: + - it: check customList + template: deployment.yaml + set: + env.customList: + - name: my_test_environment_name + value: "A test value" + image: + name: hase + tag: latest + imagePullSecret: imagePullSecret + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: my_test_environment_name + value: "A test value" + diff --git a/ozgcloud-elasticsearch-operator/src/test/helm/deployment_matchlabels_test.yaml b/ozgcloud-elasticsearch-operator/src/test/helm/deployment_matchlabels_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..6b20b3d2f94de47d71794c8110acfd824b48ff57 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/helm/deployment_matchlabels_test.yaml @@ -0,0 +1,51 @@ +# +# Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +suite: deployment matchlabels +release: + name: ozgcloud-elasticsearch-operator + namespace: sh-helm-test +templates: + - templates/deployment.yaml +tests: + - it: check matchlabels + set: + image: + name: hase + tag: latest + imagePullSecret: imagePullSecret + asserts: + - equal: + path: spec.selector.matchLabels.[app.kubernetes.io/name] + value: ozgcloud-elasticsearch-operator + - equal: + path: spec.selector.matchLabels.[app.kubernetes.io/namespace] + value: sh-helm-test + + - equal: + path: spec.template.metadata.labels.[app.kubernetes.io/name] + value: ozgcloud-elasticsearch-operator + - equal: + path: spec.template.metadata.labels.[app.kubernetes.io/namespace] + value: sh-helm-test \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/test/helm/deployment_metadata_test.yaml b/ozgcloud-elasticsearch-operator/src/test/helm/deployment_metadata_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..9679c3b4b5b17e6fbd6b920f0fc14bcf3b9c9464 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/helm/deployment_metadata_test.yaml @@ -0,0 +1,70 @@ +# +# Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +suite: deployment test metadata +release: + name: ozgcloud-elasticsearch-operator + namespace: sh-helm-test +templates: + - templates/deployment.yaml +tests: + - it: check metadata labels + set: + image: + name: hase + tag: latest + imagePullSecret: imagePullSecret + asserts: + - equal: + path: metadata.labels.[app.kubernetes.io/instance] + value: ozgcloud-elasticsearch-operator + - equal: + path: metadata.labels.[app.kubernetes.io/name] + value: ozgcloud-elasticsearch-operator + - equal: + path: metadata.labels.[app.kubernetes.io/part-of] + value: ozg + - equal: + path: metadata.labels.[app.kubernetes.io/namespace] + value: sh-helm-test + - it: check metadata name + set: + image: + name: hase + tag: latest + imagePullSecret: imagePullSecret + asserts: + - equal: + path: metadata.name + value: ozgcloud-elasticsearch-operator + - it: check metadata namespace + set: + image: + name: hase + tag: latest + imagePullSecret: imagePullSecret + asserts: + - equal: + path: metadata.namespace + value: sh-helm-test diff --git a/ozgcloud-elasticsearch-operator/src/test/helm/deployment_pull_secret_test.yaml b/ozgcloud-elasticsearch-operator/src/test/helm/deployment_pull_secret_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..983d2758b0eb06d149ad1b11264cf07890566517 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/helm/deployment_pull_secret_test.yaml @@ -0,0 +1,42 @@ +# +# Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +suite: deployment pull secret +release: + name: ozgcloud-elasticsearch-operator + namespace: sh-helm-test +templates: + - deployment.yaml +tests: + - it: validate image pull secret resource name + set: + image: + name: hase + tag: latest + imagePullSecret: imagePullSecret + asserts: + - equal: + path: spec.template.spec.imagePullSecrets[0].name + value: imagePullSecret + diff --git a/ozgcloud-elasticsearch-operator/src/test/helm/deployment_resources_test.yaml b/ozgcloud-elasticsearch-operator/src/test/helm/deployment_resources_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..5e871d26719f7d886e29729a2134f75419fd4aae --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/helm/deployment_resources_test.yaml @@ -0,0 +1,67 @@ +# +# Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +suite: deployment resources test +release: + name: afm-adapter +templates: + - templates/deployment.yaml +tests: + - it: test resources + set: + resources: + limits: + cpu: "11m" + memory: "22Mi" + requests: + cpu: "33m" + memory: "44Mi" + image: + name: hase + tag: latest + imagePullSecret: imagePullSecret + asserts: + - equal: + path: spec.template.spec.containers[0].resources.limits.cpu + value: 11m + - equal: + path: spec.template.spec.containers[0].resources.limits.memory + value: 22Mi + - equal: + path: spec.template.spec.containers[0].resources.requests.cpu + value: 33m + - equal: + path: spec.template.spec.containers[0].resources.requests.memory + value: 44Mi + + - it: test empty resources + set: + image: + name: hase + tag: latest + imagePullSecret: imagePullSecret + asserts: + - isEmpty: + path: spec.template.spec.containers[0].resources + diff --git a/ozgcloud-elasticsearch-operator/src/test/helm/deployment_template_spec_test.yaml b/ozgcloud-elasticsearch-operator/src/test/helm/deployment_template_spec_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8ec9dbacd2267f9b2579ca4c5e2e57330edf10f6 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/helm/deployment_template_spec_test.yaml @@ -0,0 +1,66 @@ +# +# Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +suite: deployment templace spec test +release: + name: elastic-test-operator + namespace: sh-helm-test +templates: + - deployment.yaml +tests: + - it: validate serviceaccount name + set: + image: + name: hase + tag: latest + imagePullSecret: imagePullSecret + asserts: + - equal: + path: spec.template.spec.serviceAccountName + value: ozgcloud-elasticsearch-operator-serviceaccount + + - it: validate restartPolicy + set: + image: + name: hase + tag: latest + imagePullSecret: imagePullSecret + asserts: + - equal: + path: spec.template.spec.restartPolicy + value: Always + + - it: validate dns config + set: + image: + name: hase + tag: latest + imagePullSecret: imagePullSecret + asserts: + - equal: + path: spec.template.spec.dnsConfig + value: {} + - equal: + path: spec.template.spec.dnsPolicy + value: ClusterFirst \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/test/helm/deployment_type_test.yaml b/ozgcloud-elasticsearch-operator/src/test/helm/deployment_type_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e992e29f94ea59c82a59b616a7db948864061c56 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/helm/deployment_type_test.yaml @@ -0,0 +1,43 @@ +# +# Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +suite: deployment type test +release: + name: elastic-test-operator + namespace: sh-helm-test +templates: + - deployment.yaml +tests: + - it: validate template type and api Version + set: + image: + name: hase + tag: latest + imagePullSecret: imagePullSecret + asserts: + - isKind: + of: Deployment + - isAPIVersion: + of: apps/v1 + diff --git a/ozgcloud-elasticsearch-operator/src/test/helm/deployment_volumes.yaml b/ozgcloud-elasticsearch-operator/src/test/helm/deployment_volumes.yaml new file mode 100644 index 0000000000000000000000000000000000000000..15ce39bf9a4c2bafc6f15c13bb54a9e86c3e4c37 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/helm/deployment_volumes.yaml @@ -0,0 +1,86 @@ +# +# Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +suite: deployment type test +release: + name: elastic-test-operator + namespace: sh-helm-test +templates: + - deployment.yaml +tests: + - it: should create SERVICE_BINDING_ROOT + set: + image: + name: hase + tag: latest + imagePullSecret: imagePullSecret + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: SERVICE_BINDING_ROOT + value: "/bindings" + + - it: should create volumes + set: + image: + name: hase + tag: latest + imagePullSecret: imagePullSecret + asserts: + - contains: + path: spec.template.spec.volumes + content: + name: bindings + configMap: + name: bindings-type + - contains: + path: spec.template.spec.volumes + content: + name: elasticsearch-certificate + secret: + secretName: ozg-search-cluster-es-http-ca-internal + optional: false + + - it: should create volumeMounts + set: + image: + name: hase + tag: latest + imagePullSecret: imagePullSecret + asserts: + - contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: bindings + mountPath: "/bindings/ca-certificates/type" + subPath: type + readOnly: true + - contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: elasticsearch-certificate + mountPath: "/bindings/ca-certificates/es-root-ca.pem" + subPath: ca.crt + readOnly: true \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/test/helm/linter_values.yaml b/ozgcloud-elasticsearch-operator/src/test/helm/linter_values.yaml new file mode 100644 index 0000000000000000000000000000000000000000..aab55aca7ad1c21dbc0730aa82a28e0b3d8d872b --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/helm/linter_values.yaml @@ -0,0 +1,11 @@ + +image: + name: test + tag: latest + +imagePullSecret: "docker-secret" + +env: + customList: + - name: my_test_environment_name + value: "A test value" \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/test/helm/rbac/ozgcloud_elasticsearch_operator_admin_secret_view_role_test.yaml b/ozgcloud-elasticsearch-operator/src/test/helm/rbac/ozgcloud_elasticsearch_operator_admin_secret_view_role_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..9604c57257fe009fdb987fb9ab9ca673da1c1344 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/helm/rbac/ozgcloud_elasticsearch_operator_admin_secret_view_role_test.yaml @@ -0,0 +1,69 @@ +# +# Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +suite: Elasticsearch admin secret view role test +release: + name: ozgcloud-elasticsearch-operator + namespace: test-namespace +templates: + - templates/rbac/ozgcloud_elasticsearch_operator_admin_secret_view_role.yaml +tests: + - it: should have apiVersion + asserts: + - equal: + path: apiVersion + value: rbac.authorization.k8s.io/v1 + - it: should have isKind of + asserts: + - isKind: + of: Role + + - it: should have metadata name + asserts: + - equal: + path: metadata.name + value: ozgcloud-elasticsearch-operator-admin-secret-view-role + - it: should have metadata namespace + asserts: + - equal: + path: metadata.namespace + value: elastic-system + + + - it: should have rules for ozgcloudelasticsearches resource + asserts: + - equal: + path: rules + value: + - apiGroups: + - "" + resourceNames: + - ozg-search-cluster-es-elastic-user + - ozg-search-cluster-es-http-ca-internal + resources: + - secrets + verbs: + - get + + \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/test/helm/rbac/ozgcloud_elasticsearch_operator_admin_secret_view_rolebinding_test.yaml b/ozgcloud-elasticsearch-operator/src/test/helm/rbac/ozgcloud_elasticsearch_operator_admin_secret_view_rolebinding_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..9cde6cc508e89f8f45d7f5afa55737d94783c826 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/helm/rbac/ozgcloud_elasticsearch_operator_admin_secret_view_rolebinding_test.yaml @@ -0,0 +1,64 @@ +# +# Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +suite: Elasticsearch admin secret view rolebinding test +release: + name: ozgcloud-elasticsearch-operator + namespace: sh-helm-test +templates: + - templates/rbac/ozgcloud_elasticsearch_operator_admin_secret_view_rolebinding.yaml +tests: + - it: should have apiVersion + asserts: + - equal: + path: apiVersion + value: rbac.authorization.k8s.io/v1 + - it: should have isKind of + asserts: + - isKind: + of: RoleBinding + + - it: should have metadata name + asserts: + - equal: + path: metadata.name + value: ozgcloud-elasticsearch-operator-admin-secret-view-role-binding + + - it: should have subjects + asserts: + - equal: + path: subjects + value: + - kind: ServiceAccount + name: ozgcloud-elasticsearch-operator-serviceaccount + namespace: sh-helm-test + + - it: should have roleRef + asserts: + - equal: + path: roleRef + value: + kind: Role + name: ozgcloud-elasticsearch-operator-admin-secret-view-role + apiGroup: rbac.authorization.k8s.io \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/test/helm/rbac/ozgcloud_elasticsearch_operator_edit_role_test.yaml b/ozgcloud-elasticsearch-operator/src/test/helm/rbac/ozgcloud_elasticsearch_operator_edit_role_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..078cf0a0cf2fd7498271d63ca3fb6e686b85e71a --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/helm/rbac/ozgcloud_elasticsearch_operator_edit_role_test.yaml @@ -0,0 +1,53 @@ +suite: elasticsearch_edit_role test +release: + name: release-name +templates: + - templates/rbac/ozgcloud_elasticsearch_operator_edit_role.yaml +tests: + - it: should have apiVersion + asserts: + - equal: + path: apiVersion + value: rbac.authorization.k8s.io/v1 + - it: should have isKind of + asserts: + - isKind: + of: ClusterRole + + - it: should have metadata name + asserts: + - equal: + path: metadata.name + value: ozgcloud-elasticsearch-operator-edit-role + - it: should have metadata labels name + asserts: + - equal: + path: metadata.labels.[app.kubernetes.io/name] + value: release-name + - it: should have metadata labels instance + asserts: + - equal: + path: metadata.labels.[app.kubernetes.io/instance] + value: release-name + - it: should have metadata labels component + asserts: + - equal: + path: metadata.labels.[app.kubernetes.io/component] + value: ozgcloud-elasticsearch-operator + + - it: should have rules for ozgcloudelasticsearchs resource + asserts: + - contains: + path: rules + content: + apiGroups: + - operator.ozgcloud.de + resources: + - ozgcloudelasticsearchs + - ozgcloudelasticsearchs/status + - ozgcloudelasticsearchs/finalizers + verbs: + - patch + - update + + \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/test/helm/rbac/ozgcloud_elasticsearch_operator_edit_rolebinding_test.yaml b/ozgcloud-elasticsearch-operator/src/test/helm/rbac/ozgcloud_elasticsearch_operator_edit_rolebinding_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..25e7418ed1558fa4c2d0ca7274c05442477a70d0 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/helm/rbac/ozgcloud_elasticsearch_operator_edit_rolebinding_test.yaml @@ -0,0 +1,41 @@ + + +suite: elasticsearch_view_rolebinding test +release: + namespace: sh-helm-test +templates: + - templates/rbac/ozgcloud_elasticsearch_operator_view_rolebinding.yaml +tests: + - it: should have apiVersion + asserts: + - equal: + path: apiVersion + value: rbac.authorization.k8s.io/v1 + - it: should have isKind of + asserts: + - isKind: + of: ClusterRoleBinding + + - it: should have metadata name + asserts: + - equal: + path: metadata.name + value: ozgcloud-elasticsearch-operator-view-role-binding + + - it: should have subjects + asserts: + - equal: + path: subjects + value: + - kind: ServiceAccount + name: ozgcloud-elasticsearch-operator-serviceaccount + namespace: sh-helm-test + + - it: should have roleRef + asserts: + - equal: + path: roleRef + value: + kind: ClusterRole + name: ozgcloud-elasticsearch-operator-view-role + apiGroup: rbac.authorization.k8s.io \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/test/helm/rbac/ozgcloud_elasticsearch_operator_serviceaccount_test.yaml b/ozgcloud-elasticsearch-operator/src/test/helm/rbac/ozgcloud_elasticsearch_operator_serviceaccount_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c2a93ca53960bf57424ff9d3f153e5cfbecb9a43 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/helm/rbac/ozgcloud_elasticsearch_operator_serviceaccount_test.yaml @@ -0,0 +1,41 @@ +# +# Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +suite: ServiceAccount test +release: + name: ozgcloud-elasticsearch-operator + namespace: test-namespace +templates: + - templates/rbac/ozgcloud_elasticsearch_operator_serviceaccount.yaml +tests: + - it: test metadata + asserts: + - isKind: + of: ServiceAccount + - equal: + path: metadata.name + value: ozgcloud-elasticsearch-operator-serviceaccount + - equal: + path: metadata.namespace + value: test-namespace \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/test/helm/rbac/ozgcloud_elasticsearch_operator_view_role_test.yaml b/ozgcloud-elasticsearch-operator/src/test/helm/rbac/ozgcloud_elasticsearch_operator_view_role_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..5112d901aaeeaa0ca71735fcc76417364cbaafac --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/helm/rbac/ozgcloud_elasticsearch_operator_view_role_test.yaml @@ -0,0 +1,53 @@ +suite: elasticsearch_view_role test +release: + name: release-name +templates: + - templates/rbac/ozgcloud_elasticsearch_operator_view_role.yaml +tests: + - it: should have apiVersion + asserts: + - equal: + path: apiVersion + value: rbac.authorization.k8s.io/v1 + - it: should have isKind of + asserts: + - isKind: + of: ClusterRole + + - it: should have metadata name + asserts: + - equal: + path: metadata.name + value: ozgcloud-elasticsearch-operator-view-role + - it: should have metadata labels name + asserts: + - equal: + path: metadata.labels.[app.kubernetes.io/name] + value: release-name + - it: should have metadata labels instance + asserts: + - equal: + path: metadata.labels.[app.kubernetes.io/instance] + value: release-name + - it: should have metadata labels component + asserts: + - equal: + path: metadata.labels.[app.kubernetes.io/component] + value: ozgcloud-elasticsearch-operator + + - it: should have rules for ozgcloudelasticsearchs resource + asserts: + - contains: + path: rules + content: + apiGroups: + - operator.ozgcloud.de + resources: + - secrets + - ozgcloudelasticsearchs + - ozgcloudelasticsearchs/status + - ozgcloudelasticsearchs/finalizers + verbs: + - get + - list + - watch diff --git a/ozgcloud-elasticsearch-operator/src/test/helm/rbac/ozgcloud_elasticsearch_operator_view_rolebinding_test.yaml b/ozgcloud-elasticsearch-operator/src/test/helm/rbac/ozgcloud_elasticsearch_operator_view_rolebinding_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e5191b1292c29fbe58af8360d27f0f954487980d --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/helm/rbac/ozgcloud_elasticsearch_operator_view_rolebinding_test.yaml @@ -0,0 +1,41 @@ + + +suite: elasticsearch_edit_rolebinding test +release: + namespace: sh-helm-test +templates: + - templates/rbac/ozgcloud_elasticsearch_operator_edit_rolebinding.yaml +tests: + - it: should have apiVersion + asserts: + - equal: + path: apiVersion + value: rbac.authorization.k8s.io/v1 + - it: should have isKind of + asserts: + - isKind: + of: ClusterRoleBinding + + - it: should have metadata name + asserts: + - equal: + path: metadata.name + value: ozgcloud-elasticsearch-operator-edit-role-binding + + - it: should have subjects + asserts: + - equal: + path: subjects + value: + - kind: ServiceAccount + name: ozgcloud-elasticsearch-operator-serviceaccount + namespace: sh-helm-test + + - it: should have roleRef + asserts: + - equal: + path: roleRef + value: + kind: ClusterRole + name: ozgcloud-elasticsearch-operator-edit-role + apiGroup: rbac.authorization.k8s.io \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/ElasticsearchCustomResourceStatusTestFactory.java b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/ElasticsearchCustomResourceStatusTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..c3e4eb3f132a2914615670b7948c2cef29648a9f --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/ElasticsearchCustomResourceStatusTestFactory.java @@ -0,0 +1,15 @@ +package de.ozgcloud.operator; + +public class ElasticsearchCustomResourceStatusTestFactory { + + public final static CustomResourceStatus STATUS = CustomResourceStatus.OK; + + public static OzgCloudElasticsearchCustomResourceStatus create() { + return createBuilder().build(); + } + + public static OzgCloudElasticsearchCustomResourceStatus.OzgCloudElasticsearchCustomResourceStatusBuilder createBuilder() { + return OzgCloudElasticsearchCustomResourceStatus.builder() + .status(STATUS); + } +} \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/ElasticsearchCustomResourceTestFactory.java b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/ElasticsearchCustomResourceTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..3f4a5402b2a8dcce256cb3ca50906ef830986aff --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/ElasticsearchCustomResourceTestFactory.java @@ -0,0 +1,11 @@ +package de.ozgcloud.operator; + +public class ElasticsearchCustomResourceTestFactory { + + public static OzgCloudElasticsearchCustomResource create() { + var resource = new OzgCloudElasticsearchCustomResource(); + resource.setStatus(ElasticsearchCustomResourceStatusTestFactory.create()); + resource.setMetadata(ObjectMetaTestFactory.create()); + return resource; + } +} \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/ElasticsearchReconcilerITCase.java b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/ElasticsearchReconcilerITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..66f872dd9f706bd3052598a5a6797cc73659f3e0 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/ElasticsearchReconcilerITCase.java @@ -0,0 +1,64 @@ +package de.ozgcloud.operator; + +import static org.assertj.core.api.Assertions.*; + +import java.net.HttpURLConnection; + +import org.junit.Rule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import io.fabric8.kubernetes.api.model.PodBuilder; +import io.fabric8.kubernetes.api.model.PodList; +import io.fabric8.kubernetes.api.model.PodListBuilder; +import io.fabric8.kubernetes.client.NamespacedKubernetesClient; +import io.fabric8.kubernetes.client.server.mock.KubernetesServer; + +class ElasticsearchReconcilerITCase { + + @Rule + private final KubernetesServer server = new KubernetesServer(); + private NamespacedKubernetesClient client; + + @BeforeEach + void init() { + server.before(); + client = server.getClient(); + } + + @DisplayName("Reconcile") + @Nested + class TestReconcile { + + private final PodList podList = new PodListBuilder().withItems( + new PodBuilder().withNewMetadata().withName("pod1").endMetadata() + .build(), + new PodBuilder().withNewMetadata().withName("pod2").endMetadata() + .build()) + .build(); + + @Test + public void shouldGetMockedPods() { + server.expect().get().withPath("/api/v1/pods").andReturn(HttpURLConnection.HTTP_OK, podList).once(); + + var podList = client.pods().inAnyNamespace().list(); + + assertThat(podList).isNotNull(); + assertThat(podList.getItems()).hasSize(2); + } + + @Test + public void shouldGetMockedPodsInNamespace() { + var namespace = "default"; + server.expect().get().withPath("/api/v1/namespaces/" + namespace + "/pods") + .andReturn(HttpURLConnection.HTTP_OK, new PodListBuilder().build()) + .once(); + + var podList = client.pods().inNamespace(namespace).list(); + + assertThat(podList.getItems()).isEmpty(); + } + } +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..163b26d782bb6b73c2174d5b8f51d79a2b257098 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/ElasticsearchReconcilerTest.java @@ -0,0 +1,232 @@ +package de.ozgcloud.operator; + +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; +import org.junit.jupiter.api.Test; +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; + +class ElasticsearchReconcilerTest { + + @Spy + @InjectMocks + private ElasticsearchReconciler reconciler; + @Mock + private OzgCloudElasticsearchService service; + + @DisplayName("Reconcile") + @Nested + class TestReconcile { + + @Mock + private Context<OzgCloudElasticsearchCustomResource> context; + + private final OzgCloudElasticsearchCustomResource resource = ElasticsearchCustomResourceTestFactory.create(); + + 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, 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() { + var deleteControl = reconciler.cleanup(resource, context); + + assertThat(deleteControl).isNotNull(); + } + } +} \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/IndicesPrivilegesDataTestFactory.java b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/IndicesPrivilegesDataTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..9333775ebdc8fc9b1ffcb510e09b78781519ae90 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/IndicesPrivilegesDataTestFactory.java @@ -0,0 +1,21 @@ +package de.ozgcloud.operator; + +import com.thedeanda.lorem.LoremIpsum; + +import de.ozgcloud.operator.PutRoleRequestData.IndicesPrivilegesData; + +public class IndicesPrivilegesDataTestFactory { + + public static final String NAME = LoremIpsum.getInstance().getFirstName(); + public static final String PRIVILEGES = IndicesPrivilege.ALL.getValue(); + + public static IndicesPrivilegesData create() { + return createBuilder().build(); + } + + public static IndicesPrivilegesData.IndicesPrivilegesDataBuilder createBuilder(){ + return IndicesPrivilegesData.builder() + .names(NAME) + .privileges(PRIVILEGES); + } +} diff --git a/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/ObjectMetaTestFactory.java b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/ObjectMetaTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..c86a0813c25cd1cf7c0315db3078f1af8df934d1 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/ObjectMetaTestFactory.java @@ -0,0 +1,14 @@ +package de.ozgcloud.operator; + +import de.ozgcloud.operator.common.kubernetes.NamespaceTestFactory; +import io.fabric8.kubernetes.api.model.ObjectMeta; + +public class ObjectMetaTestFactory { + + public static ObjectMeta create() { + var objectMeta = new ObjectMeta(); + objectMeta.setNamespace(NamespaceTestFactory.NAMESPACE); + + return objectMeta; + } +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..5cba01b723db24e94b8f02da258f9a57543ab408 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/OzgCloudElasticsearchSecretHelperTest.java @@ -0,0 +1,174 @@ +package de.ozgcloud.operator; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.Map; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import de.ozgcloud.operator.OzgCloudElasticsearchProperties.OzgCloudElasticsearchServerProperties; +import de.ozgcloud.operator.common.kubernetes.NamespaceTestFactory; +import de.ozgcloud.operator.common.kubernetes.SecretTestFactory; +import io.fabric8.kubernetes.api.model.Secret; + +class OzgCloudElasticsearchSecretHelperTest { + + @Spy + @InjectMocks + 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.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.getStringData()).containsEntry(OzgCloudElasticsearchSecretHelper.CREDENTIAL_SECRET_INDEX_FIELD, + NamespaceTestFactory.NAMESPACE); + } + + @Test + void shouldContainPassword() { + var secret = buildCredentialSecret(); + + 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.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)); + } + } +} diff --git a/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/OzgCloudElasticsearchServiceTest.java b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/OzgCloudElasticsearchServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8d0c3734879f08925a6dce3fa0fcc6ee97dc711d --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/OzgCloudElasticsearchServiceTest.java @@ -0,0 +1,418 @@ +package de.ozgcloud.operator; + +import static de.ozgcloud.operator.common.kubernetes.NamespaceTestFactory.*; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import com.thedeanda.lorem.LoremIpsum; + +import de.ozgcloud.operator.OzgCloudElasticsearchProperties.OzgCloudElasticsearchServerProperties; +import de.ozgcloud.operator.common.elasticsearch.ElasticsearchRemoteService; +import de.ozgcloud.operator.common.kubernetes.KubernetesRemoteService; +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.client.dsl.Resource; +import io.fabric8.kubernetes.client.extension.ResourceAdapter; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import lombok.SneakyThrows; + +class OzgCloudElasticsearchServiceTest { + + @Spy + @InjectMocks + private OzgCloudElasticsearchService service; + @Mock + private ElasticsearchRemoteService remoteService; + @Mock + private OzgCloudElasticsearchSecretHelper secretHelper; + @Mock + private OzgCloudElasticsearchProperties properties; + @Mock + private KubernetesRemoteService kubernetesService; + + @DisplayName("Get or create secret") + @Nested + class TestGetOrCreateSecret { + + @Mock + private Context<OzgCloudElasticsearchCustomResource> context; + @Mock + private ResourceAdapter<Secret> resourceAdapter; + @Mock + private Resource<Secret> secretResource; + + private final Secret secret = SecretTestFactory.create(); + + private final OzgCloudElasticsearchCustomResource resource = ElasticsearchCustomResourceTestFactory.create(); + + @BeforeEach + void mock() { + when(kubernetesService.getSecretResource(any(), any())).thenReturn(secretResource); + when(secretResource.get()).thenReturn(secret); + when(properties.getSecretCredentialsName()).thenReturn(IndicesPrivilegesDataTestFactory.PRIVILEGES); + } + + @Test + void shouldGetSecret() { + service.getOrCreateCredentialSecret(resource, context); + + verify(kubernetesService).getSecretResource(NamespaceTestFactory.NAMESPACE, IndicesPrivilegesDataTestFactory.PRIVILEGES); + } + + @DisplayName("on existing") + @Nested + class TestOnExisting { + + @Test + void shouldReturnSecret() { + var secret = service.getOrCreateCredentialSecret(resource, context); + + assertThat(secret).isNotNull(); + } + } + + @DisplayName("on missing secret") + @Nested + class TestOnMissingSecret { + + @BeforeEach + void mock() { + when(secretResource.get()).thenReturn(null); + + doReturn(resourceAdapter).when(service).createAdapter(any()); + } + + @Test + void shouldBuildSecret() { + service.getOrCreateCredentialSecret(resource, context); + + verify(secretHelper).buildCredentialSecret(NamespaceTestFactory.NAMESPACE, IndicesPrivilegesDataTestFactory.PRIVILEGES); + } + + @Test + void shouldCreateSecret() { + when(secretHelper.buildCredentialSecret(any(), any())).thenReturn(secret); + + service.getOrCreateCredentialSecret(resource, context); + + verify(resourceAdapter).create(secret); + } + } + } + + @DisplayName("Create index if missing") + @Nested + class TestCreateIndexIfMissing { + + @SneakyThrows + @Test + void shouldCheckIfIndexExists() { + service.createIndexIfMissing(NAMESPACE); + + verify(remoteService).existsIndex(NAMESPACE); + } + + @SneakyThrows + @Test + void shouldCreateIndexIfMissing() { + when(remoteService.existsIndex(any())).thenReturn(false); + + service.createIndexIfMissing(NAMESPACE); + + verify(remoteService).createIndex(NAMESPACE); + } + } + + @DisplayName("Check security role if missing") + @Nested + class TestCheckSecurityRoleIfMissing { + + private final PutRoleRequestData putRoleRequest = PutRoleRequestDataTestFactory.create(); + + @SneakyThrows + @Test + void shouldCheckIfSecurityRoleExists() { + service.createSecurityRoleIfMissing(NAMESPACE); + + verify(remoteService).existsSecurityRole(NAMESPACE); + } + + @SneakyThrows + @Test + void shouldCreateSecurityRoleIfMissing() { + when(remoteService.existsSecurityRole(any())).thenReturn(false); + doReturn(putRoleRequest).when(service).buildPutRoleRequestData(any()); + + service.createSecurityRoleIfMissing(NAMESPACE); + + verify(remoteService).createSecurityRole(putRoleRequest); + } + + @DisplayName("create put role request data") + @Nested + class TestCreatePutRoleRequestData { + + @Test + void shouldContainName() { + var requestData = buildPutRoleRequestData(); + + assertThat(requestData.getName()).isEqualTo(NAMESPACE); + } + + @DisplayName("indices privileges data") + @Nested + class TestIndicesPrivilegesData { + + @Test + void shouldContainName() { + var requestData = buildPutRoleRequestData(); + + assertThat(requestData.getIndivesPrivilegesData().getNames()).isEqualTo(NAMESPACE); + } + + @Test + void shouldContainPrivileges() { + var requestData = buildPutRoleRequestData(); + + assertThat(requestData.getIndivesPrivilegesData().getPrivileges()).isEqualTo(IndicesPrivilegesDataTestFactory.PRIVILEGES); + } + } + + private PutRoleRequestData buildPutRoleRequestData() { + return service.buildPutRoleRequestData(NAMESPACE); + } + } + } + + @DisplayName("Create security user if missing") + @Nested + class TestCheckSecurityUserIfMissing { + + private final PutUserRequestData putUserRequestData = PutUserRequestDataTestFactory.create(); + + @SneakyThrows + @Test + void shouldCheckIfSecurityUserExists() { + service.createSecurityUserIfMissing(NAMESPACE, PutUserRequestDataTestFactory.PASSWORD); + + verify(remoteService).existsSecurityUser(NAMESPACE); + } + + @SneakyThrows + @Test + void shouldCreateSecurityUserIfMissing() { + when(remoteService.existsSecurityUser(any())).thenReturn(false); + doReturn(putUserRequestData).when(service).buildPutUserRequestData(any(), any()); + + service.createSecurityUserIfMissing(NAMESPACE, PutUserRequestDataTestFactory.PASSWORD); + + verify(remoteService).createSecurityUser(putUserRequestData); + } + + @DisplayName("create put user request data") + @Nested + class TestCreatePutUserRequestData { + + @Test + void shouldContainUsername() { + var requestData = buildPutUserRequestData(); + + assertThat(requestData.getUsername()).isEqualTo(NAMESPACE); + } + + @Test + void shouldContainRoles() { + var requestData = buildPutUserRequestData(); + + assertThat(requestData.getRoles()).isEqualTo(NAMESPACE); + } + + @Test + void shouldContainPassword() { + var requestData = buildPutUserRequestData(); + + assertThat(requestData.getPassword()).isEqualTo(PutUserRequestDataTestFactory.PASSWORD); + } + + private PutUserRequestData buildPutUserRequestData() { + return service.buildPutUserRequestData(NAMESPACE, PutUserRequestDataTestFactory.PASSWORD); + } + } + } + + @DisplayName("Delete security user if exists") + @Nested + class TestDeleteSecurityUserIfExists { + + @SneakyThrows + @Test + void shouldCheckIfSecurityUserExists() { + service.deleteSecurityUserIfExists(PutUserRequestDataTestFactory.USERNAME); + + verify(remoteService).existsSecurityUser(PutUserRequestDataTestFactory.USERNAME); + } + + @SneakyThrows + @Test + void shouldDeleteSecurityUserIfExists() { + when(remoteService.existsSecurityUser(any())).thenReturn(true); + + service.deleteSecurityUserIfExists(PutUserRequestDataTestFactory.USERNAME); + + verify(remoteService).deleteSecurityUser(PutUserRequestDataTestFactory.USERNAME); + } + } + + @DisplayName("Delete security role if exists") + @Nested + class TestDeleteSecurityRoleIfExists { + + @SneakyThrows + @Test + void shouldCheckIfSecurityRoleExists() { + service.deleteSecurityRoleIfExists(PutRoleRequestDataTestFactory.NAME); + + verify(remoteService).existsSecurityRole(PutRoleRequestDataTestFactory.NAME); + } + + @SneakyThrows + @Test + void shouldDeleteSecurityRoleIfExists() { + when(remoteService.existsSecurityRole(any())).thenReturn(true); + + service.deleteSecurityRoleIfExists(PutRoleRequestDataTestFactory.NAME); + + verify(remoteService).deleteSecurityRole(PutRoleRequestDataTestFactory.NAME); + } + } + + @DisplayName("Delete index if exists") + @Nested + class TestDeleteIndexIfExists { + + private static final String INDEX_NAME = NAMESPACE; + + @SneakyThrows + @Test + void shouldCheckIfIndexExists() { + service.deleteIndexIfExists(INDEX_NAME); + + verify(remoteService).existsIndex(INDEX_NAME); + } + + @SneakyThrows + @Test + void shouldDeleteSecurityRoleIfExists() { + when(remoteService.existsIndex(any())).thenReturn(true); + + service.deleteIndexIfExists(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_CERTIFICATE_SECRET_NAME = LoremIpsum.getInstance().getWords(1); + + private static final String SERVER_CERTIFICATE_SECRET_DATA_KEY = LoremIpsum.getInstance().getWords(1); + private static final String SERVER_CERTIFICATE_SECRET_DATA = LoremIpsum.getInstance().getWords(1); + private static final String SERVER_CERTIFICATE_NAMESPACE = LoremIpsum.getInstance().getWords(1); + private static final Secret SERVER_CERTIFICATE_SECRET = SecretTestFactory.createBuilder() + .addToData(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.getServer()).thenReturn(serverProperties); + when(serverProperties.getCertificateSecretName()).thenReturn(SERVER_CERTIFICATE_SECRET_NAME); + when(serverProperties.getCertificateSecretDataKey()).thenReturn(SERVER_CERTIFICATE_SECRET_DATA_KEY); + when(serverProperties.getCertificateNamespace()).thenReturn(SERVER_CERTIFICATE_NAMESPACE); + 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 shouldGetCertificateSecret() { + service.createCredentialSecret(NAMESPACE, secretResource); + + verify(kubernetesService).getSecretResource(SERVER_CERTIFICATE_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 diff --git a/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/PutRoleRequestDataTestFactory.java b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/PutRoleRequestDataTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..3950d7f545ad1e1aa95cbacc70abfefa0df3059e --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/PutRoleRequestDataTestFactory.java @@ -0,0 +1,18 @@ +package de.ozgcloud.operator; + +import com.thedeanda.lorem.LoremIpsum; + +public class PutRoleRequestDataTestFactory { + + public static final String NAME = LoremIpsum.getInstance().getFirstName(); + + public static PutRoleRequestData create() { + return createBuilder().build(); + } + + public static PutRoleRequestData.PutRoleRequestDataBuilder createBuilder() { + return PutRoleRequestData.builder() + .name(NAME) + .indivesPrivilegesData(IndicesPrivilegesDataTestFactory.create()); + } +} diff --git a/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/PutUserRequestDataTestFactory.java b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/PutUserRequestDataTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..0c8fc65a96eac4761f82b126be7af594ca6c58ac --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/PutUserRequestDataTestFactory.java @@ -0,0 +1,23 @@ +package de.ozgcloud.operator; + +import org.apache.commons.lang3.RandomStringUtils; + +import com.thedeanda.lorem.LoremIpsum; + +public class PutUserRequestDataTestFactory { + + public static final String USERNAME = LoremIpsum.getInstance().getFirstName(); + public static final String ROLES = LoremIpsum.getInstance().getWords(1); + public static final String PASSWORD = RandomStringUtils.randomAlphanumeric(6); + + public static PutUserRequestData create() { + return createBuilder().build(); + } + + public static PutUserRequestData.PutUserRequestDataBuilder createBuilder(){ + return PutUserRequestData.builder() + .username(USERNAME) + .roles(ROLES) + .password(PASSWORD); + } +} \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/common/elasticsearch/ElasticsearchClientConfigurationTest.java b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/common/elasticsearch/ElasticsearchClientConfigurationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..627170a48b4cacc7acf7a3b8c838fe1bd5564f59 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/common/elasticsearch/ElasticsearchClientConfigurationTest.java @@ -0,0 +1,67 @@ +package de.ozgcloud.operator.common.elasticsearch; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.util.Base64; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import de.ozgcloud.operator.OzgCloudElasticsearchProperties; +import de.ozgcloud.operator.OzgCloudElasticsearchProperties.OzgCloudElasticsearchServerProperties; +import de.ozgcloud.operator.common.kubernetes.KubernetesRemoteService; +import de.ozgcloud.operator.common.kubernetes.SecretTestFactory; +import io.fabric8.kubernetes.api.model.Secret; +import io.fabric8.kubernetes.client.dsl.Resource; + +class ElasticsearchClientConfigurationTest { + + @Spy + @InjectMocks + private ElasticsearchClientConfiguration configuration; + @Mock + private OzgCloudElasticsearchProperties properties; + @Mock + private KubernetesRemoteService kubernetesRemoteService; + + @DisplayName("Create elasticsearch client") + @Nested + class TestCreateElasticsearchClient { + + private static final String SECRET_DATA_KEY = "dsefsfef"; + private static final String SECRET_DATA_VALUE = "testPassword"; + private static final String SECRET_DATA_ENCODED_VALUE = encodeStringBase64(SECRET_DATA_VALUE); + private static final Secret SECRET = SecretTestFactory.createBuilder().addToData(SECRET_DATA_KEY, SECRET_DATA_ENCODED_VALUE).build(); + + @Mock + private Resource<Secret> secretResource; + @Mock + private OzgCloudElasticsearchServerProperties serverProperties; + + @BeforeEach + void mock() { + when(properties.getServer()).thenReturn(serverProperties); + when(serverProperties.getSecretDataKey()).thenReturn(SECRET_DATA_KEY); + when(kubernetesRemoteService.getSecretResource(any(), any())).thenReturn(secretResource); + when(secretResource.get()).thenReturn(SECRET); + } + + @Test + void shouldReturnPasssowrd() { + var password = configuration.getPassword(); + + assertThat(password).isEqualTo(SECRET_DATA_VALUE); + } + + private static String encodeStringBase64(String string) { + return Base64.getEncoder().encodeToString(string.getBytes()); + } + } +} \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/common/elasticsearch/ElasticsearchRemoteServiceITCase.java b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/common/elasticsearch/ElasticsearchRemoteServiceITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..5ac49af8471222eff42202a3ec63a15a14cd579a --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/common/elasticsearch/ElasticsearchRemoteServiceITCase.java @@ -0,0 +1,352 @@ +package de.ozgcloud.operator.common.elasticsearch; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch.indices.ExistsRequest; +import co.elastic.clients.elasticsearch.security.IndicesPrivileges; +import co.elastic.clients.elasticsearch.security.PutRoleRequest; +import co.elastic.clients.elasticsearch.security.PutUserRequest; +import de.ozgcloud.operator.IndicesPrivilegesDataTestFactory; +import de.ozgcloud.operator.PutRoleRequestDataTestFactory; +import de.ozgcloud.operator.PutUserRequestDataTestFactory; +import lombok.SneakyThrows; + +class ElasticsearchRemoteServiceITCase { + + private static final String INDEX_NAME = "test_index"; + + private final ElasticsearchClient client = ElasticsearchTestClient.create(); + private final ElasticsearchRemoteService service = new ElasticsearchRemoteService(client); + + @BeforeAll + public static void startContainer() { + ElasticsearchTestClient.ELASTICSEARCH_CONTAINER.start(); + } + + @AfterAll + public static void stopContainer() { + ElasticsearchTestClient.ELASTICSEARCH_CONTAINER.stop(); + } + + @DisplayName("Exists index") + @Nested + class TestExistsIndex { + + @DisplayName("on existing") + @Nested + class TestOnExisting { + + @SneakyThrows + @BeforeEach + private void initIndex() { + createIndex(); + } + + @AfterEach + void cleanup() { + deleteIndex(); + } + + @SneakyThrows + @Test + void shouldReturnTrue() { + var exists = service.existsIndex(INDEX_NAME); + + assertThat(exists).isTrue(); + } + } + + @SneakyThrows + @Test + void shouldReturnFalseIfMissing() { + var exists = service.existsIndex(INDEX_NAME); + + assertThat(exists).isFalse(); + } + } + + @DisplayName("Create index") + @Nested + class TestCreateIndex { + + @AfterEach + void cleanup() { + deleteIndex(); + } + + @SneakyThrows + @Test + void shouldCreateIndex() { + service.createIndex(INDEX_NAME); + + assertThat(existsIndex()).isTrue(); + } + } + + @DisplayName("Exists security role") + @Nested + class TestExistsSecurityRole { + + @DisplayName("on existing") + @Nested + class TestOnExisting { + + @SneakyThrows + @BeforeEach + private void initSecurityRole() { + createIndex(); + client.security().putRole(service.createPutRoleRequest(PutRoleRequestDataTestFactory.create())); + } + + @AfterEach + void cleanup() { + deleteIndex(); + } + + @SneakyThrows + @Test + void shouldReturnTrue() { + var exists = service.existsSecurityRole(PutRoleRequestDataTestFactory.NAME); + + assertThat(exists).isTrue(); + } + } + + @SneakyThrows + @Test + void shouldReturnFalseIfMissing() { + var exists = service.existsSecurityRole(PutRoleRequestDataTestFactory.NAME); + + assertThat(exists).isFalse(); + } + } + + @DisplayName("Create security role") + @Nested + class TestCreateSecurityRole { + + @AfterEach + void cleanup() { + deleteSecurityRole(); + } + + @SneakyThrows + @Test + void shouldCreateSecurityRole() { + service.createSecurityRole(PutRoleRequestDataTestFactory.create()); + + assertThat(existsSecurityRole()).isTrue(); + } + + @SneakyThrows + private void deleteSecurityRole() { + client.security().deleteRole(builder -> builder.name(PutRoleRequestDataTestFactory.NAME)); + } + } + + @DisplayName("Exists security user") + @Nested + class TestExistsSecurityUser { + + @DisplayName("on existing") + @Nested + class TestOnExisting { + + @SneakyThrows + @BeforeEach + private void initSecurityUser() { + createIndex(); + client.security().putUser(service.createPutUserRequest(PutUserRequestDataTestFactory.create())); + } + + @AfterEach + void cleanup() { + deleteIndex(); + deleteSecurityRole(); + } + + @SneakyThrows + @Test + void shouldReturnTrue() { + var exists = service.existsSecurityUser(PutUserRequestDataTestFactory.USERNAME); + + assertThat(exists).isTrue(); + } + } + + @SneakyThrows + @Test + void shouldReturnFalseIfMissing() { + var exists = service.existsSecurityUser(PutUserRequestDataTestFactory.USERNAME); + + assertThat(exists).isFalse(); + } + } + + @SneakyThrows + private void deleteSecurityRole() { + client.security().deleteUser(builder -> builder.username(PutUserRequestDataTestFactory.USERNAME)); + } + @SneakyThrows + private void deleteIndex() { + client.indices().delete(builder -> builder.index(INDEX_NAME)); + } + + @DisplayName("Create security user") + @Nested + class TestCreateSecurityUser { + + @AfterEach + void cleanup() { + deleteSecurityUser(); + } + + @SneakyThrows + @Test + void shouldCreateSecurityUser() { + service.createSecurityUser(PutUserRequestDataTestFactory.create()); + + assertThat(existsSecurityUser()).isTrue(); + } + + @SneakyThrows + private boolean existsSecurityUser() { + return !client.security().getUser(builder -> builder.username(PutUserRequestDataTestFactory.USERNAME)).result().isEmpty(); + } + + @SneakyThrows + private void deleteSecurityUser() { + client.security().deleteUser(builder -> builder.username(PutUserRequestDataTestFactory.USERNAME)); + } + } + + @DisplayName("Delete index") + @Nested + class TestDeleteIndex { + + @BeforeEach + void init() { + createIndex(); + } + + @SneakyThrows + @Test + void shouldDeleteIfExists() { + service.deleteIndex(INDEX_NAME); + + assertThat(existsIndex()).isFalse(); + } + } + + @DisplayName("Delete security role") + @Nested + class TestDeleteSecurityRole { + + @BeforeEach + void init() { + createIndex(); + createSecurityRole(); + } + + @AfterEach + void cleanup() { + deleteIndex(); + } + + @SneakyThrows + @Test + void shouldDeleteIfExists() { + assertThat(existsSecurityRole()).isTrue(); + + service.deleteSecurityRole(PutRoleRequestDataTestFactory.NAME); + + assertThat(existsSecurityRole()).isFalse(); + } + } + + @DisplayName("Delete security user") + @Nested + class TestDeleteSecurityUser { + + @BeforeEach + void init() { + createIndex(); + createSecurityUser(); + } + + @AfterEach + void cleanup() { + deleteIndex(); + } + + @SneakyThrows + @Test + void shouldDeleteIfExists() { + assertThat(existsSecurityUser()).isTrue(); + + service.deleteSecurityUser(PutUserRequestDataTestFactory.USERNAME); + + assertThat(existsSecurityUser()).isFalse(); + } + } + + @SneakyThrows + private boolean existsIndex() { + return client.indices().exists(ExistsRequest.of(builder -> builder.index(INDEX_NAME))).value(); + } + + @SneakyThrows + private void createIndex() { + client.indices().create(builder -> builder.index(INDEX_NAME)); + } + + @SneakyThrows + private void createSecurityRole() { + client.security().putRole(this::buildRequest); + } + + private PutRoleRequest.Builder buildRequest(PutRoleRequest.Builder requestBuilder) { + requestBuilder.name(PutRoleRequestDataTestFactory.NAME); + requestBuilder.indices(this::buildIndicesPrivilegesRequest); + return requestBuilder; + } + + private IndicesPrivileges.Builder buildIndicesPrivilegesRequest(IndicesPrivileges.Builder builder) { + builder.names(IndicesPrivilegesDataTestFactory.NAME); + builder.privileges(IndicesPrivilegesDataTestFactory.PRIVILEGES); + + return builder; + } + + @SneakyThrows + private boolean existsSecurityRole() { + return !client.security().getRole(builder -> builder.name(PutRoleRequestDataTestFactory.NAME)).result().isEmpty(); + } + + @SneakyThrows + private void createSecurityUser() { + client.security().putUser(this::buildPutUserRequest); + } + + private PutUserRequest.Builder buildPutUserRequest(PutUserRequest.Builder builder) { + builder.username(PutUserRequestDataTestFactory.USERNAME); + builder.roles(PutUserRequestDataTestFactory.ROLES); + builder.password(PutUserRequestDataTestFactory.PASSWORD); + + return builder; + } + + @SneakyThrows + private boolean existsSecurityUser() { + return !client.security().getUser(builder -> builder.username(PutUserRequestDataTestFactory.USERNAME)).result().isEmpty(); + } +} \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/common/elasticsearch/ElasticsearchRemoteServiceTest.java b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/common/elasticsearch/ElasticsearchRemoteServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f7fdc948bc05d4eb5d147e6e39f257f6acb456e5 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/common/elasticsearch/ElasticsearchRemoteServiceTest.java @@ -0,0 +1,91 @@ +package de.ozgcloud.operator.common.elasticsearch; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import de.ozgcloud.operator.IndicesPrivilegesDataTestFactory; +import de.ozgcloud.operator.PutRoleRequestDataTestFactory; +import de.ozgcloud.operator.PutUserRequestDataTestFactory; + +class ElasticsearchRemoteServiceTest { + + @Spy + @InjectMocks + private ElasticsearchRemoteService service; + @Mock + private ElasticsearchClient client; + + @DisplayName("Create security role") + @Nested + class TestCreateSecurityRole { + + @DisplayName("create put role request") + @Nested + class TestCreatePutRoleRequest { + + @Test + void shouldHaveName() { + var request = service.createPutRoleRequest(PutRoleRequestDataTestFactory.create()); + + assertThat(request.name()).isEqualTo(PutRoleRequestDataTestFactory.NAME); + } + + @DisplayName("indices privileges") + @Nested + class TestIndicesPrivileges { + + @Test + void shouldHaveName() { + var request = service.createPutRoleRequest(PutRoleRequestDataTestFactory.create()); + + assertThat(request.indices().get(0).names()).containsExactly(IndicesPrivilegesDataTestFactory.NAME); + } + + @Test + void shouldHavePrivileges() { + var request = service.createPutRoleRequest(PutRoleRequestDataTestFactory.create()); + + assertThat(request.indices().get(0).privileges()).containsExactly(IndicesPrivilegesDataTestFactory.PRIVILEGES); + } + } + } + } + + @DisplayName("Create security user") + @Nested + class TestCreateSecurityUser { + + @DisplayName("create put user request") + @Nested + class TestCreatePutUserRequest { + + @Test + void shouldHaveName() { + var request = service.createPutUserRequest(PutUserRequestDataTestFactory.create()); + + assertThat(request.username()).isEqualTo(PutUserRequestDataTestFactory.USERNAME); + } + + @Test + void shouldHaveRoles() { + var request = service.createPutUserRequest(PutUserRequestDataTestFactory.create()); + + assertThat(request.roles()).containsExactly(PutUserRequestDataTestFactory.ROLES); + } + + @Test + void shouldHavePassword() { + var request = service.createPutUserRequest(PutUserRequestDataTestFactory.create()); + + assertThat(request.password()).isEqualTo(PutUserRequestDataTestFactory.PASSWORD); + } + } + } +} \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/common/elasticsearch/ElasticsearchTestClient.java b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/common/elasticsearch/ElasticsearchTestClient.java new file mode 100644 index 0000000000000000000000000000000000000000..ac592cd1c66251c7d8185afaf8496f0fbb314583 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/common/elasticsearch/ElasticsearchTestClient.java @@ -0,0 +1,59 @@ +package de.ozgcloud.operator.common.elasticsearch; + +import java.time.Duration; +import java.time.temporal.ChronoUnit; + +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.elasticsearch.client.RestClient; +import org.testcontainers.elasticsearch.ElasticsearchContainer; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.json.jackson.JacksonJsonpMapper; +import co.elastic.clients.transport.rest_client.RestClientTransport; + +public class ElasticsearchTestClient { + + private static final String IMAGE_NAME = "docker.elastic.co/elasticsearch/elasticsearch:8.11.3"; + private static final String USER = "elastic"; + private static final String PASSWORD = "s3cret"; + private static final int PORT = 9200; + private static final String SCHEME = "https"; + private static final Duration STARTUP_TIMEOUT = Duration.of(2, ChronoUnit.MINUTES); + + public static final ElasticsearchContainer ELASTICSEARCH_CONTAINER = new ElasticsearchContainer(IMAGE_NAME) + .withExposedPorts(PORT) + .withPassword(PASSWORD) + .withStartupTimeout(STARTUP_TIMEOUT); + + public static ElasticsearchClient create() { + var transport = new RestClientTransport(buildRestClient(), new JacksonJsonpMapper()); + return new ElasticsearchClient(transport); + } + + private static RestClient buildRestClient() { + var host = new HttpHost("localhost", ELASTICSEARCH_CONTAINER.getMappedPort(PORT), SCHEME); + var credentialsProvider = buildCredentialsProvider(); + var builder = RestClient.builder(host); + + builder.setHttpClientConfigCallback(clientBuilder -> { + clientBuilder.setSSLContext(ELASTICSEARCH_CONTAINER.createSslContextFromCa()); + clientBuilder.setDefaultCredentialsProvider(credentialsProvider); + return clientBuilder; + }); +// builder.setNodeSelector(INGEST_NODE_SELECTOR); +// final ObjectMapper mapper = new ObjectMapper(); +// mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); +// mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + return builder.build(); + } + + private static BasicCredentialsProvider buildCredentialsProvider() { + var credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(USER, PASSWORD)); + return credentialsProvider; + } + +} diff --git a/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/common/kubernetes/KubernetesRemoteServiceITCase.java b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/common/kubernetes/KubernetesRemoteServiceITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..847b78880357e5939b4112a7e5131274d697a90e --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/common/kubernetes/KubernetesRemoteServiceITCase.java @@ -0,0 +1,52 @@ +package de.ozgcloud.operator.common.kubernetes; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.extension.ResourceAdapter; +import io.javaoperatorsdk.jenvtest.junit.EnableKubeAPIServer; + +@EnableKubeAPIServer +class KubernetesRemoteServiceITCase { + + private static KubernetesClient client; + private final KubernetesRemoteService service = new KubernetesRemoteService(client); + + @DisplayName("Get secret resource") + @Nested + class TestGetSecretResource { + + @Test + void shouldReturnEmptyResourceIfMissing() { + var resource = service.getSecretResource(NamespaceTestFactory.NAMESPACE, SecretTestFactory.NAME); + + assertThat(resource.get()).isNull(); + } + + @Test + void shouldReturnSecretResourceIfExists() { + createNamespace(); + createSecret(); + + var resource = service.getSecretResource(NamespaceTestFactory.NAMESPACE, SecretTestFactory.NAME); + + assertThat(resource.get()).isNotNull(); + } + + private void createNamespace() { + var resource = client.namespaces().withName(NamespaceTestFactory.NAMESPACE); + var adapter = new ResourceAdapter<>(resource); + adapter.create(NamespaceTestFactory.create()); + } + + private void createSecret() { + var resource = client.secrets().inNamespace(NamespaceTestFactory.NAMESPACE).withName(SecretTestFactory.NAME); + var adapter = new ResourceAdapter<>(resource); + adapter.create(SecretTestFactory.create()); + } + } +} \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/common/kubernetes/KubernetesRemoteServiceTest.java b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/common/kubernetes/KubernetesRemoteServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1d650f5b76e9fe1e0af7a0ec8a6c412cf35efd34 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/common/kubernetes/KubernetesRemoteServiceTest.java @@ -0,0 +1,63 @@ +package de.ozgcloud.operator.common.kubernetes; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.Rule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import io.fabric8.kubernetes.api.model.Secret; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.extension.ResourceAdapter; +import io.fabric8.kubernetes.client.server.mock.KubernetesServer; + +class KubernetesRemoteServiceTest { + + @Rule + private KubernetesServer server; + private KubernetesClient client; + private KubernetesRemoteService service; + + @BeforeEach + void init() { + server = new KubernetesServer(true, true); + server.before(); + client = server.getClient(); + service = new KubernetesRemoteService(client); + } + + @DisplayName("Get secret") + @Nested + class TestGetSecret { + + private Secret secret = SecretTestFactory.create(); + + @Test + void shouldReturnExistingResourceIfExists() { + createSecret(); + + var secret = getSecret(); + + assertThat(secret).isNotNull().isEqualTo(secret); + } + + private void createSecret() { + var secretResource = client.secrets().withName(SecretTestFactory.NAME); + var adapter = new ResourceAdapter<>(secretResource); + adapter.create(secret); + } + + @Test + void shouldReturnNullNOTExists() { + var secret = getSecret(); + + assertThat(secret).isNull(); + } + + private Secret getSecret() { + return service.getSecretResource(NamespaceTestFactory.NAMESPACE, SecretTestFactory.NAME).get(); + } + } +} \ No newline at end of file diff --git a/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/common/kubernetes/NamespaceTestFactory.java b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/common/kubernetes/NamespaceTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..b4982af7f0898f21c29810ecead06822087ba5c1 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/common/kubernetes/NamespaceTestFactory.java @@ -0,0 +1,17 @@ +package de.ozgcloud.operator.common.kubernetes; + +import io.fabric8.kubernetes.api.model.Namespace; +import io.fabric8.kubernetes.api.model.NamespaceBuilder; + +public class NamespaceTestFactory { + + public static final String NAMESPACE ="test-namespace"; + + public static final Namespace create() { + return createBuilder().build(); + } + + public static NamespaceBuilder createBuilder() { + return new NamespaceBuilder().withNewMetadata().withName(NAMESPACE).endMetadata(); + } +} diff --git a/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/common/kubernetes/SecretTestFactory.java b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/common/kubernetes/SecretTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..9820f6ec977ce5b615273d13f3487d3b492f4f28 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/java/de/ozgcloud/operator/common/kubernetes/SecretTestFactory.java @@ -0,0 +1,20 @@ +package de.ozgcloud.operator.common.kubernetes; + +import io.fabric8.kubernetes.api.model.Secret; +import io.fabric8.kubernetes.api.model.SecretBuilder; + +public class SecretTestFactory { + + public final static String NAME = "secret-name"; + + public static final Secret create() { + return createBuilder().build(); + } + + public static final SecretBuilder createBuilder() { + var builder = new SecretBuilder(); + builder.withNewMetadata().withName(NAME).withNamespace(NamespaceTestFactory.NAMESPACE).endMetadata().build(); + + return builder; + } +} diff --git a/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension b/ozgcloud-elasticsearch-operator/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension similarity index 100% rename from src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension rename to ozgcloud-elasticsearch-operator/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension diff --git a/ozgcloud-elasticsearch-operator/src/test/resources/application.yml b/ozgcloud-elasticsearch-operator/src/test/resources/application.yml new file mode 100644 index 0000000000000000000000000000000000000000..dee0cbb80474c74bd0fff2abdbc95dab71ddfc34 --- /dev/null +++ b/ozgcloud-elasticsearch-operator/src/test/resources/application.yml @@ -0,0 +1,12 @@ +ozgcloud: + elasticsearch: + secretCredentialsName: elasticsearch-credentials + certificateSecretName: elasticsearch-certificate + server: + namespace: elastic-system + secretName: ozg-search-cluster-es-elastic-user + secretDataKey: elastic + host: ozg-search-cluster-es-http.${ozgcloud.elasticsearch.server.namespace} + port: 9200 + scheme: https + certificateSecretName: elasticsearch-certificate \ No newline at end of file diff --git a/src/test/resources/junit-platform.properties b/ozgcloud-elasticsearch-operator/src/test/resources/junit-platform.properties similarity index 100% rename from src/test/resources/junit-platform.properties rename to ozgcloud-elasticsearch-operator/src/test/resources/junit-platform.properties diff --git a/README.md b/ozgcloud-keycloak-operator/README.MD similarity index 100% rename from README.md rename to ozgcloud-keycloak-operator/README.MD diff --git a/doc/examples/client-alfa.yaml b/ozgcloud-keycloak-operator/doc/examples/client-alfa.yaml similarity index 100% rename from doc/examples/client-alfa.yaml rename to ozgcloud-keycloak-operator/doc/examples/client-alfa.yaml diff --git a/doc/examples/realm-berlin.yaml b/ozgcloud-keycloak-operator/doc/examples/realm-berlin.yaml similarity index 100% rename from doc/examples/realm-berlin.yaml rename to ozgcloud-keycloak-operator/doc/examples/realm-berlin.yaml diff --git a/doc/examples/user-helge.yaml b/ozgcloud-keycloak-operator/doc/examples/user-helge.yaml similarity index 100% rename from doc/examples/user-helge.yaml rename to ozgcloud-keycloak-operator/doc/examples/user-helge.yaml diff --git a/mvnw b/ozgcloud-keycloak-operator/mvnw similarity index 100% rename from mvnw rename to ozgcloud-keycloak-operator/mvnw diff --git a/mvnw.cmd b/ozgcloud-keycloak-operator/mvnw.cmd similarity index 100% rename from mvnw.cmd rename to ozgcloud-keycloak-operator/mvnw.cmd diff --git a/ozgcloud-keycloak-operator/pom.xml b/ozgcloud-keycloak-operator/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..cb36f4172e4fe6b8e5838866e9c5c36b6b8f9e13 --- /dev/null +++ b/ozgcloud-keycloak-operator/pom.xml @@ -0,0 +1,135 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>de.ozgcloud</groupId> + <artifactId>ozgcloud-operator-parent</artifactId> + <version>2.1.0-SNAPSHOT</version> + <relativePath>../</relativePath> + </parent> + + <artifactId>ozgcloud-keycloak-operator</artifactId> + <packaging>jar</packaging> + + <name>OzgCloud Keycloak Operator</name> + <description>OzgCloud Keycloak Operator</description> + + <properties> + <spring-boot.build-image.imageName>docker.ozg-sh.de/ozgcloud-keycloak-operator:build-latest</spring-boot.build-image.imageName> + </properties> + + <dependencies> + <!-- spring --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter</artifactId> + </dependency> + <dependency> + <groupId>io.javaoperatorsdk</groupId> + <artifactId>operator-framework-spring-boot-starter</artifactId> + </dependency> + + <!-- keycloak --> + <dependency> + <groupId>org.keycloak</groupId> + <artifactId>keycloak-admin-client</artifactId> + </dependency> + + <!-- tools --> + <dependency> + <groupId>org.mapstruct</groupId> + <artifactId>mapstruct</artifactId> + </dependency> + + <!-- javax --> + <dependency> + <groupId>javax.validation</groupId> + <artifactId>validation-api</artifactId> + </dependency> + <dependency> + <groupId>jakarta.xml.bind</groupId> + <artifactId>jakarta.xml.bind-api</artifactId> + </dependency> + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + </dependency> + <dependency> + <groupId>org.reflections</groupId> + <artifactId>reflections</artifactId> + </dependency> + <dependency> + <groupId>commons-beanutils</groupId> + <artifactId>commons-beanutils</artifactId> + </dependency> + + <!-- test --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-test</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-params</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <fork>true</fork> + <annotationProcessorPaths> + <path> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + <version>${lombok.version}</version> + </path> + <path> + <groupId>org.mapstruct</groupId> + <artifactId>mapstruct-processor</artifactId> + <version>${mapstruct.version}</version> + </path> + </annotationProcessorPaths> + <showWarnings>true</showWarnings> + <compilerArgs> + <compilerArg> + -Amapstruct.defaultComponentModel=spring + </compilerArg> + <compilerArg> + -Amapstruct.unmappedTargetPolicy=WARN + </compilerArg> + </compilerArgs> + </configuration> + </plugin> + </plugins> + </build> + + <distributionManagement> + <repository> + <id>ozg-nexus</id> + <name>ozg-releases</name> + <url>https://nexus.ozg-sh.de/repository/ozg-releases/</url> + </repository> + <snapshotRepository> + <id>ozg-snapshots-nexus</id> + <name>ozg-snapshots</name> + <url>https://nexus.ozg-sh.de/repository/ozg-snapshots/</url> + </snapshotRepository> + </distributionManagement> + +</project> diff --git a/run-local.sh b/ozgcloud-keycloak-operator/run-local.sh similarity index 100% rename from run-local.sh rename to ozgcloud-keycloak-operator/run-local.sh diff --git a/ozgcloud-keycloak-operator/run_helm_test.sh b/ozgcloud-keycloak-operator/run_helm_test.sh new file mode 100755 index 0000000000000000000000000000000000000000..8097a39a04ff100d5c9350e205d942100dd37894 --- /dev/null +++ b/ozgcloud-keycloak-operator/run_helm_test.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +set -e + +helm template ./src/main/helm/ -f src/test/helm/linter_values.yaml +helm lint -f src/test/helm/linter_values.yaml ./src/main/helm/ +cd src/main/helm && helm unittest --helm3 -f '../../test/helm/*/*.yaml' -f '../../test/helm/*.yaml' . diff --git a/samples/crd/KeycloakUser b/ozgcloud-keycloak-operator/samples/crd/KeycloakUser similarity index 100% rename from samples/crd/KeycloakUser rename to ozgcloud-keycloak-operator/samples/crd/KeycloakUser diff --git a/src/main/helm/Chart.yaml b/ozgcloud-keycloak-operator/src/main/helm/Chart.yaml similarity index 100% rename from src/main/helm/Chart.yaml rename to ozgcloud-keycloak-operator/src/main/helm/Chart.yaml diff --git a/src/main/helm/templates/_helpers.tpl b/ozgcloud-keycloak-operator/src/main/helm/templates/_helpers.tpl similarity index 100% rename from src/main/helm/templates/_helpers.tpl rename to ozgcloud-keycloak-operator/src/main/helm/templates/_helpers.tpl diff --git a/src/main/helm/templates/crds/operator.ozgcloud.de_OzgKeycloakClient.yaml b/ozgcloud-keycloak-operator/src/main/helm/templates/crds/operator.ozgcloud.de_OzgKeycloakClient.yaml similarity index 100% rename from src/main/helm/templates/crds/operator.ozgcloud.de_OzgKeycloakClient.yaml rename to ozgcloud-keycloak-operator/src/main/helm/templates/crds/operator.ozgcloud.de_OzgKeycloakClient.yaml diff --git a/src/main/helm/templates/crds/operator.ozgcloud.de_OzgKeycloakGroup.yaml b/ozgcloud-keycloak-operator/src/main/helm/templates/crds/operator.ozgcloud.de_OzgKeycloakGroup.yaml similarity index 100% rename from src/main/helm/templates/crds/operator.ozgcloud.de_OzgKeycloakGroup.yaml rename to ozgcloud-keycloak-operator/src/main/helm/templates/crds/operator.ozgcloud.de_OzgKeycloakGroup.yaml diff --git a/src/main/helm/templates/crds/operator.ozgcloud.de_OzgKeycloakRealms.yaml b/ozgcloud-keycloak-operator/src/main/helm/templates/crds/operator.ozgcloud.de_OzgKeycloakRealms.yaml similarity index 100% rename from src/main/helm/templates/crds/operator.ozgcloud.de_OzgKeycloakRealms.yaml rename to ozgcloud-keycloak-operator/src/main/helm/templates/crds/operator.ozgcloud.de_OzgKeycloakRealms.yaml diff --git a/src/main/helm/templates/crds/operator.ozgcloud.de_OzgKeycloakUser.yaml b/ozgcloud-keycloak-operator/src/main/helm/templates/crds/operator.ozgcloud.de_OzgKeycloakUser.yaml similarity index 100% rename from src/main/helm/templates/crds/operator.ozgcloud.de_OzgKeycloakUser.yaml rename to ozgcloud-keycloak-operator/src/main/helm/templates/crds/operator.ozgcloud.de_OzgKeycloakUser.yaml diff --git a/src/main/helm/templates/deployment.yaml b/ozgcloud-keycloak-operator/src/main/helm/templates/deployment.yaml similarity index 100% rename from src/main/helm/templates/deployment.yaml rename to ozgcloud-keycloak-operator/src/main/helm/templates/deployment.yaml diff --git a/src/main/helm/templates/image_pull_secret.yaml b/ozgcloud-keycloak-operator/src/main/helm/templates/image_pull_secret.yaml similarity index 100% rename from src/main/helm/templates/image_pull_secret.yaml rename to ozgcloud-keycloak-operator/src/main/helm/templates/image_pull_secret.yaml diff --git a/src/main/helm/templates/rbacs/keycloak_admin_secret_read.yaml b/ozgcloud-keycloak-operator/src/main/helm/templates/rbacs/keycloak_admin_secret_read.yaml similarity index 100% rename from src/main/helm/templates/rbacs/keycloak_admin_secret_read.yaml rename to ozgcloud-keycloak-operator/src/main/helm/templates/rbacs/keycloak_admin_secret_read.yaml diff --git a/src/main/helm/templates/rbacs/keycloak_read.yaml b/ozgcloud-keycloak-operator/src/main/helm/templates/rbacs/keycloak_read.yaml similarity index 100% rename from src/main/helm/templates/rbacs/keycloak_read.yaml rename to ozgcloud-keycloak-operator/src/main/helm/templates/rbacs/keycloak_read.yaml diff --git a/src/main/helm/templates/rbacs/keycloak_write.yaml b/ozgcloud-keycloak-operator/src/main/helm/templates/rbacs/keycloak_write.yaml similarity index 100% rename from src/main/helm/templates/rbacs/keycloak_write.yaml rename to ozgcloud-keycloak-operator/src/main/helm/templates/rbacs/keycloak_write.yaml diff --git a/src/main/helm/templates/rbacs/serviceaccount.yaml b/ozgcloud-keycloak-operator/src/main/helm/templates/rbacs/serviceaccount.yaml similarity index 100% rename from src/main/helm/templates/rbacs/serviceaccount.yaml rename to ozgcloud-keycloak-operator/src/main/helm/templates/rbacs/serviceaccount.yaml diff --git a/src/main/helm/values.yaml b/ozgcloud-keycloak-operator/src/main/helm/values.yaml similarity index 100% rename from src/main/helm/values.yaml rename to ozgcloud-keycloak-operator/src/main/helm/values.yaml diff --git a/src/main/java/de/ozgcloud/operator/Config.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/Config.java similarity index 100% rename from src/main/java/de/ozgcloud/operator/Config.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/Config.java diff --git a/src/main/java/de/ozgcloud/operator/OzgOperatorApplication.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/KeycloakOperatorApplication.java similarity index 91% rename from src/main/java/de/ozgcloud/operator/OzgOperatorApplication.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/KeycloakOperatorApplication.java index 454595caa34862daf2388138d8f290ff6bb8ea76..27c3b688cde8c4fcdb5cfe23c058ffc9c2a5f192 100644 --- a/src/main/java/de/ozgcloud/operator/OzgOperatorApplication.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/KeycloakOperatorApplication.java @@ -27,9 +27,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication -public class OzgOperatorApplication { +public class KeycloakOperatorApplication { public static void main(String[] args) { - SpringApplication.run(OzgOperatorApplication.class, args); + SpringApplication.run(KeycloakOperatorApplication.class, args); } } diff --git a/src/main/java/de/ozgcloud/operator/SpringNativeConfiguration.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/SpringNativeConfiguration.java similarity index 95% rename from src/main/java/de/ozgcloud/operator/SpringNativeConfiguration.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/SpringNativeConfiguration.java index 60899d6ae6d55ebbb760f7bfd54f897f56d2df2d..7209006b8ae74602b72a9a1ef450c6191da8484e 100644 --- a/src/main/java/de/ozgcloud/operator/SpringNativeConfiguration.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/SpringNativeConfiguration.java @@ -36,10 +36,10 @@ import org.springframework.aot.hint.RuntimeHintsRegistrar; import io.fabric8.kubernetes.api.model.KubernetesResource; import io.fabric8.kubernetes.api.model.NamedCluster; -import lombok.extern.slf4j.Slf4j; +import lombok.extern.log4j.Log4j2; -@Slf4j -public class SpringNativeConfiguration { +@Log4j2 +class SpringNativeConfiguration { static class KuberenetesCLientImplHints implements RuntimeHintsRegistrar { @@ -96,9 +96,7 @@ public class SpringNativeConfiguration { } private void register(RuntimeHints hints, Class<?> clazz) { - if (log.isDebugEnabled()) { - log.debug("trying to register " + clazz.getName() + " for reflection"); - } + LOG.debug("trying to register {} for reflection.", clazz.getName()); hints.reflection().registerType(clazz, MemberCategory.values()); } } diff --git a/src/main/java/de/ozgcloud/operator/keycloak/KeycloakClient.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/KeycloakClient.java similarity index 86% rename from src/main/java/de/ozgcloud/operator/keycloak/KeycloakClient.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/KeycloakClient.java index 8a92e32ac943a5d05850ff5d0d38a5358c7a0e96..28bba540623182bdf36b18f026d2c0d83eecb6b8 100644 --- a/src/main/java/de/ozgcloud/operator/keycloak/KeycloakClient.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/KeycloakClient.java @@ -27,7 +27,6 @@ import java.util.Base64; import org.keycloak.admin.client.Keycloak; import org.keycloak.admin.client.KeycloakBuilder; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; @@ -35,21 +34,30 @@ import org.springframework.context.annotation.Scope; import io.fabric8.kubernetes.api.model.Secret; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.dsl.Resource; +import lombok.RequiredArgsConstructor; +@RequiredArgsConstructor @Configuration public class KeycloakClient { - @Autowired - private KubernetesClient kubernetesClient; + private final KubernetesClient kubernetesClient; @Bean @Scope("singleton") Keycloak createKeycloak() { + setProperties(); + return buildKeycloak(); + } + + private void setProperties() { System.setProperty("com.sun.xml.ws.transport.http.client.HttpTransportPipe.dump", "true"); System.setProperty("com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump", "true"); System.setProperty("com.sun.xml.ws.transport.http.HttpAdapter.dump", "true"); System.setProperty("com.sun.xml.internal.ws.transport.http.HttpAdapter.dump", "true"); System.setProperty("com.sun.xml.internal.ws.transport.http.HttpAdapter.dumpTreshold", "999999"); + } + + private Keycloak buildKeycloak() { return KeycloakBuilder.builder() .serverUrl("http://keycloak-keycloakx-http.keycloak") .realm("master") @@ -59,20 +67,20 @@ public class KeycloakClient { .build(); } - String getKeycloakAdminPassword() { + private String getKeycloakAdminPassword() { return decodeBase64(getKeycloakRealmAdminSecret() .get() .getData() .get("password")); } - Resource<Secret> getKeycloakRealmAdminSecret() { + private Resource<Secret> getKeycloakRealmAdminSecret() { return kubernetesClient.secrets() .inNamespace("keycloak") .withName("keycloak-admin-secret"); } - String decodeBase64(String base64String) { + private String decodeBase64(String base64String) { return new String(Base64.getDecoder().decode(base64String)); } } diff --git a/src/main/java/de/ozgcloud/operator/keycloak/KeycloakException.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/KeycloakException.java similarity index 100% rename from src/main/java/de/ozgcloud/operator/keycloak/KeycloakException.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/KeycloakException.java diff --git a/src/main/java/de/ozgcloud/operator/keycloak/KeycloakGenericRemoteService.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/KeycloakGenericRemoteService.java similarity index 54% rename from src/main/java/de/ozgcloud/operator/keycloak/KeycloakGenericRemoteService.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/KeycloakGenericRemoteService.java index fd1e5ad4a2800edc7a50a8adad0a44976249cba5..53e57ffed8c03c1406bc0ecdd4c877b35873aed9 100644 --- a/src/main/java/de/ozgcloud/operator/keycloak/KeycloakGenericRemoteService.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/KeycloakGenericRemoteService.java @@ -1,47 +1,21 @@ package de.ozgcloud.operator.keycloak; -/* - * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ import java.util.Objects; import java.util.Optional; -import java.util.logging.Level; import org.keycloak.admin.client.Keycloak; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RoleRepresentation; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import lombok.extern.java.Log; +import lombok.RequiredArgsConstructor; +@RequiredArgsConstructor @Component -@Log public class KeycloakGenericRemoteService { - @Autowired - private Keycloak keycloak; + private final Keycloak keycloak; public Optional<ClientRepresentation> getByClientId(String clientId, String realm) { return keycloak.realm(realm).clients().findAll().stream() @@ -61,7 +35,6 @@ public class KeycloakGenericRemoteService { } public Optional<RoleRepresentation> getClientRole(String roleName, String realClientId, String realm) { - log.log(Level.INFO, "Get role " + roleName + " from client with ID " + realClientId + " in realm " + realm); return Optional.ofNullable(keycloak.realm(realm).clients().get(realClientId)) .orElseThrow(() -> new KeycloakException("Client with ID " + realClientId + " for realm " + realm + " not found.")) .roles() diff --git a/src/main/java/de/ozgcloud/operator/keycloak/KeycloakResultParser.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/KeycloakResultParser.java similarity index 92% rename from src/main/java/de/ozgcloud/operator/keycloak/KeycloakResultParser.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/KeycloakResultParser.java index 70320488270f14ee88f82c14168a29561d0b576c..28e044c19a17e877843f687c1bdcfd2a6e212f71 100644 --- a/src/main/java/de/ozgcloud/operator/keycloak/KeycloakResultParser.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/KeycloakResultParser.java @@ -26,15 +26,14 @@ package de.ozgcloud.operator.keycloak; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; -import java.util.logging.Level; import javax.ws.rs.core.Response; import lombok.AccessLevel; import lombok.NoArgsConstructor; -import lombok.extern.java.Log; +import lombok.extern.log4j.Log4j2; -@Log +@Log4j2 @NoArgsConstructor(access = AccessLevel.PRIVATE) public class KeycloakResultParser { @@ -43,7 +42,7 @@ public class KeycloakResultParser { String body = null; if (response.hasEntity() && response.getEntity() instanceof InputStream inputStream) { body = new String(inputStream.readNBytes(256), StandardCharsets.UTF_8); - log.info("Entity: " + body); + LOG.info("Entity: {}.", body); } if (!response.getStatusInfo().equals(Response.Status.CREATED)) { Response.StatusType statusInfo = response.getStatusInfo(); @@ -52,7 +51,7 @@ public class KeycloakResultParser { "expected status: Created (201) with Body: " + body); } } catch (IOException e) { - log.log(Level.SEVERE, "Could not parse Keycloak Response", e); + LOG.error("Could not parse Keycloak Response", e); } } } diff --git a/src/main/java/de/ozgcloud/operator/keycloak/OzgCloudCustomResourceStatus.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/OzgCloudCustomResourceStatus.java similarity index 100% rename from src/main/java/de/ozgcloud/operator/keycloak/OzgCloudCustomResourceStatus.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/OzgCloudCustomResourceStatus.java diff --git a/src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientMapper.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientMapper.java similarity index 100% rename from src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientMapper.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientMapper.java diff --git a/src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientPreconditionService.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientPreconditionService.java similarity index 86% rename from src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientPreconditionService.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientPreconditionService.java index 93b4130f0c71831f12c882667e5038c1aebc34e3..7491e77768576a3fb8a39defccc618bc2b91ed68 100644 --- a/src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientPreconditionService.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientPreconditionService.java @@ -25,18 +25,18 @@ package de.ozgcloud.operator.keycloak.client; import java.util.Optional; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import de.ozgcloud.operator.keycloak.KeycloakGenericRemoteService; +import lombok.RequiredArgsConstructor; +@RequiredArgsConstructor @Component class KeycloakClientPreconditionService { - @Autowired - private KeycloakGenericRemoteService keycloakGenericRemoteService; + private final KeycloakGenericRemoteService keycloakGenericRemoteService; - Optional<String> getReconcilePreconditionErrors(OzgCloudKeycloakClient resource) { + public Optional<String> getReconcilePreconditionErrors(OzgCloudKeycloakClient resource) { String namespace = resource.getMetadata().getNamespace(); diff --git a/src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientReconciler.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientReconciler.java similarity index 80% rename from src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientReconciler.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientReconciler.java index 11f4f71bddbd9bbb8f8b9c181a861dd6bd85a75b..a4c174f7147e0abd32007d20e16662f8e337fac5 100644 --- a/src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientReconciler.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientReconciler.java @@ -24,9 +24,7 @@ package de.ozgcloud.operator.keycloak.client; import java.util.Optional; -import java.util.logging.Level; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import de.ozgcloud.operator.Config; @@ -35,26 +33,24 @@ import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.Reconciler; import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; -import lombok.extern.java.Log; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +@RequiredArgsConstructor @ControllerConfiguration @Component -@Log +@Log4j2 public class KeycloakClientReconciler implements Reconciler<OzgCloudKeycloakClient> { - @Autowired - private KeycloakClientService service; + private final KeycloakClientService service; - @Autowired - private KeycloakClientPreconditionService preconditionService; + private final KeycloakClientPreconditionService preconditionService; @Override public UpdateControl<OzgCloudKeycloakClient> reconcile(OzgCloudKeycloakClient resource, Context<OzgCloudKeycloakClient> context) { try { - String crdName = resource.getMetadata().getName(); - - log.info("Reconcile KeycloakClient " + crdName); + LOG.info("{} reconciling...", resource.getMetadata().getName()); Optional<String> preconditionError = preconditionService.getReconcilePreconditionErrors(resource); if (preconditionError.isPresent()) { @@ -65,10 +61,7 @@ public class KeycloakClientReconciler implements Reconciler<OzgCloudKeycloakClie return buildStatusOk(resource); } catch (Exception e) { - log.log(Level.SEVERE, - "Could not reconcile client " + resource.getMetadata().getName() + " in namespace " + resource.getMetadata().getNamespace() + ": " - + e.getMessage(), - e); + LOG.error(resource.getMetadata().getName() + " could not reconcile in namespace " + resource.getMetadata().getNamespace(), e); resource.setStatus(OzgCloudKeycloakClientStatus.builder().status(OzgCloudCustomResourceStatus.ERROR).message(e.getMessage()).build()); return UpdateControl.updateStatus(resource).rescheduleAfter(Config.RECONCILER_RETRY_SECONDS); } @@ -80,9 +73,8 @@ public class KeycloakClientReconciler implements Reconciler<OzgCloudKeycloakClie } private UpdateControl<OzgCloudKeycloakClient> buildStatusInProgress(OzgCloudKeycloakClient resource, String errorMessage) { - log.log(Level.INFO, - "Could not yet reconcile client " + resource.getMetadata().getName() + " in namespace " + resource.getMetadata().getNamespace() + ":" - + errorMessage); + LOG.info("{} could not yet reconcile in namespace {}: {}", resource.getMetadata().getName(), resource.getMetadata().getNamespace(), + errorMessage); resource.setStatus(OzgCloudKeycloakClientStatus.builder().status(OzgCloudCustomResourceStatus.IN_PROGRESS).message(errorMessage).build()); return UpdateControl.updateStatus(resource).rescheduleAfter(Config.RECONCILER_RETRY_SECONDS); } diff --git a/src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientRemoteService.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientRemoteService.java similarity index 89% rename from src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientRemoteService.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientRemoteService.java index 7c3d4c1bf7c6748c85a0df583d46c60faae9356d..c5e0e4f65d07e4dcf89e124ff1198569c861a786 100644 --- a/src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientRemoteService.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientRemoteService.java @@ -23,33 +23,31 @@ */ package de.ozgcloud.operator.keycloak.client; -import java.util.logging.Level; - import org.keycloak.admin.client.CreatedResponseUtil; import org.keycloak.admin.client.Keycloak; import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.RoleRepresentation; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import de.ozgcloud.operator.keycloak.KeycloakResultParser; -import lombok.extern.java.Log; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +@Log4j2 +@RequiredArgsConstructor @Component -@Log class KeycloakClientRemoteService { - @Autowired - private Keycloak keycloak; + private final Keycloak keycloak; public void updateClient(ClientRepresentation client, String realm) { getClientResource(realm, client.getId()).update(client); } public String createClient(ClientRepresentation client, String realm) { - log.log(Level.FINE, "Creating client {0} in realm {1}", new String[] { client.getId(), realm }); + LOG.debug("Creating client {} in realm {}", client.getId(), realm); var response = getRealm(realm).clients().create(client); KeycloakResultParser.parseCreatedResponse(response); return CreatedResponseUtil.getCreatedId(response); diff --git a/src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientService.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientService.java similarity index 90% rename from src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientService.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientService.java index 90e9671841a6e00f29ebc63ff904e8a0c9cb1956..3182648ff9b5e59ac3176bc87b27389a1e51125d 100644 --- a/src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientService.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientService.java @@ -26,25 +26,22 @@ package de.ozgcloud.operator.keycloak.client; import java.util.List; import org.keycloak.representations.idm.ClientRepresentation; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import de.ozgcloud.operator.keycloak.KeycloakGenericRemoteService; +import lombok.RequiredArgsConstructor; +@RequiredArgsConstructor @Component class KeycloakClientService { - @Autowired - private KeycloakClientRemoteService remoteService; + private final KeycloakClientRemoteService remoteService; - @Autowired - private KeycloakGenericRemoteService genericRemoteService; + private final KeycloakGenericRemoteService genericRemoteService; - @Autowired - private ProtocolMapperRepresentationHelper mapperRepresentationBuilder; + private final ProtocolMapperRepresentationHelper mapperRepresentationBuilder; - @Autowired - private KeycloakClientMapper mapper; + private final KeycloakClientMapper mapper; void createOrUpdateClient(OzgCloudKeycloakClientSpec spec, String namespace) { genericRemoteService.getByClientId(spec.getClientId(), namespace) diff --git a/src/main/java/de/ozgcloud/operator/keycloak/client/OzgCloudKeycloakClient.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/client/OzgCloudKeycloakClient.java similarity index 100% rename from src/main/java/de/ozgcloud/operator/keycloak/client/OzgCloudKeycloakClient.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/client/OzgCloudKeycloakClient.java diff --git a/src/main/java/de/ozgcloud/operator/keycloak/client/OzgCloudKeycloakClientSpec.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/client/OzgCloudKeycloakClientSpec.java similarity index 100% rename from src/main/java/de/ozgcloud/operator/keycloak/client/OzgCloudKeycloakClientSpec.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/client/OzgCloudKeycloakClientSpec.java diff --git a/src/main/java/de/ozgcloud/operator/keycloak/client/OzgCloudKeycloakClientStatus.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/client/OzgCloudKeycloakClientStatus.java similarity index 100% rename from src/main/java/de/ozgcloud/operator/keycloak/client/OzgCloudKeycloakClientStatus.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/client/OzgCloudKeycloakClientStatus.java diff --git a/src/main/java/de/ozgcloud/operator/keycloak/client/ProtocolMapperRepresentationHelper.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/client/ProtocolMapperRepresentationHelper.java similarity index 94% rename from src/main/java/de/ozgcloud/operator/keycloak/client/ProtocolMapperRepresentationHelper.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/client/ProtocolMapperRepresentationHelper.java index 402c3eee8f6957cdecaaab0192dd6b172ff45626..2c38785d4c3f8e3d7dde8fcf6f1e9617f11cefb1 100644 --- a/src/main/java/de/ozgcloud/operator/keycloak/client/ProtocolMapperRepresentationHelper.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/client/ProtocolMapperRepresentationHelper.java @@ -2,15 +2,14 @@ package de.ozgcloud.operator.keycloak.client; import java.util.HashMap; import java.util.Map; -import java.util.logging.Level; import org.apache.commons.lang3.StringUtils; import org.keycloak.representations.idm.ProtocolMapperRepresentation; import org.springframework.stereotype.Component; -import lombok.extern.java.Log; +import lombok.extern.log4j.Log4j2; -@Log +@Log4j2 @Component class ProtocolMapperRepresentationHelper { @@ -37,7 +36,7 @@ class ProtocolMapperRepresentationHelper { static final String CLIENT_ROLES_MAPPER_NAME = "client roles"; public ProtocolMapperRepresentation createOrganisationsEinheitIdMapper() { - log.log(Level.FINE, "Create createOrganisationsEinheitIdMapper..."); + LOG.debug("Create createOrganisationsEinheitIdMapper..."); return createUserAttributeMapper(ORGANISATIONS_EINHEIT_ID_MAPPER_NAME, buildOrganisationsEinheitIdMapperConfig()); } @@ -55,7 +54,7 @@ class ProtocolMapperRepresentationHelper { } public ProtocolMapperRepresentation createOzgCloudUserIdMapper() { - log.log(Level.FINE, "Create createOzgCloudUserIdMapper..."); + LOG.debug("Create createOzgCloudUserIdMapper..."); return createUserAttributeMapper(OZGCLOUD_USER_ID_MAPPER_NAME, buildOzgCloudUserIdMapperConfig()); } @@ -73,7 +72,7 @@ class ProtocolMapperRepresentationHelper { } public ProtocolMapperRepresentation createOrganisationeEinheitIdLdapMapper() { - log.log(Level.FINE, "Create createOrganisationeEinheitIdLdapMapper..."); + LOG.debug("Create createOrganisationeEinheitIdLdapMapper..."); return createUserAttributeMapper(ORGANISATIONS_EINHEIT_ID_LDAP_MAPPER_NAME, buildOrganisationsEinheitIdLdapMapperConfig()); } @@ -101,7 +100,7 @@ class ProtocolMapperRepresentationHelper { } public ProtocolMapperRepresentation createClientRolesMapper() { - log.log(Level.FINE, "Create createClientRolesMapper..."); + LOG.debug("Create createOrganisationeEinheitIdLdapMapper..."); return createUserClientRoleMapper(CLIENT_ROLES_MAPPER_NAME, buildClientRolesMapperConfig()); } diff --git a/src/main/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupMapper.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupMapper.java similarity index 89% rename from src/main/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupMapper.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupMapper.java index 519f6617a41e3b3a206e37fc3331958579c69adf..7c7b782fd244c677b1a37d4bc0d1848225f74d6a 100644 --- a/src/main/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupMapper.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupMapper.java @@ -38,10 +38,11 @@ import org.mapstruct.ReportingPolicy; interface KeycloakGroupMapper { @Mapping(target = "attributes", source = "attributes", qualifiedByName = "mapAttributes") - GroupRepresentation map(OzgCloudKeycloakGroupSpec group); + public GroupRepresentation map(OzgCloudKeycloakGroupSpec group); @Named("mapAttributes") default Map<String, List<String>> mapAttributes(List<OzgCloudKeycloakGroupSpec.Attribute> attributes) { - return attributes.stream().collect(Collectors.toMap(OzgCloudKeycloakGroupSpec.Attribute::getName, attribute -> List.of(attribute.getValue()))); + return attributes.stream() + .collect(Collectors.toMap(OzgCloudKeycloakGroupSpec.Attribute::getName, attribute -> List.of(attribute.getValue()))); } } diff --git a/src/main/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupPreconditionService.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupPreconditionService.java similarity index 82% rename from src/main/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupPreconditionService.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupPreconditionService.java index 0f6571bf732fc8156724b2b52c980f4f18d5be05..56477e69bc54f2f90af087697dad20658a913db6 100644 --- a/src/main/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupPreconditionService.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupPreconditionService.java @@ -25,20 +25,19 @@ package de.ozgcloud.operator.keycloak.group; import java.util.Optional; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import de.ozgcloud.operator.keycloak.KeycloakGenericRemoteService; +import lombok.RequiredArgsConstructor; +@RequiredArgsConstructor @Component class KeycloakGroupPreconditionService { - @Autowired - private KeycloakGenericRemoteService keycloakGenericRemoteService; + private final KeycloakGenericRemoteService keycloakGenericRemoteService; - Optional<String> getReconcilePreconditionErrors(OzgCloudKeycloakGroup resource) { - - String namespace = resource.getMetadata().getNamespace(); + public Optional<String> getReconcilePreconditionErrors(OzgCloudKeycloakGroup resource) { + var namespace = resource.getMetadata().getNamespace(); if (!keycloakGenericRemoteService.realmExists(namespace)) { return Optional.of("Realm " + namespace + " does not yet exist"); @@ -46,4 +45,4 @@ class KeycloakGroupPreconditionService { return Optional.empty(); } -} +} \ No newline at end of file diff --git a/src/main/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupReconciler.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupReconciler.java similarity index 74% rename from src/main/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupReconciler.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupReconciler.java index 7388466efab7780dab2f49aeec1d8ba70af92f47..3eea1b1ec467aa1e041e906709f1a31e6bd1bb29 100644 --- a/src/main/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupReconciler.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupReconciler.java @@ -24,9 +24,7 @@ package de.ozgcloud.operator.keycloak.group; import java.util.Optional; -import java.util.logging.Level; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import de.ozgcloud.operator.Config; @@ -35,26 +33,23 @@ import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.Reconciler; import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; -import lombok.extern.java.Log; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +@Log4j2 +@RequiredArgsConstructor @ControllerConfiguration @Component -@Log public class KeycloakGroupReconciler implements Reconciler<OzgCloudKeycloakGroup> { - @Autowired - private KeycloakGroupService service; + private final KeycloakGroupService service; - @Autowired - private KeycloakGroupPreconditionService preconditionService; + private final KeycloakGroupPreconditionService preconditionService; @Override public UpdateControl<OzgCloudKeycloakGroup> reconcile(OzgCloudKeycloakGroup resource, Context<OzgCloudKeycloakGroup> context) { - try { - String crdName = resource.getMetadata().getName(); - - log.info("Reconcile KeycloakGroup " + crdName); + LOG.info("{} reconiling..", resource.getMetadata().getName()); Optional<String> preconditionError = preconditionService.getReconcilePreconditionErrors(resource); if (preconditionError.isPresent()) { @@ -65,24 +60,20 @@ public class KeycloakGroupReconciler implements Reconciler<OzgCloudKeycloakGroup return buildStatusOk(resource); } catch (Exception e) { - log.log(Level.SEVERE, - "Could not reconcile group " + resource.getMetadata().getName() + " in namespace " + resource.getMetadata().getNamespace() + ": " - + e.getMessage(), - e); + LOG.warn(resource.getMetadata().getName() + " could not reconcile in namespace " + resource.getMetadata().getNamespace(), e); resource.setStatus(OzgCloudKeycloakGroupStatus.builder().status(OzgCloudCustomResourceStatus.ERROR).message(e.getMessage()).build()); return UpdateControl.updateStatus(resource).rescheduleAfter(Config.RECONCILER_RETRY_SECONDS); } } - UpdateControl<OzgCloudKeycloakGroup> buildStatusOk(OzgCloudKeycloakGroup resource) { + private UpdateControl<OzgCloudKeycloakGroup> buildStatusOk(OzgCloudKeycloakGroup resource) { resource.setStatus(OzgCloudKeycloakGroupStatus.builder().status(OzgCloudCustomResourceStatus.OK).message(null).build()); return UpdateControl.updateStatus(resource); } - UpdateControl<OzgCloudKeycloakGroup> buildStatusInProgress(OzgCloudKeycloakGroup resource, String errorMessage) { - log.log(Level.INFO, - "Could not yet reconcile group " + resource.getMetadata().getName() + " in namespace " + resource.getMetadata().getNamespace() + ": " - + errorMessage); + private UpdateControl<OzgCloudKeycloakGroup> buildStatusInProgress(OzgCloudKeycloakGroup resource, String errorMessage) { + LOG.warn("{} could not yet reconcile group in namespace {}: {}", resource.getMetadata().getName(), resource.getMetadata().getNamespace(), + errorMessage); resource.setStatus(OzgCloudKeycloakGroupStatus.builder().status(OzgCloudCustomResourceStatus.IN_PROGRESS).message(errorMessage).build()); return UpdateControl.updateStatus(resource).rescheduleAfter(Config.RECONCILER_RETRY_SECONDS); } diff --git a/src/main/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupRemoteService.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupRemoteService.java similarity index 74% rename from src/main/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupRemoteService.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupRemoteService.java index af4c33a57d55aebff5a65159c4d9fbe88d2d4b91..353727e163a313c7b9165f9efac0ea96f5f6faa8 100644 --- a/src/main/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupRemoteService.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupRemoteService.java @@ -25,34 +25,31 @@ package de.ozgcloud.operator.keycloak.group; import java.util.Objects; import java.util.Optional; -import java.util.logging.Level; - -import javax.ws.rs.core.Response; import org.keycloak.admin.client.Keycloak; import org.keycloak.representations.idm.GroupRepresentation; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import de.ozgcloud.operator.keycloak.KeycloakResultParser; -import lombok.extern.java.Log; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +@Log4j2 +@RequiredArgsConstructor @Component -@Log class KeycloakGroupRemoteService { - @Autowired - private Keycloak keycloak; + private final Keycloak keycloak; - Optional<GroupRepresentation> getGroupByName(String groupName, String realm) { + public Optional<GroupRepresentation> getGroupByName(String groupName, String realm) { return keycloak.realm(realm).groups().groups() .stream().filter(group -> Objects.equals(groupName, group.getName())) .findFirst(); } - void createGroup(GroupRepresentation group, String realm) { - log.log(Level.FINE, "Creating group {0} in realm {1}", new Object[] { group.getName(), realm }); - Response response = keycloak.realm(realm).groups().add(group); + public void createGroup(GroupRepresentation group, String realm) { + LOG.debug("Creating group {} in realm {}", group.getName(), realm); + var response = keycloak.realm(realm).groups().add(group); KeycloakResultParser.parseCreatedResponse(response); } } diff --git a/src/main/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupService.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupService.java similarity index 84% rename from src/main/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupService.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupService.java index 4eaa2eddbee5b0543f7112a953e0ce6979fe86b3..e8783ffe8c0ebeebf3b215f9edabe73f22d6b86d 100644 --- a/src/main/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupService.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupService.java @@ -25,19 +25,19 @@ package de.ozgcloud.operator.keycloak.group; import java.util.Optional; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor @Component class KeycloakGroupService { - @Autowired - private KeycloakGroupRemoteService remoteService; + private final KeycloakGroupRemoteService remoteService; - @Autowired - private KeycloakGroupMapper mapper; + private final KeycloakGroupMapper mapper; - void createGroup(OzgCloudKeycloakGroupSpec group, String realm) { + public void createGroup(OzgCloudKeycloakGroupSpec group, String realm) { Optional.of(group) .map(mapper::map) .filter(groupRepresentation -> remoteService.getGroupByName(group.getName(), realm).isEmpty()) diff --git a/src/main/java/de/ozgcloud/operator/keycloak/group/OzgCloudKeycloakGroup.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/group/OzgCloudKeycloakGroup.java similarity index 100% rename from src/main/java/de/ozgcloud/operator/keycloak/group/OzgCloudKeycloakGroup.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/group/OzgCloudKeycloakGroup.java diff --git a/src/main/java/de/ozgcloud/operator/keycloak/group/OzgCloudKeycloakGroupSpec.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/group/OzgCloudKeycloakGroupSpec.java similarity index 100% rename from src/main/java/de/ozgcloud/operator/keycloak/group/OzgCloudKeycloakGroupSpec.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/group/OzgCloudKeycloakGroupSpec.java diff --git a/src/main/java/de/ozgcloud/operator/keycloak/group/OzgCloudKeycloakGroupStatus.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/group/OzgCloudKeycloakGroupStatus.java similarity index 100% rename from src/main/java/de/ozgcloud/operator/keycloak/group/OzgCloudKeycloakGroupStatus.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/group/OzgCloudKeycloakGroupStatus.java diff --git a/src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmMapper.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmMapper.java similarity index 96% rename from src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmMapper.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmMapper.java index 8b5e7bebf74cfe94492a642ad13b98bb22f31bdd..5af876447d55a10bf33180920c1e9d6a9a79fa2a 100644 --- a/src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmMapper.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmMapper.java @@ -41,7 +41,7 @@ interface KeycloakRealmMapper { @Mapping(target = "defaultLocale", constant = "de") @Mapping(target = "internationalizationEnabled", constant = "true") @Mapping(target = "passwordPolicy", constant = "upperCase(1) and lowerCase(1) and length(8) and notUsername") - RealmRepresentation map(OzgCloudKeycloakRealmSpec realm); + public RealmRepresentation map(OzgCloudKeycloakRealmSpec realm); @Named("supportedLocales") default Set<String> mapPassword(OzgCloudKeycloakRealmSpec spec) { diff --git a/src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmReconciler.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmReconciler.java similarity index 76% rename from src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmReconciler.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmReconciler.java index 4b62b2b7b131a179f96330657e862b6e6aecf690..dc3e333e5b42269e4cfe2ec8a00418b676ef595b 100644 --- a/src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmReconciler.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmReconciler.java @@ -23,9 +23,6 @@ */ package de.ozgcloud.operator.keycloak.realm; -import java.util.logging.Level; - -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import de.ozgcloud.operator.Config; @@ -36,23 +33,22 @@ import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.DeleteControl; import io.javaoperatorsdk.operator.api.reconciler.Reconciler; import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; -import lombok.extern.java.Log; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +@Log4j2 +@RequiredArgsConstructor @ControllerConfiguration @Component -@Log public class KeycloakRealmReconciler implements Reconciler<OzgCloudKeycloakRealm>, Cleaner<OzgCloudKeycloakRealm> { - @Autowired - private KeycloakRealmService service; + private final KeycloakRealmService service; @Override public UpdateControl<OzgCloudKeycloakRealm> reconcile(OzgCloudKeycloakRealm resource, Context<OzgCloudKeycloakRealm> context) { - log.info("Realm reconciler reconcile, keep after delete is set to: " + resource.getSpec().isKeepAfterDelete()); + LOG.info("{} reconciling...", resource.getMetadata().getName()); try { - String realmName = resource.getMetadata().getNamespace(); - - log.info("Reconcile KeycloakRealm " + realmName + " (crd name " + resource.getMetadata().getName() + ")"); + var realmName = resource.getMetadata().getNamespace(); service.createRealm(resource.getSpec(), realmName); @@ -60,10 +56,7 @@ public class KeycloakRealmReconciler implements Reconciler<OzgCloudKeycloakRealm return UpdateControl.updateStatus(resource); } catch (Exception e) { - log.log(Level.SEVERE, - "Could not reconcile realm " + resource.getMetadata().getName() + " in namespace " + resource.getMetadata().getNamespace() + ": " - + e.getMessage(), - e); + LOG.warn(resource.getMetadata().getName() + " could not reconcile in namespace " + resource.getMetadata().getNamespace(), e); resource.setStatus(OzgCloudKeycloakRealmStatus.builder().status(OzgCloudCustomResourceStatus.ERROR).message(e.getMessage()).build()); return UpdateControl.updateStatus(resource).rescheduleAfter(Config.RECONCILER_RETRY_SECONDS); } @@ -71,8 +64,9 @@ public class KeycloakRealmReconciler implements Reconciler<OzgCloudKeycloakRealm @Override public DeleteControl cleanup(OzgCloudKeycloakRealm realm, Context<OzgCloudKeycloakRealm> context) { - log.info("Realm reconciler cleanup, keep after delete is set to: " + realm.getSpec().isKeepAfterDelete()); + LOG.info("{} cleanup...", realm.getMetadata().getName()); if (realm.getSpec().isKeepAfterDelete()) { + LOG.info("keep data"); return DeleteControl.defaultDelete(); } return cleanup(realm); @@ -80,12 +74,12 @@ public class KeycloakRealmReconciler implements Reconciler<OzgCloudKeycloakRealm DeleteControl cleanup(OzgCloudKeycloakRealm realm) { var realmName = realm.getMetadata().getNamespace(); - log.info("Deleting KeycloakRealm " + realmName); + LOG.info("{} do cleanup...", realmName); try { service.deleteRealm(realmName); return DeleteControl.defaultDelete(); } catch (Exception e) { - log.log(Level.SEVERE, "Could not delete KeycloakRealm " + realmName, e); + LOG.warn(realmName + " could not delete.", e); return DeleteControl.defaultDelete(); } } diff --git a/src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmRemoteService.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmRemoteService.java similarity index 86% rename from src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmRemoteService.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmRemoteService.java index 97801f718bc4915bb5a61dee50eb55533f85f1e8..124f1d5dc65bcf9a39d05c279e8d8f4ac5b48150 100644 --- a/src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmRemoteService.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmRemoteService.java @@ -25,20 +25,21 @@ package de.ozgcloud.operator.keycloak.realm; import org.keycloak.admin.client.Keycloak; import org.keycloak.representations.idm.RealmRepresentation; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor @Component class KeycloakRealmRemoteService { - @Autowired - Keycloak keycloak; + private final Keycloak keycloak; - void createRealm(RealmRepresentation realm) { + public void createRealm(RealmRepresentation realm) { keycloak.realms().create(realm); } - void deleteRealm(String realmName) { + public void deleteRealm(String realmName) { keycloak.realm(realmName).remove(); } } diff --git a/src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmService.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmService.java similarity index 82% rename from src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmService.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmService.java index 6911ce510d6ff3187f86de6bf94794abb7c3e05e..279a05573ba927a7b84b4f76166785b1fb2641e1 100644 --- a/src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmService.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmService.java @@ -26,24 +26,22 @@ package de.ozgcloud.operator.keycloak.realm; import java.util.Optional; import org.keycloak.representations.idm.RealmRepresentation; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import de.ozgcloud.operator.keycloak.KeycloakGenericRemoteService; +import lombok.RequiredArgsConstructor; +@RequiredArgsConstructor @Component class KeycloakRealmService { - @Autowired - private KeycloakRealmRemoteService remoteService; + private final KeycloakRealmRemoteService remoteService; - @Autowired - private KeycloakRealmMapper mapper; + private final KeycloakRealmMapper mapper; - @Autowired - private KeycloakGenericRemoteService keycloakGenericRemoteService; + private final KeycloakGenericRemoteService keycloakGenericRemoteService; - void createRealm(OzgCloudKeycloakRealmSpec realm, String realmName) { + public void createRealm(OzgCloudKeycloakRealmSpec realm, String realmName) { Optional.of(realm) .map(mapper::map) .map(realmRepresentation -> addRealmName(realmRepresentation, realmName)) @@ -56,7 +54,7 @@ class KeycloakRealmService { return realm; } - void deleteRealm(String realmName) { + public void deleteRealm(String realmName) { remoteService.deleteRealm(realmName); } } diff --git a/src/main/java/de/ozgcloud/operator/keycloak/realm/OzgCloudKeycloakRealm.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/realm/OzgCloudKeycloakRealm.java similarity index 100% rename from src/main/java/de/ozgcloud/operator/keycloak/realm/OzgCloudKeycloakRealm.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/realm/OzgCloudKeycloakRealm.java diff --git a/src/main/java/de/ozgcloud/operator/keycloak/realm/OzgCloudKeycloakRealmSpec.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/realm/OzgCloudKeycloakRealmSpec.java similarity index 100% rename from src/main/java/de/ozgcloud/operator/keycloak/realm/OzgCloudKeycloakRealmSpec.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/realm/OzgCloudKeycloakRealmSpec.java diff --git a/src/main/java/de/ozgcloud/operator/keycloak/realm/OzgCloudKeycloakRealmStatus.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/realm/OzgCloudKeycloakRealmStatus.java similarity index 100% rename from src/main/java/de/ozgcloud/operator/keycloak/realm/OzgCloudKeycloakRealmStatus.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/realm/OzgCloudKeycloakRealmStatus.java diff --git a/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserMapper.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserMapper.java similarity index 100% rename from src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserMapper.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserMapper.java diff --git a/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserPreconditionService.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserPreconditionService.java similarity index 95% rename from src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserPreconditionService.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserPreconditionService.java index 6366465994d50b5f7f97acf82baa6c65e6135ba8..a2a19c68883187f986ebc18e7e1b912a5bd563c4 100644 --- a/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserPreconditionService.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserPreconditionService.java @@ -25,18 +25,18 @@ package de.ozgcloud.operator.keycloak.user; import java.util.Optional; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import de.ozgcloud.operator.keycloak.KeycloakGenericRemoteService; import de.ozgcloud.operator.keycloak.user.OzgCloudKeycloakUserSpec.KeycloakUserSpecClientRole; import de.ozgcloud.operator.keycloak.user.OzgCloudKeycloakUserSpec.KeycloakUserSpecUserGroup; +import lombok.RequiredArgsConstructor; +@RequiredArgsConstructor @Component class KeycloakUserPreconditionService { - @Autowired - private KeycloakGenericRemoteService keycloakGenericRemoteService; + private final KeycloakGenericRemoteService keycloakGenericRemoteService; public Optional<String> getPreconditionErrors(OzgCloudKeycloakUser user) { var namespace = user.getMetadata().getNamespace(); diff --git a/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserReconciler.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserReconciler.java similarity index 72% rename from src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserReconciler.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserReconciler.java index 26aab3f7af098cadf48b91ee7a828a852642543d..6470b4e9c9d664629b23c18b2cbea9694f338cf5 100644 --- a/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserReconciler.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserReconciler.java @@ -23,11 +23,6 @@ */ package de.ozgcloud.operator.keycloak.user; -import java.util.logging.Level; - -import javax.ws.rs.NotFoundException; - -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import de.ozgcloud.operator.Config; @@ -38,32 +33,31 @@ import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.DeleteControl; import io.javaoperatorsdk.operator.api.reconciler.Reconciler; import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; -import lombok.extern.java.Log; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; -@Log +@Log4j2 +@RequiredArgsConstructor @ControllerConfiguration @Component public class KeycloakUserReconciler implements Reconciler<OzgCloudKeycloakUser>, Cleaner<OzgCloudKeycloakUser> { - @Autowired - private KeycloakUserService service; - @Autowired - private KeycloakUserPreconditionService preconditionService; + private final KeycloakUserService service; + private final KeycloakUserPreconditionService preconditionService; @Override public UpdateControl<OzgCloudKeycloakUser> reconcile(OzgCloudKeycloakUser resource, Context<OzgCloudKeycloakUser> context) { var userName = resource.getMetadata().getName(); var namespace = resource.getMetadata().getNamespace(); - log.info(String.format("Reconciling user %s...", userName)); - log.info("User reconciler reconcile, keep after delete is set to: " + resource.getSpec().isKeepAfterDelete()); + LOG.info("{} reconciling...", userName); try { var preconditionError = preconditionService.getPreconditionErrors(resource); if (preconditionError.isPresent()) { var errorMessage = preconditionError.get(); - log.warning(String.format("Could not reconcile user %s in namespace %s: %s", userName, namespace, errorMessage)); + LOG.warn("{} could not reconcile in namespace {}: {}.", userName, namespace, errorMessage); return UserUpdateControlBuilder.fromResource(resource) .withStatus(OzgCloudCustomResourceStatus.IN_PROGRESS) @@ -77,13 +71,11 @@ public class KeycloakUserReconciler implements Reconciler<OzgCloudKeycloakUser>, return UserUpdateControlBuilder.fromResource(resource).withStatus(OzgCloudCustomResourceStatus.OK).build(); } catch (Exception e) { - var errorMessage = e.getMessage(); - - log.log(Level.SEVERE, String.format("Could not reconcile user %s for namespace %s: %s", userName, namespace, errorMessage), e); + LOG.warn(userName + " could not reconcile in namespace " + namespace, e); return UserUpdateControlBuilder.fromResource(resource) .withStatus(OzgCloudCustomResourceStatus.ERROR) - .withMessage(errorMessage) + .withMessage(e.getMessage()) .withReschedule(Config.RECONCILER_RETRY_SECONDS_ON_ERROR) .build(); } @@ -91,8 +83,9 @@ public class KeycloakUserReconciler implements Reconciler<OzgCloudKeycloakUser>, @Override public DeleteControl cleanup(OzgCloudKeycloakUser user, Context<OzgCloudKeycloakUser> context) { - log.info("User reconciler cleanup, keep after delete is set to: " + user.getSpec().isKeepAfterDelete()); + LOG.info("{} cleanup...", user.getMetadata().getName()); if (user.getSpec().isKeepAfterDelete()) { + LOG.info("keep data"); return DeleteControl.defaultDelete(); } return cleanup(user); @@ -101,15 +94,12 @@ public class KeycloakUserReconciler implements Reconciler<OzgCloudKeycloakUser>, DeleteControl cleanup(OzgCloudKeycloakUser user) { var userName = user.getMetadata().getName(); var namespace = user.getMetadata().getNamespace(); - log.info(String.format("Deleting KeycloakUser %s", userName)); + LOG.info("{} do cleanup...", userName); try { service.deleteUser(user.getSpec(), namespace); return DeleteControl.defaultDelete(); - } catch (NotFoundException e) { - log.log(Level.INFO, String.format("Could not delete user %s in namespace %s, user not found.", userName, namespace)); - return DeleteControl.defaultDelete(); } catch (Exception e) { - log.log(Level.SEVERE, String.format("Could not delete user %s in namespace %s", userName, namespace), e); + LOG.warn(userName + " could not delete user in namespace " + namespace, e); return DeleteControl.defaultDelete(); } } diff --git a/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserRemoteService.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserRemoteService.java similarity index 95% rename from src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserRemoteService.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserRemoteService.java index 4e1d2f02fbfb611f550e9682ef5b0dedd7224d66..b38218f6504b5dc1c0d26f17109151f56695f0c5 100644 --- a/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserRemoteService.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserRemoteService.java @@ -32,21 +32,20 @@ import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.UserRepresentation; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import de.ozgcloud.operator.keycloak.KeycloakException; import de.ozgcloud.operator.keycloak.KeycloakGenericRemoteService; import de.ozgcloud.operator.keycloak.KeycloakResultParser; +import lombok.RequiredArgsConstructor; +@RequiredArgsConstructor @Component class KeycloakUserRemoteService { - @Autowired - private Keycloak keycloak; + private final Keycloak keycloak; - @Autowired - private KeycloakGenericRemoteService keycloakGenericRemoteService; + private final KeycloakGenericRemoteService keycloakGenericRemoteService; public void createUser(UserRepresentation user, String namespace) { var realmResource = getRealm(namespace); diff --git a/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserService.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserService.java similarity index 90% rename from src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserService.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserService.java index 13e2cb942453183040d56a1cc07b7d2127d9eae3..c8343f80700a4d031455c212a3e7ce5a6896378f 100644 --- a/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserService.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserService.java @@ -26,20 +26,19 @@ package de.ozgcloud.operator.keycloak.user; import java.util.Optional; import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor @Component class KeycloakUserService { - @Autowired - private KeycloakUserRemoteService remoteService; + private final KeycloakUserRemoteService remoteService; - @Autowired - private UserSecretService userSecretService; + private final UserSecretService userSecretService; - @Autowired - private KeycloakUserMapper userMapper; + private final KeycloakUserMapper userMapper; public void createOrUpdateUser(OzgCloudKeycloakUserSpec userSpec, String namespace) { diff --git a/src/main/java/de/ozgcloud/operator/keycloak/user/KubernetesRemoteService.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/KubernetesRemoteService.java similarity index 63% rename from src/main/java/de/ozgcloud/operator/keycloak/user/KubernetesRemoteService.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/KubernetesRemoteService.java index c865115328e08f8c576fe0337b20a8594fdd4a6f..60d18d2ed869d51469ffc39cd32fe19117b691b2 100644 --- a/src/main/java/de/ozgcloud/operator/keycloak/user/KubernetesRemoteService.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/KubernetesRemoteService.java @@ -1,22 +1,22 @@ package de.ozgcloud.operator.keycloak.user; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import io.fabric8.kubernetes.api.model.Secret; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.dsl.Resource; -import lombok.extern.java.Log; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; -@Log +@RequiredArgsConstructor +@Log4j2 @Component class KubernetesRemoteService { - @Autowired - private KubernetesClient kubernetesClient; + private final KubernetesClient kubernetesClient; public Resource<Secret> getSecret(String namespace, String name) { - log.info(String.format("KubernetesClient: Get %s secret from %s namespace.", name, namespace)); + LOG.info("KubernetesClient: Get {} secret from {} namespace.", name, namespace); return kubernetesClient.secrets().inNamespace(namespace).withName(name); } } diff --git a/src/main/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUser.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUser.java similarity index 100% rename from src/main/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUser.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUser.java diff --git a/src/main/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUserSpec.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUserSpec.java similarity index 100% rename from src/main/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUserSpec.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUserSpec.java diff --git a/src/main/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUserStatus.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUserStatus.java similarity index 100% rename from src/main/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUserStatus.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUserStatus.java diff --git a/src/main/java/de/ozgcloud/operator/keycloak/user/UserNameConverter.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/UserNameConverter.java similarity index 100% rename from src/main/java/de/ozgcloud/operator/keycloak/user/UserNameConverter.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/UserNameConverter.java diff --git a/src/main/java/de/ozgcloud/operator/keycloak/user/UserSecretBuilder.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/UserSecretBuilder.java similarity index 83% rename from src/main/java/de/ozgcloud/operator/keycloak/user/UserSecretBuilder.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/UserSecretBuilder.java index e72f995476d93e663608c0152cbffb56908a3043..070fdbf3b593c966ddf462c0a7c45e48bf10f7ca 100644 --- a/src/main/java/de/ozgcloud/operator/keycloak/user/UserSecretBuilder.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/UserSecretBuilder.java @@ -1,5 +1,7 @@ package de.ozgcloud.operator.keycloak.user; +import java.util.Base64; + import org.apache.commons.lang3.RandomStringUtils; import org.springframework.stereotype.Component; @@ -20,14 +22,18 @@ class UserSecretBuilder { .withType(SECRET_TYPE) .withMetadata(createMetaData(name, namespace)) .addToStringData(SECRET_NAME_FIELD, userSpec.getUsername()) - .addToStringData(SECRET_PASSWORD_FIELD, generatePassword()) + .addToData(SECRET_PASSWORD_FIELD, generatePassword()) .build(); } String generatePassword() { var upperCaseCharacter = RandomStringUtils.randomAlphabetic(1).toUpperCase(); var randomString = RandomStringUtils.randomAlphanumeric(7); - return upperCaseCharacter + randomString; + return encode(upperCaseCharacter + randomString); + } + + String encode(String strValue) { + return new String(Base64.getEncoder().encode(strValue.getBytes())); } private ObjectMeta createMetaData(String name, String namespace) { diff --git a/src/main/java/de/ozgcloud/operator/keycloak/user/UserSecretReader.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/UserSecretReader.java similarity index 96% rename from src/main/java/de/ozgcloud/operator/keycloak/user/UserSecretReader.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/UserSecretReader.java index f82e19b6e5b3d9854da2f5322cbb1f4d70c1a49e..8e314c3f1cd83b67809c13d59997c64572012b96 100644 --- a/src/main/java/de/ozgcloud/operator/keycloak/user/UserSecretReader.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/UserSecretReader.java @@ -43,7 +43,7 @@ class UserSecretReader { try { return new String(Base64.decode(encodedPassword)); } catch (IOException e) { - throw new RuntimeException("Could not decode content from secret (base64) for secret " + secret.getFullResourceName()); + throw new RuntimeException("Could not decode content from secret (base64) for secret " + secret.getFullResourceName(), e); } } } diff --git a/src/main/java/de/ozgcloud/operator/keycloak/user/UserSecretService.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/UserSecretService.java similarity index 82% rename from src/main/java/de/ozgcloud/operator/keycloak/user/UserSecretService.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/UserSecretService.java index 23bab6cdf8a741a590a5a17c3485922046efeacf..a82bc2f9dfe1d7d3203ea815a288acb154b2854d 100644 --- a/src/main/java/de/ozgcloud/operator/keycloak/user/UserSecretService.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/UserSecretService.java @@ -2,24 +2,21 @@ package de.ozgcloud.operator.keycloak.user; import java.util.Optional; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import io.fabric8.kubernetes.api.model.Secret; import io.fabric8.kubernetes.client.dsl.Resource; import io.fabric8.kubernetes.client.extension.ResourceAdapter; +import lombok.RequiredArgsConstructor; +@RequiredArgsConstructor @Component class UserSecretService { - @Autowired - private UserNameConverter userNameConverter; - @Autowired - private UserSecretBuilder secretBuilder; - @Autowired - private UserSecretReader secretReader; - @Autowired - private KubernetesRemoteService kubernetesRemoteService; + private final UserNameConverter userNameConverter; + private final UserSecretBuilder secretBuilder; + private final UserSecretReader secretReader; + private final KubernetesRemoteService kubernetesRemoteService; public Secret create(OzgCloudKeycloakUserSpec userSpec, String namespace) { var secretName = userNameConverter.toSecretName(userSpec.getKeycloakUser()); diff --git a/src/main/java/de/ozgcloud/operator/keycloak/user/UserUpdateControlBuilder.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/UserUpdateControlBuilder.java similarity index 100% rename from src/main/java/de/ozgcloud/operator/keycloak/user/UserUpdateControlBuilder.java rename to ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/UserUpdateControlBuilder.java diff --git a/src/main/resources/application.yml b/ozgcloud-keycloak-operator/src/main/resources/application.yml similarity index 100% rename from src/main/resources/application.yml rename to ozgcloud-keycloak-operator/src/main/resources/application.yml diff --git a/src/test/helm/deployment_env_test.yaml b/ozgcloud-keycloak-operator/src/test/helm/deployment_env_test.yaml similarity index 100% rename from src/test/helm/deployment_env_test.yaml rename to ozgcloud-keycloak-operator/src/test/helm/deployment_env_test.yaml diff --git a/src/test/helm/deployment_matchlabels_test.yaml b/ozgcloud-keycloak-operator/src/test/helm/deployment_matchlabels_test.yaml similarity index 100% rename from src/test/helm/deployment_matchlabels_test.yaml rename to ozgcloud-keycloak-operator/src/test/helm/deployment_matchlabels_test.yaml diff --git a/src/test/helm/deployment_metadata_labels_test.yaml b/ozgcloud-keycloak-operator/src/test/helm/deployment_metadata_labels_test.yaml similarity index 100% rename from src/test/helm/deployment_metadata_labels_test.yaml rename to ozgcloud-keycloak-operator/src/test/helm/deployment_metadata_labels_test.yaml diff --git a/src/test/helm/deployment_pull_secret_test.yaml b/ozgcloud-keycloak-operator/src/test/helm/deployment_pull_secret_test.yaml similarity index 100% rename from src/test/helm/deployment_pull_secret_test.yaml rename to ozgcloud-keycloak-operator/src/test/helm/deployment_pull_secret_test.yaml diff --git a/src/test/helm/deployment_resources_test.yaml b/ozgcloud-keycloak-operator/src/test/helm/deployment_resources_test.yaml similarity index 100% rename from src/test/helm/deployment_resources_test.yaml rename to ozgcloud-keycloak-operator/src/test/helm/deployment_resources_test.yaml diff --git a/src/test/helm/deployment_test.yaml b/ozgcloud-keycloak-operator/src/test/helm/deployment_test.yaml similarity index 100% rename from src/test/helm/deployment_test.yaml rename to ozgcloud-keycloak-operator/src/test/helm/deployment_test.yaml diff --git a/src/test/helm/image_pull_secret_test.yaml b/ozgcloud-keycloak-operator/src/test/helm/image_pull_secret_test.yaml similarity index 100% rename from src/test/helm/image_pull_secret_test.yaml rename to ozgcloud-keycloak-operator/src/test/helm/image_pull_secret_test.yaml diff --git a/src/test/helm/linter_values.yaml b/ozgcloud-keycloak-operator/src/test/helm/linter_values.yaml similarity index 100% rename from src/test/helm/linter_values.yaml rename to ozgcloud-keycloak-operator/src/test/helm/linter_values.yaml diff --git a/src/test/helm/rbacs/keycloak_admin_secret_read.yaml b/ozgcloud-keycloak-operator/src/test/helm/rbacs/keycloak_admin_secret_read_test.yaml similarity index 100% rename from src/test/helm/rbacs/keycloak_admin_secret_read.yaml rename to ozgcloud-keycloak-operator/src/test/helm/rbacs/keycloak_admin_secret_read_test.yaml diff --git a/src/test/helm/rbacs/keycloak_read_test.yaml b/ozgcloud-keycloak-operator/src/test/helm/rbacs/keycloak_read_test.yaml similarity index 100% rename from src/test/helm/rbacs/keycloak_read_test.yaml rename to ozgcloud-keycloak-operator/src/test/helm/rbacs/keycloak_read_test.yaml diff --git a/src/test/helm/rbacs/keycloak_write_test.yaml b/ozgcloud-keycloak-operator/src/test/helm/rbacs/keycloak_write_test.yaml similarity index 100% rename from src/test/helm/rbacs/keycloak_write_test.yaml rename to ozgcloud-keycloak-operator/src/test/helm/rbacs/keycloak_write_test.yaml diff --git a/src/test/helm/rbacs/serviceaccount_test.yaml b/ozgcloud-keycloak-operator/src/test/helm/rbacs/serviceaccount_test.yaml similarity index 100% rename from src/test/helm/rbacs/serviceaccount_test.yaml rename to ozgcloud-keycloak-operator/src/test/helm/rbacs/serviceaccount_test.yaml diff --git a/src/test/java/de/ozgcloud/operator/OzgOperatorApplicationTests.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/KeycloakOperatorApplicationTest.java similarity index 96% rename from src/test/java/de/ozgcloud/operator/OzgOperatorApplicationTests.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/KeycloakOperatorApplicationTest.java index c1f44350b15d1699808938451c32e7ae06a8af6a..52a4ee18784f2e3cd66467e8323b4fbe66ac3fe3 100644 --- a/src/test/java/de/ozgcloud/operator/OzgOperatorApplicationTests.java +++ b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/KeycloakOperatorApplicationTest.java @@ -27,7 +27,7 @@ import org.junit.jupiter.api.Test; //@SpringBootTest //@EnableMockOperator -class OzgOperatorApplicationTests { +class KeycloakOperatorApplicationTest { @Test void contextLoads() { diff --git a/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/KeycloakClientTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/KeycloakClientTest.java new file mode 100644 index 0000000000000000000000000000000000000000..64d1be69f681da74c8fc3ac60114478421832042 --- /dev/null +++ b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/KeycloakClientTest.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.operator.keycloak; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import io.fabric8.kubernetes.client.KubernetesClient; + +class KeycloakClientTest { + + @DisplayName("Create keycloak") + @Nested + class TestCreateKeycloak { + + @Mock + private KubernetesClient kubernetsClient; + + @Test + void shouldInitKeycloakClient() { + var keycloak = new KeycloakClient(kubernetsClient); + + assertThat(keycloak).isNotNull(); + } + } +} diff --git a/src/test/java/de/ozgcloud/operator/keycloak/KeycloakGenericRemoteServiceTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/KeycloakGenericRemoteServiceTest.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/KeycloakGenericRemoteServiceTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/KeycloakGenericRemoteServiceTest.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/KeycloakLivelTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/KeycloakLivelTest.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/KeycloakLivelTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/KeycloakLivelTest.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/client/ClientRepresentationTestFactory.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/client/ClientRepresentationTestFactory.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/client/ClientRepresentationTestFactory.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/client/ClientRepresentationTestFactory.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/client/KeycloakClientMapperTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/client/KeycloakClientMapperTest.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/client/KeycloakClientMapperTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/client/KeycloakClientMapperTest.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/client/KeycloakClientPreconditionServiceTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/client/KeycloakClientPreconditionServiceTest.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/client/KeycloakClientPreconditionServiceTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/client/KeycloakClientPreconditionServiceTest.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/client/KeycloakClientReconcilerTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/client/KeycloakClientReconcilerTest.java similarity index 97% rename from src/test/java/de/ozgcloud/operator/keycloak/client/KeycloakClientReconcilerTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/client/KeycloakClientReconcilerTest.java index c8f41130a340729d0bc529d78fed419219798e88..01b9a7c05e509a97e5c9ce7f9736f2788a137fb5 100644 --- a/src/test/java/de/ozgcloud/operator/keycloak/client/KeycloakClientReconcilerTest.java +++ b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/client/KeycloakClientReconcilerTest.java @@ -39,8 +39,6 @@ import de.ozgcloud.operator.keycloak.OzgCloudCustomResourceStatus; class KeycloakClientReconcilerTest { - public static final String ERROR_MESSAGE = "ErrorMessage"; - @Spy @InjectMocks private KeycloakClientReconciler reconciler; @@ -54,6 +52,8 @@ class KeycloakClientReconcilerTest { @Nested class TestReconcile { + private static final String ERROR_MESSAGE = "ErrorMessage"; + @Test void shouldCallServiceAddClient() { OzgCloudKeycloakClient client = OzgCloudKeycloakClientTestFactory.create(); diff --git a/src/test/java/de/ozgcloud/operator/keycloak/client/KeycloakClientRemoteServiceTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/client/KeycloakClientRemoteServiceTest.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/client/KeycloakClientRemoteServiceTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/client/KeycloakClientRemoteServiceTest.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/client/KeycloakClientServiceTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/client/KeycloakClientServiceTest.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/client/KeycloakClientServiceTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/client/KeycloakClientServiceTest.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/client/KeycloakLivelTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/client/KeycloakLivelTest.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/client/KeycloakLivelTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/client/KeycloakLivelTest.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/client/OzgCloudKeycloakClientSpecProtocolMapperTestFactory.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/client/OzgCloudKeycloakClientSpecProtocolMapperTestFactory.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/client/OzgCloudKeycloakClientSpecProtocolMapperTestFactory.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/client/OzgCloudKeycloakClientSpecProtocolMapperTestFactory.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/client/OzgCloudKeycloakClientSpecTestFactory.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/client/OzgCloudKeycloakClientSpecTestFactory.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/client/OzgCloudKeycloakClientSpecTestFactory.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/client/OzgCloudKeycloakClientSpecTestFactory.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/client/OzgCloudKeycloakClientStatusTestFactory.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/client/OzgCloudKeycloakClientStatusTestFactory.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/client/OzgCloudKeycloakClientStatusTestFactory.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/client/OzgCloudKeycloakClientStatusTestFactory.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/client/OzgCloudKeycloakClientTestFactory.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/client/OzgCloudKeycloakClientTestFactory.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/client/OzgCloudKeycloakClientTestFactory.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/client/OzgCloudKeycloakClientTestFactory.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/client/ProtocolMapperRepresentationHelperTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/client/ProtocolMapperRepresentationHelperTest.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/client/ProtocolMapperRepresentationHelperTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/client/ProtocolMapperRepresentationHelperTest.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/client/ProtocolMapperRepresentationTestFactory.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/client/ProtocolMapperRepresentationTestFactory.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/client/ProtocolMapperRepresentationTestFactory.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/client/ProtocolMapperRepresentationTestFactory.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/client/RoleRepresentationTestFactory.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/client/RoleRepresentationTestFactory.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/client/RoleRepresentationTestFactory.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/client/RoleRepresentationTestFactory.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/group/GroupRepresentationTestFactory.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/group/GroupRepresentationTestFactory.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/group/GroupRepresentationTestFactory.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/group/GroupRepresentationTestFactory.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupMapperTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupMapperTest.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupMapperTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupMapperTest.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupPreconditionServiceTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupPreconditionServiceTest.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupPreconditionServiceTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupPreconditionServiceTest.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupReconcilerTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupReconcilerTest.java similarity index 80% rename from src/test/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupReconcilerTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupReconcilerTest.java index c4eca85fbecda61936da1891720e9ebf3fdb9863..5e02bd50768c8afa95d5bb2a66acd798d0d8d308 100644 --- a/src/test/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupReconcilerTest.java +++ b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupReconcilerTest.java @@ -39,8 +39,6 @@ import de.ozgcloud.operator.keycloak.OzgCloudCustomResourceStatus; class KeycloakGroupReconcilerTest { - public static final String ERROR_MESSAGE = "ErrorMessage"; - @Spy @InjectMocks private KeycloakGroupReconciler reconciler; @@ -54,25 +52,26 @@ class KeycloakGroupReconcilerTest { @Nested class TestReconcile { + private static final String ERROR_MESSAGE = "ErrorMessage"; + private static final OzgCloudKeycloakGroup KEYCLOAK_GROUP = OzgCloudKeycloakGroupTestFactory.create(); + @Test void shouldCallServiceAddGroup() { - OzgCloudKeycloakGroup group = OzgCloudKeycloakGroupTestFactory.create(); - - reconciler.reconcile(group, null); + reconciler.reconcile(KEYCLOAK_GROUP, null); - verify(service).createGroup(group.getSpec(), OzgCloudKeycloakGroupTestFactory.METADATA_NAMESPACE); + verify(service).createGroup(KEYCLOAK_GROUP.getSpec(), OzgCloudKeycloakGroupTestFactory.METADATA_NAMESPACE); } @Test void shouldReturnUpdateStatus() { - var response = reconciler.reconcile(OzgCloudKeycloakGroupTestFactory.create(), null); + var response = reconciler.reconcile(KEYCLOAK_GROUP, null); assertThat(response.getResource()).isNotNull(); } @Test void shouldSetStatusOk() { - var response = reconciler.reconcile(OzgCloudKeycloakGroupTestFactory.create(), null); + var response = reconciler.reconcile(KEYCLOAK_GROUP, null); assertThat(response.getResource().getStatus().getStatus()).isEqualTo(OzgCloudCustomResourceStatus.OK); } @@ -81,7 +80,7 @@ class KeycloakGroupReconcilerTest { void shouldReturnInProgressStatusIfPreconditionsNotMet() { when(preconditionService.getReconcilePreconditionErrors(any())).thenReturn(Optional.of(ERROR_MESSAGE)); - var response = reconciler.reconcile(OzgCloudKeycloakGroupTestFactory.create(), null); + var response = reconciler.reconcile(KEYCLOAK_GROUP, null); assertThat(response.getResource().getStatus().getStatus()).isEqualTo(OzgCloudCustomResourceStatus.IN_PROGRESS); } diff --git a/src/test/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupRemoteServiceTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupRemoteServiceTest.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupRemoteServiceTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupRemoteServiceTest.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupServiceTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupServiceTest.java similarity index 81% rename from src/test/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupServiceTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupServiceTest.java index 1bc024a25ae520702044b8efa4a72640845a541a..b78cb0f74366340a21151c211e792a9ad8cc38fe 100644 --- a/src/test/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupServiceTest.java +++ b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/group/KeycloakGroupServiceTest.java @@ -52,43 +52,43 @@ class KeycloakGroupServiceTest { @Nested class TestCreateGroup { + private final static OzgCloudKeycloakGroupSpec KEYCLOAK_GROUP = OzgCloudKeycloakGroupSpecTestFactory.create(); + @Test void shouldCallGetGroupRemoteService() { var groupRepresentation = GroupRepresentationTestFactory.create(); when(mapper.map(any())).thenReturn(groupRepresentation); - service.createGroup(OzgCloudKeycloakGroupSpecTestFactory.create(), REALM); + service.createGroup(KEYCLOAK_GROUP, REALM); verify(remoteService).getGroupByName(OzgCloudKeycloakGroupSpecTestFactory.NAME, REALM); } @Test void shouldCallMapper() { - var group = OzgCloudKeycloakGroupSpecTestFactory.create(); - service.createGroup(group, REALM); - verify(mapper).map(group); + service.createGroup(KEYCLOAK_GROUP, REALM); + + verify(mapper).map(KEYCLOAK_GROUP); } @Test void shouldCreateGroupIfNotExists() { - var ozgGroup = OzgCloudKeycloakGroupSpecTestFactory.create(); var groupRepresentation = GroupRepresentationTestFactory.create(); when(remoteService.getGroupByName(OzgCloudKeycloakGroupSpecTestFactory.NAME, REALM)).thenReturn(Optional.empty()); - when(mapper.map(ozgGroup)).thenReturn(groupRepresentation); + when(mapper.map(KEYCLOAK_GROUP)).thenReturn(groupRepresentation); - service.createGroup(ozgGroup, REALM); + service.createGroup(KEYCLOAK_GROUP, REALM); verify(remoteService).createGroup(groupRepresentation, REALM); } @Test void shouldNotCreateGroupIfAlreadyExists() { - var ozgGroup = OzgCloudKeycloakGroupSpecTestFactory.create(); when(remoteService.getGroupByName(OzgCloudKeycloakGroupSpecTestFactory.NAME, REALM)).thenReturn(Optional.of(mock(GroupRepresentation.class))); - when(mapper.map(ozgGroup)).thenReturn(GroupRepresentationTestFactory.create()); + when(mapper.map(KEYCLOAK_GROUP)).thenReturn(GroupRepresentationTestFactory.create()); - service.createGroup(ozgGroup, REALM); + service.createGroup(KEYCLOAK_GROUP, REALM); verify(remoteService, never()).createGroup(any(), anyString()); } diff --git a/src/test/java/de/ozgcloud/operator/keycloak/group/KeycloakLivelTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/group/KeycloakLivelTest.java similarity index 92% rename from src/test/java/de/ozgcloud/operator/keycloak/group/KeycloakLivelTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/group/KeycloakLivelTest.java index 91ccd1f7ba8e7805522aeaca4bce060169221114..0da2e64f31296badb50a8339acdc5484231880d3 100644 --- a/src/test/java/de/ozgcloud/operator/keycloak/group/KeycloakLivelTest.java +++ b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/group/KeycloakLivelTest.java @@ -35,13 +35,13 @@ import org.mockito.InjectMocks; import org.mockito.Spy; import org.springframework.test.context.junit.jupiter.SpringExtension; -import lombok.extern.java.Log; +import lombok.extern.log4j.Log4j2; @Disabled("Should only be used manually") -@Log +@Log4j2 @ExtendWith(SpringExtension.class) class KeycloakLivelTest { - + private static final String NAMESPACE = "by-torsten-ozgcloud-keycloak-operator-dev"; @Spy @@ -68,8 +68,8 @@ class KeycloakLivelTest { FieldUtils.writeField(remoteService, "keycloak", kc, true); remoteService.createGroup(createGroup(), NAMESPACE); - log.info("Hase: " + remoteService.getGroupByName("hase", NAMESPACE)); - log.info("Maus: " + remoteService.getGroupByName("maus", NAMESPACE)); + LOG.info("Hase: {}.", remoteService.getGroupByName("hase", NAMESPACE)); + LOG.info("Maus: {}.", remoteService.getGroupByName("maus", NAMESPACE)); service.createGroup(OzgCloudKeycloakGroupSpec.builder().name("Fuchs").build(), NAMESPACE); } diff --git a/src/test/java/de/ozgcloud/operator/keycloak/group/OzgCloudKeycloakGroupSpecAttributeTestFactory.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/group/OzgCloudKeycloakGroupSpecAttributeTestFactory.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/group/OzgCloudKeycloakGroupSpecAttributeTestFactory.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/group/OzgCloudKeycloakGroupSpecAttributeTestFactory.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/group/OzgCloudKeycloakGroupSpecTestFactory.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/group/OzgCloudKeycloakGroupSpecTestFactory.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/group/OzgCloudKeycloakGroupSpecTestFactory.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/group/OzgCloudKeycloakGroupSpecTestFactory.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/group/OzgCloudKeycloakGroupTestFactory.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/group/OzgCloudKeycloakGroupTestFactory.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/group/OzgCloudKeycloakGroupTestFactory.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/group/OzgCloudKeycloakGroupTestFactory.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/realm/KeycloakLivelTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/realm/KeycloakLivelTest.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/realm/KeycloakLivelTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/realm/KeycloakLivelTest.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmMapperTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmMapperTest.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmMapperTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmMapperTest.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmReconcilerTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmReconcilerTest.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmReconcilerTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmReconcilerTest.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmRemoteServiceTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmRemoteServiceTest.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmRemoteServiceTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmRemoteServiceTest.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmServiceTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmServiceTest.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmServiceTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmServiceTest.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/realm/OzgCloudKeycloakRealmSpecTestFactory.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/realm/OzgCloudKeycloakRealmSpecTestFactory.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/realm/OzgCloudKeycloakRealmSpecTestFactory.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/realm/OzgCloudKeycloakRealmSpecTestFactory.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/realm/OzgCloudKeycloakRealmStatusTestFactory.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/realm/OzgCloudKeycloakRealmStatusTestFactory.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/realm/OzgCloudKeycloakRealmStatusTestFactory.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/realm/OzgCloudKeycloakRealmStatusTestFactory.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/realm/OzgCloudKeycloakRealmTestFactory.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/realm/OzgCloudKeycloakRealmTestFactory.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/realm/OzgCloudKeycloakRealmTestFactory.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/realm/OzgCloudKeycloakRealmTestFactory.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/realm/RealmRepresentationTestFactory.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/realm/RealmRepresentationTestFactory.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/realm/RealmRepresentationTestFactory.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/realm/RealmRepresentationTestFactory.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakLivelTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakLivelTest.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakLivelTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakLivelTest.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserMapperTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserMapperTest.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserMapperTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserMapperTest.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserPreconditionServiceTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserPreconditionServiceTest.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserPreconditionServiceTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserPreconditionServiceTest.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserReconcilerTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserReconcilerTest.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserReconcilerTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserReconcilerTest.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserRemoteServiceTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserRemoteServiceTest.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserRemoteServiceTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserRemoteServiceTest.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserServiceTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserServiceTest.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserServiceTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserServiceTest.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserSpecUserTestFactory.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserSpecUserTestFactory.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserSpecUserTestFactory.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserSpecUserTestFactory.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/user/KubernetesRemoteServiceTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/KubernetesRemoteServiceTest.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/user/KubernetesRemoteServiceTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/KubernetesRemoteServiceTest.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUserSpecTestFactory.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUserSpecTestFactory.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUserSpecTestFactory.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUserSpecTestFactory.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUserStatusTestFactory.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUserStatusTestFactory.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUserStatusTestFactory.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUserStatusTestFactory.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUserTestFactory.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUserTestFactory.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUserTestFactory.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUserTestFactory.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/user/SecretTestFactory.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/SecretTestFactory.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/user/SecretTestFactory.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/SecretTestFactory.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/user/UserNameConverterTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/UserNameConverterTest.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/user/UserNameConverterTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/UserNameConverterTest.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/user/UserRepresentationTestFactory.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/UserRepresentationTestFactory.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/user/UserRepresentationTestFactory.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/UserRepresentationTestFactory.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/user/UserSecretBuilderTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/UserSecretBuilderTest.java similarity index 78% rename from src/test/java/de/ozgcloud/operator/keycloak/user/UserSecretBuilderTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/UserSecretBuilderTest.java index 7cfbbf8e13b7d2e1d89104616c6aa28a6550e372..0cc1d435be4aa4ae3b05cc6fe6f69abafd994087 100644 --- a/src/test/java/de/ozgcloud/operator/keycloak/user/UserSecretBuilderTest.java +++ b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/UserSecretBuilderTest.java @@ -1,8 +1,11 @@ package de.ozgcloud.operator.keycloak.user; import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +import java.util.Base64; + import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -11,7 +14,7 @@ import org.mockito.Spy; import de.ozgcloud.operator.keycloak.user.OzgCloudKeycloakUserSpec.KeycloakUserSpecUser; -public class UserSecretBuilderTest { +class UserSecretBuilderTest { private final static String NAME = "dummyName"; private final static String NAMESPACE = "dummyNamespace"; @@ -52,7 +55,7 @@ public class UserSecretBuilderTest { var secret = builder.build(NAME, userSpec, NAMESPACE); - assertThat(secret.getStringData()).containsEntry(UserSecretBuilder.SECRET_PASSWORD_FIELD, SecretTestFactory.PASSWORD); + assertThat(secret.getData()).containsEntry(UserSecretBuilder.SECRET_PASSWORD_FIELD, SecretTestFactory.PASSWORD); } @DisplayName("metadata") @@ -80,24 +83,35 @@ public class UserSecretBuilderTest { @Test void shouldHaveSize() { - var password = builder.generatePassword(); + var password = decode(builder.generatePassword()); assertThat(password).hasSize(8); } @Test void shouldHaveUpperCaseLetterAtFirst() { - var password = builder.generatePassword(); + var password = decode(builder.generatePassword()); assertThat(StringUtils.substring(password, 0, 1)).isUpperCase(); } @Test void shouldContainsAlphanumericOnly() { - var password = builder.generatePassword(); + var password = decode(builder.generatePassword()); assertThat(password).isAlphanumeric(); } + + @Test + void shouldEncode() { + builder.generatePassword(); + + verify(builder).encode(any()); + } + + private String decode(String strValue) { + return new String(Base64.getDecoder().decode(strValue.getBytes())); + } } } } diff --git a/src/test/java/de/ozgcloud/operator/keycloak/user/UserSecretReaderTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/UserSecretReaderTest.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/user/UserSecretReaderTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/UserSecretReaderTest.java diff --git a/src/test/java/de/ozgcloud/operator/keycloak/user/UserSecretServiceTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/UserSecretServiceTest.java similarity index 100% rename from src/test/java/de/ozgcloud/operator/keycloak/user/UserSecretServiceTest.java rename to ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/UserSecretServiceTest.java diff --git a/src/test/resources/KeycloakUserTest.yaml b/ozgcloud-keycloak-operator/src/test/resources/KeycloakUserTest.yaml similarity index 100% rename from src/test/resources/KeycloakUserTest.yaml rename to ozgcloud-keycloak-operator/src/test/resources/KeycloakUserTest.yaml diff --git a/ozgcloud-keycloak-operator/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension b/ozgcloud-keycloak-operator/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension new file mode 100644 index 0000000000000000000000000000000000000000..79b126e6cdb86bec1f4f08c205de8961bde1934a --- /dev/null +++ b/ozgcloud-keycloak-operator/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension @@ -0,0 +1 @@ +org.mockito.junit.jupiter.MockitoExtension \ No newline at end of file diff --git a/src/test/resources/application.yaml b/ozgcloud-keycloak-operator/src/test/resources/application.yaml similarity index 100% rename from src/test/resources/application.yaml rename to ozgcloud-keycloak-operator/src/test/resources/application.yaml diff --git a/ozgcloud-keycloak-operator/src/test/resources/junit-platform.properties b/ozgcloud-keycloak-operator/src/test/resources/junit-platform.properties new file mode 100644 index 0000000000000000000000000000000000000000..b059a65dc46792ea5494de02f888ab2ad08cd420 --- /dev/null +++ b/ozgcloud-keycloak-operator/src/test/resources/junit-platform.properties @@ -0,0 +1 @@ +junit.jupiter.extensions.autodetection.enabled=true \ No newline at end of file diff --git a/pom.xml b/pom.xml index 176165c6c7d73e6412cd0c46fdf63a390c6034d8..fbd13b21d2fbfebd615c1c634643b77410110777 100644 --- a/pom.xml +++ b/pom.xml @@ -1,79 +1,85 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> - + <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.1.3</version> - <relativePath/> + <relativePath /> </parent> <groupId>de.ozgcloud</groupId> - <artifactId>ozgcloud-keycloak-operator</artifactId> + <artifactId>ozgcloud-operator-parent</artifactId> <version>2.1.0-SNAPSHOT</version> - <name>OzgCloud Keycloak Operator</name> - <description>OzgCloud Keycloak Operator</description> + <packaging>pom</packaging> + + <name>OzgCloud Operator Parent</name> + <description>OzgCloud Operator Parent</description> + <modules> + <module>ozgcloud-keycloak-operator</module> + <module>ozgcloud-elasticsearch-operator</module> + </modules> + <properties> - <spring-boot.build-image.imageName>docker.ozg-sh.de/ozgcloud-keycloak-operator:build-latest</spring-boot.build-image.imageName> - - <operator-sdk.version>5.2.0</operator-sdk.version> + <spring-boot.version>3.1.3</spring-boot.version> + <operator-sdk.version>5.4.1</operator-sdk.version> + + <!-- tools --> + <commons-beanutils.version>1.9.4</commons-beanutils.version> + <lombok.version>1.18.28</lombok.version> <mapstruct.version>1.5.5.Final</mapstruct.version> <keycloak-adapter.version>20.0.5</keycloak-adapter.version> - <commons-beanutils.version>1.9.4</commons-beanutils.version> <reflections.version>0.10.2</reflections.version> <validation-api.version>2.0.1.Final</validation-api.version> - </properties> + <lorem.version>2.2</lorem.version> + + <!-- test --> + <junit-jupiter.version>5.9.3</junit-jupiter.version> + <kubernetes-server-mock.version>6.9.2</kubernetes-server-mock.version> + <io.javaoperatorsdk.version>0.9.5</io.javaoperatorsdk.version> + <!-- plugin --> + <license-maven-plugin.version>4.1</license-maven-plugin.version> + <ozgcloud-license.version>1.6.0</ozgcloud-license.version> + </properties> + <dependencies> + <!-- spring --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> - <dependency> - <groupId>org.keycloak</groupId> - <artifactId>keycloak-admin-client</artifactId> - <version>${keycloak-adapter.version}</version> - </dependency> - <dependency> - <groupId>org.mapstruct</groupId> - <artifactId>mapstruct</artifactId> - <version>${mapstruct.version}</version> - </dependency> <dependency> <groupId>io.javaoperatorsdk</groupId> <artifactId>operator-framework-spring-boot-starter</artifactId> - <version>${operator-sdk.version}</version> - </dependency> - <dependency> - <groupId>javax.validation</groupId> - <artifactId>validation-api</artifactId> - <version>${validation-api.version}</version> </dependency> <dependency> - <groupId>jakarta.xml.bind</groupId> - <artifactId>jakarta.xml.bind-api</artifactId> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-actuator</artifactId> </dependency> + + <!-- tools --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> - <dependency> - <groupId>org.reflections</groupId> - <artifactId>reflections</artifactId> - <version>${reflections.version}</version> - </dependency> <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> - <version>${commons-beanutils.version}</version> + </dependency> + <dependency> + <groupId>com.thedeanda</groupId> + <artifactId>lorem</artifactId> </dependency> + + <!-- test --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> - <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> @@ -91,44 +97,141 @@ <artifactId>junit-jupiter-params</artifactId> <scope>test</scope> </dependency> - + <dependency> + <groupId>io.fabric8</groupId> + <artifactId>kubernetes-server-mock</artifactId> + <scope>test</scope> + </dependency> </dependencies> - <build> - <plugins> - <plugin> + <dependencyManagement> + <dependencies> + <!-- own projects --> + <dependency> + <groupId>de.ozgcloud.common</groupId> + <artifactId>ozgcloud-common-license</artifactId> + <version>${ozgcloud-license.version}</version> + </dependency> + + <!-- spring --> + <dependency> <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-maven-plugin</artifactId> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-compiler-plugin</artifactId> - <configuration> - <fork>true</fork> - <annotationProcessorPaths> - <path> - <groupId>org.projectlombok</groupId> - <artifactId>lombok</artifactId> - <version>${lombok.version}</version> - </path> - <path> - <groupId>org.mapstruct</groupId> - <artifactId>mapstruct-processor</artifactId> - <version>${mapstruct.version}</version> - </path> - </annotationProcessorPaths> - <showWarnings>true</showWarnings> - <compilerArgs> - <compilerArg> - -Amapstruct.defaultComponentModel=spring - </compilerArg> - <compilerArg> - -Amapstruct.unmappedTargetPolicy=WARN - </compilerArg> - </compilerArgs> - </configuration> - </plugin> - </plugins> + <artifactId>spring-boot-starter</artifactId> + <version>${spring-boot.version}</version> + </dependency> + <dependency> + <groupId>io.javaoperatorsdk</groupId> + <artifactId>operator-framework-spring-boot-starter</artifactId> + <version>${operator-sdk.version}</version> + </dependency> + + <!-- keycloak --> + <dependency> + <groupId>org.keycloak</groupId> + <artifactId>keycloak-admin-client</artifactId> + <version>${keycloak-adapter.version}</version> + </dependency> + + <!-- tools --> + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + <version>${lombok.version}</version> + </dependency> + <dependency> + <groupId>org.mapstruct</groupId> + <artifactId>mapstruct</artifactId> + <version>${mapstruct.version}</version> + </dependency> + <dependency> + <groupId>org.reflections</groupId> + <artifactId>reflections</artifactId> + <version>${reflections.version}</version> + </dependency> + <dependency> + <groupId>commons-beanutils</groupId> + <artifactId>commons-beanutils</artifactId> + <version>${commons-beanutils.version}</version> + </dependency> + <dependency> + <groupId>com.thedeanda</groupId> + <artifactId>lorem</artifactId> + <version>${lorem.version}</version> + </dependency> + + <!-- javax --> + <dependency> + <groupId>javax.validation</groupId> + <artifactId>validation-api</artifactId> + <version>${validation-api.version}</version> + </dependency> + + <!-- test --> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <version>${junit-jupiter.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-params</artifactId> + <version>${junit-jupiter.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>io.fabric8</groupId> + <artifactId>kubernetes-server-mock</artifactId> + <version>${kubernetes-server-mock.version}</version> + </dependency> + <dependency> + <groupId>io.javaoperatorsdk</groupId> + <artifactId>jenvtest-fabric8-client-support</artifactId> + <version>${io.javaoperatorsdk.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>io.javaoperatorsdk</groupId> + <artifactId>jenvtest</artifactId> + <version>${io.javaoperatorsdk.version}</version> + <scope>test</scope> + </dependency> + </dependencies> + </dependencyManagement> + + <build> + <pluginManagement> + <plugins> + <plugin> + <groupId>com.mycila</groupId> + <artifactId>license-maven-plugin</artifactId> + <version>${license-maven-plugin.version}</version> + <configuration> + <mapping> + <ts>SLASHSTAR_STYLE</ts> + <config>SCRIPT_STYLE</config> + </mapping> + <licenseSets> + <licenseSet> + <header>license/eupl_v1_2_de/header.txt</header> + <excludes> + <exclude>**/README</exclude> + <exclude>src/test/resources/**</exclude> + <exclude>src/main/resources/**</exclude> + </excludes> + </licenseSet> + </licenseSets> + </configuration> + <dependencies> + <dependency> + <groupId>de.ozgcloud.common</groupId> + <artifactId>ozgcloud-common-license</artifactId> + <version>${ozgcloud-license.version}</version> + </dependency> + </dependencies> + </plugin> + </plugins> + </pluginManagement> </build> <distributionManagement> @@ -143,5 +246,5 @@ <url>https://nexus.ozg-sh.de/repository/ozg-snapshots/</url> </snapshotRepository> </distributionManagement> - -</project> + +</project> \ No newline at end of file diff --git a/release-erstellen.sh b/release-erstellen.sh index dfe2a755312486a606fc94a2187bd1e52c2e3271..fc5a1f481ba0e2d029766c45d2551321d93495f4 100755 --- a/release-erstellen.sh +++ b/release-erstellen.sh @@ -1,7 +1,7 @@ #!/bin/sh if [ "$#" -ne 1 ]; then - echo "Aufruf: ozg-release-erstellen.sh JA" + echo "Aufruf: release-erstellen.sh JA" echo "Als Parameter bitte 'JA' eintragen zur Sicherheit" exit 1 fi diff --git a/release-startdev.sh b/release-startdev.sh index f02c90d2bb6c5136860c392415b827a92637bb35..4fc6aa98500e450dfdd07d026a46772accdb514e 100755 --- a/release-startdev.sh +++ b/release-startdev.sh @@ -3,7 +3,7 @@ #set -x if [ "$#" -ne 1 ]; then - echo "Aufruf: ozg-release-startdev.sh NEWVERSION" + echo "Aufruf: release-startdev.sh NEWVERSION" exit 1 fi