diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 659248dc89..5bb80f7289 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -601,7 +601,18 @@ jobs: echo "Tests: $TESTS" ./mvnw test ${{ env.SUREFIRE_RETRY }} -Pauth-server-quarkus -Pdb-${{ matrix.db }} "-Dwebdriver.chrome.driver=$CHROMEWEBDRIVER/chromedriver" -Dtest=$TESTS -pl testsuite/integration-arquillian/tests/base 2>&1 | misc/log/trimmer.sh - - name: Run cluster JDBC_PING2 smoke test + - name: Run cluster JDBC_PING2 UDP smoke test + run: | + ./mvnw test ${{ env.SUREFIRE_RETRY }} \ + -Pauth-server-cluster-quarkus \ + -Pdb-${{ matrix.db }} \ + -Dtest=RealmInvalidationClusterTest \ + -Dsession.cache.owners=2 \ + -Dauth.server.quarkus.cluster.stack=jdbc-ping-udp \ + -pl testsuite/integration-arquillian/tests/base \ + 2>&1 | misc/log/trimmer.sh + + - name: Run cluster JDBC_PING2 TCP smoke test run: | ./mvnw test ${{ env.SUREFIRE_RETRY }} \ -Pauth-server-cluster-quarkus \ diff --git a/docs/guides/server/caching.adoc b/docs/guides/server/caching.adoc index 0d09e3960b..aed0f1b5d8 100644 --- a/docs/guides/server/caching.adoc +++ b/docs/guides/server/caching.adoc @@ -219,7 +219,7 @@ To apply a specific cache stack, enter this command: <@kc.start parameters="--cache-stack="/> -The default stack is set to `jdbc-ping` when distributed caches are enabled. +The default stack is set to `jdbc-ping-udp` when distributed caches are enabled. === Available transport stacks @@ -231,7 +231,8 @@ The following table shows transport stacks that are available without any furthe |`tcp`|TCP|MPING (uses UDP multicast). |`udp`|UDP|UDP multicast -|`jdbc-ping`|UDP|JDBC_PING2 +|`jdbc-ping-udp`|UDP|JDBC_PING2 +|`jdbc-ping`|TCP|JDBC_PING2 |=== diff --git a/quarkus/config-api/src/main/java/org/keycloak/config/CachingOptions.java b/quarkus/config-api/src/main/java/org/keycloak/config/CachingOptions.java index ec4704da31..f11391c057 100644 --- a/quarkus/config-api/src/main/java/org/keycloak/config/CachingOptions.java +++ b/quarkus/config-api/src/main/java/org/keycloak/config/CachingOptions.java @@ -46,16 +46,24 @@ public class CachingOptions { public enum Stack { tcp, udp, + jdbc_ping, + jdbc_ping_udp, kubernetes, ec2, azure, - google + google; + + @Override + public String toString() { + return CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_HYPHEN, super.toString()); + } } public static final Option CACHE_STACK = new OptionBuilder<>("cache-stack", Stack.class) .category(OptionCategory.CACHE) .expectedValues(false) - .description("Define the default stack to use for cluster communication and node discovery. This option only takes effect if 'cache' is set to 'ispn'. Default: udp.") + .description("Define the default stack to use for cluster communication and node discovery.") + .defaultValue(Stack.jdbc_ping_udp) .build(); public static final Option CACHE_CONFIG_FILE = new OptionBuilder<>(CACHE_CONFIG_FILE_PROPERTY, File.class) diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/CachingPropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/CachingPropertyMappers.java index 346b17d437..dd367f2564 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/CachingPropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/CachingPropertyMappers.java @@ -7,6 +7,7 @@ import java.io.File; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.function.BooleanSupplier; import org.keycloak.config.CachingOptions; @@ -19,6 +20,8 @@ final class CachingPropertyMappers { private static final String REMOTE_HOST_SET = "remote host is set"; + private static final String CACHE_STACK_SET_TO_ISPN = "'cache' type is set to '" + CachingOptions.Mechanism.ispn.name() + "'"; + private CachingPropertyMappers() { } @@ -28,6 +31,7 @@ final class CachingPropertyMappers { .paramLabel("type") .build(), fromOption(CachingOptions.CACHE_STACK) + .isEnabled(CachingPropertyMappers::cacheSetToInfinispan, CACHE_STACK_SET_TO_ISPN) .to("kc.spi-connections-infinispan-quarkus-stack") .paramLabel("stack") .build(), @@ -101,6 +105,11 @@ final class CachingPropertyMappers { return getOptionalKcValue(CachingOptions.CACHE_REMOTE_HOST_PROPERTY).isPresent(); } + private static boolean cacheSetToInfinispan() { + Optional cache = getOptionalKcValue(CachingOptions.CACHE); + return cache.isPresent() && cache.get().equals(CachingOptions.Mechanism.ispn.name()); + } + private static String resolveConfigFile(String value, ConfigSourceInterceptorContext context) { String homeDir = Environment.getHomeDir(); diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/infinispan/CacheManagerFactory.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/infinispan/CacheManagerFactory.java index 0edd03eb4e..118171c3ce 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/infinispan/CacheManagerFactory.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/infinispan/CacheManagerFactory.java @@ -371,53 +371,65 @@ public class CacheManagerFactory { } private void configureTransportStack(ConfigurationBuilderHolder builder, KeycloakSession keycloakSession) { - String transportStack = Configuration.getRawValue("kc.cache-stack"); - - var jdbcStackName = "jdbc-ping"; var transportConfig = builder.getGlobalConfigurationBuilder().transport(); - var stackXmlAttribute = transportConfig.defaultTransport().attributes().attribute(STACK); - if (transportStack != null && !transportStack.isBlank() && !jdbcStackName.equals(transportStack)) { - transportConfig.defaultTransport().stack(transportStack); - } else if (!stackXmlAttribute.isModified() || jdbcStackName.equals(stackXmlAttribute.get())){ - EntityManager em = keycloakSession.getProvider(JpaConnectionProvider.class).getEntityManager(); - var tableName = JpaUtils.getTableNameForNativeQuery("JGROUPS_PING", em); - var attributes = Map.of( - // Leave initialize_sql blank as table is already created by Keycloak - "initialize_sql", "", - // Explicitly specify clear and select_all SQL to ensure "cluster_name" column is used, as the default - // "cluster" cannot be used with Oracle DB as it's a reserved word. - "clear_sql", String.format("DELETE from %s WHERE cluster_name=?", tableName), - "delete_single_sql", String.format("DELETE from %s WHERE address=?", tableName), - "insert_single_sql", String.format("INSERT INTO %s values (?, ?, ?, ?, ?)", tableName), - "select_all_pingdata_sql", String.format("SELECT address, name, ip, coord FROM %s WHERE cluster_name=?", tableName), - "remove_all_data_on_view_change", "true", - "register_shutdown_hook", "false", - "stack.combine", "REPLACE", - "stack.position", "PING" - ); - var stack = List.of(new ProtocolConfiguration(JDBC_PING2.class.getSimpleName(), attributes)); - builder.addJGroupsStack(new EmbeddedJGroupsChannelConfigurator(jdbcStackName, stack, null), "udp"); - - Supplier dataSourceSupplier = Arc.container().select(AgroalDataSource.class)::get; - transportConfig.addProperty(JGroupsTransport.DATA_SOURCE, dataSourceSupplier); - transportConfig.defaultTransport().stack(jdbcStackName); - } - if (Configuration.isTrue(CachingOptions.CACHE_EMBEDDED_MTLS_ENABLED_PROPERTY)) { validateTlsAvailable(transportConfig.build()); var tls = new TLS() - .enabled(true) - .setKeystorePath(requiredStringProperty(CACHE_EMBEDDED_MTLS_KEYSTORE_FILE_PROPERTY)) - .setKeystorePassword(requiredStringProperty(CACHE_EMBEDDED_MTLS_KEYSTORE_PASSWORD_PROPERTY)) - .setKeystoreType("pkcs12") - .setTruststorePath(requiredStringProperty(CACHE_EMBEDDED_MTLS_TRUSTSTORE_FILE_PROPERTY)) - .setTruststorePassword(requiredStringProperty(CACHE_EMBEDDED_MTLS_TRUSTSTORE_PASSWORD_PROPERTY)) - .setTruststoreType("pkcs12") - .setClientAuth(TLSClientAuth.NEED) - .setProtocols(new String[]{"TLSv1.3"}); + .enabled(true) + .setKeystorePath(requiredStringProperty(CACHE_EMBEDDED_MTLS_KEYSTORE_FILE_PROPERTY)) + .setKeystorePassword(requiredStringProperty(CACHE_EMBEDDED_MTLS_KEYSTORE_PASSWORD_PROPERTY)) + .setKeystoreType("pkcs12") + .setTruststorePath(requiredStringProperty(CACHE_EMBEDDED_MTLS_TRUSTSTORE_FILE_PROPERTY)) + .setTruststorePassword(requiredStringProperty(CACHE_EMBEDDED_MTLS_TRUSTSTORE_PASSWORD_PROPERTY)) + .setTruststoreType("pkcs12") + .setClientAuth(TLSClientAuth.NEED) + .setProtocols(new String[]{"TLSv1.3"}); transportConfig.addProperty(JGroupsTransport.SOCKET_FACTORY, tls.createSocketFactory()); logger.info("MTLS enabled for communications for embedded caches"); } + + String transportStack = Configuration.getRawValue("kc.cache-stack"); + if (transportStack != null && !transportStack.isBlank() && !isJdbcPingStack(transportStack)) { + transportConfig.defaultTransport().stack(transportStack); + return; + } + + var stackXmlAttribute = transportConfig.defaultTransport().attributes().attribute(STACK); + // If the user has explicitly defined a transport stack that is not jdbc-ping or jdbc-ping-udp, return + if (stackXmlAttribute.isModified() && !isJdbcPingStack(stackXmlAttribute.get())) + return; + + var stackName = transportStack != null ? + transportStack : + stackXmlAttribute.isModified() ? stackXmlAttribute.get() : "jdbc-ping-udp"; + var udp = stackName.endsWith("udp"); + + EntityManager em = keycloakSession.getProvider(JpaConnectionProvider.class).getEntityManager(); + var tableName = JpaUtils.getTableNameForNativeQuery("JGROUPS_PING", em); + var attributes = Map.of( + // Leave initialize_sql blank as table is already created by Keycloak + "initialize_sql", "", + // Explicitly specify clear and select_all SQL to ensure "cluster_name" column is used, as the default + // "cluster" cannot be used with Oracle DB as it's a reserved word. + "clear_sql", String.format("DELETE from %s WHERE cluster_name=?", tableName), + "delete_single_sql", String.format("DELETE from %s WHERE address=?", tableName), + "insert_single_sql", String.format("INSERT INTO %s values (?, ?, ?, ?, ?)", tableName), + "select_all_pingdata_sql", String.format("SELECT address, name, ip, coord FROM %s WHERE cluster_name=?", tableName), + "remove_all_data_on_view_change", "true", + "register_shutdown_hook", "false", + "stack.combine", "REPLACE", + "stack.position", udp ? "PING" : "MPING" + ); + var stack = List.of(new ProtocolConfiguration(JDBC_PING2.class.getSimpleName(), attributes)); + builder.addJGroupsStack(new EmbeddedJGroupsChannelConfigurator(stackName, stack, null), udp ? "udp" : "tcp"); + + Supplier dataSourceSupplier = Arc.container().select(AgroalDataSource.class)::get; + transportConfig.addProperty(JGroupsTransport.DATA_SOURCE, dataSourceSupplier); + transportConfig.defaultTransport().stack(stackName); + } + + private boolean isJdbcPingStack(String stackName) { + return "jdbc-ping".equals(stackName) || "jdbc-ping-udp".equals(stackName); } private static void validateTlsAvailable(GlobalConfiguration config) { diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ClusterConfigDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ClusterConfigDistTest.java index 99612da398..7d99355354 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ClusterConfigDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ClusterConfigDistTest.java @@ -47,6 +47,15 @@ public class ClusterConfigDistTest { void changeClusterSetting(LaunchResult result) { CLIResult cliResult = (CLIResult) result; cliResult.assertClusteredCache(); + assertThat(cliResult.getOutput(), Matchers.containsString("ISPN000078: Starting JGroups channel `ISPN` with stack `jdbc-ping-udp`")); + } + + @Test + @Launch({ "start-dev", "--cache=ispn", "--cache-stack=jdbc-ping"}) + void testJdbcPingTCP(LaunchResult result) { + CLIResult cliResult = (CLIResult) result; + cliResult.assertClusteredCache(); + assertThat(cliResult.getOutput(), Matchers.containsString("ISPN000078: Starting JGroups channel `ISPN` with stack `jdbc-ping`")); } @Test diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartDevHelp.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartDevHelp.approved.txt index 6637885be5..d82e171751 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartDevHelp.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartDevHelp.approved.txt @@ -69,11 +69,6 @@ Cache: specified via XML file (see 'cache-config-file' option.). If the option is specified, 'cache-remote-username' and 'cache-remote-password' are required as well and the related configuration in XML file should not be present. ---cache-stack - Define the default stack to use for cluster communication and node discovery. - This option only takes effect if 'cache' is set to 'ispn'. Default: udp. - Possible values are: tcp, udp, kubernetes, ec2, azure, google, or a custom - one. Config: diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartDevHelpAll.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartDevHelpAll.approved.txt index 8236288fec..b324495cec 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartDevHelpAll.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartDevHelpAll.approved.txt @@ -97,9 +97,9 @@ Cache: host is set. --cache-stack Define the default stack to use for cluster communication and node discovery. - This option only takes effect if 'cache' is set to 'ispn'. Default: udp. - Possible values are: tcp, udp, kubernetes, ec2, azure, google, or a custom - one. + Possible values are: tcp, udp, jdbc-ping, jdbc-ping-udp, kubernetes, ec2, + azure, google, or a custom one. Default: jdbc-ping-udp. Available only when + 'cache' type is set to 'ispn'. Config: diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartHelp.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartHelp.approved.txt index 2113a6d1c2..0fb013837a 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartHelp.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartHelp.approved.txt @@ -72,9 +72,9 @@ Cache: as well and the related configuration in XML file should not be present. --cache-stack Define the default stack to use for cluster communication and node discovery. - This option only takes effect if 'cache' is set to 'ispn'. Default: udp. - Possible values are: tcp, udp, kubernetes, ec2, azure, google, or a custom - one. + Possible values are: tcp, udp, jdbc-ping, jdbc-ping-udp, kubernetes, ec2, + azure, google, or a custom one. Default: jdbc-ping-udp. Available only when + 'cache' type is set to 'ispn'. Config: diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartHelpAll.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartHelpAll.approved.txt index 97780a0092..afda7dff3c 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartHelpAll.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartHelpAll.approved.txt @@ -98,9 +98,9 @@ Cache: host is set. --cache-stack Define the default stack to use for cluster communication and node discovery. - This option only takes effect if 'cache' is set to 'ispn'. Default: udp. - Possible values are: tcp, udp, kubernetes, ec2, azure, google, or a custom - one. + Possible values are: tcp, udp, jdbc-ping, jdbc-ping-udp, kubernetes, ec2, + azure, google, or a custom one. Default: jdbc-ping-udp. Available only when + 'cache' type is set to 'ispn'. Config: diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartOptimizedHelp.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartOptimizedHelp.approved.txt index 1e816d70dc..879b8766ae 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartOptimizedHelp.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartOptimizedHelp.approved.txt @@ -72,9 +72,9 @@ Cache: as well and the related configuration in XML file should not be present. --cache-stack Define the default stack to use for cluster communication and node discovery. - This option only takes effect if 'cache' is set to 'ispn'. Default: udp. - Possible values are: tcp, udp, kubernetes, ec2, azure, google, or a custom - one. + Possible values are: tcp, udp, jdbc-ping, jdbc-ping-udp, kubernetes, ec2, + azure, google, or a custom one. Default: jdbc-ping-udp. Available only when + 'cache' type is set to 'ispn'. Config: diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartOptimizedHelpAll.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartOptimizedHelpAll.approved.txt index 71725da9e7..d78a8517c4 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartOptimizedHelpAll.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartOptimizedHelpAll.approved.txt @@ -98,9 +98,9 @@ Cache: host is set. --cache-stack Define the default stack to use for cluster communication and node discovery. - This option only takes effect if 'cache' is set to 'ispn'. Default: udp. - Possible values are: tcp, udp, kubernetes, ec2, azure, google, or a custom - one. + Possible values are: tcp, udp, jdbc-ping, jdbc-ping-udp, kubernetes, ec2, + azure, google, or a custom one. Default: jdbc-ping-udp. Available only when + 'cache' type is set to 'ispn'. Config: