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 e2c298f795..9ef6af9ec4 100644 --- a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakController.java +++ b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakController.java @@ -33,6 +33,7 @@ import io.javaoperatorsdk.operator.processing.event.source.EventSource; import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; import io.javaoperatorsdk.operator.processing.event.source.informer.Mappers; import io.quarkus.logging.Log; + import org.keycloak.operator.Config; import org.keycloak.operator.Constants; import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak; @@ -40,10 +41,11 @@ import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatus; import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusAggregator; import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusCondition; -import jakarta.inject.Inject; import java.util.Map; import java.util.concurrent.TimeUnit; +import jakarta.inject.Inject; + import static io.javaoperatorsdk.operator.api.reconciler.Constants.WATCH_CURRENT_NAMESPACE; @ControllerConfiguration(namespaces = WATCH_CURRENT_NAMESPACE) @@ -102,6 +104,13 @@ public class KeycloakController implements Reconciler, EventSourceInit Log.infof("--- Reconciling Keycloak: %s in namespace: %s", kcName, namespace); + if (kc.getSpec().getInstances() == null) { + // explicitly set defaults - and let another reconciliation happen + // this avoids ensuring unintentional modifications have not been made to the cr + kc.getSpec().setInstances(1); + return UpdateControl.updateResource(kc); + } + var statusAggregator = new KeycloakStatusAggregator(kc.getStatus(), kc.getMetadata().getGeneration()); var kcAdminSecret = new KeycloakAdminSecret(client, kc); diff --git a/operator/src/main/java/org/keycloak/operator/crds/v2alpha1/deployment/KeycloakSpec.java b/operator/src/main/java/org/keycloak/operator/crds/v2alpha1/deployment/KeycloakSpec.java index 56f79134cb..4ee48714ec 100644 --- a/operator/src/main/java/org/keycloak/operator/crds/v2alpha1/deployment/KeycloakSpec.java +++ b/operator/src/main/java/org/keycloak/operator/crds/v2alpha1/deployment/KeycloakSpec.java @@ -39,7 +39,7 @@ public class KeycloakSpec { @SpecReplicas @JsonPropertyDescription("Number of Keycloak instances in HA mode. Default is 1.") - private int instances = 1; + private Integer instances; @JsonPropertyDescription("Custom Keycloak image to be used.") private String image; @@ -137,11 +137,11 @@ public class KeycloakSpec { this.hostnameSpec = hostnameSpec; } - public int getInstances() { + public Integer getInstances() { return instances; } - public void setInstances(int instances) { + public void setInstances(Integer instances) { this.instances = instances; } diff --git a/operator/src/test/java/org/keycloak/operator/testsuite/integration/CRDTest.java b/operator/src/test/java/org/keycloak/operator/testsuite/integration/CRDTest.java index ccd8812ce4..c84262843f 100644 --- a/operator/src/test/java/org/keycloak/operator/testsuite/integration/CRDTest.java +++ b/operator/src/test/java/org/keycloak/operator/testsuite/integration/CRDTest.java @@ -33,6 +33,7 @@ import java.io.FileNotFoundException; import com.fasterxml.jackson.databind.ObjectMapper; import org.keycloak.operator.crds.v2alpha1.realmimport.KeycloakRealmImportBuilder; +import org.keycloak.operator.testsuite.utils.K8sUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -82,6 +83,14 @@ public class CRDTest { assertThat(kc.getMetadata().getAnnotations()).containsEntry("x", "y"); } + @Test + public void testKeycloakWithDefaultReplicas() { + var kc = K8sUtils.getDefaultKeycloakDeployment(); + kc.getSpec().setInstances(null); + + assertThat(client.resource(kc).create().getSpec().getInstances()).isNull(); + } + private void roundTrip(String resourceFile, Class type) { // could also test the status, but that is not part of the expected files // also to test the status we may need the operator to not be running, which diff --git a/operator/src/test/java/org/keycloak/operator/testsuite/integration/ClusteringTest.java b/operator/src/test/java/org/keycloak/operator/testsuite/integration/ClusteringTest.java index 4bb8fc4673..4813670059 100644 --- a/operator/src/test/java/org/keycloak/operator/testsuite/integration/ClusteringTest.java +++ b/operator/src/test/java/org/keycloak/operator/testsuite/integration/ClusteringTest.java @@ -108,8 +108,9 @@ public class ClusteringTest extends BaseOperatorTest { @Test public void testKeycloakScaleAsExpected() { - // given + // given a starting point of a default keycloak with null/default instances var kc = getTestKeycloakDeployment(false); + kc.getSpec().setInstances(null); var crSelector = k8sclient.resource(kc); K8sUtils.deployKeycloak(k8sclient, kc, true);