Skip to content

Commit

Permalink
Added option to only start container without re-creating it
Browse files Browse the repository at this point in the history
  • Loading branch information
honza-kasik committed Jan 27, 2020
1 parent ba2cde2 commit dc87210
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
import org.junit.runner.RunWith;

import com.google.common.io.Files;
import com.sun.security.auth.module.UnixSystem;

/**
* Test MP Fault tolerance service with crashing database.
Expand Down Expand Up @@ -111,9 +110,9 @@ public static void startDatabase() throws Exception {
.withCmdOption("-v")
.withCmdOption(postgresDataDir.getAbsolutePath() + ":/var/lib/postgresql/data")
.withCmdOption("-u")
.withCmdOption(String.valueOf(new UnixSystem().getUid()))
.withCmdOption("501")
.build();
postgresDB.start();
postgresDB.run();
}

@BeforeClass
Expand Down Expand Up @@ -205,6 +204,7 @@ public void dropDatabaseSchema() {
public static void tearDown() throws Exception {
// stop DB if still running and delete data directory
postgresDB.stop();
postgresDB.remove();
postgresDataDir.delete();
MicroProfileFaultToleranceServerConfiguration.disableFaultTolerance();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@
public interface Container {

/**
* Start this container
* Start a previously stopped or killed container
*/
void start() throws ContainerStartException;

/**
* Create and start new container
*
* @throws ContainerStartException thrown when start of container fails
*/
void start() throws ContainerStartException;
void run() throws ContainerStartException;

/**
* @return Returns true if docker container is running. It does NOT check whether container is ready.
Expand All @@ -34,4 +39,11 @@ public interface Container {
*/
void kill() throws ContainerKillException;

/**
* Remove a container from system
*
* @throws ContainerRemoveException thrown when removing fails
*/
void remove() throws ContainerRemoveException;

}
Original file line number Diff line number Diff line change
Expand Up @@ -54,20 +54,46 @@ private Docker(Builder builder) {
this.out = builder.out;
}

/**
* Start this container
*
* @throws ContainerStartException thrown when start of container fails
*/
@Override
public void start() throws ContainerStartException {
if (isRunning()) {
throw new ContainerStartException("Container '" + this.uuid + "' is already running!");
}
if (!containerExists()) {
throw new ContainerStartException("Container '" + this.uuid + "' doesn't exist!");
}
try {
final Process startProcess = runDockerCommand("start", this.uuid);
this.outputPrintingThread = startPrinterThread(startProcess, this.out, this.name);
final long startTime = System.currentTimeMillis();
while (!isContainerReady(this.uuid, this.containerReadyCondition)) {
if (System.currentTimeMillis() - startTime > containerReadyTimeout) {
stop();
remove();
throw new ContainerStartException(
"Container '" + this.uuid + "' was not ready in " + this.containerReadyTimeout + " ms");
}
// fail fast mechanism in case of malformed docker command, for example bad arguments, invalid format of port mapping, image version,...
if (!startProcess.isAlive() && startProcess.exitValue() != 0) {
throw new ContainerStartException("Failed to start '" + this.uuid + "' container!");
}
}
} catch (DockerCommandException | ContainerReadyConditionException | ContainerStopException
| ContainerRemoveException e) {
throw new ContainerStartException("Failed to start container '" + this.uuid + "'!", e);
}
}

