From 91cbf4bb7c75c5d3244978e4f35c01ec519c2205 Mon Sep 17 00:00:00 2001 From: Robin Meese <39960884+robson90@users.noreply.github.com> Date: Wed, 29 Jan 2025 20:56:17 +0100 Subject: [PATCH] Add capability to use --optimized option (#170) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add capability to use --optimized option Signed-off-by: Robin Meese <39960884+robson90@users.noreply.github.com> * Fixes comments on PR Signed-off-by: Robin Meese <39960884+robson90@users.noreply.github.com> * Add Documentation and remove todo´s Signed-off-by: Robin Meese <39960884+robson90@users.noreply.github.com> * add implicit call for .withProductionMode() Signed-off-by: Robin Meese <39960884+robson90@users.noreply.github.com> * slightly adjusted and moved documentation to proper chapter Signed-off-by: Niko Köbler --------- Signed-off-by: Robin Meese <39960884+robson90@users.noreply.github.com> Signed-off-by: Niko Köbler Co-authored-by: Niko Köbler --- README.md | 15 ++++ .../keycloak/ExtendableKeycloakContainer.java | 25 ++++++- .../KeycloakContainerOptimizedTest.java | 69 +++++++++++++++++++ .../keycloak/KeycloakContainerTest.java | 1 - src/test/resources/Dockerfile | 16 +++++ 5 files changed, 122 insertions(+), 4 deletions(-) create mode 100644 src/test/java/dasniko/testcontainers/keycloak/KeycloakContainerOptimizedTest.java create mode 100644 src/test/resources/Dockerfile diff --git a/README.md b/README.md index 439ede3..c43bbbd 100644 --- a/README.md +++ b/README.md @@ -239,6 +239,21 @@ KeycloakContainer keycloak = new KeycloakContainer() .withProductionMode(); ``` +### Optimized flag + +It is possible that you use your own pre-build image with the `--optimized` flag. +Setting this option will implicitly enable production mode! + +```java +@Container +KeycloakContainer keycloak = new KeycloakContainer("" + ":") + .withOptimizedFlag(); +``` + +NOTE: If you don't enable the health endpoint, the container will not be healthy. +In this case please provide your own waitStrategy. +Check out the tests at [`KeycloakContainerOptimizedTest`](./src/test/java/dasniko/testcontainers/keycloak/KeycloakContainerOptimizedTest.java). + ## Testing Custom Extensions To ease extension testing, you can tell the Keycloak Testcontainer to detect extensions in a given classpath folder. diff --git a/src/main/java/dasniko/testcontainers/keycloak/ExtendableKeycloakContainer.java b/src/main/java/dasniko/testcontainers/keycloak/ExtendableKeycloakContainer.java index 0e2ef58..e265e4f 100644 --- a/src/main/java/dasniko/testcontainers/keycloak/ExtendableKeycloakContainer.java +++ b/src/main/java/dasniko/testcontainers/keycloak/ExtendableKeycloakContainer.java @@ -126,6 +126,7 @@ public abstract class ExtendableKeycloakContainer customCommandParts; private boolean bootstrapAdmin = true; + private boolean optimizeFlag = false; /** * Create a KeycloakContainer with default image and version tag @@ -199,10 +200,17 @@ protected void configure() { }); withEnv("KC_TRUSTSTORE_PATHS", String.join(",", truststorePaths)); } - withEnv("KC_HTTPS_CLIENT_AUTH", httpsClientAuth.toString()); - withEnv("KC_HTTPS_MANAGEMENT_CLIENT_AUTH", HttpsClientAuth.NONE.toString()); - withEnv("KC_METRICS_ENABLED", Boolean.toString(metricsEnabled)); + if (!this.optimizeFlag) { + // here are all default build options listed, that would prevent Keycloak from starting, without a new build + withEnv("KC_HTTPS_CLIENT_AUTH", httpsClientAuth.toString()); + withEnv("KC_HTTPS_MANAGEMENT_CLIENT_AUTH", HttpsClientAuth.NONE.toString()); + + withEnv("KC_METRICS_ENABLED", Boolean.toString(metricsEnabled)); + } else { + commandParts.add("--optimized"); + } + withEnv("KC_HEALTH_ENABLED", Boolean.toString(Boolean.TRUE)); if (!customWaitStrategySet) { HttpWaitStrategy waitStrategy = Wait.forHttp(contextPath + "/health/started").forPort(KEYCLOAK_PORT_MGMT); @@ -526,6 +534,17 @@ public SELF withBootstrapAdminDisabled() { return self(); } + /** + * Will add the "--optimized" flag to Keycloak startup command. + * It will ignore all build time options, that have been set. + * For example: KC_HTTPS_CLIENT_AUTH, KC_HTTPS_MANAGEMENT_CLIENT_AUTH + * + */ + public SELF withOptimizedFlag() { + this.optimizeFlag = true; + return self().withProductionMode(); + } + /** * Returns the keycloak admin. Note that this may not return a functioning admin client * if the master realm including users were imported. diff --git a/src/test/java/dasniko/testcontainers/keycloak/KeycloakContainerOptimizedTest.java b/src/test/java/dasniko/testcontainers/keycloak/KeycloakContainerOptimizedTest.java new file mode 100644 index 0000000..56fa2c2 --- /dev/null +++ b/src/test/java/dasniko/testcontainers/keycloak/KeycloakContainerOptimizedTest.java @@ -0,0 +1,69 @@ +package dasniko.testcontainers.keycloak; + +import io.restassured.RestAssured; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.images.builder.ImageFromDockerfile; + +import java.nio.file.Paths; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.startsWith; + +public class KeycloakContainerOptimizedTest { + + public static final String UPDATING_THE_CONFIGURATION = "Updating the configuration and installing your custom providers, if any. Please wait."; + private KeycloakContainer keycloakContainer; + + @BeforeEach + void setUp() { + try (GenericContainer container = new GenericContainer<>( + new ImageFromDockerfile("temporary-keycloak-image", false) + .withDockerfile(Paths.get("src/test/resources/Dockerfile")) + .withTarget("builder"))) { + try (KeycloakContainer keycloak = new KeycloakContainer(container.getDockerImageName() + ":latest") + .useTls() + .withEnv("KC_HOSTNAME_STRICT", "false") + .withOptimizedFlag()) { + keycloakContainer = keycloak; + } + } + } + + @Test + public void shouldBeAvailableOnHTTPS() { + keycloakContainer.start(); + RestAssured.useRelaxedHTTPSValidation(); + assertThat(keycloakContainer.getAuthServerUrl(), startsWith("https://")); + given() + .when().get(keycloakContainer.getAuthServerUrl()) + .then().statusCode(200); + } + + @Test + public void shouldBeAvailableWithProfileProd() { + keycloakContainer.start(); + assertThat(keycloakContainer.getLogs(), containsString("Profile prod activated.")); + } + + @Test + public void shouldBeAvailableWithoutInstallingProviderLogMessage() { + keycloakContainer.start(); + assertThat(keycloakContainer.getLogs(), + not(containsString(UPDATING_THE_CONFIGURATION))); + } + + @Test + public void shouldStartWithProviderInstallationLogMessageWhenOptimizedIsNotSet() { + try (KeycloakContainer keycloak = new KeycloakContainer()) { + keycloak.start(); + assertThat(keycloak.getLogs(), + containsString(UPDATING_THE_CONFIGURATION)); + } + } + +} diff --git a/src/test/java/dasniko/testcontainers/keycloak/KeycloakContainerTest.java b/src/test/java/dasniko/testcontainers/keycloak/KeycloakContainerTest.java index dc36038..8bbe222 100644 --- a/src/test/java/dasniko/testcontainers/keycloak/KeycloakContainerTest.java +++ b/src/test/java/dasniko/testcontainers/keycloak/KeycloakContainerTest.java @@ -230,5 +230,4 @@ private void testDebugPortAvailable(final String debugHost, final int debugPort) } } } - } diff --git a/src/test/resources/Dockerfile b/src/test/resources/Dockerfile new file mode 100644 index 0000000..fe97b3c --- /dev/null +++ b/src/test/resources/Dockerfile @@ -0,0 +1,16 @@ +FROM quay.io/keycloak/keycloak:nightly AS builder + +# copied from: https://www.keycloak.org/server/containers +# DISCLAIMER THIS IS ONLY FOR TESTING PURPOSE. DO NOT, I REAPEAT DO NOT USE THIS Dockerfile IN PRODUCTION + +ENV KC_HEALTH_ENABLED=true + +WORKDIR /opt/keycloak +# for demonstration purposes only, please make sure to use proper certificates in production instead +RUN keytool -genkeypair -storepass password -storetype PKCS12 -keyalg RSA -keysize 2048 -dname "CN=server" -alias server -ext "SAN:c=DNS:localhost,IP:127.0.0.1" -keystore conf/server.keystore +RUN /opt/keycloak/bin/kc.sh build + +FROM quay.io/keycloak/keycloak:nightly +COPY --from=builder /opt/keycloak/ /opt/keycloak/ + +ENTRYPOINT ["/opt/keycloak/bin/kc.sh"]