Stabilize docker distribution tests

This commit is contained in:
andreaTP 2021-12-15 16:10:48 +00:00 committed by Pedro Igor
parent 8fb45d3c8d
commit 79463ffa58

View file

@ -6,12 +6,18 @@ import org.keycloak.common.Version;
import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.output.OutputFrame;
import org.testcontainers.containers.output.ToStringConsumer; import org.testcontainers.containers.output.ToStringConsumer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.images.builder.ImageFromDockerfile; import org.testcontainers.images.builder.ImageFromDockerfile;
import org.testcontainers.shaded.com.google.common.util.concurrent.RateLimiter;
import org.testcontainers.utility.ResourceReaper;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.time.Duration;
import java.util.List; import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
public final class DockerKeycloakDistribution implements KeycloakDistribution { public final class DockerKeycloakDistribution implements KeycloakDistribution {
@ -30,6 +36,14 @@ public final class DockerKeycloakDistribution implements KeycloakDistribution {
private boolean dockerfileFetched = false; private boolean dockerfileFetched = false;
private GenericContainer keycloakContainer = null; private GenericContainer keycloakContainer = null;
private String containerId = null;
private Executor parallelReaperExecutor = Executors.newSingleThreadExecutor();
public <T> DockerKeycloakDistribution(boolean debug, boolean manualStop, boolean reCreate) {
this.debug = debug;
this.manualStop = manualStop;
}
private File createDockerCacheFile() { private File createDockerCacheFile() {
try { try {
@ -58,34 +72,36 @@ public final class DockerKeycloakDistribution implements KeycloakDistribution {
} }
fetchDockerfile(); fetchDockerfile();
return new GenericContainer( return new GenericContainer(
new ImageFromDockerfile() new ImageFromDockerfile("keycloak.x-under-test", false)
.withFileFromFile("keycloakx.tar.gz", distributionFile) .withFileFromFile("keycloakx.tar.gz", distributionFile)
.withFileFromFile("Dockerfile", cachedDockerfile) .withFileFromFile("Dockerfile", cachedDockerfile)
.withBuildArg("KEYCLOAK_DIST", "keycloakx.tar.gz") .withBuildArg("KEYCLOAK_DIST", "keycloakx.tar.gz")
) )
.withExposedPorts(8080); .withExposedPorts(8080)
} .withStartupAttempts(1)
.withStartupTimeout(Duration.ofSeconds(120))
public <T> DockerKeycloakDistribution(boolean debug, boolean manualStop, boolean reCreate) { .waitingFor(Wait.forListeningPort());
this.debug = debug;
this.manualStop = manualStop;
} }
@Override @Override
public void start(List<String> arguments) { public void start(List<String> arguments) {
stop();
try { try {
this.exitCode = -1; this.exitCode = -1;
this.stdout = ""; this.stdout = "";
this.stderr = ""; this.stderr = "";
this.containerId = null;
this.backupConsumer = new ToStringConsumer(); this.backupConsumer = new ToStringConsumer();
keycloakContainer = getKeycloakContainer(); keycloakContainer = getKeycloakContainer();
keycloakContainer keycloakContainer
.withLogConsumer(backupConsumer) .withLogConsumer(backupConsumer)
.withCommand(arguments.toArray(new String[0])) .withCommand(arguments.toArray(new String[0]))
.start(); .start();
containerId = keycloakContainer.getContainerId();
waitForStableOutput();
// TODO: this is based on a lot of assumptions // TODO: this is based on a lot of assumptions
io.restassured.RestAssured.port = keycloakContainer.getMappedPort(8080); io.restassured.RestAssured.port = keycloakContainer.getMappedPort(8080);
@ -93,15 +109,38 @@ public final class DockerKeycloakDistribution implements KeycloakDistribution {
this.exitCode = -1; this.exitCode = -1;
this.stdout = backupConsumer.toUtf8String(); this.stdout = backupConsumer.toUtf8String();
this.stderr = backupConsumer.toUtf8String(); this.stderr = backupConsumer.toUtf8String();
cleanupContainer();
keycloakContainer = null; keycloakContainer = null;
LOGGER.warn("Failed to start Keycloak container", cause); LOGGER.warn("Failed to start Keycloak container", cause);
} }
} }
// After the web server is responding we are still producing some logs that got checked in the tests
private void waitForStableOutput() {
String lastLine = "";
boolean stableOutput = false;
while (!stableOutput) {
if (keycloakContainer.isRunning()) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
String[] splitted = keycloakContainer.getLogs().split("\n");
String newLastLine = splitted[splitted.length - 1];
stableOutput = lastLine.equals(newLastLine);
lastLine = newLastLine;
} else {
stableOutput = true;
}
}
}
@Override @Override
public void stop() { public void stop() {
try { try {
if (keycloakContainer != null) { if (keycloakContainer != null) {
containerId = keycloakContainer.getContainerId();
this.stdout = fetchOutputStream(); this.stdout = fetchOutputStream();
this.stderr = fetchErrorStream(); this.stderr = fetchErrorStream();
@ -112,9 +151,34 @@ public final class DockerKeycloakDistribution implements KeycloakDistribution {
this.exitCode = -1; this.exitCode = -1;
throw new RuntimeException("Failed to stop the server", cause); throw new RuntimeException("Failed to stop the server", cause);
} finally { } finally {
cleanupContainer();
keycloakContainer = null; keycloakContainer = null;
} }
} }
private void cleanupContainer() {
if (containerId != null) {
try {
final String finalContainerId = containerId;
Runnable reaper = new Runnable() {
@Override
public void run() {
try {
ResourceReaper
.instance()
.stopAndRemoveContainer(finalContainerId);
} catch (Exception cause) {
throw new RuntimeException("Failed to stop and remove container", cause);
}
}
};
parallelReaperExecutor.execute(reaper);
} catch (Exception cause) {
throw new RuntimeException("Failed to schecdule the removal of the container", cause);
}
}
}
private String fetchOutputStream() { private String fetchOutputStream() {
if (keycloakContainer != null && keycloakContainer.isRunning()) { if (keycloakContainer != null && keycloakContainer.isRunning()) {
return keycloakContainer.getLogs(OutputFrame.OutputType.STDOUT); return keycloakContainer.getLogs(OutputFrame.OutputType.STDOUT);
@ -159,4 +223,5 @@ public final class DockerKeycloakDistribution implements KeycloakDistribution {
public boolean isManualStop() { public boolean isManualStop() {
return this.manualStop; return this.manualStop;
} }
} }