diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ceacbc1fbd..f93a8ea26d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,6 @@ jobs: ./mvnw clean install -nsu -B -e -DskipTests -Pdistribution ./mvnw clean install -nsu -B -e -f testsuite/integration-arquillian/servers/auth-server -Pauth-server-quarkus ./mvnw clean install -nsu -B -e -f testsuite/integration-arquillian/servers/auth-server -Pauth-server-undertow - ./mvnw clean install -nsu -B -e -f testsuite/integration-arquillian/servers/cache-server -Pcache-server-infinispan - name: Store Keycloak artifacts id: store-keycloak diff --git a/testsuite/integration-arquillian/HOW-TO-RUN.md b/testsuite/integration-arquillian/HOW-TO-RUN.md index 096aeaafb3..5cbb891ca0 100644 --- a/testsuite/integration-arquillian/HOW-TO-RUN.md +++ b/testsuite/integration-arquillian/HOW-TO-RUN.md @@ -964,6 +964,90 @@ because this is not UI testing). For debugging purposes you can override the hea For changing the hostname in the hostname tests (e.g. [DefaultHostnameTest](https://github.com/keycloak/keycloak/blob/main/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/url/DefaultHostnameTest.java)), we rely on [nip.io](https://nip.io) for DNS switching, so tests will work everywhere without fiddling with `etc/hosts` locally. +## Running base testsuite with Map storage + +To run base testsuite with new storage run the following command (this will execute testsuite with ConcurrentHashMap storage): +```shell +mvn clean install -f testsuite/integration-arquillian/tests/base \ + -Pauth-server-quarkus -Pmap-storage +``` + +### Running tests with JPA Map storage + +Run PostgreSQL database: +```shell +podman run --name postgres -p 5432:5432 -e POSTGRES_PASSWORD=pass -e POSTGRES_USER=keycloak -e POSTGRES_DB=keycloak -d postgres:13.2 +``` + +Execute tests: +```shell +mvn clean install -f testsuite/integration-arquillian/tests/base \ + -Pmap-storage,map-storage-jpa +``` + +### Running tests with HotRod Map storage + +By default, Base testsuite with `map-storage-hotrod` profile spawn a new Infinispan container +with each test execution. To run the tests execute: +```shell +mvn clean install -f testsuite/integration-arquillian/tests/base \ + -Pmap-storage,map-storage-hotrod +``` +Note: For running Infinispan server we are using Testcontainer, see section +_Usage of Testcontainers_ for details on how to set up your container engine. + +It is also possible, to configure Base testsuite to +connect to an external instance of Infinispan. To do so, execute tests with +the following command: +```shell +mvn clean install -f testsuite/integration-arquillian/tests/base \ + -Pmap-storage,map-storage-hotrod + -Dkeycloak.testsuite.start-hotrod-container=false \ + -Dkeycloak.connectionsHotRod.host= \ + -Dkeycloak.connectionsHotRod.port= \ + -Dkeycloak.connectionsHotRod.username= \ + -Dkeycloak.connectionsHotRod.password= +``` + +### 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/). + ### Tips & Tricks: Although it _should_ work in general, you may experience an exception like this: ``` diff --git a/testsuite/integration-arquillian/servers/cache-server/infinispan/pom.xml b/testsuite/integration-arquillian/servers/cache-server/infinispan/pom.xml index e02960548a..12d418c2dd 100644 --- a/testsuite/integration-arquillian/servers/cache-server/infinispan/pom.xml +++ b/testsuite/integration-arquillian/servers/cache-server/infinispan/pom.xml @@ -255,8 +255,6 @@ myuser -p "qwer1234!" - -g - admin diff --git a/testsuite/integration-arquillian/tests/base/pom.xml b/testsuite/integration-arquillian/tests/base/pom.xml index 55eee00f1e..16f71cf736 100644 --- a/testsuite/integration-arquillian/tests/base/pom.xml +++ b/testsuite/integration-arquillian/tests/base/pom.xml @@ -101,7 +101,6 @@ org.testcontainers testcontainers ${testcontainers.version} - test com.google.guava diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java index e274758769..a231413184 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java @@ -142,10 +142,6 @@ public class AuthServerTestEnricher { public static final String AUTH_SERVER_CROSS_DC_PROPERTY = "auth.server.crossdc"; public static final boolean AUTH_SERVER_CROSS_DC = Boolean.parseBoolean(System.getProperty(AUTH_SERVER_CROSS_DC_PROPERTY, "false")); - - public static final String HOT_ROD_STORE_ENABLED_PROPERTY = "hotrod.store.enabled"; - public static final boolean HOT_ROD_STORE_ENABLED = Boolean.parseBoolean(System.getProperty(HOT_ROD_STORE_ENABLED_PROPERTY, "false")); - public static final String AUTH_SERVER_HOME_PROPERTY = "auth.server.home"; public static final String CACHE_SERVER_LIFECYCLE_SKIP_PROPERTY = "cache.server.lifecycle.skip"; @@ -350,17 +346,6 @@ public class AuthServerTestEnricher { } } - if (HOT_ROD_STORE_ENABLED) { - HotRodStoreTestEnricher.initializeSuiteContext(suiteContext); - - for (ContainerInfo container : suiteContext.getContainers()) { - // migrated auth server - if (container.getQualifier().equals("hot-rod-store")) { - suiteContext.setHotRodStoreInfo(container); - } - } - } - suiteContextProducer.set(suiteContext); CrossDCTestEnricher.initializeSuiteContext(suiteContext); log.info("\n\n" + suiteContext); diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/HotRodStoreTestEnricher.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/HotRodStoreTestEnricher.java index 293ba5a033..1824ab25d4 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/HotRodStoreTestEnricher.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/HotRodStoreTestEnricher.java @@ -1,53 +1,29 @@ package org.keycloak.testsuite.arquillian; -import org.jboss.arquillian.container.spi.event.StartContainer; -import org.jboss.arquillian.container.spi.event.StopContainer; -import org.jboss.arquillian.container.test.api.ContainerController; -import org.jboss.arquillian.core.api.Event; -import org.jboss.arquillian.core.api.Instance; -import org.jboss.arquillian.core.api.annotation.Inject; -import org.jboss.arquillian.core.api.annotation.Observes; -import org.jboss.arquillian.core.spi.Validate; -import org.jboss.arquillian.test.spi.event.suite.AfterSuite; -import org.jboss.logging.Logger; import org.jboss.arquillian.container.spi.event.StartSuiteContainers; +import org.jboss.arquillian.core.api.annotation.Observes; +import org.jboss.arquillian.test.spi.event.suite.AfterSuite; +import org.keycloak.testsuite.util.InfinispanContainer; public class HotRodStoreTestEnricher { - private static SuiteContext suiteContext; - private static final Logger log = Logger.getLogger(HotRodStoreTestEnricher.class); + public static final String HOT_ROD_STORE_HOST_PROPERTY = "keycloak.connectionsHotRod.host"; - @Inject - private Event startContainerEvent; + public static final boolean HOT_ROD_START_CONTAINER = Boolean.parseBoolean(System.getProperty("keycloak.testsuite.start-hotrod-container", "false")); - @Inject - private Event stopContainerEvent; - - static void initializeSuiteContext(SuiteContext suiteContext) { - Validate.notNull(suiteContext, "Suite context cannot be null."); - HotRodStoreTestEnricher.suiteContext = suiteContext; - } + private final InfinispanContainer hotRodContainer = new InfinispanContainer(); public void beforeContainerStarted(@Observes(precedence = 1) StartSuiteContainers event) { - if (!AuthServerTestEnricher.HOT_ROD_STORE_ENABLED) return; + if (!HOT_ROD_START_CONTAINER) return; + hotRodContainer.start(); - ContainerInfo hotRodContainer = suiteContext.getHotRodStoreInfo(); - - if (hotRodContainer != null && !hotRodContainer.isStarted()) { - log.infof("HotRod store starting: %s", hotRodContainer.getQualifier()); - startContainerEvent.fire(new StartContainer(hotRodContainer.getArquillianContainer())); - } + // Add env variable, so it can be picked up by Keycloak + System.setProperty(HOT_ROD_STORE_HOST_PROPERTY, hotRodContainer.getHost()); } public void afterSuite(@Observes(precedence = 4) AfterSuite event) { - if (!AuthServerTestEnricher.HOT_ROD_STORE_ENABLED) return; - - ContainerInfo hotRodContainer = suiteContext.getHotRodStoreInfo(); - - if (hotRodContainer != null && hotRodContainer.isStarted()) { - log.infof("HotRod store stopping: %s", hotRodContainer.getQualifier()); - stopContainerEvent.fire(new StopContainer(hotRodContainer.getArquillianContainer())); - } + if (!HOT_ROD_START_CONTAINER) return; + hotRodContainer.stop(); } } diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/SuiteContext.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/SuiteContext.java index 71e8f497c3..0641112184 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/SuiteContext.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/SuiteContext.java @@ -49,8 +49,6 @@ public final class SuiteContext { private ContainerInfo migratedAuthServerInfo; private final MigrationContext migrationContext = new MigrationContext(); - private ContainerInfo hotRodStoreInfo; - private boolean adminPasswordUpdated; private final Map smtpServer = new HashMap<>(); @@ -176,14 +174,6 @@ public final class SuiteContext { this.migratedAuthServerInfo = migratedAuthServerInfo; } - public ContainerInfo getHotRodStoreInfo() { - return hotRodStoreInfo; - } - - public void setHotRodStoreInfo(ContainerInfo hotRodStoreInfo) { - this.hotRodStoreInfo = hotRodStoreInfo; - } - public boolean isAuthServerCluster() { return ! authServerBackendsInfo.isEmpty(); } diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/InfinispanContainer.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/InfinispanContainer.java new file mode 100644 index 0000000000..1653e523dd --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/InfinispanContainer.java @@ -0,0 +1,87 @@ +/* + * 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.util; + +import org.jboss.logging.Logger; +import org.keycloak.testsuite.arquillian.HotRodStoreTestEnricher; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; + +import java.time.Duration; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class InfinispanContainer extends GenericContainer { + + private final Logger LOG = Logger.getLogger(getClass()); + private static final String PORT = System.getProperty("keycloak.connectionsHotRod.port", "11222"); + private static String HOST = System.getProperty(HotRodStoreTestEnricher.HOT_ROD_STORE_HOST_PROPERTY); + private static final String USERNAME = System.getProperty("keycloak.connectionsHotRod.username", "admin"); + private static final String PASSWORD = System.getProperty("keycloak.connectionsHotRod.password", "admin"); + + 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); + + public InfinispanContainer() { + super("quay.io/infinispan/server:" + System.getProperty("infinispan.version")); + withEnv("USER", USERNAME); + withEnv("PASS", PASSWORD); + withNetworkMode("host"); + + withStartupTimeout(Duration.ofMinutes(5)); + waitingFor(Wait.forLogMessage(".*Infinispan Server.*started in.*", 1)); + } + + public String getHost() { + if (HOST == null && this.isRunning()) { + Matcher matcher = IP_ADDRESS_PATTERN.matcher(getLogs()); + if (!matcher.find()) { + LOG.errorf("Cannot find IP address of the infinispan server in log:\\n%s ", getLogs()); + throw new IllegalStateException("Cannot find IP address of the Infinispan server. See test log for Infinispan container log."); + } + HOST = matcher.group(1); + } + + return HOST; + } + + @Override + public void start() { + super.start(); + } + + public String getPort() { + return PORT; + } + + public String getUsername() { + return USERNAME; + } + + public String getPassword() { + return PASSWORD; + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json index 994914ec0f..d6e11c55df 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json @@ -297,13 +297,12 @@ "connectionsHotRod": { "default": { - "embedded": "${keycloak.connectionsHotRod.embedded:false}", - "port": "${keycloak.connectionsHotRod.port:14232}", + "port": "${keycloak.connectionsHotRod.port:11222}", + "host": "${keycloak.connectionsHotRod.host:127.0.0.1}", "configureRemoteCaches": "${keycloak.connectionsHotRod.configureRemoteCaches:true}", - "username": "${keycloak.connectionsHotRod.username:myuser}", - "password": "${keycloak.connectionsHotRod.password:qwer1234!}", - "enableSecurity": "${keycloak.connectionsHotRod.enableSecurity:true}", - "reindexCaches": "${keycloak.connectionsHotRod.reindexCaches:all}" + "username": "${keycloak.connectionsHotRod.username:admin}", + "password": "${keycloak.connectionsHotRod.password:admin}", + "enableSecurity": "${keycloak.connectionsHotRod.enableSecurity:true}" } }, diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml index f26d93fe76..a450f7a670 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml @@ -641,19 +641,6 @@ - - - ${hotrod.store.enabled} - org.keycloak.testsuite.arquillian.containers.InfinispanServerDeployableContainer - ${cache.server.home}-hot-rod-store - - ${hotrod.store.port.offset} - ${hotrod.store.management.port} - ${cache.server.java.home} - - - - ${auth.server.remote} diff --git a/testsuite/integration-arquillian/tests/pom.xml b/testsuite/integration-arquillian/tests/pom.xml index 05f1f9c653..2fb07cf45d 100755 --- a/testsuite/integration-arquillian/tests/pom.xml +++ b/testsuite/integration-arquillian/tests/pom.xml @@ -49,7 +49,6 @@ false false - false false false false @@ -141,9 +140,6 @@ true false - 3010 - 13000 - - ${hotrod.store.enabled} - ${hotrod.store.port.offset} - ${hotrod.store.management.port} - ${cache.server} ${cache.server.legacy} ${cache.server.1.port.offset} @@ -1434,9 +1425,7 @@ map-storage-hot-rod - true - false - infinispan + true hotrod @@ -1444,54 +1433,6 @@ - - maven-dependency-plugin - - - unpack-cache-server-standalone-infinispan - generate-resources - - unpack - - - - - org.keycloak.testsuite - integration-arquillian-servers-cache-server-infinispan-infinispan - ${project.version} - zip - ${containers.home} - - - true - - - - - - - maven-antrun-plugin - - - copy-cache-server-to-hot-rod-directory - process-resources - - run - - - ${skip.copy.hotrod.server} - - - - - - - - - - - - org.apache.maven.plugins maven-surefire-plugin @@ -1510,6 +1451,8 @@ hotrod hotrod hotrod + ${infinispan.version} + ${keycloak.testsuite.start-hotrod-container} diff --git a/testsuite/model/README.md b/testsuite/model/README.md index f1012f5478..6b91873bc5 100644 --- a/testsuite/model/README.md +++ b/testsuite/model/README.md @@ -98,9 +98,9 @@ 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= + -Dkeycloak.testsuite.start-hotrod-container=false \ + -Dkeycloak.connectionsHotRod.host= \ + -Dkeycloak.connectionsHotRod.port= \ + -Dkeycloak.connectionsHotRod.username= \ + -Dkeycloak.connectionsHotRod.password= ``` 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 092231817a..48934178c2 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 @@ -50,6 +50,7 @@ import org.keycloak.provider.Spi; import org.keycloak.sessions.AuthenticationSessionSpi; import org.keycloak.testsuite.model.Config; import org.keycloak.testsuite.model.KeycloakModelParameters; +import org.keycloak.testsuite.util.InfinispanContainer; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; @@ -65,23 +66,7 @@ import java.util.regex.Pattern; 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); - + public static final Boolean START_CONTAINER = Boolean.valueOf(System.getProperty("keycloak.testsuite.start-hotrod-container", "true")); static final Set> ALLOWED_SPIS = ImmutableSet.>builder() .add(HotRodConnectionSpi.class) .build(); @@ -91,10 +76,7 @@ public class HotRodMapStorage extends KeycloakModelParameters { .add(HotRodConnectionProviderFactory.class) .build(); - private final GenericContainer hotRodContainer = new GenericContainer("quay.io/infinispan/server:" + System.getProperty("infinispan.version")) - .withEnv("USER", USERNAME) - .withEnv("PASS", PASSWORD) - .withNetworkMode("host"); + private final InfinispanContainer hotRodContainer = new InfinispanContainer(); @Override public void updateConfig(Config cf) { @@ -121,30 +103,18 @@ 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("host", HOST) - .config("port", PORT) - .config("username", USERNAME) - .config("password", PASSWORD) + .config("host", hotRodContainer.getHost()) + .config("port", hotRodContainer.getPort()) + .config("username", hotRodContainer.getUsername()) + .config("password", hotRodContainer.getPassword()) .config("configureRemoteCaches", "true"); } @Override public void beforeSuite(Config cf) { if (START_CONTAINER) { - hotRodContainer - .withStartupTimeout(Duration.ofMinutes(5)) - .waitingFor(Wait.forLogMessage(".*Infinispan Server.*started in.*", 1)) - .start(); + hotRodContainer.start(); } }