Add JDBC_PING2 stacks for both TCP and UDP

Closes #34265

Signed-off-by: Ryan Emerson <remerson@redhat.com>
Signed-off-by: Alexander Schwartz <aschwart@redhat.com>
Co-authored-by: Alexander Schwartz <aschwart@redhat.com>
This commit is contained in:
Ryan Emerson 2024-10-24 23:17:44 +01:00 committed by GitHub
parent fd1dd49ade
commit 6eb870fcfc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 110 additions and 65 deletions

View file

@ -601,7 +601,18 @@ jobs:
echo "Tests: $TESTS" 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 ./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: | run: |
./mvnw test ${{ env.SUREFIRE_RETRY }} \ ./mvnw test ${{ env.SUREFIRE_RETRY }} \
-Pauth-server-cluster-quarkus \ -Pauth-server-cluster-quarkus \

View file

@ -219,7 +219,7 @@ To apply a specific cache stack, enter this command:
<@kc.start parameters="--cache-stack=<stack>"/> <@kc.start parameters="--cache-stack=<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 === 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). |`tcp`|TCP|MPING (uses UDP multicast).
|`udp`|UDP|UDP multicast |`udp`|UDP|UDP multicast
|`jdbc-ping`|UDP|JDBC_PING2 |`jdbc-ping-udp`|UDP|JDBC_PING2
|`jdbc-ping`|TCP|JDBC_PING2
|=== |===

View file

@ -46,16 +46,24 @@ public class CachingOptions {
public enum Stack { public enum Stack {
tcp, tcp,
udp, udp,
jdbc_ping,
jdbc_ping_udp,
kubernetes, kubernetes,
ec2, ec2,
azure, azure,
google google;
@Override
public String toString() {
return CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_HYPHEN, super.toString());
}
} }
public static final Option<Stack> CACHE_STACK = new OptionBuilder<>("cache-stack", Stack.class) public static final Option<Stack> CACHE_STACK = new OptionBuilder<>("cache-stack", Stack.class)
.category(OptionCategory.CACHE) .category(OptionCategory.CACHE)
.expectedValues(false) .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(); .build();
public static final Option<File> CACHE_CONFIG_FILE = new OptionBuilder<>(CACHE_CONFIG_FILE_PROPERTY, File.class) public static final Option<File> CACHE_CONFIG_FILE = new OptionBuilder<>(CACHE_CONFIG_FILE_PROPERTY, File.class)

View file

@ -7,6 +7,7 @@ import java.io.File;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;
import org.keycloak.config.CachingOptions; 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 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() { private CachingPropertyMappers() {
} }
@ -28,6 +31,7 @@ final class CachingPropertyMappers {
.paramLabel("type") .paramLabel("type")
.build(), .build(),
fromOption(CachingOptions.CACHE_STACK) fromOption(CachingOptions.CACHE_STACK)
.isEnabled(CachingPropertyMappers::cacheSetToInfinispan, CACHE_STACK_SET_TO_ISPN)
.to("kc.spi-connections-infinispan-quarkus-stack") .to("kc.spi-connections-infinispan-quarkus-stack")
.paramLabel("stack") .paramLabel("stack")
.build(), .build(),
@ -101,6 +105,11 @@ final class CachingPropertyMappers {
return getOptionalKcValue(CachingOptions.CACHE_REMOTE_HOST_PROPERTY).isPresent(); return getOptionalKcValue(CachingOptions.CACHE_REMOTE_HOST_PROPERTY).isPresent();
} }
private static boolean cacheSetToInfinispan() {
Optional<String> cache = getOptionalKcValue(CachingOptions.CACHE);
return cache.isPresent() && cache.get().equals(CachingOptions.Mechanism.ispn.name());
}
private static String resolveConfigFile(String value, ConfigSourceInterceptorContext context) { private static String resolveConfigFile(String value, ConfigSourceInterceptorContext context) {
String homeDir = Environment.getHomeDir(); String homeDir = Environment.getHomeDir();

View file

@ -371,53 +371,65 @@ public class CacheManagerFactory {
} }
private void configureTransportStack(ConfigurationBuilderHolder builder, KeycloakSession keycloakSession) { private void configureTransportStack(ConfigurationBuilderHolder builder, KeycloakSession keycloakSession) {
String transportStack = Configuration.getRawValue("kc.cache-stack");
var jdbcStackName = "jdbc-ping";
var transportConfig = builder.getGlobalConfigurationBuilder().transport(); 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<DataSource> 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)) { if (Configuration.isTrue(CachingOptions.CACHE_EMBEDDED_MTLS_ENABLED_PROPERTY)) {
validateTlsAvailable(transportConfig.build()); validateTlsAvailable(transportConfig.build());
var tls = new TLS() var tls = new TLS()
.enabled(true) .enabled(true)
.setKeystorePath(requiredStringProperty(CACHE_EMBEDDED_MTLS_KEYSTORE_FILE_PROPERTY)) .setKeystorePath(requiredStringProperty(CACHE_EMBEDDED_MTLS_KEYSTORE_FILE_PROPERTY))
.setKeystorePassword(requiredStringProperty(CACHE_EMBEDDED_MTLS_KEYSTORE_PASSWORD_PROPERTY)) .setKeystorePassword(requiredStringProperty(CACHE_EMBEDDED_MTLS_KEYSTORE_PASSWORD_PROPERTY))
.setKeystoreType("pkcs12") .setKeystoreType("pkcs12")
.setTruststorePath(requiredStringProperty(CACHE_EMBEDDED_MTLS_TRUSTSTORE_FILE_PROPERTY)) .setTruststorePath(requiredStringProperty(CACHE_EMBEDDED_MTLS_TRUSTSTORE_FILE_PROPERTY))
.setTruststorePassword(requiredStringProperty(CACHE_EMBEDDED_MTLS_TRUSTSTORE_PASSWORD_PROPERTY)) .setTruststorePassword(requiredStringProperty(CACHE_EMBEDDED_MTLS_TRUSTSTORE_PASSWORD_PROPERTY))
.setTruststoreType("pkcs12") .setTruststoreType("pkcs12")
.setClientAuth(TLSClientAuth.NEED) .setClientAuth(TLSClientAuth.NEED)
.setProtocols(new String[]{"TLSv1.3"}); .setProtocols(new String[]{"TLSv1.3"});
transportConfig.addProperty(JGroupsTransport.SOCKET_FACTORY, tls.createSocketFactory()); transportConfig.addProperty(JGroupsTransport.SOCKET_FACTORY, tls.createSocketFactory());
logger.info("MTLS enabled for communications for embedded caches"); 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<DataSource> 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) { private static void validateTlsAvailable(GlobalConfiguration config) {

View file

@ -47,6 +47,15 @@ public class ClusterConfigDistTest {
void changeClusterSetting(LaunchResult result) { void changeClusterSetting(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
cliResult.assertClusteredCache(); 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 @Test

View file

@ -69,11 +69,6 @@ Cache:
specified via XML file (see 'cache-config-file' option.). If the option is specified via XML file (see 'cache-config-file' option.). If the option is
specified, 'cache-remote-username' and 'cache-remote-password' are required specified, 'cache-remote-username' and 'cache-remote-password' are required
as well and the related configuration in XML file should not be present. as well and the related configuration in XML file should not be present.
--cache-stack <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: Config:

View file

@ -97,9 +97,9 @@ Cache:
host is set. host is set.
--cache-stack <stack> --cache-stack <stack>
Define the default stack to use for cluster communication and node discovery. 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, jdbc-ping, jdbc-ping-udp, kubernetes, ec2,
Possible values are: tcp, udp, kubernetes, ec2, azure, google, or a custom azure, google, or a custom one. Default: jdbc-ping-udp. Available only when
one. 'cache' type is set to 'ispn'.
Config: Config:

View file

@ -72,9 +72,9 @@ Cache:
as well and the related configuration in XML file should not be present. as well and the related configuration in XML file should not be present.
--cache-stack <stack> --cache-stack <stack>
Define the default stack to use for cluster communication and node discovery. 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, jdbc-ping, jdbc-ping-udp, kubernetes, ec2,
Possible values are: tcp, udp, kubernetes, ec2, azure, google, or a custom azure, google, or a custom one. Default: jdbc-ping-udp. Available only when
one. 'cache' type is set to 'ispn'.
Config: Config:

View file

@ -98,9 +98,9 @@ Cache:
host is set. host is set.
--cache-stack <stack> --cache-stack <stack>
Define the default stack to use for cluster communication and node discovery. 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, jdbc-ping, jdbc-ping-udp, kubernetes, ec2,
Possible values are: tcp, udp, kubernetes, ec2, azure, google, or a custom azure, google, or a custom one. Default: jdbc-ping-udp. Available only when
one. 'cache' type is set to 'ispn'.
Config: Config:

View file

@ -72,9 +72,9 @@ Cache:
as well and the related configuration in XML file should not be present. as well and the related configuration in XML file should not be present.
--cache-stack <stack> --cache-stack <stack>
Define the default stack to use for cluster communication and node discovery. 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, jdbc-ping, jdbc-ping-udp, kubernetes, ec2,
Possible values are: tcp, udp, kubernetes, ec2, azure, google, or a custom azure, google, or a custom one. Default: jdbc-ping-udp. Available only when
one. 'cache' type is set to 'ispn'.
Config: Config:

View file

@ -98,9 +98,9 @@ Cache:
host is set. host is set.
--cache-stack <stack> --cache-stack <stack>
Define the default stack to use for cluster communication and node discovery. 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, jdbc-ping, jdbc-ping-udp, kubernetes, ec2,
Possible values are: tcp, udp, kubernetes, ec2, azure, google, or a custom azure, google, or a custom one. Default: jdbc-ping-udp. Available only when
one. 'cache' type is set to 'ispn'.
Config: Config: