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"
./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 \

View file

@ -219,7 +219,7 @@ To apply a specific cache stack, enter this command:
<@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
@ -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
|===

View file

@ -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<Stack> 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<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.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<String> 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();

View file

@ -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<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)) {
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<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) {

View file

@ -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

View file

@ -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 <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:

View file

@ -97,9 +97,9 @@ Cache:
host is set.
--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.
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:

View file

@ -72,9 +72,9 @@ Cache:
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.
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:

View file

@ -98,9 +98,9 @@ Cache:
host is set.
--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.
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:

View file

@ -72,9 +72,9 @@ Cache:
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.
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:

View file

@ -98,9 +98,9 @@ Cache:
host is set.
--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.
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: