diff --git a/model/map-hot-rod/pom.xml b/model/map-hot-rod/pom.xml index 6939547577..a7ec96efd8 100644 --- a/model/map-hot-rod/pom.xml +++ b/model/map-hot-rod/pom.xml @@ -30,18 +30,6 @@ org.infinispan infinispan-remote-query-client - - org.infinispan - infinispan-server-router - - - org.infinispan - infinispan-server-rest - - - org.infinispan - infinispan-server-runtime - org.infinispan.protostream protostream-processor diff --git a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/common/HotRodUtils.java b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/common/HotRodUtils.java index 739dbfc332..c1dd4f769f 100644 --- a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/common/HotRodUtils.java +++ b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/common/HotRodUtils.java @@ -16,26 +16,7 @@ */ package org.keycloak.models.map.storage.hotRod.common; -import org.infinispan.manager.DefaultCacheManager; import org.infinispan.query.dsl.Query; -import org.infinispan.rest.RestServer; -import org.infinispan.rest.configuration.RestServerConfigurationBuilder; -import org.infinispan.server.configuration.endpoint.SinglePortServerConfigurationBuilder; -import org.infinispan.server.hotrod.HotRodServer; -import org.infinispan.server.hotrod.configuration.HotRodServerConfigurationBuilder; -import org.infinispan.server.router.RoutingTable; -import org.infinispan.server.router.configuration.SinglePortRouterConfiguration; -import org.infinispan.server.router.router.impl.singleport.SinglePortEndpointRouter; -import org.infinispan.server.router.routes.Route; -import org.infinispan.server.router.routes.RouteDestination; -import org.infinispan.server.router.routes.RouteSource; -import org.infinispan.server.router.routes.hotrod.HotRodServerRouteDestination; -import org.infinispan.server.router.routes.rest.RestServerRouteDestination; -import org.infinispan.server.router.routes.singleport.SinglePortRouteSource; - -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; /** * @author Martin Kanis @@ -44,51 +25,6 @@ public class HotRodUtils { public static final int DEFAULT_MAX_RESULTS = Integer.MAX_VALUE >> 1; - /** - * Not suitable for a production usage. Only for development and test purposes. - * Also do not use in clustered environment. - * @param hotRodServer HotRodServer - * @param hotRodCacheManager DefaultCacheManager - * @param embeddedPort int - */ - public static void createHotRodMapStoreServer(HotRodServer hotRodServer, DefaultCacheManager hotRodCacheManager, int embeddedPort) { - HotRodServerConfigurationBuilder hotRodServerConfigurationBuilder = new HotRodServerConfigurationBuilder(); - hotRodServerConfigurationBuilder.startTransport(false); - hotRodServerConfigurationBuilder.port(embeddedPort); - hotRodServer.start(hotRodServerConfigurationBuilder.build(), hotRodCacheManager); - - RestServerConfigurationBuilder restServerConfigurationBuilder = new RestServerConfigurationBuilder(); - restServerConfigurationBuilder.startTransport(false); - restServerConfigurationBuilder.port(embeddedPort); - RestServer restServer = new RestServer(); - restServer.start(restServerConfigurationBuilder.build(), hotRodCacheManager); - - SinglePortRouteSource routeSource = new SinglePortRouteSource(); - Set> routes = new HashSet<>(); - routes.add(new Route<>(routeSource, new HotRodServerRouteDestination("hotrod", hotRodServer))); - routes.add(new Route<>(routeSource, new RestServerRouteDestination("rest", restServer))); - - SinglePortRouterConfiguration singlePortRouter = new SinglePortServerConfigurationBuilder().port(embeddedPort).build(); - SinglePortEndpointRouter endpointServer = new SinglePortEndpointRouter(singlePortRouter); - endpointServer.start(new RoutingTable(routes)); - } - - /** - * Not suitable for a production usage. Only for development and test purposes. - * Also do not use in clustered environment. - * @param embeddedPort int - */ - public static void createHotRodMapStoreServer(int embeddedPort) { - DefaultCacheManager hotRodCacheManager = null; - try { - hotRodCacheManager = new DefaultCacheManager("config/infinispan.xml"); - } catch (IOException e) { - new RuntimeException("Cannot initialize cache manager!", e); - } - - HotRodUtils.createHotRodMapStoreServer(new HotRodServer(), hotRodCacheManager, embeddedPort); - } - public static Query paginateQuery(Query query, Integer first, Integer max) { if (first != null && first > 0) { query = query.startOffset(first); diff --git a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/connections/DefaultHotRodConnectionProviderFactory.java b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/connections/DefaultHotRodConnectionProviderFactory.java index 4832a6fb41..05172220aa 100644 --- a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/connections/DefaultHotRodConnectionProviderFactory.java +++ b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/connections/DefaultHotRodConnectionProviderFactory.java @@ -30,7 +30,6 @@ import org.jboss.logging.Logger; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.map.storage.hotRod.common.HotRodEntityDescriptor; -import org.keycloak.models.map.storage.hotRod.common.HotRodUtils; import org.keycloak.models.map.storage.hotRod.common.CommonPrimitivesProtoSchemaInitializer; import org.keycloak.models.map.storage.hotRod.common.HotRodVersionUtils; @@ -89,14 +88,10 @@ public class DefaultHotRodConnectionProviderFactory implements HotRodConnectionP } public void lazyInit() { - if (config.getBoolean("embedded", false)) { - HotRodUtils.createHotRodMapStoreServer(config.getInt("embeddedPort", 11444)); - } - ConfigurationBuilder remoteBuilder = new ConfigurationBuilder(); remoteBuilder.addServer() .host(config.get("host", "localhost")) - .port(config.getInt("port", 11444)) + .port(config.getInt("port", 11222)) .clientIntelligence(ClientIntelligence.HASH_DISTRIBUTION_AWARE) .marshaller(new ProtoStreamMarshaller()); diff --git a/pom.xml b/pom.xml index 6090ad143f..0005681733 100644 --- a/pom.xml +++ b/pom.xml @@ -178,6 +178,7 @@ https://issues.redhat.com/browse/KEYCLOAK-17585?focusedCommentId=16002705&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-16002705 --> 2.3 + 1.16.3 1.3.5 diff --git a/quarkus/runtime/pom.xml b/quarkus/runtime/pom.xml index ea7da5fc5a..f8b21b6a74 100644 --- a/quarkus/runtime/pom.xml +++ b/quarkus/runtime/pom.xml @@ -469,28 +469,6 @@ - - org.infinispan - infinispan-server-rest - ${infinispan.version} - - - * - * - - - - - org.infinispan - infinispan-server-router - ${infinispan.version} - - - * - * - - - org.infinispan infinispan-cachestore-remote diff --git a/testsuite/integration-arquillian/pom.xml b/testsuite/integration-arquillian/pom.xml index 1b04f5a581..21f489c984 100644 --- a/testsuite/integration-arquillian/pom.xml +++ b/testsuite/integration-arquillian/pom.xml @@ -55,7 +55,6 @@ 1.1.0.Final 1.0.0.Alpha2 1.6.1 - 1.5.1 6.1.0 diff --git a/testsuite/model/README.md b/testsuite/model/README.md index c0cd5a2b71..f1012f5478 100644 --- a/testsuite/model/README.md +++ b/testsuite/model/README.md @@ -48,3 +48,59 @@ mvn test -Pjpa -Dtest=ClientModelTest \ ``` The results are available in the `target/profile.html` file. + +Usage of Testcontainers +----------------------- + +Some profiles within model tests require running 3rd party software, for +example, database or Infinispan. For running these we are using +[Testcontainers](https://www.testcontainers.org/). This may require some +additional configuration of your container engine. + +#### Podman settings + +For more details see the following [Podman guide from Quarkus webpage](https://quarkus.io/guides/podman). + +Specifically, these steps are required: +```shell +# Enable the podman socket with Docker REST API (only needs to be done once) +systemctl --user enable podman.socket --now + +# Set the required environment variables (need to be run everytime or added to profile) +export DOCKER_HOST=unix:///run/user/${UID}/podman/podman.sock +``` + +Testcontainers are using [ryuk](https://hub.docker.com/r/testcontainers/ryuk) +to cleanup containers after tests. To make this work with Podman add the +following line to `~/.testcontainers.properties` +```shell +ryuk.container.privileged=true +``` +Alternatively, disable usage of ryuk (using this may result in stale containers +still running after tests finish. This is not recommended especially if you are +executing tests from Intellij IDE as it [may not stop](https://youtrack.jetbrains.com/issue/IDEA-190385) +the containers created during test run). +```shell +export TESTCONTAINERS_RYUK_DISABLED=true #not recommended - see above! +``` + +#### Docker settings + +To use Testcontainers with Docker it is necessary to +[make Docker available for non-root users](https://docs.docker.com/engine/install/linux-postinstall/). + +Running HotRod tests with external Infinispan +--------------------------------------------- + +By default, Model tests with `hot-rod` profile spawn a new Infinispan container +with each test execution. It is also possible, to configure Model tests to +connect to an external instance of Infinispan. To do so, execute tests with +the following command: +```shell +mvn test -Phot-rod \ + -Dhot-rod.start-container=false \ + -Dhot-rod.connection.host= \ + -Dhot-rod.connection.port= \ + -Dhot-rod.connection.username= \ + -Dhot-rod.connection.password= +``` diff --git a/testsuite/model/pom.xml b/testsuite/model/pom.xml index e099eb258f..f573b55a37 100644 --- a/testsuite/model/pom.xml +++ b/testsuite/model/pom.xml @@ -120,6 +120,12 @@ postgresql ${postgresql.driver.version} + + org.testcontainers + testcontainers + ${testcontainers.version} + test + @@ -171,7 +177,14 @@ ${keycloak.userSessions.infinispan.preloadOfflineSessionsFromDatabase} org.jboss.logmanager.LogManager log4j + ${infinispan.version} + + + listener + org.keycloak.testsuite.model.AfterSuiteListener + + diff --git a/testsuite/model/src/main/java/org/keycloak/testsuite/model/HotRodServerRule.java b/testsuite/model/src/main/java/org/keycloak/testsuite/model/HotRodServerRule.java index 3ffb7579dd..64e55531ac 100644 --- a/testsuite/model/src/main/java/org/keycloak/testsuite/model/HotRodServerRule.java +++ b/testsuite/model/src/main/java/org/keycloak/testsuite/model/HotRodServerRule.java @@ -84,29 +84,6 @@ public class HotRodServerRule extends ExternalResource { InfinispanUtil.setTimeServiceToKeycloakTime(hotRodCacheManager2); } - public void createHotRodMapStoreServer() { - hotRodCacheManager = configureHotRodCacheManager("hotrod/infinispan.xml"); - hotRodServer = new HotRodServer(); - - HotRodUtils.createHotRodMapStoreServer(hotRodServer, hotRodCacheManager, 11444); - - org.infinispan.client.hotrod.configuration.ConfigurationBuilder remoteBuilder = new org.infinispan.client.hotrod.configuration.ConfigurationBuilder(); - org.infinispan.client.hotrod.configuration.Configuration cfg = remoteBuilder - .addServers(hotRodServer.getHost() + ":" + hotRodServer.getPort()).build(); - remoteCacheManager = new RemoteCacheManager(cfg); - } - - private DefaultCacheManager configureHotRodCacheManager(String configPath) { - DefaultCacheManager manager = null; - try { - manager = new DefaultCacheManager(configPath); - } catch (IOException e) { - new RuntimeException(e); - } - - return manager; - } - private void getCaches(String... cache) { for (String c: cache) { hotRodCacheManager.getCache(c, true); diff --git a/testsuite/model/src/main/java/org/keycloak/testsuite/model/KeycloakModelParameters.java b/testsuite/model/src/main/java/org/keycloak/testsuite/model/KeycloakModelParameters.java index 5285642a68..5c0a137cb5 100644 --- a/testsuite/model/src/main/java/org/keycloak/testsuite/model/KeycloakModelParameters.java +++ b/testsuite/model/src/main/java/org/keycloak/testsuite/model/KeycloakModelParameters.java @@ -72,4 +72,8 @@ public class KeycloakModelParameters { public void beforeSuite(Config cf) { } + + public void afterSuite() { + + } } diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/AfterSuiteListener.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/AfterSuiteListener.java new file mode 100644 index 0000000000..30538d0dd1 --- /dev/null +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/AfterSuiteListener.java @@ -0,0 +1,32 @@ +/* + * Copyright 2022 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. + */ + +package org.keycloak.testsuite.model; + +import org.junit.runner.Result; +import org.junit.runner.notification.RunListener; + +public class AfterSuiteListener extends RunListener { + + @Override + public void testRunFinished(Result result) throws Exception { + KeycloakModelTest.closeKeycloakSessionFactory(); + for (KeycloakModelParameters kmp : KeycloakModelTest.MODEL_PARAMETERS) { + kmp.afterSuite(); + } + } +} diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/HotRodMapStorage.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/HotRodMapStorage.java index b9b496847e..092231817a 100644 --- a/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/HotRodMapStorage.java +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/HotRodMapStorage.java @@ -17,8 +17,7 @@ package org.keycloak.testsuite.model.parameters; import com.google.common.collect.ImmutableSet; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; +import org.jboss.logging.Logger; import org.keycloak.authorization.store.StoreFactorySpi; import org.keycloak.events.EventStoreSpi; import org.keycloak.models.ActionTokenStoreSpi; @@ -31,7 +30,6 @@ import org.keycloak.models.map.authSession.MapRootAuthenticationSessionProviderF import org.keycloak.models.map.authorization.MapAuthorizationStoreFactory; import org.keycloak.models.map.client.MapClientProviderFactory; import org.keycloak.models.map.clientscope.MapClientScopeProviderFactory; -import org.keycloak.models.map.events.MapEventStoreProviderFactory; import org.keycloak.models.map.keys.MapPublicKeyStorageProviderFactory; import org.keycloak.models.map.singleUseObject.MapSingleUseObjectProviderFactory; import org.keycloak.models.map.storage.hotRod.connections.DefaultHotRodConnectionProviderFactory; @@ -51,10 +49,14 @@ import org.keycloak.provider.ProviderFactory; import org.keycloak.provider.Spi; import org.keycloak.sessions.AuthenticationSessionSpi; import org.keycloak.testsuite.model.Config; -import org.keycloak.testsuite.model.HotRodServerRule; import org.keycloak.testsuite.model.KeycloakModelParameters; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import java.time.Duration; import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * @@ -62,6 +64,24 @@ import java.util.Set; */ public class HotRodMapStorage extends KeycloakModelParameters { + private final Logger LOG = Logger.getLogger(getClass()); + public static final String PORT = System.getProperty("hot-rod.connection.port", "11222"); + public static String HOST = System.getProperty("hot-rod.connection.host"); + public static final String USERNAME = System.getProperty("hot-rod.connection.username", "admin"); + public static final String PASSWORD = System.getProperty("hot-rod.connection.password", "admin"); + public static final Boolean START_CONTAINER = Boolean.valueOf(System.getProperty("hot-rod.start-container", "true")); + + private static final String ZERO_TO_255 + = "(\\d{1,2}|(0|1)\\" + + "d{2}|2[0-4]\\d|25[0-5])"; + private static final String IP_ADDRESS_REGEX + = ZERO_TO_255 + "\\." + + ZERO_TO_255 + "\\." + + ZERO_TO_255 + "\\." + + ZERO_TO_255; + + private static final Pattern IP_ADDRESS_PATTERN = Pattern.compile("listening on (" + IP_ADDRESS_REGEX + "):" + PORT); + static final Set> ALLOWED_SPIS = ImmutableSet.>builder() .add(HotRodConnectionSpi.class) .build(); @@ -69,10 +89,12 @@ public class HotRodMapStorage extends KeycloakModelParameters { static final Set> ALLOWED_FACTORIES = ImmutableSet.>builder() .add(HotRodMapStorageProviderFactory.class) .add(HotRodConnectionProviderFactory.class) - .add(ConcurrentHashMapStorageProviderFactory.class) // TODO: this should be removed when we have a HotRod implementation for each area .build(); - private HotRodServerRule hotRodServerRule = new HotRodServerRule(); + private final GenericContainer hotRodContainer = new GenericContainer("quay.io/infinispan/server:" + System.getProperty("infinispan.version")) + .withEnv("USER", USERNAME) + .withEnv("PASS", PASSWORD) + .withNetworkMode("host"); @Override public void updateConfig(Config cf) { @@ -99,19 +121,38 @@ public class HotRodMapStorage extends KeycloakModelParameters { .config("dir", "${project.build.directory:target}") .config("keyType.single-use-objects", "string"); + if (HOST == null && START_CONTAINER) { + Matcher matcher = IP_ADDRESS_PATTERN.matcher(hotRodContainer.getLogs()); + if (!matcher.find()) { + LOG.errorf("Cannot find IP address of the infinispan server in log:\\n%s ", hotRodContainer.getLogs()); + throw new IllegalStateException("Cannot find IP address of the Infinispan server. See test log for Infinispan container log."); + } + HOST = matcher.group(1); + } + cf.spi(HotRodConnectionSpi.NAME).provider(DefaultHotRodConnectionProviderFactory.PROVIDER_ID) - .config("enableSecurity", "false") - .config("configureRemoteCaches", "false"); + .config("host", HOST) + .config("port", PORT) + .config("username", USERNAME) + .config("password", PASSWORD) + .config("configureRemoteCaches", "true"); } @Override public void beforeSuite(Config cf) { - hotRodServerRule.createHotRodMapStoreServer(); + if (START_CONTAINER) { + hotRodContainer + .withStartupTimeout(Duration.ofMinutes(5)) + .waitingFor(Wait.forLogMessage(".*Infinispan Server.*started in.*", 1)) + .start(); + } } @Override - public Statement classRule(Statement base, Description description) { - return hotRodServerRule.apply(base, description); + public void afterSuite() { + if (START_CONTAINER) { + hotRodContainer.stop(); + } } public HotRodMapStorage() { diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/UserSessionConcurrencyTest.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/UserSessionConcurrencyTest.java index dc379a17cb..00a40352e2 100644 --- a/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/UserSessionConcurrencyTest.java +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/UserSessionConcurrencyTest.java @@ -98,7 +98,6 @@ public class UserSessionConcurrencyTest extends KeycloakModelTest { // This is basically the same as JpaMapKeycloakTransaction#read method is doing after calling lockUserSessionsForModification() method if (isHotRodStore) { SYNC_USESSION.lock(); - releaseLockOnTransactionCommit(session, SYNC_USESSION); } UserSessionModel uSession = lockUserSessionsForModification(session, () -> session.sessions().getUserSession(realm, uId)); @@ -110,6 +109,10 @@ public class UserSessionConcurrencyTest extends KeycloakModelTest { cSession.setNote(OIDCLoginProtocol.STATE_PARAM, "state-" + n); + if (isHotRodStore) { + releaseLockOnTransactionCommit(session, SYNC_USESSION); + } + return null; }));