[KEYCLOAK-11330] - Quarkus clustering tests
This commit is contained in:
parent
3d5e976097
commit
a8bad5b9bb
18 changed files with 412 additions and 84 deletions
|
@ -131,7 +131,7 @@ public class Retry {
|
|||
while (true) {
|
||||
try {
|
||||
return supplier.get(iteration);
|
||||
} catch (RuntimeException | AssertionError e) {
|
||||
} catch (Exception | AssertionError e) {
|
||||
attemptsCount--;
|
||||
iteration++;
|
||||
if (attemptsCount > 0) {
|
||||
|
|
|
@ -649,6 +649,46 @@ After you build the distribution, you run this command to setup servers and run
|
|||
-Dauth.server.log.check=false \
|
||||
-Dfrontend.console.output=true \
|
||||
-Dtest=org.keycloak.testsuite.cluster.**.*Test clean install
|
||||
|
||||
### Cluster tests with Keycloak on Quarkus
|
||||
|
||||
Run tests using the `auth-server-cluster-quarkus` profile:
|
||||
|
||||
mvn -f testsuite/integration-arquillian/tests/base/pom.xml clean install \
|
||||
-Pauth-server-cluster-quarkus \
|
||||
-Dsession.cache.owners=2 \
|
||||
-Dtest=AuthenticationSessionFailoverClusterTest
|
||||
|
||||
---
|
||||
**NOTE**
|
||||
|
||||
Right now, tests are using a H2 database.
|
||||
|
||||
To run tests using a different database such as PostgreSQL, add the following properties into the `testsuite/integration-arquillian/servers/auth-server/quarkus/src/main/content/conf/keycloak.properties` configuration file:
|
||||
|
||||
```
|
||||
# HA using PostgreSQL
|
||||
%ha.datasource.dialect=org.hibernate.dialect.PostgreSQL9Dialect
|
||||
%ha.datasource.driver = org.postgresql.xa.PGXADataSource
|
||||
%ha.datasource.url = jdbc:postgresql://localhost/keycloak
|
||||
%ha.datasource.username = keycloak
|
||||
%ha.datasource.password = password
|
||||
```
|
||||
|
||||
The `ha` profile is automatically set when running clustering tests.
|
||||
|
||||
This is temporary and database configuration should be more integrated with the test suite once we review Quarkus configuration.
|
||||
|
||||
---
|
||||
|
||||
#### Run cluster tests from IDE on Quarkus
|
||||
|
||||
Activate the following profiles:
|
||||
|
||||
* `quarkus`
|
||||
* `auth-server-cluster-quarkus`
|
||||
|
||||
Then run any cluster test as usual.
|
||||
|
||||
### Cluster tests with Keycloak on embedded undertow
|
||||
|
||||
|
@ -915,13 +955,22 @@ When running the test, add the following arguments to the command line:
|
|||
Java 11 requires some arguments to be passed to JVM. Those can be activated using `-Pjava11-auth-server` and
|
||||
`-Pjava11-app-server` profiles, respectively.
|
||||
|
||||
### Running tests using Quarkus distribution
|
||||
## Running tests using Quarkus distribution
|
||||
|
||||
### Before Everything
|
||||
|
||||
Make sure you build the project using the `quarkus` profile as follows:
|
||||
|
||||
mvn -Pdistribution,quarkus clean install
|
||||
|
||||
### Running tests
|
||||
|
||||
Then, just run tests using the `auth-server-quarkus` profile:
|
||||
Run tests using the `auth-server-quarkus` profile:
|
||||
|
||||
mvn -f testsuite/integration-arquillian/tests/base/pom.xml clean install -Pauth-server-quarkus
|
||||
|
||||
### Debug the Server
|
||||
|
||||
Right now, the server runs in a separate process. To debug the server set `auth.server.debug` system property to `true`.
|
||||
|
||||
To configure the debugger port, set the `auth.server.debug.port` system property with any valid port number. Default is `5005`.
|
|
@ -42,5 +42,12 @@
|
|||
<fileMode>0755</fileMode>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
|
||||
<files>
|
||||
<file>
|
||||
<source>src/main/content/conf/cluster-${auth.server.quarkus.config}.xml</source>
|
||||
<outputDirectory>/auth-server-quarkus/conf</outputDirectory>
|
||||
<destName>cluster.xml</destName>
|
||||
<filtered>true</filtered>
|
||||
</file>
|
||||
</files>
|
||||
</assembly>
|
||||
|
|
|
@ -14,6 +14,10 @@
|
|||
|
||||
<properties>
|
||||
<auth.server.home>${project.build.directory}/unpacked/keycloak.x-${project.version}</auth.server.home>
|
||||
<session.cache.owners>2</session.cache.owners>
|
||||
<offline.session.cache.owners>2</offline.session.cache.owners>
|
||||
<login.failure.cache.owners>2</login.failure.cache.owners>
|
||||
<auth.server.quarkus.config>local</auth.server.quarkus.config>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
@ -112,4 +116,13 @@
|
|||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>auth-server-cluster-quarkus</id>
|
||||
<properties>
|
||||
<auth.server.quarkus.config>ha</auth.server.quarkus.config>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
|
@ -0,0 +1,62 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2019 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.
|
||||
-->
|
||||
|
||||
<infinispan
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="urn:infinispan:config:9.4 http://www.infinispan.org/schemas/infinispan-config-9.4.xsd"
|
||||
xmlns="urn:infinispan:config:9.4">
|
||||
|
||||
<!-- Distributed Cache Container Configuration -->
|
||||
<cache-container name="keycloak">
|
||||
<transport lock-timeout="60000" node-name="${jboss.node.name}" />
|
||||
<local-cache name="realms">
|
||||
<memory>
|
||||
<object size="10000"/>
|
||||
</memory>
|
||||
</local-cache>
|
||||
<local-cache name="users">
|
||||
<memory>
|
||||
<object size="10000"/>
|
||||
</memory>
|
||||
</local-cache>
|
||||
<distributed-cache name="sessions" owners="${session.cache.owners}"/>
|
||||
<distributed-cache name="authenticationSessions" owners="${session.cache.owners}"/>
|
||||
<distributed-cache name="offlineSessions" owners="${offline.session.cache.owners}"/>
|
||||
<distributed-cache name="clientSessions" owners="${session.cache.owners}"/>
|
||||
<distributed-cache name="offlineClientSessions" owners="${offline.session.cache.owners}"/>
|
||||
<distributed-cache name="loginFailures" owners="${login.failure.cache.owners}"/>
|
||||
<local-cache name="authorization">
|
||||
<memory>
|
||||
<object size="10000"/>
|
||||
</memory>
|
||||
</local-cache>
|
||||
<replicated-cache name="work"/>
|
||||
<local-cache name="keys">
|
||||
<expiration max-idle="3600000"/>
|
||||
<memory>
|
||||
<object size="1000"/>
|
||||
</memory>
|
||||
</local-cache>
|
||||
<distributed-cache name="actionTokens" owners="2">
|
||||
<expiration max-idle="-1" interval="300000"/>
|
||||
<memory>
|
||||
<object size="-1"/>
|
||||
</memory>
|
||||
</distributed-cache>
|
||||
</cache-container>
|
||||
</infinispan>
|
|
@ -21,46 +21,6 @@
|
|||
xsi:schemaLocation="urn:infinispan:config:9.4 http://www.infinispan.org/schemas/infinispan-config-9.4.xsd"
|
||||
xmlns="urn:infinispan:config:9.4">
|
||||
|
||||
<!-- Distributed Cache Container Configuration
|
||||
<cache-container name="keycloak">
|
||||
<transport lock-timeout="60000"/>
|
||||
<local-cache name="realms">
|
||||
<memory>
|
||||
<object size="10000"/>
|
||||
</memory>
|
||||
</local-cache>
|
||||
<local-cache name="users">
|
||||
<memory>
|
||||
<object size="10000"/>
|
||||
</memory>
|
||||
</local-cache>
|
||||
<distributed-cache name="sessions" owners="1"/>
|
||||
<distributed-cache name="authenticationSessions" owners="1"/>
|
||||
<distributed-cache name="offlineSessions" owners="1"/>
|
||||
<distributed-cache name="clientSessions" owners="1"/>
|
||||
<distributed-cache name="offlineClientSessions" owners="1"/>
|
||||
<distributed-cache name="loginFailures" owners="1"/>
|
||||
<local-cache name="authorization">
|
||||
<memory>
|
||||
<object size="10000"/>
|
||||
</memory>
|
||||
</local-cache>
|
||||
<replicated-cache name="work"/>
|
||||
<local-cache name="keys">
|
||||
<expiration max-idle="3600000"/>
|
||||
<memory>
|
||||
<object size="1000"/>
|
||||
</memory>
|
||||
</local-cache>
|
||||
<distributed-cache name="actionTokens" owners="2">
|
||||
<expiration max-idle="-1" interval="300000"/>
|
||||
<memory>
|
||||
<object size="-1"/>
|
||||
</memory>
|
||||
</distributed-cache>
|
||||
</cache-container>
|
||||
-->
|
||||
|
||||
<!-- Local Cache Container Configuration -->
|
||||
<cache-container name="keycloak">
|
||||
<local-cache name="default">
|
|
@ -6,7 +6,7 @@ datasource.jdbc.transactions=xa
|
|||
# H2
|
||||
datasource.dialect=org.hibernate.dialect.H2Dialect
|
||||
datasource.driver=org.h2.jdbcx.JdbcDataSource
|
||||
datasource.url = jdbc:h2:file:${keycloak.home.dir}/data/keycloakdb;AUTO_SERVER=TRUE
|
||||
datasource.url = jdbc:h2:file:${keycloak.home.dir}/data/keycloakdb;AUTO_SERVER=TRUE;DB_CLOSE_DELAY=-1
|
||||
datasource.username = sa
|
||||
datasource.password = keycloak
|
||||
|
||||
|
@ -14,6 +14,9 @@ datasource.password = keycloak
|
|||
http.ssl.certificate.key-store-file=${keycloak.home.dir}/conf/keycloak.jks
|
||||
http.ssl.certificate.key-store-password=secret
|
||||
|
||||
# Proxy
|
||||
http.proxy-address-forwarding=true
|
||||
|
||||
# Truststore Provider
|
||||
truststore.file.file=${keycloak.home.dir}/conf/keycloak.truststore
|
||||
truststore.file.password=secret
|
|
@ -1,11 +1,16 @@
|
|||
package org.keycloak.testsuite.arquillian.containers;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import org.jboss.arquillian.container.spi.ConfigurationException;
|
||||
import org.jboss.arquillian.container.spi.client.container.ContainerConfiguration;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author mhajas
|
||||
|
@ -20,6 +25,10 @@ public class KeycloakQuarkusConfiguration implements ContainerConfiguration {
|
|||
private int bindHttpsPort = Integer.valueOf(System.getProperty("auth.server.https.port", "8543"));
|
||||
private Path providersPath = Paths.get(System.getProperty("auth.server.home"));
|
||||
private int startupTimeoutInSeconds = 60;
|
||||
private String route;
|
||||
private String keycloakConfigPropertyOverrides;
|
||||
private HashMap<String, Object> keycloakConfigPropertyOverridesMap;
|
||||
private String profile;
|
||||
|
||||
@Override
|
||||
public void validate() throws ConfigurationException {
|
||||
|
@ -32,6 +41,15 @@ public class KeycloakQuarkusConfiguration implements ContainerConfiguration {
|
|||
setBindHttpsPort(newHttpsPort);
|
||||
|
||||
log.info("Keycloak will listen for http on port: " + newPort + " and for https on port: " + newHttpsPort);
|
||||
|
||||
if (this.keycloakConfigPropertyOverrides != null) {
|
||||
try {
|
||||
TypeReference<HashMap<String,Object>> typeRef = new TypeReference<HashMap<String,Object>>() {};
|
||||
this.keycloakConfigPropertyOverridesMap = JsonSerialization.sysPropertiesAwareMapper.readValue(this.keycloakConfigPropertyOverrides, typeRef);
|
||||
} catch (IOException ex) {
|
||||
throw new ConfigurationException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getBindHttpPortOffset() {
|
||||
|
@ -81,4 +99,32 @@ public class KeycloakQuarkusConfiguration implements ContainerConfiguration {
|
|||
public void setStartupTimeoutInSeconds(int startupTimeoutInSeconds) {
|
||||
this.startupTimeoutInSeconds = startupTimeoutInSeconds;
|
||||
}
|
||||
|
||||
public String getRoute() {
|
||||
return route;
|
||||
}
|
||||
|
||||
public void setRoute(String route) {
|
||||
this.route = route;
|
||||
}
|
||||
|
||||
public String getProfile() {
|
||||
return profile;
|
||||
}
|
||||
|
||||
public void setProfile(String profile) {
|
||||
this.profile = profile;
|
||||
}
|
||||
|
||||
public String getKeycloakConfigPropertyOverrides() {
|
||||
return keycloakConfigPropertyOverrides;
|
||||
}
|
||||
|
||||
public void setKeycloakConfigPropertyOverrides(String keycloakConfigPropertyOverrides) {
|
||||
this.keycloakConfigPropertyOverrides = keycloakConfigPropertyOverrides;
|
||||
}
|
||||
|
||||
public Map<String, Object> getKeycloakConfigPropertyOverridesMap() {
|
||||
return keycloakConfigPropertyOverridesMap;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ public class KeycloakQuarkusServerDeployableContainer implements DeployableConta
|
|||
|
||||
private KeycloakQuarkusConfiguration configuration;
|
||||
private Process container;
|
||||
private AtomicBoolean restart = new AtomicBoolean();
|
||||
private static AtomicBoolean restart = new AtomicBoolean();
|
||||
|
||||
@Inject
|
||||
private Instance<SuiteContext> suiteContext;
|
||||
|
@ -130,6 +130,12 @@ public class KeycloakQuarkusServerDeployableContainer implements DeployableConta
|
|||
commands.add("-Dquarkus.http.port=" + configuration.getBindHttpPort());
|
||||
commands.add("-Dquarkus.http.ssl-port=" + configuration.getBindHttpsPort());
|
||||
|
||||
if (configuration.getRoute() != null) {
|
||||
commands.add("-Djboss.node.name=" + configuration.getRoute());
|
||||
}
|
||||
|
||||
commands.add("-Dquarkus.profile=" + System.getProperty("auth.server.quarkus.config", "local"));
|
||||
|
||||
return commands.toArray(new String[commands.size()]);
|
||||
}
|
||||
|
||||
|
@ -137,7 +143,7 @@ public class KeycloakQuarkusServerDeployableContainer implements DeployableConta
|
|||
SuiteContext suiteContext = this.suiteContext.get();
|
||||
//TODO: not sure if the best endpoint but it makes sure that everything is properly initialized. Once we have
|
||||
// support for MP Health this should change
|
||||
URL contextRoot = new URL(suiteContext.getAuthServerInfo().getContextRoot() + "/auth/realms/master/");
|
||||
URL contextRoot = new URL(getBaseUrl(suiteContext) + "/auth/realms/master/");
|
||||
HttpURLConnection connection;
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
|
@ -150,7 +156,7 @@ public class KeycloakQuarkusServerDeployableContainer implements DeployableConta
|
|||
try {
|
||||
// wait before checking for opening a new connection
|
||||
Thread.sleep(1000);
|
||||
if ("https".equals(contextRoot.toURI().getScheme())) {
|
||||
if ("https".equals(contextRoot.getProtocol())) {
|
||||
HttpsURLConnection httpsConnection = (HttpsURLConnection) (connection = (HttpURLConnection) contextRoot.openConnection());
|
||||
httpsConnection.setSSLSocketFactory(createInsecureSslSocketFactory());
|
||||
httpsConnection.setHostnameVerifier(createInsecureHostnameVerifier());
|
||||
|
@ -171,7 +177,19 @@ public class KeycloakQuarkusServerDeployableContainer implements DeployableConta
|
|||
}
|
||||
}
|
||||
|
||||
log.infof("Keycloak is ready at %s", this.suiteContext.get().getAuthServerInfo().getContextRoot());
|
||||
log.infof("Keycloak is ready at %s", contextRoot);
|
||||
}
|
||||
|
||||
private URL getBaseUrl(SuiteContext suiteContext) throws MalformedURLException {
|
||||
URL baseUrl = suiteContext.getAuthServerInfo().getContextRoot();
|
||||
|
||||
// might be running behind a load balancer
|
||||
if ("https".equals(baseUrl.getProtocol())) {
|
||||
baseUrl = new URL(baseUrl.toString().replace(String.valueOf(baseUrl.getPort()), String.valueOf(configuration.getBindHttpsPort())));
|
||||
} else {
|
||||
baseUrl = new URL(baseUrl.toString().replace(String.valueOf(baseUrl.getPort()), String.valueOf(configuration.getBindHttpPort())));
|
||||
}
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
private HostnameVerifier createInsecureHostnameVerifier() {
|
||||
|
|
|
@ -61,8 +61,15 @@ public abstract class AbstractClusterTest extends AbstractKeycloakTest {
|
|||
}
|
||||
|
||||
// Assume that route like "node6" will have corresponding backend container like "auth-server-wildfly-backend6"
|
||||
protected void setCurrentFailNodeForRoute(String route) {
|
||||
String routeNumber = route.substring(route.length() - 1);
|
||||
protected void setCurrentFailNodeForRoute(String nodeName) {
|
||||
String route = nodeName.substring(nodeName.lastIndexOf('.') + 1);
|
||||
String routeNumber;
|
||||
int portSeparator = route.indexOf('-');
|
||||
if (portSeparator == -1) {
|
||||
routeNumber = route.substring(route.length() - 1);
|
||||
} else {
|
||||
routeNumber = route.substring(portSeparator - 1, portSeparator);
|
||||
}
|
||||
currentFailNodeIndex = Integer.parseInt(routeNumber) - 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -99,7 +99,7 @@ public class AuthenticationSessionClusterTest extends AbstractClusterTest {
|
|||
driver.manage().deleteAllCookies();
|
||||
}
|
||||
|
||||
Assert.assertThat(visitedRoutes, Matchers.containsInAnyOrder("node1", "node2"));
|
||||
Assert.assertThat(visitedRoutes, Matchers.containsInAnyOrder(Matchers.startsWith("node1"), Matchers.startsWith("node2")));
|
||||
}
|
||||
|
||||
|
||||
|
@ -128,7 +128,7 @@ public class AuthenticationSessionClusterTest extends AbstractClusterTest {
|
|||
getTestingClientFor(backendNode(0)).server().run(session -> {
|
||||
Cache authSessionCache = session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.AUTHENTICATION_SESSIONS_CACHE_NAME);
|
||||
String keyOwner = InfinispanUtil.getTopologyInfo(session).getRouteName(authSessionCache, authSessionCookie);
|
||||
Assert.assertEquals("node1", keyOwner);
|
||||
Assert.assertTrue(keyOwner.startsWith("node1"));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.jboss.arquillian.graphene.page.Page;
|
|||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
|
@ -80,9 +81,10 @@ public class AuthenticationSessionFailoverClusterTest extends AbstractFailoverCl
|
|||
.enabled(true)
|
||||
.requiredAction(UserModel.RequiredAction.UPDATE_PASSWORD.toString())
|
||||
.requiredAction(UserModel.RequiredAction.UPDATE_PROFILE.toString())
|
||||
.password("password")
|
||||
.build();
|
||||
|
||||
userId = ApiUtil.createUserAndResetPasswordWithAdminClient(adminClient.realm("test"), user, "password");
|
||||
userId = ApiUtil.createUserWithAdminClient(adminClient.realm("test"), user);
|
||||
getCleanup().addUserId(userId);
|
||||
|
||||
oauth.clientId("test-app");
|
||||
|
|
|
@ -4,6 +4,7 @@ import org.apache.commons.lang.RandomStringUtils;
|
|||
import org.junit.Before;
|
||||
import org.keycloak.admin.client.resource.ClientResource;
|
||||
import org.keycloak.admin.client.resource.ClientsResource;
|
||||
import org.keycloak.common.util.Retry;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.arquillian.ContainerInfo;
|
||||
|
@ -58,24 +59,38 @@ public class ClientInvalidationClusterTest extends AbstractInvalidationClusterTe
|
|||
|
||||
@Override
|
||||
protected ClientRepresentation readEntity(ClientRepresentation client, ContainerInfo node) {
|
||||
ClientRepresentation u = null;
|
||||
try {
|
||||
u = entityResource(client, node).toRepresentation();
|
||||
} catch (NotFoundException nfe) {
|
||||
// expected when client doesn't exist
|
||||
}
|
||||
ClientRepresentation u = Retry.call(new Retry.Supplier<ClientRepresentation>() {
|
||||
@Override
|
||||
public ClientRepresentation get(int iteration) {
|
||||
try {
|
||||
return entityResource(client, node).toRepresentation();
|
||||
} catch (NotFoundException nfe) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}, 3, 5000);
|
||||
return u;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClientRepresentation updateEntity(ClientRepresentation client, ContainerInfo node) {
|
||||
entityResource(client, node).update(client);
|
||||
Retry.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
entityResource(client, node).update(client);
|
||||
}
|
||||
}, 3, 5000);
|
||||
return readEntity(client, node);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deleteEntity(ClientRepresentation client, ContainerInfo node) {
|
||||
entityResource(client, node).remove();
|
||||
Retry.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
entityResource(client, node).remove();
|
||||
}
|
||||
}, 3, 5000);
|
||||
assertNull(readEntity(client, node));
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import org.apache.commons.lang.RandomStringUtils;
|
|||
import org.junit.Before;
|
||||
import org.keycloak.admin.client.resource.GroupResource;
|
||||
import org.keycloak.admin.client.resource.GroupsResource;
|
||||
import org.keycloak.common.util.Retry;
|
||||
import org.keycloak.representations.idm.GroupRepresentation;
|
||||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.arquillian.ContainerInfo;
|
||||
|
@ -66,24 +67,38 @@ public class GroupInvalidationClusterTest extends AbstractInvalidationClusterTes
|
|||
|
||||
@Override
|
||||
protected GroupRepresentation readEntity(GroupRepresentation group, ContainerInfo node) {
|
||||
GroupRepresentation u = null;
|
||||
try {
|
||||
u = entityResource(group, node).toRepresentation();
|
||||
} catch (NotFoundException nfe) {
|
||||
// expected when group doesn't exist
|
||||
}
|
||||
GroupRepresentation u = Retry.call(new Retry.Supplier<GroupRepresentation>() {
|
||||
@Override
|
||||
public GroupRepresentation get(int iteration) {
|
||||
try {
|
||||
return entityResource(group, node).toRepresentation();
|
||||
} catch (NotFoundException nfe) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}, 3, 5000);
|
||||
return u;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GroupRepresentation updateEntity(GroupRepresentation group, ContainerInfo node) {
|
||||
entityResource(group, node).update(group);
|
||||
Retry.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
entityResource(group, node).update(group);
|
||||
}
|
||||
}, 3, 5000);
|
||||
return readEntity(group, node);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deleteEntity(GroupRepresentation group, ContainerInfo node) {
|
||||
entityResource(group, node).remove();
|
||||
Retry.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
entityResource(group, node).remove();
|
||||
}
|
||||
}, 3, 5000);
|
||||
assertNull(readEntity(group, node));
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.keycloak.testsuite.cluster;
|
|||
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.admin.client.resource.RealmsResource;
|
||||
import org.keycloak.common.util.Retry;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.testsuite.arquillian.ContainerInfo;
|
||||
|
||||
|
@ -43,12 +44,16 @@ public class RealmInvalidationClusterTest extends AbstractInvalidationClusterTes
|
|||
|
||||
@Override
|
||||
protected RealmRepresentation readEntity(RealmRepresentation realm, ContainerInfo node) {
|
||||
RealmRepresentation realmOnNode = null;
|
||||
try {
|
||||
realmOnNode = entityResource(realm, node).toRepresentation();
|
||||
} catch (NotFoundException nfe) {
|
||||
// expected if realm not found
|
||||
}
|
||||
RealmRepresentation realmOnNode = Retry.call(new Retry.Supplier<RealmRepresentation>() {
|
||||
@Override
|
||||
public RealmRepresentation get(int iteration) {
|
||||
try {
|
||||
return entityResource(realm, node).toRepresentation();
|
||||
} catch (NotFoundException nfe) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}, 3, 5000);
|
||||
return realmOnNode;
|
||||
}
|
||||
|
||||
|
@ -58,7 +63,12 @@ public class RealmInvalidationClusterTest extends AbstractInvalidationClusterTes
|
|||
}
|
||||
|
||||
private RealmRepresentation updateEntity(String realmName, RealmRepresentation realm, ContainerInfo node) {
|
||||
entityResource(realmName, node).update(realm);
|
||||
Retry.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
entityResource(realmName, node).update(realm);
|
||||
}
|
||||
}, 3, 5000);
|
||||
return readEntity(realm, node);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package org.keycloak.testsuite.cluster;
|
|||
import org.apache.commons.lang.RandomStringUtils;
|
||||
import org.keycloak.admin.client.resource.RoleResource;
|
||||
import org.keycloak.admin.client.resource.RolesResource;
|
||||
import org.keycloak.common.util.Retry;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.testsuite.arquillian.ContainerInfo;
|
||||
|
||||
|
@ -48,11 +49,16 @@ public class RoleInvalidationClusterTest extends AbstractInvalidationClusterTest
|
|||
@Override
|
||||
protected RoleRepresentation readEntity(RoleRepresentation role, ContainerInfo node) {
|
||||
RoleRepresentation u = null;
|
||||
try {
|
||||
u = entityResource(role, node).toRepresentation();
|
||||
} catch (NotFoundException nfe) {
|
||||
// expected when role doesn't exist
|
||||
}
|
||||
u = Retry.call(new Retry.Supplier<RoleRepresentation>() {
|
||||
@Override
|
||||
public RoleRepresentation get(int iteration) {
|
||||
try {
|
||||
return entityResource(role, node).toRepresentation();
|
||||
} catch (NotFoundException nfe) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}, 3, 5000);
|
||||
return u;
|
||||
}
|
||||
|
||||
|
@ -62,13 +68,23 @@ public class RoleInvalidationClusterTest extends AbstractInvalidationClusterTest
|
|||
}
|
||||
|
||||
private RoleRepresentation updateEntity(String roleName, RoleRepresentation role, ContainerInfo node) {
|
||||
entityResource(roleName, node).update(role);
|
||||
Retry.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
entityResource(roleName, node).update(role);
|
||||
}
|
||||
}, 3, 5000);
|
||||
return readEntity(role, node);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deleteEntity(RoleRepresentation role, ContainerInfo node) {
|
||||
entityResource(role, node).remove();
|
||||
Retry.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
entityResource(role, node).remove();
|
||||
}
|
||||
}, 3, 5000);
|
||||
assertNull(readEntity(role, node));
|
||||
}
|
||||
|
||||
|
|
|
@ -619,9 +619,54 @@
|
|||
<configuration>
|
||||
<property name="enabled">${auth.server.quarkus}</property>
|
||||
<property name="adapterImplClass">org.keycloak.testsuite.arquillian.containers.KeycloakQuarkusServerDeployableContainer</property>
|
||||
<property name="bindHttpPortOffset">${auth.server.port.offset}</property>
|
||||
</configuration>
|
||||
</container>
|
||||
|
||||
<!-- Clustering with Quarkus -->
|
||||
<group qualifier="auth-server-quarkus-cluster">
|
||||
<container qualifier="auth-server-quarkus-backend1" mode="manual" >
|
||||
<configuration>
|
||||
<property name="enabled">${auth.server.quarkus.cluster}</property>
|
||||
<property name="adapterImplClass">org.keycloak.testsuite.arquillian.containers.KeycloakQuarkusServerDeployableContainer</property>
|
||||
<property name="bindAddress">localhost</property>
|
||||
<property name="bindHttpPort">${auth.server.http.port}</property>
|
||||
<property name="bindHttpsPort">${auth.server.https.port}</property>
|
||||
<property name="bindHttpPortOffset">1</property>
|
||||
<property name="bindHttpsPortOffset">1</property>
|
||||
<property name="route">node1</property>
|
||||
<property name="remoteMode">${quarkus.remote}</property>
|
||||
<property name="profile">ha</property>
|
||||
<property name="keycloakConfigPropertyOverrides">{
|
||||
"keycloak.connectionsInfinispan.jgroupsUdpMcastAddr": "234.56.78.8",
|
||||
"keycloak.connectionsInfinispan.nodeName": "node1",
|
||||
"keycloak.connectionsInfinispan.clustered": "${keycloak.connectionsInfinispan.clustered:true}"
|
||||
}
|
||||
</property>
|
||||
</configuration>
|
||||
</container>
|
||||
<container qualifier="auth-server-quarkus-backend2" mode="manual" >
|
||||
<configuration>
|
||||
<property name="enabled">${auth.server.quarkus.cluster}</property>
|
||||
<property name="adapterImplClass">org.keycloak.testsuite.arquillian.containers.KeycloakQuarkusServerDeployableContainer</property>
|
||||
<property name="bindAddress">localhost</property>
|
||||
<property name="bindHttpPort">${auth.server.http.port}</property>
|
||||
<property name="bindHttpsPort">${auth.server.https.port}</property>
|
||||
<property name="bindHttpPortOffset">2</property>
|
||||
<property name="bindHttpsPortOffset">2</property>
|
||||
<property name="route">node2</property>
|
||||
<property name="remoteMode">${quarkus.remote}</property>
|
||||
<property name="profile">ha</property>
|
||||
<property name="keycloakConfigPropertyOverrides">{
|
||||
"keycloak.connectionsInfinispan.jgroupsUdpMcastAddr": "234.56.78.8",
|
||||
"keycloak.connectionsInfinispan.nodeName": "node2",
|
||||
"keycloak.connectionsInfinispan.clustered": "${keycloak.connectionsInfinispan.clustered:true}"
|
||||
}
|
||||
</property>
|
||||
</configuration>
|
||||
</container>
|
||||
</group>
|
||||
|
||||
<!-- PREVIOUS VERSION OF KEYCLOAK FOR MIGRATION TESTS -->
|
||||
|
||||
<container qualifier="auth-server-jboss-migration" mode="manual" >
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
<auth.server.undertow.cluster>false</auth.server.undertow.cluster>
|
||||
<auth.server.jboss.cluster>false</auth.server.jboss.cluster>
|
||||
<auth.server.jboss.legacy>false</auth.server.jboss.legacy>
|
||||
<auth.server.quarkus.cluster>false</auth.server.quarkus.cluster>
|
||||
|
||||
<auth.server.crossdc>false</auth.server.crossdc>
|
||||
<auth.server.undertow.crossdc>false</auth.server.undertow.crossdc>
|
||||
|
@ -77,6 +78,8 @@
|
|||
|
||||
<auth.server.jboss.artifactId>integration-arquillian-servers-auth-server-${auth.server}</auth.server.jboss.artifactId>
|
||||
<auth.server.jboss.skip.unpack>${auth.server.undertow}</auth.server.jboss.skip.unpack>
|
||||
<auth.server.quarkus.skip.unpack>true</auth.server.quarkus.skip.unpack>
|
||||
<auth.server.undertow.skip.unpack>false</auth.server.undertow.skip.unpack>
|
||||
<auth.server.jboss.startup.timeout>300</auth.server.jboss.startup.timeout>
|
||||
|
||||
<!--debug properties-->
|
||||
|
@ -215,6 +218,7 @@
|
|||
|
||||
<auth.server.ocsp.responder.enabled>false</auth.server.ocsp.responder.enabled>
|
||||
<keycloak.x509cert.lookup.provider>default</keycloak.x509cert.lookup.provider>
|
||||
<auth.server.quarkus.config>local</auth.server.quarkus.config>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
|
@ -260,6 +264,26 @@
|
|||
</artifactItem>
|
||||
</artifactItems>
|
||||
<includes>*.jks,*.crt,*.truststore,*.crl,*.key,certs/clients/*</includes>
|
||||
<skip>${auth.server.undertow.skip.unpack}</skip>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>unpack-quarkus-server</id>
|
||||
<phase>generate-test-resources</phase>
|
||||
<goals>
|
||||
<goal>unpack</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>org.keycloak.testsuite</groupId>
|
||||
<artifactId>integration-arquillian-servers-auth-server-quarkus</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>zip</type>
|
||||
<outputDirectory>${containers.home}</outputDirectory>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
<skip>${auth.server.quarkus.skip.unpack}</skip>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
|
@ -580,6 +604,8 @@
|
|||
<auth.server.undertow.cluster>${auth.server.undertow.cluster}</auth.server.undertow.cluster>
|
||||
<auth.server.jboss.cluster>${auth.server.jboss.cluster}</auth.server.jboss.cluster>
|
||||
<auth.server.jboss.legacy>${auth.server.jboss.legacy}</auth.server.jboss.legacy>
|
||||
<auth.server.quarkus.cluster>${auth.server.quarkus.cluster}</auth.server.quarkus.cluster>
|
||||
<auth.server.quarkus.config>${auth.server.quarkus.config}</auth.server.quarkus.config>
|
||||
|
||||
<!--cache server properties-->
|
||||
<auth.server.crossdc>${auth.server.crossdc}</auth.server.crossdc>
|
||||
|
@ -701,9 +727,43 @@
|
|||
<auth.server.jboss>false</auth.server.jboss>
|
||||
<auth.server.undertow>false</auth.server.undertow>
|
||||
<auth.server.config.dir>${auth.server.home}/conf</auth.server.config.dir>
|
||||
<auth.server.quarkus.skip.unpack>false</auth.server.quarkus.skip.unpack>
|
||||
<auth.server.undertow.skip.unpack>true</auth.server.undertow.skip.unpack>
|
||||
<auth.server.jboss.skip.unpack>true</auth.server.jboss.skip.unpack>
|
||||
</properties>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>auth-server-cluster-quarkus</id>
|
||||
<properties>
|
||||
<!--disable exclusion pattern for cluster test which is enabled by default in base/pom.xml-->
|
||||
<exclude.cluster>-</exclude.cluster>
|
||||
<auth.server.cluster>true</auth.server.cluster>
|
||||
<auth.server.quarkus.cluster>true</auth.server.quarkus.cluster>
|
||||
<auth.server.quarkus.config>ha</auth.server.quarkus.config>
|
||||
<auth.server>quarkus</auth.server>
|
||||
<auth.server.quarkus>true</auth.server.quarkus>
|
||||
<auth.server.jboss>false</auth.server.jboss>
|
||||
<auth.server.undertow>false</auth.server.undertow>
|
||||
<auth.server.config.dir>${auth.server.home}/conf</auth.server.config.dir>
|
||||
<auth.server.quarkus.skip.unpack>false</auth.server.quarkus.skip.unpack>
|
||||
<auth.server.undertow.skip.unpack>true</auth.server.undertow.skip.unpack>
|
||||
<auth.server.jboss.skip.unpack>true</auth.server.jboss.skip.unpack>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<systemPropertyVariables>
|
||||
<pageload.timeout>20000</pageload.timeout>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>auth-server-wildfly</id>
|
||||
<properties>
|
||||
|
|
Loading…
Reference in a new issue