Switch to StatefulSet (#12757)
This commit is contained in:
parent
46b4b0851d
commit
f2d71cd1c7
9 changed files with 158 additions and 99 deletions
|
@ -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<Keycloak>, EventSourceInit
|
|||
public List<EventSource> prepareEventSources(EventSourceContext<Keycloak> context) {
|
||||
String namespace = context.getConfigurationService().getClientConfiguration().getNamespace();
|
||||
|
||||
SharedIndexInformer<Deployment> deploymentInformer =
|
||||
client.apps().deployments().inNamespace(namespace)
|
||||
SharedIndexInformer<StatefulSet> deploymentInformer =
|
||||
client.apps().statefulSets().inNamespace(namespace)
|
||||
.withLabels(Constants.DEFAULT_LABELS)
|
||||
.runnableInformer(0);
|
||||
|
||||
|
|
|
@ -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<String> 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<HasMetadata> 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<String> 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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -9,7 +9,7 @@ rules:
|
|||
# https://github.com/fabric8io/kubernetes-client/issues/3996
|
||||
- extensions
|
||||
resources:
|
||||
- deployments
|
||||
- statefulsets
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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<String> adminUsername = new AtomicReference<>();
|
||||
AtomicReference<String> adminPassword = new AtomicReference<>();
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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<String> getPodNamesForCrs(Set<Keycloak> 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());
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue