diff --git a/README.md b/README.md index b451844b0..ba8a98b9f 100644 --- a/README.md +++ b/README.md @@ -149,6 +149,22 @@ public class DockerCompositionTest { This will automatically record logs for all containers in real time to the specified directory. Collection will stop when the containers terminate. +Skipping shutdown +----------------- + +To skip shutdown of containers after tests are finished executing: + +```java +public class DockerCompositionTest { + @ClassRule + public DockerComposition composition = DockerComposition.of("src/test/resources/docker-compose.yml") + .skipShutdown(true) + .build(); +} +``` + +This can shorten iteration time when services take a long time to start. Remember to never leave it on in CI! + Docker Machine -------------- diff --git a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java index fdca2955d..2579bcaf6 100644 --- a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java +++ b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java @@ -77,6 +77,11 @@ protected int retryAttempts() { return DEFAULT_RETRY_ATTEMPTS; } + @Value.Default + protected boolean skipShutdown() { + return false; + } + @Value.Default protected LogCollector logCollector() { return new DoNothingLogCollector(); @@ -99,10 +104,20 @@ public void before() throws IOException, InterruptedException { @Override public void after() { try { - log.debug("Killing docker-compose cluster"); - dockerCompose().down(); - dockerCompose().kill(); - dockerCompose().rm(); + if (skipShutdown()) { + log.error("******************************************************************************************\n" + + "* docker-compose-rule has been configured to skip docker-compose shutdown: *\n" + + "* this means the containers will be left running after tests finish executing. *\n" + + "* If you see this message when running on CI it means you are potentially abandoning *\n" + + "* long running processes and leaking resources. *\n" + + "*******************************************************************************************"); + } else { + log.debug("Killing docker-compose cluster"); + dockerCompose().down(); + dockerCompose().kill(); + dockerCompose().rm(); + } + logCollector().stopCollecting(); } catch (IOException | InterruptedException e) { throw new RuntimeException("Error cleaning up docker compose cluster", e); diff --git a/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java b/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java index b62a024ba..a39a5698d 100644 --- a/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java +++ b/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java @@ -82,6 +82,11 @@ public DockerCompositionBuilder retryAttempts(int retryAttempts) { return this; } + public DockerCompositionBuilder skipShutdown(boolean skipShutdown) { + builder.skipShutdown(skipShutdown); + return this; + } + public DockerComposition build() { DockerComposeRule rule = builder.build(); return new DockerComposition(rule); diff --git a/src/test/java/com/palantir/docker/compose/DockerComposeRuleTest.java b/src/test/java/com/palantir/docker/compose/DockerComposeRuleTest.java index 4fd6dafec..2b1353a3e 100644 --- a/src/test/java/com/palantir/docker/compose/DockerComposeRuleTest.java +++ b/src/test/java/com/palantir/docker/compose/DockerComposeRuleTest.java @@ -27,6 +27,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import com.google.common.collect.ImmutableList; @@ -40,6 +41,7 @@ import com.palantir.docker.compose.connection.waiting.SingleServiceHealthCheck; import com.palantir.docker.compose.connection.waiting.SuccessOrFailure; import com.palantir.docker.compose.execution.DockerCompose; +import com.palantir.docker.compose.logging.LogCollector; import java.io.File; import java.io.IOException; import java.io.OutputStream; @@ -66,7 +68,8 @@ public class DockerComposeRuleTest { private final MockDockerEnvironment env = new MockDockerEnvironment(dockerCompose); private DockerComposeFiles mockFiles = mock(DockerComposeFiles.class); private DockerMachine machine = mock(DockerMachine.class); - private final DockerComposeRule rule = DockerComposeRule.builder().dockerCompose(dockerCompose).files(mockFiles).machine(machine).build(); + private LogCollector logCollector = mock(LogCollector.class); + private final DockerComposeRule rule = defaultBuilder().build(); @Test public void docker_compose_build_and_up_is_called_before_tests_are_run() throws IOException, InterruptedException { @@ -189,10 +192,25 @@ public void logs_can_be_saved_to_a_directory_while_containers_are_running() thro assertThat(new File(logLocation, "db.log"), is(fileContainingString("db log"))); } + @Test + public void when_skipShutdown_is_true_shutdown_does_not_happen() throws InterruptedException { + defaultBuilder().skipShutdown(true) + .build() + .after(); + verifyNoMoreInteractions(dockerCompose); + verify(logCollector, times(1)).stopCollecting(); + } + public Container withComposeExecutableReturningContainerFor(String containerName) { final Container container = new Container(containerName, dockerCompose); when(dockerCompose.container(containerName)).thenReturn(container); return container; } + private ImmutableDockerComposeRule.Builder defaultBuilder() { + return DockerComposeRule.builder().dockerCompose(dockerCompose) + .files(mockFiles) + .machine(machine) + .logCollector(logCollector); + } }