From f2d71cd1c728cc6d1b7c74c11976fa9d58bf9c30 Mon Sep 17 00:00:00 2001 From: Andrea Peruffo Date: Wed, 13 Jul 2022 15:58:06 +0100 Subject: [PATCH] Switch to StatefulSet (#12757) --- .../controllers/KeycloakController.java | 6 +- .../controllers/KeycloakDeployment.java | 112 +++++++----------- .../controllers/KeycloakRealmImportJob.java | 34 +++++- operator/src/main/kubernetes/kubernetes.yml | 2 +- .../integration/BaseOperatorTest.java | 14 ++- .../integration/KeycloakDeploymentTest.java | 51 +++++--- .../integration/RealmImportTest.java | 5 + .../integration/WatchedSecretsTest.java | 25 +++- .../testsuite/unit/PodTemplateTest.java | 8 +- 9 files changed, 158 insertions(+), 99 deletions(-) diff --git a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakController.java b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakController.java index d456a0d6c4..6e5adc5fc0 100644 --- a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakController.java +++ b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakController.java @@ -17,7 +17,7 @@ package org.keycloak.operator.controllers; import io.fabric8.kubernetes.api.model.Service; -import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.fabric8.kubernetes.api.model.apps.StatefulSet; import io.fabric8.kubernetes.api.model.networking.v1.Ingress; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.informers.SharedIndexInformer; @@ -61,8 +61,8 @@ public class KeycloakController implements Reconciler, EventSourceInit public List prepareEventSources(EventSourceContext context) { String namespace = context.getConfigurationService().getClientConfiguration().getNamespace(); - SharedIndexInformer deploymentInformer = - client.apps().deployments().inNamespace(namespace) + SharedIndexInformer deploymentInformer = + client.apps().statefulSets().inNamespace(namespace) .withLabels(Constants.DEFAULT_LABELS) .runnableInformer(0); diff --git a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakDeployment.java b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakDeployment.java index 7789448b59..b3ec993c31 100644 --- a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakDeployment.java +++ b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakDeployment.java @@ -22,13 +22,12 @@ import io.fabric8.kubernetes.api.model.EnvVarBuilder; import io.fabric8.kubernetes.api.model.EnvVarSourceBuilder; import io.fabric8.kubernetes.api.model.ExecActionBuilder; import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.api.model.IntOrString; import io.fabric8.kubernetes.api.model.PodTemplateSpec; import io.fabric8.kubernetes.api.model.ResourceRequirements; import io.fabric8.kubernetes.api.model.VolumeBuilder; import io.fabric8.kubernetes.api.model.VolumeMountBuilder; -import io.fabric8.kubernetes.api.model.apps.Deployment; -import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder; +import io.fabric8.kubernetes.api.model.apps.StatefulSet; +import io.fabric8.kubernetes.api.model.apps.StatefulSetBuilder; import io.fabric8.kubernetes.client.KubernetesClient; import io.quarkus.logging.Log; import org.keycloak.operator.Config; @@ -55,13 +54,13 @@ public class KeycloakDeployment extends OperatorManagedResource implements Statu private final Config config; private final Keycloak keycloakCR; - private final Deployment existingDeployment; - private final Deployment baseDeployment; + private final StatefulSet existingDeployment; + private final StatefulSet baseDeployment; private final String adminSecretName; private Set serverConfigSecretsNames; - public KeycloakDeployment(KubernetesClient client, Config config, Keycloak keycloakCR, Deployment existingDeployment, String adminSecretName) { + public KeycloakDeployment(KubernetesClient client, Config config, Keycloak keycloakCR, StatefulSet existingDeployment, String adminSecretName) { super(client, keycloakCR); this.config = config; this.keycloakCR = keycloakCR; @@ -81,15 +80,15 @@ public class KeycloakDeployment extends OperatorManagedResource implements Statu @Override public Optional getReconciledResource() { - Deployment baseDeployment = new DeploymentBuilder(this.baseDeployment).build(); // clone not to change the base template - Deployment reconciledDeployment; + StatefulSet baseDeployment = new StatefulSetBuilder(this.baseDeployment).build(); // clone not to change the base template + StatefulSet reconciledDeployment; if (existingDeployment == null) { Log.info("No existing Deployment found, using the default"); reconciledDeployment = baseDeployment; } else { Log.info("Existing Deployment found, updating specs"); - reconciledDeployment = new DeploymentBuilder(existingDeployment).build(); + reconciledDeployment = new StatefulSetBuilder(existingDeployment).build(); // don't overwrite metadata, just specs reconciledDeployment.setSpec(baseDeployment.getSpec()); @@ -106,10 +105,10 @@ public class KeycloakDeployment extends OperatorManagedResource implements Statu return Optional.of(reconciledDeployment); } - private Deployment fetchExistingDeployment() { + private StatefulSet fetchExistingDeployment() { return client .apps() - .deployments() + .statefulSets() .inNamespace(getNamespace()) .withName(getName()) .get(); @@ -319,7 +318,7 @@ public class KeycloakDeployment extends OperatorManagedResource implements Statu } } - private void configureHostname(Deployment deployment) { + private void configureHostname(StatefulSet deployment) { var kcContainer = deployment.getSpec().getTemplate().getSpec().getContainers().get(0); var hostname = this.keycloakCR.getSpec().getHostname(); var envVars = kcContainer.getEnv(); @@ -346,7 +345,7 @@ public class KeycloakDeployment extends OperatorManagedResource implements Statu } } - private void configureTLS(Deployment deployment) { + private void configureTLS(StatefulSet deployment) { var kcContainer = deployment.getSpec().getTemplate().getSpec().getContainers().get(0); var tlsSecret = this.keycloakCR.getSpec().getTlsSecret(); var envVars = kcContainer.getEnv(); @@ -462,8 +461,8 @@ public class KeycloakDeployment extends OperatorManagedResource implements Statu } } - private Deployment createBaseDeployment() { - Deployment baseDeployment = new DeploymentBuilder() + private StatefulSet createBaseDeployment() { + StatefulSet baseDeployment = new StatefulSetBuilder() .withNewMetadata() .endMetadata() .withNewSpec() @@ -475,40 +474,33 @@ public class KeycloakDeployment extends OperatorManagedResource implements Statu .addToLabels("app", "") .endMetadata() .withNewSpec() - .withRestartPolicy("Always") - .withTerminationGracePeriodSeconds(30L) - .withDnsPolicy("ClusterFirst") - .addNewContainer() - .withName("keycloak") - .withArgs("start") - .addNewPort() - .withContainerPort(8443) - .withProtocol("TCP") - .endPort() - .addNewPort() - .withContainerPort(8080) - .withProtocol("TCP") - .endPort() - .withNewReadinessProbe() - .withInitialDelaySeconds(20) - .withPeriodSeconds(2) - .withFailureThreshold(250) - .endReadinessProbe() - .withNewLivenessProbe() - .withInitialDelaySeconds(20) - .withPeriodSeconds(2) - .withFailureThreshold(150) - .endLivenessProbe() + .withRestartPolicy("Always") + .withTerminationGracePeriodSeconds(30L) + .withDnsPolicy("ClusterFirst") + .addNewContainer() + .withName("keycloak") + .withArgs("start") + .addNewPort() + .withContainerPort(8443) + .withProtocol("TCP") + .endPort() + .addNewPort() + .withContainerPort(8080) + .withProtocol("TCP") + .endPort() + .withNewReadinessProbe() + .withInitialDelaySeconds(20) + .withPeriodSeconds(2) + .withFailureThreshold(250) + .endReadinessProbe() + .withNewLivenessProbe() + .withInitialDelaySeconds(20) + .withPeriodSeconds(2) + .withFailureThreshold(150) + .endLivenessProbe() .endContainer() .endSpec() .endTemplate() - .withNewStrategy() - .withNewRollingUpdate() - // Same defaults as for a StatefulSet: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#maximum-unavailable-pods - .withNewMaxSurge(1) // maximum number of Pods that can be created over the desired number of Pods - .withNewMaxUnavailable(1) // maximum number of Pods that can be unavailable during the update process - .endRollingUpdate() - .endStrategy() .endSpec() .build(); @@ -601,15 +593,7 @@ public class KeycloakDeployment extends OperatorManagedResource implements Statu public void updateStatus(KeycloakStatusBuilder status) { validatePodTemplate(status); if (existingDeployment == null) { - status.addNotReadyMessage("No existing Deployment found, waiting for creating a new one"); - return; - } - - var replicaFailure = existingDeployment.getStatus().getConditions().stream() - .filter(d -> d.getType().equals("ReplicaFailure")).findFirst(); - if (replicaFailure.isPresent()) { - status.addNotReadyMessage("Deployment failures"); - status.addErrorMessage("Deployment failure: " + replicaFailure.get()); + status.addNotReadyMessage("No existing StatefulSet found, waiting for creating a new one"); return; } @@ -619,16 +603,12 @@ public class KeycloakDeployment extends OperatorManagedResource implements Statu status.addNotReadyMessage("Waiting for more replicas"); } - var progressing = existingDeployment.getStatus().getConditions().stream() - .filter(c -> c.getType().equals("Progressing")).findFirst(); - progressing.ifPresent(p -> { - String reason = p.getReason(); - // https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#progressing-deployment - if (p.getStatus().equals("True") && - (reason.equals("NewReplicaSetCreated") || reason.equals("FoundNewReplicaSet") || reason.equals("ReplicaSetUpdated"))) { - status.addRollingUpdateMessage("Rolling out deployment update"); - } - }); + if (existingDeployment.getStatus() != null + && existingDeployment.getStatus().getCurrentRevision() != null + && existingDeployment.getStatus().getUpdateRevision() != null + && !existingDeployment.getStatus().getCurrentRevision().equals(existingDeployment.getStatus().getUpdateRevision())) { + status.addRollingUpdateMessage("Rolling out deployment update"); + } } public Set getConfigSecretsNames() { @@ -645,7 +625,7 @@ public class KeycloakDeployment extends OperatorManagedResource implements Statu } public void rollingRestart() { - client.apps().deployments() + client.apps().statefulSets() .inNamespace(getNamespace()) .withName(getName()) .rolling().restart(); diff --git a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakRealmImportJob.java b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakRealmImportJob.java index 60de1ddb00..28465e3a29 100644 --- a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakRealmImportJob.java +++ b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakRealmImportJob.java @@ -17,13 +17,14 @@ package org.keycloak.operator.controllers; import io.fabric8.kubernetes.api.model.Container; +import io.fabric8.kubernetes.api.model.EnvVarBuilder; import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.api.model.PodTemplateSpec; import io.fabric8.kubernetes.api.model.SecretVolumeSourceBuilder; import io.fabric8.kubernetes.api.model.Volume; import io.fabric8.kubernetes.api.model.VolumeBuilder; import io.fabric8.kubernetes.api.model.VolumeMountBuilder; -import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.fabric8.kubernetes.api.model.apps.StatefulSet; import io.fabric8.kubernetes.api.model.batch.v1.Job; import io.fabric8.kubernetes.api.model.batch.v1.JobBuilder; import io.fabric8.kubernetes.client.KubernetesClient; @@ -37,11 +38,14 @@ import org.keycloak.operator.crds.v2alpha1.realmimport.KeycloakRealmImportStatus import java.util.List; import java.util.Optional; +import static org.keycloak.operator.Constants.DEFAULT_DIST_CONFIG; +import static org.keycloak.operator.controllers.KeycloakDeployment.getEnvVarName; + public class KeycloakRealmImportJob extends OperatorManagedResource { private final Keycloak keycloak; private final KeycloakRealmImport realmCR; - private final Deployment existingDeployment; + private final StatefulSet existingDeployment; private final Job existingJob; private final String secretName; private final String volumeName; @@ -80,10 +84,10 @@ public class KeycloakRealmImportJob extends OperatorManagedResource { .get(); } - private Deployment fetchExistingDeployment() { + private StatefulSet fetchExistingDeployment() { return client .apps() - .deployments() + .statefulSets() .inNamespace(getNamespace()) .withName(getKeycloakName()) .get(); @@ -129,6 +133,26 @@ public class KeycloakRealmImportJob extends OperatorManagedResource { buildKeycloakJobContainer(keycloakPodTemplate.getSpec().getContainers().get(0)); keycloakPodTemplate.getSpec().getVolumes().add(buildSecretVolume()); + var labels = keycloakPodTemplate.getMetadata().getLabels(); + + // The Job should not be selected with app=keycloak + labels.put("app", "keycloak-realm-import"); + + var envvars = keycloakPodTemplate + .getSpec() + .getContainers() + .get(0) + .getEnv(); + + var cacheEnvVarName = getEnvVarName("cache"); + var healthEnvVarName = getEnvVarName("health-enabled"); + envvars.removeIf(e -> e.getName().equals(cacheEnvVarName) || e.getName().equals(healthEnvVarName)); + + // The Job should not connect to the cache + envvars.add(new EnvVarBuilder().withName(cacheEnvVarName).withValue("local").build()); + // The Job doesn't need health to be enabled + envvars.add(new EnvVarBuilder().withName(healthEnvVarName).withValue("false").build()); + return buildJob(keycloakPodTemplate); } @@ -205,7 +229,7 @@ public class KeycloakRealmImportJob extends OperatorManagedResource { private String getRealmName() { return realmCR.getSpec().getRealm().getRealm(); } private void rollingRestart() { - client.apps().deployments() + client.apps().statefulSets() .inNamespace(getNamespace()) .withName(getKeycloakName()) .rolling().restart(); diff --git a/operator/src/main/kubernetes/kubernetes.yml b/operator/src/main/kubernetes/kubernetes.yml index dfb24ce4b7..b03dc82fae 100644 --- a/operator/src/main/kubernetes/kubernetes.yml +++ b/operator/src/main/kubernetes/kubernetes.yml @@ -9,7 +9,7 @@ rules: # https://github.com/fabric8io/kubernetes-client/issues/3996 - extensions resources: - - deployments + - statefulsets verbs: - get - list diff --git a/operator/src/test/java/org/keycloak/operator/testsuite/integration/BaseOperatorTest.java b/operator/src/test/java/org/keycloak/operator/testsuite/integration/BaseOperatorTest.java index 5a31f1b5d6..2b36ebee77 100644 --- a/operator/src/test/java/org/keycloak/operator/testsuite/integration/BaseOperatorTest.java +++ b/operator/src/test/java/org/keycloak/operator/testsuite/integration/BaseOperatorTest.java @@ -20,7 +20,6 @@ package org.keycloak.operator.testsuite.integration; import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.api.model.NamespaceBuilder; import io.fabric8.kubernetes.api.model.Secret; -import io.fabric8.kubernetes.api.model.apps.Deployment; import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.ConfigBuilder; import io.fabric8.kubernetes.client.DefaultKubernetesClient; @@ -186,6 +185,17 @@ public abstract class BaseOperatorTest { protected static void deleteDB() { // Delete the Postgres StatefulSet k8sclient.apps().statefulSets().inNamespace(namespace).withName("postgresql-db").delete(); + Awaitility.await() + .ignoreExceptions() + .untilAsserted(() -> { + Log.infof("Waiting for postgres to be deleted"); + assertThat(k8sclient + .apps() + .statefulSets() + .inNamespace(namespace) + .withName("postgresql-db") + .get()).isNull(); + }); } // TODO improve this (preferably move to JOSDK) @@ -220,7 +230,7 @@ public abstract class BaseOperatorTest { .untilAsserted(() -> { var kcDeployments = k8sclient .apps() - .deployments() + .statefulSets() .inNamespace(namespace) .withLabels(Constants.DEFAULT_LABELS) .list() diff --git a/operator/src/test/java/org/keycloak/operator/testsuite/integration/KeycloakDeploymentTest.java b/operator/src/test/java/org/keycloak/operator/testsuite/integration/KeycloakDeploymentTest.java index 49a305f383..2207dedcf3 100644 --- a/operator/src/test/java/org/keycloak/operator/testsuite/integration/KeycloakDeploymentTest.java +++ b/operator/src/test/java/org/keycloak/operator/testsuite/integration/KeycloakDeploymentTest.java @@ -20,7 +20,7 @@ package org.keycloak.operator.testsuite.integration; import io.fabric8.kubernetes.api.model.EnvVarBuilder; import io.fabric8.kubernetes.api.model.SecretBuilder; import io.fabric8.kubernetes.api.model.SecretKeySelectorBuilder; -import io.fabric8.kubernetes.api.model.apps.DeploymentSpecBuilder; +import io.fabric8.kubernetes.api.model.apps.StatefulSetSpecBuilder; import io.quarkus.logging.Log; import io.quarkus.test.junit.QuarkusTest; import org.awaitility.Awaitility; @@ -65,17 +65,17 @@ public class KeycloakDeploymentTest extends BaseOperatorTest { // Check Operator has deployed Keycloak Log.info("Checking Operator has deployed Keycloak deployment"); - assertThat(k8sclient.apps().deployments().inNamespace(namespace).withName(deploymentName).get()).isNotNull(); + assertThat(k8sclient.apps().statefulSets().inNamespace(namespace).withName(deploymentName).get()).isNotNull(); // Check Keycloak has correct replicas Log.info("Checking Keycloak pod has ready replicas == 1"); - assertThat(k8sclient.apps().deployments().inNamespace(namespace).withName(deploymentName).get().getStatus().getReadyReplicas()).isEqualTo(1); + assertThat(k8sclient.apps().statefulSets().inNamespace(namespace).withName(deploymentName).get().getStatus().getReadyReplicas()).isEqualTo(1); // Delete CR Log.info("Deleting Keycloak CR and watching cleanup"); k8sclient.resources(Keycloak.class).delete(kc); Awaitility.await() - .untilAsserted(() -> assertThat(k8sclient.apps().deployments().inNamespace(namespace).withName(deploymentName).get()).isNull()); + .untilAsserted(() -> assertThat(k8sclient.apps().statefulSets().inNamespace(namespace).withName(deploymentName).get()).isNull()); } catch (Exception e) { savePodLogs(); throw e; @@ -99,7 +99,7 @@ public class KeycloakDeploymentTest extends BaseOperatorTest { Awaitility.await() .during(Duration.ofSeconds(15)) // check if the Deployment is stable .untilAsserted(() -> { - var c = k8sclient.apps().deployments().inNamespace(namespace).withName(deploymentName).get() + var c = k8sclient.apps().statefulSets().inNamespace(namespace).withName(deploymentName).get() .getSpec().getTemplate().getSpec().getContainers().get(0); assertThat(c.getImage()).isEqualTo("quay.io/keycloak/non-existing-keycloak"); assertThat(c.getEnv().stream() @@ -132,7 +132,7 @@ public class KeycloakDeploymentTest extends BaseOperatorTest { .ignoreExceptions() .untilAsserted(() -> { Log.info("Asserting default value was overwritten by CR value"); - var c = k8sclient.apps().deployments().inNamespace(namespace).withName(kc.getMetadata().getName()).get() + var c = k8sclient.apps().statefulSets().inNamespace(namespace).withName(kc.getMetadata().getName()).get() .getSpec().getTemplate().getSpec().getContainers().get(0); assertThat(c.getEnv()).contains(e); @@ -151,29 +151,29 @@ public class KeycloakDeploymentTest extends BaseOperatorTest { deployKeycloak(k8sclient, kc, true); Log.info("Trying to delete deployment"); - assertThat(k8sclient.apps().deployments().withName(deploymentName).delete()).isTrue(); + assertThat(k8sclient.apps().statefulSets().withName(deploymentName).delete()).isTrue(); Awaitility.await() - .untilAsserted(() -> assertThat(k8sclient.apps().deployments().withName(deploymentName).get()).isNotNull()); + .untilAsserted(() -> assertThat(k8sclient.apps().statefulSets().withName(deploymentName).get()).isNotNull()); waitForKeycloakToBeReady(k8sclient, kc); // wait for reconciler to calm down to avoid race condititon Log.info("Trying to modify deployment"); - var deployment = k8sclient.apps().deployments().withName(deploymentName).get(); + var deployment = k8sclient.apps().statefulSets().withName(deploymentName).get(); var labels = Map.of("address", "EvergreenTerrace742"); var flandersEnvVar = new EnvVarBuilder().withName("NEIGHBOR").withValue("Stupid Flanders!").build(); - var origSpecs = new DeploymentSpecBuilder(deployment.getSpec()).build(); // deep copy + var origSpecs = new StatefulSetSpecBuilder(deployment.getSpec()).build(); // deep copy deployment.getMetadata().getLabels().putAll(labels); deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(List.of(flandersEnvVar)); - k8sclient.apps().deployments().createOrReplace(deployment); + k8sclient.apps().statefulSets().createOrReplace(deployment); Awaitility.await() .atMost(5, MINUTES) .pollDelay(1, SECONDS) .ignoreExceptions() .untilAsserted(() -> { - var d = k8sclient.apps().deployments().withName(deploymentName).get(); + var d = k8sclient.apps().statefulSets().withName(deploymentName).get(); assertThat(d.getMetadata().getLabels().entrySet().containsAll(labels.entrySet())).isTrue(); // additional labels should not be overwritten assertThat(d.getSpec()).isEqualTo(origSpecs); // specs should be reconciled back to original values }); @@ -286,15 +286,36 @@ public class KeycloakDeploymentTest extends BaseOperatorTest { @Test public void testInitialAdminUser() { try { + var kc = getDefaultKeycloakDeployment(); + var kcAdminSecret = new KeycloakAdminSecret(k8sclient, kc); + + k8sclient + .resources(Keycloak.class) + .inNamespace(namespace) + .delete(); + k8sclient + .secrets() + .inNamespace(namespace) + .withName(kcAdminSecret.getName()) + .delete(); + + // Making sure no other Keycloak pod is still around + Awaitility.await() + .ignoreExceptions() + .untilAsserted(() -> + assertThat(k8sclient + .pods() + .inNamespace(namespace) + .withLabel("app", "keycloak") + .list() + .getItems() + .size()).isZero()); // Recreating the database to keep this test isolated deleteDB(); deployDB(); - var kc = getDefaultKeycloakDeployment(); deployKeycloak(k8sclient, kc, true); - var decoder = Base64.getDecoder(); var service = new KeycloakService(k8sclient, kc); - var kcAdminSecret = new KeycloakAdminSecret(k8sclient, kc); AtomicReference adminUsername = new AtomicReference<>(); AtomicReference adminPassword = new AtomicReference<>(); diff --git a/operator/src/test/java/org/keycloak/operator/testsuite/integration/RealmImportTest.java b/operator/src/test/java/org/keycloak/operator/testsuite/integration/RealmImportTest.java index d3287673d9..0620676b53 100644 --- a/operator/src/test/java/org/keycloak/operator/testsuite/integration/RealmImportTest.java +++ b/operator/src/test/java/org/keycloak/operator/testsuite/integration/RealmImportTest.java @@ -36,6 +36,7 @@ import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.SECONDS; import static org.assertj.core.api.Assertions.assertThat; import static org.keycloak.operator.Constants.KEYCLOAK_HTTPS_PORT; +import static org.keycloak.operator.controllers.KeycloakDeployment.getEnvVarName; import static org.keycloak.operator.testsuite.utils.K8sUtils.deployKeycloak; import static org.keycloak.operator.testsuite.utils.K8sUtils.getDefaultKeycloakDeployment; import static org.keycloak.operator.testsuite.utils.K8sUtils.inClusterCurl; @@ -115,6 +116,10 @@ public class RealmImportTest extends BaseOperatorTest { CRAssert.assertKeycloakRealmImportStatusCondition(crSelector.get(), HAS_ERRORS, false); }); var job = k8sclient.batch().v1().jobs().inNamespace(namespace).withName("example-count0-kc").get(); + assertThat(job.getSpec().getTemplate().getMetadata().getLabels().get("app")).isEqualTo("keycloak-realm-import"); + var envvars = job.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv(); + assertThat(envvars.stream().filter(e -> e.getName().equals(getEnvVarName("cache"))).findAny().get().getValue()).isEqualTo("local"); + assertThat(envvars.stream().filter(e -> e.getName().equals(getEnvVarName("health-enabled"))).findAny().get().getValue()).isEqualTo("false"); assertThat(job.getSpec().getTemplate().getSpec().getImagePullSecrets().size()).isEqualTo(1); assertThat(job.getSpec().getTemplate().getSpec().getImagePullSecrets().get(0).getName()).isEqualTo("my-empty-secret"); diff --git a/operator/src/test/java/org/keycloak/operator/testsuite/integration/WatchedSecretsTest.java b/operator/src/test/java/org/keycloak/operator/testsuite/integration/WatchedSecretsTest.java index 17c876beca..dc966758fe 100644 --- a/operator/src/test/java/org/keycloak/operator/testsuite/integration/WatchedSecretsTest.java +++ b/operator/src/test/java/org/keycloak/operator/testsuite/integration/WatchedSecretsTest.java @@ -102,7 +102,21 @@ public class WatchedSecretsTest extends BaseOperatorTest { Log.info("Checking pod logs for DB auth failures"); var podlogs = getPodNamesForCrs(Set.of(kc)).stream() .filter(n -> !prevPodNames.contains(n)) // checking just new pods - .map(n -> k8sclient.pods().inNamespace(namespace).withName(n).getLog()) + .map(n -> { + var name = k8sclient + .pods() + .inNamespace(namespace) + .list() + .getItems() + .stream() + .filter(p -> (p.getMetadata().getName() + p.getMetadata().getCreationTimestamp()).equals(n)) + .findAny() + .get() + .getMetadata() + .getName(); + + return k8sclient.pods().inNamespace(namespace).withName(name).getLog(); + }) .collect(Collectors.toList()); assertThat(podlogs).anyMatch(l -> l.contains("password authentication failed for user \"" + username + "\"")); }); @@ -220,8 +234,13 @@ public class WatchedSecretsTest extends BaseOperatorTest { } private List getPodNamesForCrs(Set crs) { - return k8sclient.pods().inNamespace(namespace).list().getItems().stream() - .map(pod -> pod.getMetadata().getName()) + return k8sclient + .pods() + .inNamespace(namespace) + .list() + .getItems() + .stream() + .map(pod -> pod.getMetadata().getName() + pod.getMetadata().getCreationTimestamp()) .filter(pod -> crs.stream().map(c -> c.getMetadata().getName()).anyMatch(pod::startsWith)) .collect(Collectors.toList()); } diff --git a/operator/src/test/java/org/keycloak/operator/testsuite/unit/PodTemplateTest.java b/operator/src/test/java/org/keycloak/operator/testsuite/unit/PodTemplateTest.java index 7f8dad6b39..e7d2bb6e46 100644 --- a/operator/src/test/java/org/keycloak/operator/testsuite/unit/PodTemplateTest.java +++ b/operator/src/test/java/org/keycloak/operator/testsuite/unit/PodTemplateTest.java @@ -21,7 +21,7 @@ import io.fabric8.kubernetes.api.model.IntOrString; import io.fabric8.kubernetes.api.model.PodTemplateSpec; import io.fabric8.kubernetes.api.model.PodTemplateSpecBuilder; import io.fabric8.kubernetes.api.model.ProbeBuilder; -import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.fabric8.kubernetes.api.model.apps.StatefulSet; import io.quarkus.test.junit.QuarkusTest; import org.junit.jupiter.api.Test; import org.keycloak.operator.Config; @@ -36,7 +36,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; @QuarkusTest public class PodTemplateTest { - Deployment getDeployment(PodTemplateSpec podTemplate) { + StatefulSet getDeployment(PodTemplateSpec podTemplate) { var config = new Config(){ @Override public Keycloak keycloak() { @@ -58,8 +58,8 @@ public class PodTemplateTest { spec.setHostname("example.com"); spec.setTlsSecret("example-tls-secret"); kc.setSpec(spec); - var deployment = new KeycloakDeployment(null, config, kc, new Deployment(), "dummy-admin"); - return (Deployment) deployment.getReconciledResource().get(); + var deployment = new KeycloakDeployment(null, config, kc, new StatefulSet(), "dummy-admin"); + return (StatefulSet) deployment.getReconciledResource().get(); } @Test