@Override
public void run() throws ContainerStartException {
if (!isDockerPresent()) {
throw new ContainerStartException("'docker' command is not present on this machine!");
}

this.out.println(Ansi.ansi().reset().a("Starting container ").fgCyan().a(name).reset()
.a(" with ID ").fgYellow().a(uuid).reset());

final List<String> dockerStartCommand = composeStartCommand();
final List<String> dockerStartCommand = composeRunCommand();
Process dockerRunProcess;
try {
dockerRunProcess = new ProcessBuilder()
Expand All @@ -85,7 +111,7 @@ public void start() throws ContainerStartException {
while (!isContainerReady(this.uuid, this.containerReadyCondition)) {
if (System.currentTimeMillis() - startTime > containerReadyTimeout) {
stop();
removeDockerContainer(this.uuid);
remove();
throw new ContainerStartException(uuid + " - Container was not ready in " + containerReadyTimeout + " ms");
}
// fail fast mechanism in case of malformed docker command, for example bad arguments, invalid format of port mapping, image version,...
Expand All @@ -104,7 +130,7 @@ public void start() throws ContainerStartException {
}
}

private List<String> composeStartCommand() {
private List<String> composeRunCommand() {
List<String> cmd = new ArrayList<>();

cmd.add("docker");
Expand Down Expand Up @@ -172,14 +198,10 @@ private boolean isContainerReady(final String uuid, final ContainerReadyConditio

private boolean isDockerPresent() {
try {
Process dockerInfoProcess = new ProcessBuilder()
.redirectErrorStream(true)
.command(new String[] { "docker", "info" })
.start();
dockerInfoProcess.waitFor();
return dockerInfoProcess.exitValue() == 0;
} catch (IOException | InterruptedException e) {
throw new IllegalStateException("There was an exception when checking for docker command presence!");
final int processExitValue = runDockerCommand("info").exitValue();
return processExitValue == 0;
} catch (DockerCommandException e) {
throw new IllegalStateException("There was an exception when checking for docker command presence!", e);
}
}

Expand All @@ -189,13 +211,35 @@ private boolean isDockerPresent() {
public boolean isRunning() {
Process dockerRunProcess;
try {
dockerRunProcess = new ProcessBuilder()
.redirectErrorStream(true)
.command(new String[] { "docker", "ps" })
.start();
dockerRunProcess = runDockerCommand("ps");
} catch (DockerCommandException ignored) {
//when we cannot start the process for making the check container is not running
return false;
}

try (BufferedReader reader = new BufferedReader(
new InputStreamReader(dockerRunProcess.getInputStream(), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
if (line.contains(this.uuid)) {
return true;
}
}
} catch (IOException ignored) {
// ignore as any stop of docker container breaks the reader stream
// note that shutdown of docker would be already logged
}
return false;
}

dockerRunProcess.waitFor();
} catch (IOException | InterruptedException ignored) {
/**
* @return Returns true if container exists.
*/
private boolean containerExists() {
Process dockerRunProcess;
try {
dockerRunProcess = runDockerCommand("ps", "-a");
} catch (DockerCommandException ignored) {
//when we cannot start the process for making the check container is not running
return false;
}
Expand Down Expand Up @@ -262,19 +306,22 @@ public void kill() throws ContainerKillException {
}

private void terminatePrintingThread() throws InterruptedException {
outputPrintingThread.shutdown();
outputPrintingThread.awaitTermination(10, TimeUnit.SECONDS);
if (outputPrintingThread != null) {
outputPrintingThread.shutdown();
outputPrintingThread.awaitTermination(10, TimeUnit.SECONDS);
}
}

private void removeDockerContainer(final String uuid) throws ContainerRemoveException {
@Override
public void remove() throws ContainerRemoveException {
try {
runDockerCommand("rm", uuid);
runDockerCommand("rm", this.uuid);
} catch (DockerCommandException e) {
throw new ContainerRemoveException("Failed to remove the container '" + uuid + "'!", e);
throw new ContainerRemoveException("Failed to remove the container '" + this.uuid + "'!", e);
}
}

private int runDockerCommand(final String... commandArguments) throws DockerCommandException {
private Process runDockerCommand(final String... commandArguments) throws DockerCommandException {
final String dockerCommand = "docker";
final List<String> cmd = new ArrayList<>();
cmd.add(dockerCommand);
Expand All @@ -288,7 +335,7 @@ private int runDockerCommand(final String... commandArguments) throws DockerComm

process.waitFor(10, TimeUnit.SECONDS);

return process.exitValue();
return process;
} catch (InterruptedException e) {
throw new DockerCommandException("Interrupted while waiting for '" + String.join(" ", cmd) + "' to return!", e);
} catch (IOException e) {
Expand All @@ -298,14 +345,14 @@ private int runDockerCommand(final String... commandArguments) throws DockerComm

@Override
protected void before() throws ContainerStartException {
start();
run();
}

@Override
protected void after() {
try {
stop();
removeDockerContainer(this.uuid);
remove();
} catch (ContainerStopException e) {
throw new IllegalStateException("Failed to stop container '" + this.uuid + "'! ", e);
} catch (ContainerRemoveException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public void testFailFastWithMalformedDockerCommand() throws Exception {
thrown.expectMessage(containsString("Starting of docker container using command: \"docker run --name"));
thrown.expectMessage(endsWith("failed. Check that provided command is correct."));

containerWithInvalidVersion.start();
containerWithInvalidVersion.run();
}

@Test
Expand All @@ -56,7 +56,7 @@ public void testContainerWithHangingReadyCondition() throws Exception {
"Provided ContainerReadyCondition.isReady() method took longer than containerReadyTimeout"))));
// throws expected Exception
try {
containerWithHangingReadyCondition.start();
containerWithHangingReadyCondition.run();
} finally {
assertThat("ContainerReadyConditionException was thrown and starting container is expected to be stopped/killed. " +
"However this did not happen and there is still container running which is bug.",
Expand All @@ -76,7 +76,7 @@ public void testContainerReadyTimeout() throws Exception {
thrown.expectMessage(containsString("Container was not ready in"));

try {
containerWithShortTimeout.start();
containerWithShortTimeout.run();
} finally {
assertThat("DockerTimeoutException was thrown and starting container is expected to be stopped/killed. " +
"However this did not happen and there is still container running which is bug.",
Expand Down

0 comments on commit dc87210

Please sign in to comment.