Support for non-XA databases (#20141)

This commit is contained in:
Pedro Igor 2023-05-04 14:08:10 -03:00 committed by GitHub
parent b6147141f3
commit c17fcd49c8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 48 additions and 145 deletions

View file

@ -133,7 +133,7 @@ jobs:
declare -A PARAMS declare -A PARAMS
PARAMS["zip"]="" PARAMS["zip"]=""
PARAMS["container"]="-Dkc.quarkus.tests.dist=docker" PARAMS["container"]="-Dkc.quarkus.tests.dist=docker"
PARAMS["storage"]="-Ptest-database -Dtest=PostgreSQLDistTest,MariaDBDistTest#testSuccessful,MySQLDistTest#testSuccessful,DatabaseOptionsDistTest,JPAStoreDistTest,HotRodStoreDistTest,MixedStoreDistTest" PARAMS["storage"]="-Ptest-database -Dtest=PostgreSQLDistTest,MariaDBDistTest#testSuccessful,MySQLDistTest#testSuccessful,DatabaseOptionsDistTest,JPAStoreDistTest,HotRodStoreDistTest,MixedStoreDistTest,TransactionConfigurationDistTest"
./mvnw install -nsu -B -pl quarkus/tests/integration -am -DskipTests ./mvnw install -nsu -B -pl quarkus/tests/integration -am -DskipTests
./mvnw test -nsu -B -pl quarkus/tests/integration ${PARAMS["${{ matrix.server }}"]} | misc/log/trimmer.sh ./mvnw test -nsu -B -pl quarkus/tests/integration ${PARAMS["${{ matrix.server }}"]} | misc/log/trimmer.sh

View file

@ -8,11 +8,4 @@ public class TransactionOptions {
.buildTime(true) .buildTime(true)
.defaultValue(Boolean.TRUE) .defaultValue(Boolean.TRUE)
.build(); .build();
public static final Option<Boolean> TRANSACTION_JTA_ENABLED = new OptionBuilder<>("transaction-jta-enabled", Boolean.class)
.category(OptionCategory.TRANSACTION)
.description("Set if distributed transactions are supported. If set to false, transactions are managed by the server and can not be joined if multiple data sources are used. By default, distributed transactions are enabled and only XA data sources can be used.")
.buildTime(true)
.hidden()
.build();
} }

View file

@ -114,14 +114,7 @@ final class DatabasePropertyMappers {
} }
ConfigValue xaEnabledConfigValue = context.proceed("kc.transaction-xa-enabled"); ConfigValue xaEnabledConfigValue = context.proceed("kc.transaction-xa-enabled");
ConfigValue jtaEnabledConfiguration = context.proceed("kc.transaction-jta-enabled");
boolean isXaEnabled = xaEnabledConfigValue == null || Boolean.parseBoolean(xaEnabledConfigValue.getValue()); boolean isXaEnabled = xaEnabledConfigValue == null || Boolean.parseBoolean(xaEnabledConfigValue.getValue());
boolean isJtaEnabled = jtaEnabledConfiguration == null || Boolean.parseBoolean(jtaEnabledConfiguration.getValue());
if (!isJtaEnabled) {
isXaEnabled = false;
}
Optional<String> driver = Database.getDriver(value.get(), isXaEnabled); Optional<String> driver = Database.getDriver(value.get(), isXaEnabled);

View file

@ -5,6 +5,7 @@ import io.smallrye.config.ConfigValue;
import org.keycloak.config.StorageOptions; import org.keycloak.config.StorageOptions;
import org.keycloak.config.TransactionOptions; import org.keycloak.config.TransactionOptions;
import org.keycloak.quarkus.runtime.configuration.Configuration;
import static java.util.Optional.of; import static java.util.Optional.of;
import static org.keycloak.config.StorageOptions.STORAGE; import static org.keycloak.config.StorageOptions.STORAGE;
@ -23,11 +24,6 @@ public class TransactionPropertyMappers {
return new PropertyMapper[] { return new PropertyMapper[] {
fromOption(TransactionOptions.TRANSACTION_XA_ENABLED) fromOption(TransactionOptions.TRANSACTION_XA_ENABLED)
.to(QUARKUS_TXPROP_TARGET) .to(QUARKUS_TXPROP_TARGET)
.paramLabel(Boolean.TRUE + "|" + Boolean.FALSE)
.transformer(TransactionPropertyMappers::getQuarkusTransactionsValue)
.build(),
fromOption(TransactionOptions.TRANSACTION_JTA_ENABLED)
.paramLabel(Boolean.TRUE + "|" + Boolean.FALSE)
.transformer(TransactionPropertyMappers::getQuarkusTransactionsValue) .transformer(TransactionPropertyMappers::getQuarkusTransactionsValue)
.build() .build()
}; };
@ -35,32 +31,16 @@ public class TransactionPropertyMappers {
private static Optional<String> getQuarkusTransactionsValue(Optional<String> txValue, ConfigSourceInterceptorContext context) { private static Optional<String> getQuarkusTransactionsValue(Optional<String> txValue, ConfigSourceInterceptorContext context) {
boolean isXaEnabled = Boolean.parseBoolean(txValue.get()); boolean isXaEnabled = Boolean.parseBoolean(txValue.get());
boolean isJtaEnabled = getBooleanValue("kc.transaction-jta-enabled", context, true);
ConfigValue storage = context.proceed(NS_KEYCLOAK_PREFIX.concat(STORAGE.getKey())); ConfigValue storage = context.proceed(NS_KEYCLOAK_PREFIX.concat(STORAGE.getKey()));
if (storage != null && StorageOptions.StorageType.jpa.name().equals(storage.getValue())) { if (storage != null && StorageOptions.StorageType.jpa.name().equals(storage.getValue())) {
isJtaEnabled = true;
isXaEnabled = true; isXaEnabled = true;
} }
if (!isJtaEnabled) {
return of("disabled");
}
if (isXaEnabled) { if (isXaEnabled) {
return of("xa"); return of("xa");
} }
return of("enabled"); return of("enabled");
} }
private static boolean getBooleanValue(String key, ConfigSourceInterceptorContext context, boolean defaultValue) {
boolean returnValue = defaultValue;
ConfigValue configValue = context.proceed(key);
if (configValue != null) {
returnValue = Boolean.parseBoolean(configValue.getValue());
}
return returnValue;
}
} }

View file

@ -40,7 +40,6 @@ import io.quarkus.hibernate.orm.PersistenceUnit;
public abstract class AbstractJpaConnectionProviderFactory implements JpaConnectionProviderFactory { public abstract class AbstractJpaConnectionProviderFactory implements JpaConnectionProviderFactory {
protected Config.Scope config; protected Config.Scope config;
protected Boolean xaEnabled;
protected EntityManagerFactory entityManagerFactory; protected EntityManagerFactory entityManagerFactory;
@Override @Override
@ -62,7 +61,6 @@ public abstract class AbstractJpaConnectionProviderFactory implements JpaConnect
@Override @Override
public void init(Config.Scope config) { public void init(Config.Scope config) {
this.config = config; this.config = config;
xaEnabled = "xa".equals(Configuration.getRawValue("kc.transaction-xa-enabled"));
} }
@Override @Override
@ -101,13 +99,7 @@ public abstract class AbstractJpaConnectionProviderFactory implements JpaConnect
} }
protected EntityManager createEntityManager(EntityManagerFactory emf, KeycloakSession session) { protected EntityManager createEntityManager(EntityManagerFactory emf, KeycloakSession session) {
EntityManager entityManager; EntityManager entityManager = PersistenceExceptionConverter.create(session, emf.createEntityManager(SynchronizationType.SYNCHRONIZED));
if (xaEnabled) {
entityManager = PersistenceExceptionConverter.create(session, emf.createEntityManager(SynchronizationType.SYNCHRONIZED));
} else {
entityManager = PersistenceExceptionConverter.create(session, emf.createEntityManager());
}
entityManager.setFlushMode(FlushModeType.AUTO); entityManager.setFlushMode(FlushModeType.AUTO);

View file

@ -443,12 +443,6 @@ public class ConfigurationTest {
assertEquals("com.microsoft.sqlserver.jdbc.SQLServerXADataSource", xaConfig.getConfigValue("quarkus.datasource.jdbc.driver").getValue()); assertEquals("com.microsoft.sqlserver.jdbc.SQLServerXADataSource", xaConfig.getConfigValue("quarkus.datasource.jdbc.driver").getValue());
assertEquals("xa", xaConfig.getConfigValue("quarkus.datasource.jdbc.transactions").getValue()); assertEquals("xa", xaConfig.getConfigValue("quarkus.datasource.jdbc.transactions").getValue());
System.setProperty(CLI_ARGS, "--db=mssql" + ARG_SEPARATOR + "--transaction-jta-enabled=false");
SmallRyeConfig jtaDisabledConfig = createConfig();
assertEquals("com.microsoft.sqlserver.jdbc.SQLServerDriver", jtaDisabledConfig.getConfigValue("quarkus.datasource.jdbc.driver").getValue());
assertEquals("disabled", jtaDisabledConfig.getConfigValue("quarkus.datasource.jdbc.transactions").getValue());
} }
@Test @Test

View file

@ -1,54 +0,0 @@
/*
* Copyright 2021 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.it.storage.database;
import org.junit.jupiter.api.Test;
import org.keycloak.it.junit5.extension.CLIResult;
import org.keycloak.it.junit5.extension.CLITest;
import org.keycloak.it.junit5.extension.WithDatabase;
import io.quarkus.test.junit.main.Launch;
import io.quarkus.test.junit.main.LaunchResult;
@CLITest
@WithDatabase(alias = "mssql")
public class MSSQLTest extends BasicDatabaseTest {
/**
* It should be possible to enable XA but here we reproduce a managed environment where only nonXA transaction is supported
*
* @param result
*/
@Override
@Test
@Launch({ "--transaction-xa-enabled=false", "start-dev" })
void testSuccessful(LaunchResult result) {
CLIResult cliResult = (CLIResult) result;
cliResult.assertStartedDevMode();
}
@Override
protected void assertWrongUsername(CLIResult cliResult) {
cliResult.assertMessage("ERROR: Login failed for user 'wrong'");
}
@Override
protected void assertWrongPassword(CLIResult cliResult) {
cliResult.assertMessage("Login failed for user");
}
}

