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:
parent
fd1dd49ade
commit
6eb870fcfc
12 changed files with 110 additions and 65 deletions
13
.github/workflows/ci.yml
vendored
13
.github/workflows/ci.yml
vendored
|
@ -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 \
|
||||||
|
|
|
@ -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
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue