From 1d758fac2bd7be94b3885dd195da0fcb8e27467a Mon Sep 17 00:00:00 2001 From: Alexander Schwartz Date: Sat, 17 Dec 2022 08:50:21 +0100 Subject: [PATCH] Adding CRDB into GHA for the new store (#16021) The CockroachDB database is slower than PostgreSQL, therefore it will only run branches and nightly builds. Closes #16020 --- .github/workflows/ci.yml | 28 +++- quarkus/tests/integration/pom.xml | 4 + .../integration-arquillian/HOW-TO-RUN.md | 15 +- .../integration-arquillian/tests/base/pom.xml | 62 +++++++- .../CockroachdbContainerTestEnricher.java | 52 +++++++ .../KeycloakArquillianExtension.java | 1 + testsuite/model/pom.xml | 8 + .../parameters/JpaMapStorageCockroachdb.java | 139 ++++++++++++++++++ 8 files changed, 298 insertions(+), 11 deletions(-) create mode 100644 testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/CockroachdbContainerTestEnricher.java create mode 100644 testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/JpaMapStorageCockroachdb.java diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f1d2a4c37e..9f260d4cc1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -165,7 +165,7 @@ jobs: run: | TESTS=`testsuite/integration-arquillian/tests/base/testsuites/suite.sh jdk` echo "Tests: $TESTS" - ./mvnw test -nsu -B -Pauth-server-quarkus -Pdb-${{ matrix.db }} -Dtest=$TESTS -pl testsuite/integration-arquillian/tests/base | misc/log/trimmer.sh + ./mvnw test -nsu -B -Pauth-server-quarkus -Dtest=$TESTS -pl testsuite/integration-arquillian/tests/base | misc/log/trimmer.sh - id: upload-surefire-reports name: Upload Surefire reports @@ -175,14 +175,31 @@ jobs: api-key: ${{ secrets.FORESIGHT_API_KEY }} surefire-reports-path: 'testsuite/integration-arquillian/tests/base/target/surefire-reports/*.xml' + databases-new-store: + name: Databases New Store + runs-on: ubuntu-latest + steps: + - id: matrix-db + # language=bash + run: | + # For PRs, run only PostgreSQL, on branches and nightly runs run all databases + if [ "${{ github.event_name }}" == "pull_request" ]; then + echo 'db=["chm", "hot-rod", "jpa-postgres"]' >> $GITHUB_OUTPUT + else + echo 'db=["chm", "hot-rod", "jpa-postgres", "jpa-cockroach"]' >> $GITHUB_OUTPUT + fi + outputs: + db: ${{ steps.matrix-db.outputs.db }} + new-store-integration-tests: name: New Store IT - needs: build + needs: [build, databases-new-store] runs-on: ubuntu-latest - timeout-minutes: 45 + # Currently, the run of CockroachDB (only branches and nightly runs) is the longest with ~45 minutes + timeout-minutes: 70 strategy: matrix: - db: [chm, hot-rod, jpa] + db: ${{ fromJson(needs.databases-new-store.outputs.db) }} fail-fast: false steps: - uses: actions/checkout@v3 @@ -196,7 +213,8 @@ jobs: declare -A PARAMS PARAMS["chm"]="-Pmap-storage -Dpageload.timeout=90000" PARAMS["hot-rod"]="-Pmap-storage,map-storage-hot-rod -Dpageload.timeout=90000" - PARAMS["jpa"]="-Pmap-storage,map-storage-jpa -Dpageload.timeout=90000" + PARAMS["jpa-postgres"]="-Pmap-storage,map-storage-jpa-postgres -Dpageload.timeout=90000" + PARAMS["jpa-cockroach"]="-Pmap-storage,map-storage-jpa-cockroach -Dpageload.timeout=90000" TESTS=`testsuite/integration-arquillian/tests/base/testsuites/suite.sh database` echo "Tests: $TESTS" diff --git a/quarkus/tests/integration/pom.xml b/quarkus/tests/integration/pom.xml index 0fc06b5b2c..924032aca6 100644 --- a/quarkus/tests/integration/pom.xml +++ b/quarkus/tests/integration/pom.xml @@ -88,6 +88,10 @@ org.testcontainers postgresql + + org.testcontainers + cockroachdb + org.testcontainers mariadb diff --git a/testsuite/integration-arquillian/HOW-TO-RUN.md b/testsuite/integration-arquillian/HOW-TO-RUN.md index 6d80f60160..e7ea8990f3 100644 --- a/testsuite/integration-arquillian/HOW-TO-RUN.md +++ b/testsuite/integration-arquillian/HOW-TO-RUN.md @@ -837,14 +837,19 @@ mvn clean install -f testsuite/integration-arquillian/tests/base \ ### Running tests with JPA Map storage -By default tests with `map-storage-jpa` profile spawns a new Postgres container -with each test execution. Default image used is "postgres:alpine". To spawn different -version, it can be used "keycloak.map.storage.postgres.docker.image" system property. +By default, testing with the profile `map-storage-jpa-postgres` spawns a new Postgres container +with each test execution. The default image used is `postgres:alpine`. To spawn a different +version, use the system property `keycloak.map.storage.postgres.docker.image`. + +In a similar way the profile `map-storage-jpa-cockroach` spawns a new CockroachDB container +with each test execution. It uses the official CockroachDB image in the version stated in the +class `CockroachdbContainerTestEnricher`. To spawn a different +version, use the system property `keycloak.map.storage.cockroachdb.docker.image`. Execute tests: ```shell mvn clean install -f testsuite/integration-arquillian/tests/base \ - -Pmap-storage,map-storage-jpa + -Pmap-storage,map-storage-jpa-postgres ``` It's also possible to configure tests to connect to an external database, it might be useful @@ -859,7 +864,7 @@ podman run --name postgres -p 5432:5432 -e POSTGRES_PASSWORD=pass -e POSTGRES_US To run the tests without spawning the container for you, execute tests with the following command: ```shell mvn clean install -f testsuite/integration-arquillian/tests/base \ - -Pmap-storage,map-storage-jpa \ + -Pmap-storage,map-storage-jpa-postgres \ -Dpostgres.start-container=false \ -Dkeycloak.map.storage.connectionsJpa.url= \ -Dkeycloak.map.storage.connectionsJpa.user= \ diff --git a/testsuite/integration-arquillian/tests/base/pom.xml b/testsuite/integration-arquillian/tests/base/pom.xml index 7a623bb304..e0f6ad1308 100644 --- a/testsuite/integration-arquillian/tests/base/pom.xml +++ b/testsuite/integration-arquillian/tests/base/pom.xml @@ -117,6 +117,11 @@ postgresql ${testcontainers.version} + + org.testcontainers + cockroachdb + ${testcontainers.version} + com.google.guava guava @@ -814,7 +819,7 @@ - map-storage-jpa + map-storage-jpa-postgres true @@ -866,6 +871,61 @@ + + map-storage-jpa-cockroach + + true + jdbc:postgresql://localhost:26257/keycloak + + + + + maven-enforcer-plugin + + enforce + + + + + map-storage + map-storage profile not active + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + ${cockroachdb.start-container} + + ${keycloak.map.storage.connectionsJpa.url} + ${keycloak.map.storage.connectionsJpa.user} + ${keycloak.map.storage.connectionsJpa.password} + + jpa + jpa + jpa + jpa + jpa + jpa + jpa + jpa + jpa + jpa + jpa + jpa + jpa + jpa + + + + + + + + auth-server-fips140-2 diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/CockroachdbContainerTestEnricher.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/CockroachdbContainerTestEnricher.java new file mode 100644 index 0000000000..4726f3bfdb --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/CockroachdbContainerTestEnricher.java @@ -0,0 +1,52 @@ +/* + * 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.arquillian; + +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.testcontainers.containers.CockroachContainer; +import org.testcontainers.utility.DockerImageName; + +public class CockroachdbContainerTestEnricher { + + private static final Boolean START_CONTAINER = Boolean.valueOf(System.getProperty("cockroachdb.start-container", "false")); + private static final String COCKROACHDB_DOCKER_IMAGE_NAME = System.getProperty("keycloak.map.storage.cockroachdb.docker.image", "cockroachdb/cockroach:v22.1.0"); + private static final CockroachContainer COCKROACHDB_CONTAINER = new CockroachContainer(DockerImageName.parse(COCKROACHDB_DOCKER_IMAGE_NAME).asCompatibleSubstituteFor("cockroachdb")); + private static final String COCKROACHDB_DB_USER = System.getProperty("keycloak.map.storage.connectionsJpa.user", "keycloak"); + + public void beforeContainerStarted(@Observes(precedence = 1) StartSuiteContainers event) { + if (START_CONTAINER) { + COCKROACHDB_CONTAINER + // Using the environment variables for now where using the withXXX() method is not supported, yet. + // https://github.com/testcontainers/testcontainers-java/issues/6299 + .withEnv("COCKROACH_DATABASE", "keycloak") + .withEnv("COCKROACH_USER", COCKROACHDB_DB_USER) + // password is not used/supported in insecure mode + .withCommand("start-single-node", "--insecure") + .start(); + + System.setProperty("keycloak.map.storage.connectionsJpa.url", COCKROACHDB_CONTAINER.getJdbcUrl()); + } + } + + public void afterSuite(@Observes(precedence = 4) AfterSuite event) { + if (START_CONTAINER) { + COCKROACHDB_CONTAINER.stop(); + } + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/KeycloakArquillianExtension.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/KeycloakArquillianExtension.java index abb4a846dd..a4d81a8345 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/KeycloakArquillianExtension.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/KeycloakArquillianExtension.java @@ -68,6 +68,7 @@ public class KeycloakArquillianExtension implements LoadableExtension { .observer(CrossDCTestEnricher.class) .observer(HotRodStoreTestEnricher.class) .observer(PostgresContainerTestEnricher.class) + .observer(CockroachdbContainerTestEnricher.class) .observer(H2TestEnricher.class); builder .service(TestExecutionDecider.class, MigrationTestExecutionDecider.class) diff --git a/testsuite/model/pom.xml b/testsuite/model/pom.xml index 1d3be63a02..2cdfeb75be 100644 --- a/testsuite/model/pom.xml +++ b/testsuite/model/pom.xml @@ -344,6 +344,14 @@ + + map-jpa-cockroachdb + + enabled + Map,JpaMapStorageCockroachdb + + + .asyncProfiler diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/JpaMapStorageCockroachdb.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/JpaMapStorageCockroachdb.java new file mode 100644 index 0000000000..deac6f7a1b --- /dev/null +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/JpaMapStorageCockroachdb.java @@ -0,0 +1,139 @@ +/* + * 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.parameters; + +import com.google.common.collect.ImmutableSet; +import org.jboss.logging.Logger; +import org.keycloak.authorization.store.StoreFactorySpi; +import org.keycloak.events.EventStoreSpi; +import org.keycloak.models.DeploymentStateSpi; +import org.keycloak.models.SingleUseObjectSpi; +import org.keycloak.models.UserLoginFailureSpi; +import org.keycloak.models.UserSessionSpi; +import org.keycloak.models.map.authSession.MapRootAuthenticationSessionProviderFactory; +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.deploymentState.MapDeploymentStateProviderFactory; +import org.keycloak.models.map.events.MapEventStoreProviderFactory; +import org.keycloak.models.map.group.MapGroupProviderFactory; +import org.keycloak.models.map.keys.MapPublicKeyStorageProviderFactory; +import org.keycloak.models.map.loginFailure.MapUserLoginFailureProviderFactory; +import org.keycloak.models.map.realm.MapRealmProviderFactory; +import org.keycloak.models.map.role.MapRoleProviderFactory; +import org.keycloak.models.map.singleUseObject.MapSingleUseObjectProviderFactory; +import org.keycloak.models.map.storage.MapStorageSpi; +import org.keycloak.models.map.storage.chm.ConcurrentHashMapStorageProviderFactory; +import org.keycloak.models.map.storage.jpa.JpaMapStorageProviderFactory; +import org.keycloak.models.map.storage.jpa.liquibase.connection.MapLiquibaseConnectionProviderFactory; +import org.keycloak.models.map.storage.jpa.liquibase.connection.MapLiquibaseConnectionSpi; +import org.keycloak.models.map.storage.jpa.updater.MapJpaUpdaterProviderFactory; +import org.keycloak.models.map.storage.jpa.updater.MapJpaUpdaterSpi; +import org.keycloak.models.map.user.MapUserProviderFactory; +import org.keycloak.models.map.userSession.MapUserSessionProviderFactory; +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.KeycloakModelParameters; +import org.testcontainers.containers.CockroachContainer; +import org.testcontainers.utility.DockerImageName; + +import java.util.Set; + +public class JpaMapStorageCockroachdb extends KeycloakModelParameters { + + private static final Logger LOG = Logger.getLogger(JpaMapStorageCockroachdb.class.getName()); + + private static final Boolean START_CONTAINER = Boolean.valueOf(System.getProperty("cockroachdb.start-container", "true")); + private static final String COCKROACHDB_DOCKER_IMAGE_NAME = System.getProperty("keycloak.map.storage.cockroachdb.docker.image", "cockroachdb/cockroach:v22.1.0"); + private static final CockroachContainer COCKROACHDB_CONTAINER = new CockroachContainer(DockerImageName.parse(COCKROACHDB_DOCKER_IMAGE_NAME).asCompatibleSubstituteFor("cockroachdb")); + private static final String COCKROACHDB_DB_USER = System.getProperty("keycloak.map.storage.connectionsJpa.user", "keycloak"); + private static final String COCKROACHDB_DB_PASSWORD = System.getProperty("keycloak.map.storage.connectionsJpa.password", "pass"); + + private static String COCKROACHDB_DB_JDBC_URL = System.getProperty("keycloak.map.storage.connectionsJpa.url"); + + static final Set> ALLOWED_SPIS = ImmutableSet.>builder() + .add(MapJpaUpdaterSpi.class) + .add(MapLiquibaseConnectionSpi.class) + .build(); + + static final Set> ALLOWED_FACTORIES = ImmutableSet.>builder() + .add(ConcurrentHashMapStorageProviderFactory.class) + .add(JpaMapStorageProviderFactory.class) + .add(MapJpaUpdaterProviderFactory.class) + .add(MapLiquibaseConnectionProviderFactory.class) + .build(); + + public JpaMapStorageCockroachdb() { + super(ALLOWED_SPIS, ALLOWED_FACTORIES); + } + + @Override + public void updateConfig(Config cf) { + cf.spi(MapStorageSpi.NAME) + .provider(ConcurrentHashMapStorageProviderFactory.PROVIDER_ID) + .config("dir", "${project.build.directory:target}"); + + cf.spi(MapStorageSpi.NAME) + .provider(JpaMapStorageProviderFactory.PROVIDER_ID) + .config("url", COCKROACHDB_DB_JDBC_URL) + .config("user", COCKROACHDB_DB_USER) + .config("password", COCKROACHDB_DB_PASSWORD) + .config("driver", "org.postgresql.Driver") + .config("driverDialect", "org.keycloak.models.map.storage.jpa.hibernate.dialect.JsonbPostgreSQL95Dialect"); + + cf.spi(AuthenticationSessionSpi.PROVIDER_ID).provider(MapRootAuthenticationSessionProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID) + .spi("client").provider(MapClientProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID) + .spi("clientScope").provider(MapClientScopeProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID) + .spi("group").provider(MapGroupProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID) + .spi("realm").provider(MapRealmProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID) + .spi("role").provider(MapRoleProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID) + .spi(DeploymentStateSpi.NAME).provider(MapDeploymentStateProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID) + .spi(StoreFactorySpi.NAME).provider(MapAuthorizationStoreFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID) + .spi("user").provider(MapUserProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID) + .spi(UserLoginFailureSpi.NAME).provider(MapUserLoginFailureProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID) + .spi(SingleUseObjectSpi.NAME).provider(MapSingleUseObjectProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID) + .spi("publicKeyStorage").provider(MapPublicKeyStorageProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID) + .spi(UserSessionSpi.NAME).provider(MapUserSessionProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID) + .spi(EventStoreSpi.NAME).provider(MapEventStoreProviderFactory.PROVIDER_ID) .config("storage-admin-events.provider", JpaMapStorageProviderFactory.PROVIDER_ID) + .config("storage-auth-events.provider", JpaMapStorageProviderFactory.PROVIDER_ID); + } + + @Override + public void beforeSuite(Config cf) { + if (START_CONTAINER) { + COCKROACHDB_CONTAINER + // Using the environment variables for now where using the withXXX() method is not supported, yet. + // https://github.com/testcontainers/testcontainers-java/issues/6299 + .withEnv("COCKROACH_DATABASE", "keycloak") + .withEnv("COCKROACH_USER", COCKROACHDB_DB_USER) + // password is not used/supported in insecure mode + .withCommand("start-single-node", "--insecure") + .start(); + + COCKROACHDB_DB_JDBC_URL = COCKROACHDB_CONTAINER.getJdbcUrl(); + } + } + + @Override + public void afterSuite() { + if (START_CONTAINER) { + COCKROACHDB_CONTAINER.stop(); + } + } +}