[KEYCLOAK-19311] Add testcontainers to Dist.X Integration Tests (#8946)

* Supporting running tests against the server image using test containers
This commit is contained in:
Andrea Peruffo 2021-11-29 14:20:50 +00:00 committed by GitHub
parent 79931fd607
commit cd5ccdbf3e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 196 additions and 6 deletions

View file

@ -412,6 +412,12 @@ jobs:
- name: Prepare the local distribution archives - name: Prepare the local distribution archives
run: mvn clean install -DskipTests -Pdistribution run: mvn clean install -DskipTests -Pdistribution
- name: Run Quarkus Tests in Docker
run: |
mvn clean install -nsu -B -f quarkus/tests/pom.xml -Dkc.quarkus.tests.dist=docker | misc/log/trimmer.sh
TEST_RESULT=${PIPESTATUS[0]}
exit $TEST_RESULT
- name: Run Quarkus Integration Tests - name: Run Quarkus Integration Tests
run: | run: |
mvn clean install -nsu -B -f quarkus/tests/pom.xml | misc/log/trimmer.sh mvn clean install -nsu -B -f quarkus/tests/pom.xml | misc/log/trimmer.sh

View file

@ -59,7 +59,28 @@
<dependency> <dependency>
<groupId>io.rest-assured</groupId> <groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId> <artifactId>rest-assured</artifactId>
<scope>test</scope> </dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
</dependency> </dependency>
</dependencies> </dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemProperties>
<property>
<name>kc.quarkus.tests.dist</name>
<value>${kc.quarkus.tests.dist}</value>
</property>
</systemProperties>
</configuration>
</plugin>
</plugins>
</build>
</project> </project>

View file

@ -0,0 +1,6 @@
package org.junit.rules;
// WORKAROUND: https://github.com/testcontainers/testcontainers-java/issues/970#issuecomment-625044008
@SuppressWarnings("unused")
public interface TestRule {
}

View file

@ -27,6 +27,7 @@ import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ParameterContext; import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolutionException; import org.junit.jupiter.api.extension.ParameterResolutionException;
import org.keycloak.it.utils.KeycloakDistribution; import org.keycloak.it.utils.KeycloakDistribution;
import org.keycloak.it.utils.DockerKeycloakDistribution;
import org.keycloak.it.utils.RawKeycloakDistribution; import org.keycloak.it.utils.RawKeycloakDistribution;
import org.keycloak.quarkus.runtime.Environment; import org.keycloak.quarkus.runtime.Environment;
import org.keycloak.quarkus.runtime.cli.command.Start; import org.keycloak.quarkus.runtime.cli.command.Start;
@ -82,11 +83,24 @@ public class CLITestExtension extends QuarkusMainTestExtension {
} }
private KeycloakDistribution createDistribution(DistributionTest config) { private KeycloakDistribution createDistribution(DistributionTest config) {
KeycloakDistribution distribution = new RawKeycloakDistribution( KeycloakDistribution distribution = null;
config.debug(),
config.keepAlive(), switch (System.getProperty("kc.quarkus.tests.dist", "raw")) {
!DistributionTest.ReInstall.NEVER.equals(config.reInstall()) case "docker":
); distribution = new DockerKeycloakDistribution(
config.debug(),
config.keepAlive(),
!DistributionTest.ReInstall.NEVER.equals(config.reInstall())
);
break;
case "raw":
default:
distribution = new RawKeycloakDistribution(
config.debug(),
config.keepAlive(),
!DistributionTest.ReInstall.NEVER.equals(config.reInstall())
);
}
return distribution; return distribution;
} }

View file

@ -0,0 +1,143 @@
package org.keycloak.it.utils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.jboss.logging.Logger;
import org.keycloak.common.Version;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.output.OutputFrame;
import org.testcontainers.containers.output.ToStringConsumer;
import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.containers.wait.strategy.WaitStrategy;
import org.testcontainers.images.builder.ImageFromDockerfile;
import java.io.File;
import java.net.URL;
import java.time.Duration;
import java.util.List;
public final class DockerKeycloakDistribution implements KeycloakDistribution {
private static final Logger LOGGER = Logger.getLogger(DockerKeycloakDistribution.class);
private boolean debug;
private boolean manualStop;
private int exitCode = -1;
private List<String> stdout = List.of();
private List<String> stderr = List.of();
private ToStringConsumer backupConsumer = new ToStringConsumer();
private File distributionFile = new File("../../../distribution/server-x-dist/target/keycloak.x-" + Version.VERSION_KEYCLOAK + ".tar.gz");
private GenericContainer keycloakContainer = null;
private GenericContainer runKeycloakContainer() {
if (!distributionFile.exists()) {
throw new RuntimeException("Distribution archive " + distributionFile.getAbsolutePath() +" doesn't exists");
}
File dockerFile = null;
try {
dockerFile = File.createTempFile("keycloakx", "Dockerfile");
FileUtils.copyURLToFile(new URL("https://raw.githubusercontent.com/keycloak/keycloak-containers/main/server-x/Dockerfile"), dockerFile);
} catch (Exception cause) {
throw new RuntimeException("Cannot download upstream Dockerfile", cause);
}
return new GenericContainer(
new ImageFromDockerfile()
.withFileFromFile("keycloakx.tar.gz", distributionFile)
.withFileFromFile("Dockerfile", dockerFile)
.withBuildArg("KEYCLOAK_DIST", "keycloakx.tar.gz")
)
.withExposedPorts(8080)
.withStartupTimeout(Duration.ofSeconds(40))
.withStartupAttempts(1)
.waitingFor(Wait.forHttp("/").forStatusCode(200).withReadTimeout(Duration.ofSeconds(2)));
}
public <T> DockerKeycloakDistribution(boolean debug, boolean manualStop, boolean reCreate) {
this.debug = debug;
this.manualStop = manualStop;
}
@Override
public void start(List<String> arguments) {
try {
this.exitCode = -1;
this.stdout = List.of();
this.stderr = List.of();
this.backupConsumer = new ToStringConsumer();
keycloakContainer = runKeycloakContainer();
keycloakContainer
.withLogConsumer(backupConsumer)
.withCommand(arguments.toArray(new String[0]))
.start();
// TODO: this is based on a lot of assumptions
io.restassured.RestAssured.port = keycloakContainer.getMappedPort(8080);
} catch (Exception cause) {
this.exitCode = -1;
this.stdout = List.of(backupConsumer.toUtf8String());
this.stderr = List.of(backupConsumer.toUtf8String());
keycloakContainer = null;
LOGGER.warn("Failed to start Keycloak container", cause);
}
}
@Override
public void stop() {
try {
if (keycloakContainer != null) {
this.stdout = getOutputStream();
this.stderr = getErrorStream();
keycloakContainer.stop();
keycloakContainer = null;
this.exitCode = 0;
}
} catch (Exception cause) {
this.exitCode = -1;
throw new RuntimeException("Failed to stop the server", cause);
}
}
@Override
public List<String> getOutputStream() {
if (keycloakContainer != null && keycloakContainer.isRunning()) {
return List.of(keycloakContainer.getLogs(OutputFrame.OutputType.STDOUT));
} else if (this.stdout.isEmpty()) {
return List.of(backupConsumer.toUtf8String());
} else {
return this.stdout;
}
}
@Override
public List<String> getErrorStream() {
if (keycloakContainer != null && keycloakContainer.isRunning()) {
return List.of(keycloakContainer.getLogs(OutputFrame.OutputType.STDERR));
} else if (this.stderr.isEmpty()) {
return List.of(backupConsumer.toUtf8String());
} else {
return this.stderr;
}
}
@Override
public int getExitCode() {
return this.exitCode;
}
@Override
public boolean isDebug() {
return this.debug;
}
@Override
public boolean isManualStop() {
return this.manualStop;
}
}