View file

@ -1,36 +0,0 @@
/*
* Copyright 2021 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.it.storage.database.dist;
import org.junit.jupiter.api.Test;
import org.keycloak.it.junit5.extension.CLIResult;
import org.keycloak.it.junit5.extension.DistributionTest;
import io.quarkus.test.junit.main.Launch;
import io.quarkus.test.junit.main.LaunchResult;
@DistributionTest
public class CustomTransactionDistTest {
@Test
@Launch({ "build", "--db=mssql", "--transaction-xa-enabled=false" })
void testNoXa(LaunchResult result) {
CLIResult cliResult = (CLIResult) result;
cliResult.assertBuild();
}
}

View file

@ -0,0 +1,24 @@
package org.keycloak.it.storage.database.dist;
import io.quarkus.test.junit.main.Launch;
import io.quarkus.test.junit.main.LaunchResult;
import org.junit.jupiter.api.Test;
import org.keycloak.it.junit5.extension.CLIResult;
import org.keycloak.it.junit5.extension.DistributionTest;
import org.keycloak.it.junit5.extension.LegacyStore;
import org.keycloak.it.junit5.extension.WithDatabase;
@DistributionTest
@WithDatabase(alias = "mssql")
@LegacyStore
public class TransactionConfigurationDistTest {
@Test
@Launch({ "start-dev", "--db=mssql", "--transaction-xa-enabled=false" })
void testXADisabled(LaunchResult result) {
CLIResult cliResult = (CLIResult) result;
cliResult.assertStartedDevMode();
cliResult.assertNoMessage("ARJUNA016061: TransactionImple.enlistResource");
}
}

View file

@ -1 +1 @@
mcr.microsoft.com/mssql/server:2019-CU10-ubuntu-20.04 mcr.microsoft.com/mssql/server:2019-latest

View file

@ -87,6 +87,10 @@
<groupId>org.testcontainers</groupId> <groupId>org.testcontainers</groupId>
<artifactId>mysql</artifactId> <artifactId>mysql</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mssqlserver</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.apache.maven.wagon</groupId> <groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon-http-shared</artifactId> <artifactId>wagon-http-shared</artifactId>

View file

@ -22,11 +22,11 @@ import java.time.Duration;
import org.keycloak.it.utils.KeycloakDistribution; import org.keycloak.it.utils.KeycloakDistribution;
import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.JdbcDatabaseContainer; import org.testcontainers.containers.JdbcDatabaseContainer;
import org.testcontainers.containers.MSSQLServerContainer;
import org.testcontainers.containers.MariaDBContainer; import org.testcontainers.containers.MariaDBContainer;
import org.testcontainers.containers.MySQLContainer; import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.DockerImageName;
import org.testcontainers.utility.DockerImageName;
public class DatabaseContainer { public class DatabaseContainer {
@ -59,7 +59,6 @@ public class DatabaseContainer {
dist.setProperty("db-password", getPassword()); dist.setProperty("db-password", getPassword());
dist.setProperty("db-url", getJdbcUrl()); dist.setProperty("db-url", getJdbcUrl());
} }
} }
private String getJdbcUrl() { private String getJdbcUrl() {
@ -67,10 +66,16 @@ public class DatabaseContainer {
} }
String getUsername() { String getUsername() {
if (container instanceof MSSQLServerContainer) {
return ((JdbcDatabaseContainer) container).getUsername();
}
return "keycloak"; return "keycloak";
} }
String getPassword() { String getPassword() {
if (container instanceof MSSQLServerContainer) {
return ((JdbcDatabaseContainer) container).getPassword();
}
return DEFAULT_PASSWORD; return DEFAULT_PASSWORD;
} }
@ -79,7 +84,11 @@ public class DatabaseContainer {
container = null; container = null;
} }
private GenericContainer<?> configureJdbcContainer(JdbcDatabaseContainer jdbcDatabaseContainer) { private JdbcDatabaseContainer configureJdbcContainer(JdbcDatabaseContainer jdbcDatabaseContainer) {
if (jdbcDatabaseContainer instanceof MSSQLServerContainer) {
return jdbcDatabaseContainer;
}
return jdbcDatabaseContainer return jdbcDatabaseContainer
.withDatabaseName("keycloak") .withDatabaseName("keycloak")
.withUsername(getUsername()) .withUsername(getUsername())
@ -98,10 +107,12 @@ public class DatabaseContainer {
String MARIADB_IMAGE = System.getProperty("kc.db.mariadb.container.image", "mariadb:10.5.9"); String MARIADB_IMAGE = System.getProperty("kc.db.mariadb.container.image", "mariadb:10.5.9");
String MYSQL_IMAGE = System.getProperty("kc.db.mysql.container.image", "mysql:latest"); String MYSQL_IMAGE = System.getProperty("kc.db.mysql.container.image", "mysql:latest");
String INFINISPAN_IMAGE = System.getProperty("kc.infinispan.container.image"); String INFINISPAN_IMAGE = System.getProperty("kc.infinispan.container.image");
String MSSQL_IMAGE = System.getProperty("kc.db.mssql.container.image", "mcr.microsoft.com/mssql/server:2019-latest");
DockerImageName POSTGRES = DockerImageName.parse(POSTGRES_IMAGE).asCompatibleSubstituteFor("postgres"); DockerImageName POSTGRES = DockerImageName.parse(POSTGRES_IMAGE).asCompatibleSubstituteFor("postgres");
DockerImageName MARIADB = DockerImageName.parse(MARIADB_IMAGE).asCompatibleSubstituteFor("mariadb"); DockerImageName MARIADB = DockerImageName.parse(MARIADB_IMAGE).asCompatibleSubstituteFor("mariadb");
DockerImageName MYSQL = DockerImageName.parse(MYSQL_IMAGE).asCompatibleSubstituteFor("mysql"); DockerImageName MYSQL = DockerImageName.parse(MYSQL_IMAGE).asCompatibleSubstituteFor("mysql");
DockerImageName MSSQL = DockerImageName.parse(MSSQL_IMAGE).asCompatibleSubstituteFor("sqlserver");
switch (alias) { switch (alias) {
case "postgres": case "postgres":
@ -110,6 +121,8 @@ public class DatabaseContainer {
return configureJdbcContainer(new MariaDBContainer(MARIADB)); return configureJdbcContainer(new MariaDBContainer(MARIADB));
case "mysql": case "mysql":
return configureJdbcContainer(new MySQLContainer(MYSQL)); return configureJdbcContainer(new MySQLContainer(MYSQL));
case "mssql":
return configureJdbcContainer(new MSSQLServerContainer(MSSQL));
case "infinispan": case "infinispan":
return configureInfinispanUser(new GenericContainer(INFINISPAN_IMAGE)) return configureInfinispanUser(new GenericContainer(INFINISPAN_IMAGE))
.withExposedPorts(11222); .withExposedPorts(11222);