diff --git a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakDeploymentConfig.java b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakDeploymentConfig.java index 7761e64da2..18a6ee2353 100644 --- a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakDeploymentConfig.java +++ b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakDeploymentConfig.java @@ -57,7 +57,8 @@ public class KeycloakDeploymentConfig { "hostname", "tlsSecret", "features", - "features-disabled" + "features-disabled", + "transaction-xa-enabled" ); /** @@ -67,6 +68,7 @@ public class KeycloakDeploymentConfig { configureHostname(); configureTLS(); configureFeatures(); + configureTransactions(); } /** @@ -210,6 +212,21 @@ public class KeycloakDeploymentConfig { } } + public void configureTransactions() { + var transactionsSpec = keycloakCR.getSpec().getTransactionsSpec(); + if (transactionsSpec == null) return; + + var kcContainer = deployment.getSpec().getTemplate().getSpec().getContainers().get(0); + var envVars = kcContainer.getEnv(); + + if (transactionsSpec.isXaEnabled() != null) { + envVars.add(new EnvVarBuilder() + .withName("KC_TRANSACTION_XA_ENABLED") + .withValue(String.valueOf(transactionsSpec.isXaEnabled())) + .build()); + } + } + /* ---------- END of configuration of first-class citizen fields ---------- */ protected String readConfigurationValue(String key) { 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 5320fc9962..e5b1de3cde 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 @@ -23,6 +23,7 @@ import com.fasterxml.jackson.annotation.JsonPropertyDescription; import io.fabric8.kubernetes.api.model.LocalObjectReference; import org.keycloak.operator.Constants; import org.keycloak.operator.crds.v2alpha1.deployment.spec.FeatureSpec; +import org.keycloak.operator.crds.v2alpha1.deployment.spec.TransactionsSpec; import javax.validation.constraints.NotNull; import java.util.List; @@ -67,6 +68,10 @@ public class KeycloakSpec { @JsonPropertyDescription("In this section you can configure Keycloak features, which should be enabled/disabled.") private FeatureSpec featureSpec; + @JsonProperty("transaction") + @JsonPropertyDescription("In this section you can find all properties related to the settings of transaction behavior.") + private TransactionsSpec transactionsSpec; + public String getHostname() { return hostname; } @@ -117,6 +122,14 @@ public class KeycloakSpec { this.featureSpec = featureSpec; } + public TransactionsSpec getTransactionsSpec() { + return transactionsSpec; + } + + public void setTransactionsSpec(TransactionsSpec transactionsSpec) { + this.transactionsSpec = transactionsSpec; + } + public int getInstances() { return instances; } diff --git a/operator/src/main/java/org/keycloak/operator/crds/v2alpha1/deployment/spec/TransactionsSpec.java b/operator/src/main/java/org/keycloak/operator/crds/v2alpha1/deployment/spec/TransactionsSpec.java new file mode 100644 index 0000000000..b859acc14c --- /dev/null +++ b/operator/src/main/java/org/keycloak/operator/crds/v2alpha1/deployment/spec/TransactionsSpec.java @@ -0,0 +1,38 @@ +/* + * 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.crds.v2alpha1.deployment.spec; + +import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import io.sundr.builder.annotations.Buildable; + +import java.io.Serializable; + +@Buildable(editableEnabled = false, builderPackage = "io.fabric8.kubernetes.api.builder") +public class TransactionsSpec implements Serializable { + + @JsonPropertyDescription("Determine whether Keycloak should use a non-XA datasource in case the database does not support XA transactions.") + private Boolean xaEnabled; + + public Boolean isXaEnabled() { + return xaEnabled; + } + + public void setXaEnabled(Boolean xaEnabled) { + this.xaEnabled = xaEnabled; + } +} diff --git a/operator/src/test/java/org/keycloak/operator/testsuite/unit/CRSerializationTest.java b/operator/src/test/java/org/keycloak/operator/testsuite/unit/CRSerializationTest.java index 8888ee651f..202cbccd69 100644 --- a/operator/src/test/java/org/keycloak/operator/testsuite/unit/CRSerializationTest.java +++ b/operator/src/test/java/org/keycloak/operator/testsuite/unit/CRSerializationTest.java @@ -22,6 +22,7 @@ import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak; import org.keycloak.operator.crds.v2alpha1.deployment.spec.FeatureSpec; +import org.keycloak.operator.crds.v2alpha1.deployment.spec.TransactionsSpec; import java.util.List; @@ -40,6 +41,11 @@ public class CRSerializationTest { assertEquals("my-image", keycloak.getSpec().getImage()); assertEquals("my-tls-secret", keycloak.getSpec().getTlsSecret()); assertTrue(keycloak.getSpec().isDisableDefaultIngress()); + + final TransactionsSpec transactionsSpec = keycloak.getSpec().getTransactionsSpec(); + assertThat(transactionsSpec, notNullValue()); + assertThat(transactionsSpec.isXaEnabled(), notNullValue()); + assertThat(transactionsSpec.isXaEnabled(), CoreMatchers.is(false)); } @Test diff --git a/operator/src/test/java/org/keycloak/operator/testsuite/unit/KeycloakDeploymentConfigTest.java b/operator/src/test/java/org/keycloak/operator/testsuite/unit/KeycloakDeploymentConfigTest.java index c421c92f9a..e354aeec01 100644 --- a/operator/src/test/java/org/keycloak/operator/testsuite/unit/KeycloakDeploymentConfigTest.java +++ b/operator/src/test/java/org/keycloak/operator/testsuite/unit/KeycloakDeploymentConfigTest.java @@ -31,6 +31,7 @@ import org.keycloak.operator.testsuite.utils.K8sUtils; import java.util.Collections; import java.util.List; +import java.util.function.Consumer; import static org.assertj.core.api.Assertions.assertThat; @@ -39,29 +40,37 @@ public class KeycloakDeploymentConfigTest { @Test public void enabledFeatures() { - testFeatures(true, "docker", "authorization"); + testFirstClassCitizenEnvVars("KC_FEATURES", KeycloakDeploymentConfig::configureFeatures, "docker", "authorization"); } @Test public void disabledFeatures() { - testFeatures(false, "admin", "step-up-authentication"); + testFirstClassCitizenEnvVars("KC_FEATURES_DISABLED", KeycloakDeploymentConfig::configureFeatures, "admin", "step-up-authentication"); } - private void testFeatures(boolean enabledFeatures, String... features) { - final String featureEnvVar = enabledFeatures ? "KC_FEATURES" : "KC_FEATURES_DISABLED"; + @Test + public void transactions() { + testFirstClassCitizenEnvVars("KC_TRANSACTION_XA_ENABLED", KeycloakDeploymentConfig::configureTransactions, "false"); + } - final Keycloak keycloak = K8sUtils.getResourceFromFile("/test-serialization-keycloak-cr.yml", Keycloak.class); + /* UTILS */ + private void testFirstClassCitizenEnvVars(String varName, Consumer config, String... expectedValues) { + testFirstClassCitizenEnvVars("/test-serialization-keycloak-cr.yml", varName, config, expectedValues); + } + + private void testFirstClassCitizenEnvVars(String crName, String varName, Consumer config, String... expectedValues) { + final Keycloak keycloak = K8sUtils.getResourceFromFile(crName, Keycloak.class); final StatefulSet deployment = getBasicKcDeployment(); final KeycloakDeploymentConfig deploymentConfig = new KeycloakDeploymentConfig(keycloak, deployment, null); final Container container = deployment.getSpec().getTemplate().getSpec().getContainers().get(0); assertThat(container).isNotNull(); - assertEnvVarNotPresent(container.getEnv(), featureEnvVar); + assertEnvVarNotPresent(container.getEnv(), varName); - deploymentConfig.configureFeatures(); + config.accept(deploymentConfig); - assertContainerEnvVar(container.getEnv(), featureEnvVar, features); + assertContainerEnvVar(container.getEnv(), varName, expectedValues); } /** diff --git a/operator/src/test/resources/test-serialization-keycloak-cr.yml b/operator/src/test/resources/test-serialization-keycloak-cr.yml index 5d20ad26cf..ac4154d7e0 100644 --- a/operator/src/test/resources/test-serialization-keycloak-cr.yml +++ b/operator/src/test/resources/test-serialization-keycloak-cr.yml @@ -20,6 +20,8 @@ spec: - admin - step-up-authentication disableDefaultIngress: true + transaction: + xaEnabled: false unsupported: podTemplate: metadata: