parent
31db84e924
commit
075d913037
13 changed files with 181 additions and 64 deletions
|
@ -18,8 +18,10 @@ package org.keycloak.operator;
|
|||
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.ValueOrSecret;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public final class Constants {
|
||||
|
@ -33,10 +35,10 @@ public final class Constants {
|
|||
public static final String COMPONENT_LABEL = "app.kubernetes.io/component";
|
||||
public static final String KEYCLOAK_COMPONENT_LABEL = "keycloak.org/component";
|
||||
|
||||
public static final Map<String, String> DEFAULT_LABELS = Map.of(
|
||||
public static final Map<String, String> DEFAULT_LABELS = Collections.unmodifiableMap(new TreeMap<>(Map.of(
|
||||
"app", NAME,
|
||||
MANAGED_BY_LABEL, MANAGED_BY_VALUE
|
||||
);
|
||||
)));
|
||||
|
||||
public static final String DEFAULT_LABELS_AS_STRING = DEFAULT_LABELS.entrySet().stream()
|
||||
.map(e -> e.getKey() + "=" + e.getValue())
|
||||
|
|
|
@ -37,7 +37,7 @@ import org.keycloak.operator.Config;
|
|||
import org.keycloak.operator.Constants;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatus;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusBuilder;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusAggregator;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusCondition;
|
||||
|
||||
import jakarta.inject.Inject;
|
||||
|
@ -92,13 +92,13 @@ public class KeycloakController implements Reconciler<Keycloak>, EventSourceInit
|
|||
}
|
||||
|
||||
@Override
|
||||
public UpdateControl<Keycloak> reconcile(Keycloak kc, Context context) {
|
||||
public UpdateControl<Keycloak> reconcile(Keycloak kc, Context<Keycloak> context) {
|
||||
String kcName = kc.getMetadata().getName();
|
||||
String namespace = kc.getMetadata().getNamespace();
|
||||
|
||||
Log.infof("--- Reconciling Keycloak: %s in namespace: %s", kcName, namespace);
|
||||
|
||||
var statusBuilder = new KeycloakStatusBuilder();
|
||||
var statusAggregator = new KeycloakStatusAggregator();
|
||||
|
||||
var kcAdminSecret = new KeycloakAdminSecret(client, kc);
|
||||
kcAdminSecret.createOrUpdateReconciled();
|
||||
|
@ -112,21 +112,21 @@ public class KeycloakController implements Reconciler<Keycloak>, EventSourceInit
|
|||
Log.info("Config Secrets modified, restarting deployment");
|
||||
kcDeployment.rollingRestart();
|
||||
}
|
||||
kcDeployment.updateStatus(statusBuilder);
|
||||
kcDeployment.updateStatus(statusAggregator);
|
||||
watchedSecrets.createOrUpdateReconciled();
|
||||
|
||||
var kcService = new KeycloakService(client, kc);
|
||||
kcService.updateStatus(statusBuilder);
|
||||
kcService.updateStatus(statusAggregator);
|
||||
kcService.createOrUpdateReconciled();
|
||||
var kcDiscoveryService = new KeycloakDiscoveryService(client, kc);
|
||||
kcDiscoveryService.updateStatus(statusBuilder);
|
||||
kcDiscoveryService.updateStatus(statusAggregator);
|
||||
kcDiscoveryService.createOrUpdateReconciled();
|
||||
|
||||
var kcIngress = new KeycloakIngress(client, kc);
|
||||
kcIngress.updateStatus(statusBuilder);
|
||||
kcIngress.updateStatus(statusAggregator);
|
||||
kcIngress.createOrUpdateReconciled();
|
||||
|
||||
var status = statusBuilder.build();
|
||||
var status = statusAggregator.build();
|
||||
|
||||
Log.info("--- Reconciliation finished successfully");
|
||||
|
||||
|
@ -152,7 +152,7 @@ public class KeycloakController implements Reconciler<Keycloak>, EventSourceInit
|
|||
@Override
|
||||
public ErrorStatusUpdateControl<Keycloak> updateErrorStatus(Keycloak kc, Context<Keycloak> context, Exception e) {
|
||||
Log.error("--- Error reconciling", e);
|
||||
KeycloakStatus status = new KeycloakStatusBuilder()
|
||||
KeycloakStatus status = new KeycloakStatusAggregator()
|
||||
.addErrorMessage("Error performing operations:\n" + e.getMessage())
|
||||
.build();
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ import org.keycloak.common.util.CollectionUtil;
|
|||
import org.keycloak.operator.Config;
|
||||
import org.keycloak.operator.Constants;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusBuilder;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusAggregator;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.ValueOrSecret;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
@ -41,6 +41,7 @@ import java.util.ArrayList;
|
|||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
@ -50,7 +51,7 @@ import java.util.stream.Collectors;
|
|||
|
||||
import static org.keycloak.operator.crds.v2alpha1.CRDUtils.isTlsConfigured;
|
||||
|
||||
public class KeycloakDeployment extends OperatorManagedResource implements StatusUpdater<KeycloakStatusBuilder> {
|
||||
public class KeycloakDeployment extends OperatorManagedResource implements StatusUpdater<KeycloakStatusAggregator> {
|
||||
|
||||
private final Config operatorConfig;
|
||||
private final KeycloakDistConfigurator distConfigurator;
|
||||
|
@ -122,7 +123,7 @@ public class KeycloakDeployment extends OperatorManagedResource implements Statu
|
|||
.get();
|
||||
}
|
||||
|
||||
public void validatePodTemplate(KeycloakStatusBuilder status) {
|
||||
public void validatePodTemplate(KeycloakStatusAggregator status) {
|
||||
if (keycloakCR.getSpec() == null ||
|
||||
keycloakCR.getSpec().getUnsupported() == null ||
|
||||
keycloakCR.getSpec().getUnsupported().getPodTemplate() == null) {
|
||||
|
@ -379,7 +380,7 @@ public class KeycloakDeployment extends OperatorManagedResource implements Statu
|
|||
baseDeployment.getSpec().getSelector().setMatchLabels(Constants.DEFAULT_LABELS);
|
||||
baseDeployment.getSpec().setReplicas(keycloakCR.getSpec().getInstances());
|
||||
|
||||
Map<String, String> labels = new HashMap<>(Constants.DEFAULT_LABELS);
|
||||
Map<String, String> labels = new LinkedHashMap<>(Constants.DEFAULT_LABELS);
|
||||
if (operatorConfig.keycloak().podLabels() != null) {
|
||||
labels.putAll(operatorConfig.keycloak().podLabels());
|
||||
}
|
||||
|
@ -492,18 +493,22 @@ public class KeycloakDeployment extends OperatorManagedResource implements Statu
|
|||
return envVars;
|
||||
}
|
||||
|
||||
public void updateStatus(KeycloakStatusBuilder status) {
|
||||
public void updateStatus(KeycloakStatusAggregator status) {
|
||||
status.apply(b -> b.withSelector(Constants.DEFAULT_LABELS_AS_STRING));
|
||||
validatePodTemplate(status);
|
||||
if (existingDeployment == null) {
|
||||
status.addNotReadyMessage("No existing StatefulSet found, waiting for creating a new one");
|
||||
return;
|
||||
}
|
||||
|
||||
if (existingDeployment.getStatus() == null
|
||||
|| existingDeployment.getStatus().getReadyReplicas() == null
|
||||
|| existingDeployment.getStatus().getReadyReplicas() < keycloakCR.getSpec().getInstances()) {
|
||||
if (existingDeployment.getStatus() == null) {
|
||||
status.addNotReadyMessage("Waiting for deployment status");
|
||||
} else {
|
||||
status.apply(b -> b.withInstances(existingDeployment.getStatus().getReadyReplicas()));
|
||||
if (Optional.ofNullable(existingDeployment.getStatus().getReadyReplicas()).orElse(0) < keycloakCR.getSpec().getInstances()) {
|
||||
status.addNotReadyMessage("Waiting for more replicas");
|
||||
}
|
||||
}
|
||||
|
||||
if (migrationInProgress) {
|
||||
status.addNotReadyMessage("Performing Keycloak upgrade, scaling down the deployment");
|
||||
|
|
|
@ -24,11 +24,11 @@ import io.fabric8.kubernetes.api.model.ServiceSpecBuilder;
|
|||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
import org.keycloak.operator.Constants;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusBuilder;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusAggregator;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class KeycloakDiscoveryService extends OperatorManagedResource implements StatusUpdater<KeycloakStatusBuilder> {
|
||||
public class KeycloakDiscoveryService extends OperatorManagedResource implements StatusUpdater<KeycloakStatusAggregator> {
|
||||
|
||||
private Service existingService;
|
||||
|
||||
|
@ -78,7 +78,7 @@ public class KeycloakDiscoveryService extends OperatorManagedResource implements
|
|||
.get();
|
||||
}
|
||||
|
||||
public void updateStatus(KeycloakStatusBuilder status) {
|
||||
public void updateStatus(KeycloakStatusAggregator status) {
|
||||
if (existingService == null) {
|
||||
status.addNotReadyMessage("No existing Discovery Service found, waiting for creating a new one");
|
||||
return;
|
||||
|
|
|
@ -29,7 +29,7 @@ import io.quarkus.logging.Log;
|
|||
import org.keycloak.common.util.CollectionUtil;
|
||||
import org.keycloak.operator.Constants;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusBuilder;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusAggregator;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.ValueOrSecret;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.spec.DatabaseSpec;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.spec.FeatureSpec;
|
||||
|
@ -85,7 +85,7 @@ public class KeycloakDistConfigurator {
|
|||
*
|
||||
* @param status Keycloak Status builder
|
||||
*/
|
||||
public void validateOptions(KeycloakStatusBuilder status) {
|
||||
public void validateOptions(KeycloakStatusAggregator status) {
|
||||
assumeFirstClassCitizens(status);
|
||||
}
|
||||
|
||||
|
@ -175,7 +175,7 @@ public class KeycloakDistConfigurator {
|
|||
*
|
||||
* @param status Status of the deployment
|
||||
*/
|
||||
protected void assumeFirstClassCitizens(KeycloakStatusBuilder status) {
|
||||
protected void assumeFirstClassCitizens(KeycloakStatusAggregator status) {
|
||||
final var serverConfigNames = keycloakCR
|
||||
.getSpec()
|
||||
.getAdditionalOptions()
|
||||
|
|
|
@ -23,14 +23,14 @@ import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
|
|||
import org.keycloak.operator.Constants;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.spec.IngressSpec;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusBuilder;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusAggregator;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.keycloak.operator.crds.v2alpha1.CRDUtils.isTlsConfigured;
|
||||
|
||||
public class KeycloakIngress extends OperatorManagedResource implements StatusUpdater<KeycloakStatusBuilder> {
|
||||
public class KeycloakIngress extends OperatorManagedResource implements StatusUpdater<KeycloakStatusAggregator> {
|
||||
|
||||
private final Ingress existingIngress;
|
||||
private final Keycloak keycloak;
|
||||
|
@ -142,7 +142,7 @@ public class KeycloakIngress extends OperatorManagedResource implements StatusUp
|
|||
.get();
|
||||
}
|
||||
|
||||
public void updateStatus(KeycloakStatusBuilder status) {
|
||||
public void updateStatus(KeycloakStatusAggregator status) {
|
||||
IngressSpec ingressSpec = keycloak.getSpec().getIngressSpec();
|
||||
if (ingressSpec == null) {
|
||||
ingressSpec = new IngressSpec();
|
||||
|
|
|
@ -24,7 +24,7 @@ import io.fabric8.kubernetes.api.model.ServiceSpecBuilder;
|
|||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
import org.keycloak.operator.Constants;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusBuilder;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusAggregator;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.spec.HttpSpec;
|
||||
|
||||
import java.util.Optional;
|
||||
|
@ -32,7 +32,7 @@ import java.util.Optional;
|
|||
import static org.keycloak.operator.crds.v2alpha1.CRDUtils.getValueFromSubSpec;
|
||||
import static org.keycloak.operator.crds.v2alpha1.CRDUtils.isTlsConfigured;
|
||||
|
||||
public class KeycloakService extends OperatorManagedResource implements StatusUpdater<KeycloakStatusBuilder> {
|
||||
public class KeycloakService extends OperatorManagedResource implements StatusUpdater<KeycloakStatusAggregator> {
|
||||
|
||||
private Service existingService;
|
||||
private final Keycloak keycloak;
|
||||
|
@ -84,7 +84,7 @@ public class KeycloakService extends OperatorManagedResource implements StatusUp
|
|||
.get();
|
||||
}
|
||||
|
||||
public void updateStatus(KeycloakStatusBuilder status) {
|
||||
public void updateStatus(KeycloakStatusAggregator status) {
|
||||
if (existingService == null) {
|
||||
status.addNotReadyMessage("No existing Keycloak Service found, waiting for creating a new one");
|
||||
return;
|
||||
|
|
|
@ -21,6 +21,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
|||
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.LocalObjectReference;
|
||||
import io.fabric8.kubernetes.model.annotation.SpecReplicas;
|
||||
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.spec.DatabaseSpec;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.spec.FeatureSpec;
|
||||
|
@ -36,6 +37,7 @@ import java.util.List;
|
|||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class KeycloakSpec {
|
||||
|
||||
@SpecReplicas
|
||||
@JsonPropertyDescription("Number of Keycloak instances in HA mode. Default is 1.")
|
||||
private int instances = 1;
|
||||
|
||||
|
|
|
@ -19,12 +19,39 @@ package org.keycloak.operator.crds.v2alpha1.deployment;
|
|||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import io.fabric8.kubernetes.model.annotation.LabelSelector;
|
||||
import io.fabric8.kubernetes.model.annotation.StatusReplicas;
|
||||
import io.sundr.builder.annotations.Buildable;
|
||||
|
||||
/**
|
||||
* @author Vaclav Muzikar <vmuzikar@redhat.com>
|
||||
*/
|
||||
@Buildable(editableEnabled = false, builderPackage = "io.fabric8.kubernetes.api.builder", lazyCollectionInitEnabled = false)
|
||||
public class KeycloakStatus {
|
||||
|
||||
@LabelSelector
|
||||
private String selector;
|
||||
@StatusReplicas
|
||||
private Integer instances;
|
||||
|
||||
private List<KeycloakStatusCondition> conditions;
|
||||
|
||||
public String getSelector() {
|
||||
return selector;
|
||||
}
|
||||
|
||||
public void setSelector(String selector) {
|
||||
this.selector = selector;
|
||||
}
|
||||
|
||||
public Integer getInstances() {
|
||||
return instances;
|
||||
}
|
||||
|
||||
public void setInstances(Integer instances) {
|
||||
this.instances = instances;
|
||||
}
|
||||
|
||||
public List<KeycloakStatusCondition> getConditions() {
|
||||
return conditions;
|
||||
}
|
||||
|
@ -38,11 +65,13 @@ public class KeycloakStatus {
|
|||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
KeycloakStatus status = (KeycloakStatus) o;
|
||||
return Objects.equals(getConditions(), status.getConditions());
|
||||
return Objects.equals(getConditions(), status.getConditions())
|
||||
&& Objects.equals(getInstances(), status.getInstances())
|
||||
&& Objects.equals(getSelector(), status.getSelector());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getConditions());
|
||||
return Objects.hash(getConditions(), getInstances(), getSelector());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,11 +19,12 @@ package org.keycloak.operator.crds.v2alpha1.deployment;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* @author Vaclav Muzikar <vmuzikar@redhat.com>
|
||||
*/
|
||||
public class KeycloakStatusBuilder {
|
||||
public class KeycloakStatusAggregator {
|
||||
private final KeycloakStatusCondition readyCondition;
|
||||
private final KeycloakStatusCondition hasErrorsCondition;
|
||||
private final KeycloakStatusCondition rollingUpdate;
|
||||
|
@ -32,7 +33,9 @@ public class KeycloakStatusBuilder {
|
|||
private final List<String> errorMessages = new ArrayList<>();
|
||||
private final List<String> rollingUpdateMessages = new ArrayList<>();
|
||||
|
||||
public KeycloakStatusBuilder() {
|
||||
private final KeycloakStatusBuilder statusBuilder = new KeycloakStatusBuilder();
|
||||
|
||||
public KeycloakStatusAggregator() {
|
||||
readyCondition = new KeycloakStatusCondition();
|
||||
readyCondition.setType(KeycloakStatusCondition.READY);
|
||||
readyCondition.setStatus(true);
|
||||
|
@ -46,36 +49,46 @@ public class KeycloakStatusBuilder {
|
|||
rollingUpdate.setStatus(false);
|
||||
}
|
||||
|
||||
public KeycloakStatusBuilder addNotReadyMessage(String message) {
|
||||
public KeycloakStatusAggregator addNotReadyMessage(String message) {
|
||||
readyCondition.setStatus(false);
|
||||
notReadyMessages.add(message);
|
||||
return this;
|
||||
}
|
||||
|
||||
public KeycloakStatusBuilder addErrorMessage(String message) {
|
||||
public KeycloakStatusAggregator addErrorMessage(String message) {
|
||||
hasErrorsCondition.setStatus(true);
|
||||
errorMessages.add(message);
|
||||
return this;
|
||||
}
|
||||
|
||||
public KeycloakStatusBuilder addWarningMessage(String message) {
|
||||
public KeycloakStatusAggregator addWarningMessage(String message) {
|
||||
errorMessages.add("warning: " + message);
|
||||
return this;
|
||||
}
|
||||
|
||||
public KeycloakStatusBuilder addRollingUpdateMessage(String message) {
|
||||
public KeycloakStatusAggregator addRollingUpdateMessage(String message) {
|
||||
rollingUpdate.setStatus(true);
|
||||
rollingUpdateMessages.add(message);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply non-condition changes to the status
|
||||
*/
|
||||
public KeycloakStatusAggregator apply(Consumer<KeycloakStatusBuilder> toApply) {
|
||||
statusBuilder.withConditions(List.of());
|
||||
toApply.accept(statusBuilder);
|
||||
if (!statusBuilder.getConditions().isEmpty()) {
|
||||
throw new AssertionError("use addXXXMessage methods to modify conditions");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public KeycloakStatus build() {
|
||||
readyCondition.setMessage(String.join("\n", notReadyMessages));
|
||||
hasErrorsCondition.setMessage(String.join("\n", errorMessages));
|
||||
rollingUpdate.setMessage(String.join("\n", rollingUpdateMessages));
|
||||
|
||||
KeycloakStatus status = new KeycloakStatus();
|
||||
status.setConditions(List.of(readyCondition, hasErrorsCondition, rollingUpdate));
|
||||
return status;
|
||||
return statusBuilder.withConditions(List.of(readyCondition, hasErrorsCondition, rollingUpdate)).build();
|
||||
}
|
||||
}
|
|
@ -17,29 +17,31 @@
|
|||
|
||||
package org.keycloak.operator.testsuite.integration;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import io.fabric8.kubernetes.client.utils.Serialization;
|
||||
import io.quarkus.logging.Log;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.RestAssured;
|
||||
|
||||
import org.awaitility.Awaitility;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.operator.Constants;
|
||||
import org.keycloak.operator.testsuite.utils.CRAssert;
|
||||
import org.keycloak.operator.controllers.KeycloakService;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
|
||||
import org.keycloak.operator.testsuite.utils.K8sUtils;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusCondition;
|
||||
import org.keycloak.operator.crds.v2alpha1.realmimport.KeycloakRealmImport;
|
||||
import org.keycloak.operator.crds.v2alpha1.realmimport.KeycloakRealmImportStatusCondition;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusCondition;
|
||||
import org.keycloak.operator.testsuite.utils.CRAssert;
|
||||
import org.keycloak.operator.testsuite.utils.K8sUtils;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
||||
@QuarkusTest
|
||||
public class ClusteringTest extends BaseOperatorTest {
|
||||
|
||||
|
@ -55,11 +57,29 @@ public class ClusteringTest extends BaseOperatorTest {
|
|||
|
||||
var kcPodsSelector = k8sclient.pods().inNamespace(namespace).withLabel("app", "keycloak");
|
||||
|
||||
var scale = crSelector.scale();
|
||||
assertThat(scale.getSpec().getReplicas()).isEqualTo(1);
|
||||
assertThat(scale.getStatus().getReplicas()).isEqualTo(1);
|
||||
assertThat(scale.getStatus().getSelector()).isEqualTo(Constants.DEFAULT_LABELS_AS_STRING);
|
||||
|
||||
// when scale it to 0
|
||||
Keycloak scaled = crSelector.scale(0);
|
||||
assertThat(scaled.getSpec().getInstances()).isEqualTo(0);
|
||||
|
||||
Awaitility.await()
|
||||
.atMost(Duration.ofSeconds(60))
|
||||
.ignoreExceptions()
|
||||
.untilAsserted(() -> assertThat(Optional.ofNullable(crSelector.scale().getStatus().getReplicas()).orElse(0)).isEqualTo(0));
|
||||
|
||||
Awaitility.await()
|
||||
.atMost(1, MINUTES)
|
||||
.pollDelay(1, SECONDS)
|
||||
.ignoreExceptions()
|
||||
.untilAsserted(() -> CRAssert.assertKeycloakStatusCondition(crSelector.get(), KeycloakStatusCondition.READY, true));
|
||||
|
||||
// when scale it to 3
|
||||
crSelector.accept(keycloak -> {
|
||||
keycloak.getMetadata().setResourceVersion(null);
|
||||
keycloak.getSpec().setInstances(3);
|
||||
});
|
||||
crSelector.scale(3);
|
||||
assertThat(crSelector.scale().getSpec().getReplicas()).isEqualTo(3);
|
||||
|
||||
Awaitility.await()
|
||||
.atMost(1, MINUTES)
|
||||
|
@ -68,15 +88,19 @@ public class ClusteringTest extends BaseOperatorTest {
|
|||
.untilAsserted(() -> CRAssert.assertKeycloakStatusCondition(crSelector.get(), KeycloakStatusCondition.READY, false));
|
||||
|
||||
Awaitility.await()
|
||||
.atMost(Duration.ofSeconds(60))
|
||||
.atMost(Duration.ofSeconds(180))
|
||||
.ignoreExceptions()
|
||||
.untilAsserted(() -> assertThat(kcPodsSelector.list().getItems().size()).isEqualTo(3));
|
||||
|
||||
Awaitility.await()
|
||||
.atMost(Duration.ofSeconds(60))
|
||||
.ignoreExceptions()
|
||||
.untilAsserted(() -> assertThat(crSelector.scale().getStatus().getReplicas()).isEqualTo(3));
|
||||
|
||||
// when scale it down to 2
|
||||
crSelector.accept(keycloak -> {
|
||||
keycloak.getMetadata().setResourceVersion(null);
|
||||
keycloak.getSpec().setInstances(2);
|
||||
});
|
||||
crSelector.scale(2);
|
||||
assertThat(crSelector.scale().getSpec().getReplicas()).isEqualTo(2);
|
||||
|
||||
Awaitility.await()
|
||||
.atMost(Duration.ofSeconds(180))
|
||||
.ignoreExceptions()
|
||||
|
@ -88,6 +112,11 @@ public class ClusteringTest extends BaseOperatorTest {
|
|||
.ignoreExceptions()
|
||||
.untilAsserted(() -> CRAssert.assertKeycloakStatusCondition(crSelector.get(), KeycloakStatusCondition.READY, true));
|
||||
|
||||
Awaitility.await()
|
||||
.atMost(Duration.ofSeconds(60))
|
||||
.ignoreExceptions()
|
||||
.untilAsserted(() -> assertThat(crSelector.scale().getStatus().getReplicas()).isEqualTo(2));
|
||||
|
||||
// get the service
|
||||
var service = new KeycloakService(k8sclient, kc);
|
||||
String url = "https://" + service.getName() + "." + namespace + ":" + Constants.KEYCLOAK_HTTPS_PORT;
|
||||
|
|
|
@ -28,7 +28,7 @@ import org.keycloak.operator.Constants;
|
|||
import org.keycloak.operator.controllers.KeycloakDistConfigurator;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatus;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusBuilder;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusAggregator;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusCondition;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.ValueOrSecret;
|
||||
import org.keycloak.operator.testsuite.utils.K8sUtils;
|
||||
|
@ -208,7 +208,7 @@ public class KeycloakDistConfiguratorTest {
|
|||
|
||||
private void assertWarningStatusFirstClassFields(KeycloakDistConfigurator distConfig, boolean expectWarning, Collection<String> firstClassFields) {
|
||||
final String message = "warning: You need to specify these fields as the first-class citizen of the CR: ";
|
||||
final KeycloakStatusBuilder statusBuilder = new KeycloakStatusBuilder();
|
||||
final KeycloakStatusAggregator statusBuilder = new KeycloakStatusAggregator();
|
||||
distConfig.validateOptions(statusBuilder);
|
||||
final KeycloakStatus status = statusBuilder.build();
|
||||
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2022 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.operator.testsuite.unit;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatus;
|
||||
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusAggregator;
|
||||
|
||||
public class KeycloakStatusTest {
|
||||
|
||||
@Test
|
||||
public void testEqualityWithScale() {
|
||||
KeycloakStatus status1 = new KeycloakStatusAggregator().apply(b -> b.withInstances(1)).build();
|
||||
|
||||
KeycloakStatus status2 = new KeycloakStatusAggregator().apply(b -> b.withInstances(2)).build();
|
||||
|
||||
assertNotEquals(status1, status2);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue