Stabilize docker distribution tests
This commit is contained in:
parent
8fb45d3c8d
commit
79463ffa58
1 changed files with 73 additions and 8 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue