[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:
parent
79931fd607
commit
cd5ccdbf3e
5 changed files with 196 additions and 6 deletions
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
|
@ -412,6 +412,12 @@ jobs:
|
|||
- name: Prepare the local distribution archives
|
||||
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
|
||||
run: |
|
||||
mvn clean install -nsu -B -f quarkus/tests/pom.xml | misc/log/trimmer.sh
|
||||
|
|
|
@ -59,7 +59,28 @@
|
|||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>rest-assured</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
</dependency>
|
||||
</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>
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
package org.junit.rules;
|
||||
|
||||
// WORKAROUND: https://github.com/testcontainers/testcontainers-java/issues/970#issuecomment-625044008
|
||||
@SuppressWarnings("unused")
|
||||
public interface TestRule {
|
||||
}
|
|
@ -27,6 +27,7 @@ import org.junit.jupiter.api.extension.ExtensionContext;
|
|||
import org.junit.jupiter.api.extension.ParameterContext;
|
||||
import org.junit.jupiter.api.extension.ParameterResolutionException;
|
||||
import org.keycloak.it.utils.KeycloakDistribution;
|
||||
import org.keycloak.it.utils.DockerKeycloakDistribution;
|
||||
import org.keycloak.it.utils.RawKeycloakDistribution;
|
||||
import org.keycloak.quarkus.runtime.Environment;
|
||||
import org.keycloak.quarkus.runtime.cli.command.Start;
|
||||
|
@ -82,11 +83,24 @@ public class CLITestExtension extends QuarkusMainTestExtension {
|
|||
}
|
||||
|
||||
private KeycloakDistribution createDistribution(DistributionTest config) {
|
||||
KeycloakDistribution distribution = new RawKeycloakDistribution(
|
||||
config.debug(),
|
||||
config.keepAlive(),
|
||||
!DistributionTest.ReInstall.NEVER.equals(config.reInstall())
|
||||
);
|
||||
KeycloakDistribution distribution = null;
|
||||
|
||||
switch (System.getProperty("kc.quarkus.tests.dist", "raw")) {
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue