diff --git a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java new file mode 100644 index 000000000..fdca2955d --- /dev/null +++ b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java @@ -0,0 +1,153 @@ +/* + * Copyright 2016 Palantir Technologies, Inc. All rights reserved. + */ +package com.palantir.docker.compose; + +import com.palantir.docker.compose.configuration.DockerComposeFiles; +import com.palantir.docker.compose.configuration.ProjectName; +import com.palantir.docker.compose.connection.Cluster; +import com.palantir.docker.compose.connection.ContainerCache; +import com.palantir.docker.compose.connection.DockerMachine; +import com.palantir.docker.compose.connection.waiting.ClusterWait; +import com.palantir.docker.compose.connection.waiting.MultiServiceHealthCheck; +import com.palantir.docker.compose.connection.waiting.MultiServiceWait; +import com.palantir.docker.compose.connection.waiting.SingleServiceHealthCheck; +import com.palantir.docker.compose.connection.waiting.SingleServiceWait; +import com.palantir.docker.compose.execution.DefaultDockerCompose; +import com.palantir.docker.compose.execution.DockerCompose; +import com.palantir.docker.compose.execution.DockerComposeExecArgument; +import com.palantir.docker.compose.execution.DockerComposeExecOption; +import com.palantir.docker.compose.execution.DockerComposeExecutable; +import com.palantir.docker.compose.execution.RetryingDockerCompose; +import com.palantir.docker.compose.logging.DoNothingLogCollector; +import com.palantir.docker.compose.logging.FileLogCollector; +import com.palantir.docker.compose.logging.LogCollector; +import java.io.IOException; +import java.util.List; +import org.immutables.value.Value; +import org.joda.time.Duration; +import org.junit.rules.ExternalResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Value.Immutable +@Value.Style(depluralize = true) +public abstract class DockerComposeRule extends ExternalResource { + public static final Duration DEFAULT_TIMEOUT = Duration.standardMinutes(2); + public static final int DEFAULT_RETRY_ATTEMPTS = 2; + + private static final Logger log = LoggerFactory.getLogger(DockerComposeRule.class); + + public abstract DockerComposeFiles files(); + + protected abstract List clusterWaits(); + + @Value.Default + public DockerMachine machine() { + return DockerMachine.localMachine().build(); + } + + @Value.Default + public ProjectName projectName() { + return ProjectName.random(); + } + + @Value.Default + public DockerComposeExecutable executable() { + return DockerComposeExecutable.builder() + .dockerComposeFiles(files()) + .dockerConfiguration(machine()) + .projectName(projectName()) + .build(); + } + + @Value.Default + public DockerCompose dockerCompose() { + DockerCompose dockerCompose = new DefaultDockerCompose(executable(), machine()); + return new RetryingDockerCompose(retryAttempts(), dockerCompose); + } + + @Value.Default + public Cluster containers() { + return new ContainerCache(dockerCompose()); + } + + @Value.Default + protected int retryAttempts() { + return DEFAULT_RETRY_ATTEMPTS; + } + + @Value.Default + protected LogCollector logCollector() { + return new DoNothingLogCollector(); + } + + @Override + public void before() throws IOException, InterruptedException { + log.debug("Starting docker-compose cluster"); + dockerCompose().build(); + dockerCompose().up(); + + log.debug("Starting log collection"); + + logCollector().startCollecting(dockerCompose()); + log.debug("Waiting for services"); + clusterWaits().forEach(clusterWait -> clusterWait.waitUntilReady(containers())); + log.debug("docker-compose cluster started"); + } + + @Override + public void after() { + try { + 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); + } + } + + public void exec(DockerComposeExecOption options, String containerName, + DockerComposeExecArgument arguments) throws IOException, InterruptedException { + dockerCompose().exec(options, containerName, arguments); + } + + public static ImmutableDockerComposeRule.Builder builder() { + return ImmutableDockerComposeRule.builder(); + } + + public abstract static class Builder { + + public abstract ImmutableDockerComposeRule.Builder files(DockerComposeFiles files); + + public ImmutableDockerComposeRule.Builder file(String dockerComposeYmlFile) { + return files(DockerComposeFiles.from(dockerComposeYmlFile)); + } + + public abstract ImmutableDockerComposeRule.Builder logCollector(LogCollector logCollector); + + public ImmutableDockerComposeRule.Builder saveLogsTo(String path) { + return logCollector(FileLogCollector.fromPath(path)); + } + + public abstract ImmutableDockerComposeRule.Builder addClusterWait(ClusterWait clusterWait); + + public ImmutableDockerComposeRule.Builder waitingForService(String serviceName, SingleServiceHealthCheck healthCheck) { + return addClusterWait(SingleServiceWait.of(serviceName, healthCheck, DEFAULT_TIMEOUT)); + } + + public ImmutableDockerComposeRule.Builder waitingForService(String serviceName, SingleServiceHealthCheck healthCheck, Duration timeout) { + return addClusterWait(SingleServiceWait.of(serviceName, healthCheck, timeout)); + } + + public ImmutableDockerComposeRule.Builder waitingForServices(List services, MultiServiceHealthCheck healthCheck) { + return addClusterWait(MultiServiceWait.of(services, healthCheck, DEFAULT_TIMEOUT)); + } + + public ImmutableDockerComposeRule.Builder waitingForServices(List services, MultiServiceHealthCheck healthCheck, Duration timeout) { + return addClusterWait(MultiServiceWait.of(services, healthCheck, timeout)); + } + } +} diff --git a/src/main/java/com/palantir/docker/compose/DockerComposition.java b/src/main/java/com/palantir/docker/compose/DockerComposition.java index c5aba276e..f2ec766c4 100644 --- a/src/main/java/com/palantir/docker/compose/DockerComposition.java +++ b/src/main/java/com/palantir/docker/compose/DockerComposition.java @@ -15,108 +15,78 @@ */ package com.palantir.docker.compose; -import com.google.common.collect.ImmutableList; import com.palantir.docker.compose.configuration.DockerComposeFiles; import com.palantir.docker.compose.configuration.ProjectName; -import com.palantir.docker.compose.connection.ContainerCache; import com.palantir.docker.compose.connection.DockerMachine; import com.palantir.docker.compose.connection.DockerPort; -import com.palantir.docker.compose.connection.waiting.ServiceWait; -import com.palantir.docker.compose.execution.DefaultDockerCompose; import com.palantir.docker.compose.execution.DockerCompose; import com.palantir.docker.compose.execution.DockerComposeExecArgument; import com.palantir.docker.compose.execution.DockerComposeExecOption; -import com.palantir.docker.compose.logging.LogCollector; import java.io.IOException; -import java.util.List; import org.junit.rules.ExternalResource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class DockerComposition extends ExternalResource { - private static final Logger log = LoggerFactory.getLogger(DockerComposition.class); - - private final DockerCompose dockerCompose; - private final ContainerCache containers; - private final List serviceWaits; - private final LogCollector logCollector; - - public DockerComposition( - DockerCompose dockerCompose, - List serviceWaits, - LogCollector logCollector, - ContainerCache containers) { - this.dockerCompose = dockerCompose; - this.serviceWaits = ImmutableList.copyOf(serviceWaits); - this.logCollector = logCollector; - this.containers = containers; + private DockerComposeRule rule; + + public DockerComposition(DockerComposeRule rule) { + this.rule = rule; } @Override public void before() throws IOException, InterruptedException { - log.debug("Starting docker-compose cluster"); - dockerCompose.build(); - dockerCompose.up(); - - log.debug("Starting log collection"); - - logCollector.startCollecting(dockerCompose); - log.debug("Waiting for services"); - serviceWaits.forEach(ServiceWait::waitTillServiceIsUp); - log.debug("docker-compose cluster started"); + rule.before(); } @Override public void after() { - try { - 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); - } + rule.after(); } public DockerPort portOnContainerWithExternalMapping(String container, int portNumber) throws IOException, InterruptedException { - return containers.get(container) - .portMappedExternallyTo(portNumber); + return rule.containers().container(container).portMappedExternallyTo(portNumber); } public DockerPort portOnContainerWithInternalMapping(String container, int portNumber) throws IOException, InterruptedException { - return containers.get(container) - .portMappedInternallyTo(portNumber); + return rule.containers().container(container).portMappedInternallyTo(portNumber); } public static DockerCompositionBuilder of(String dockerComposeFile) { - return of(DockerComposeFiles.from(dockerComposeFile)); + return new DockerCompositionBuilder() + .files(DockerComposeFiles.from(dockerComposeFile)); } public static DockerCompositionBuilder of(DockerComposeFiles dockerComposeFiles) { - return of(dockerComposeFiles, DockerMachine.localMachine().build()); + return new DockerCompositionBuilder() + .files(dockerComposeFiles); } public static DockerCompositionBuilder of(String dockerComposeFile, DockerMachine dockerMachine) { - return of(DockerComposeFiles.from(dockerComposeFile), dockerMachine); + return new DockerCompositionBuilder() + .files(DockerComposeFiles.from(dockerComposeFile)) + .machine(dockerMachine); } public static DockerCompositionBuilder of(DockerComposeFiles dockerComposeFiles, DockerMachine dockerMachine) { - return of(new DefaultDockerCompose(dockerComposeFiles, dockerMachine, ProjectName.random())); + return new DockerCompositionBuilder() + .files(dockerComposeFiles) + .machine(dockerMachine); } public static DockerCompositionBuilder of(DockerComposeFiles dockerComposeFiles, DockerMachine dockerMachine, String projectName) { - return of(new DefaultDockerCompose(dockerComposeFiles, dockerMachine, ProjectName.fromString(projectName))); + return new DockerCompositionBuilder() + .files(dockerComposeFiles) + .machine(dockerMachine) + .projectName(ProjectName.fromString(projectName)); } - public static DockerCompositionBuilder of(DockerCompose executable) { - return new DockerCompositionBuilder(executable); + public static DockerCompositionBuilder of(DockerCompose compose) { + return new DockerCompositionBuilder().dockerCompose(compose); } public void exec(DockerComposeExecOption options, String containerName, DockerComposeExecArgument arguments) throws IOException, InterruptedException { - dockerCompose.exec(options, containerName, arguments); + rule.exec(options, containerName, arguments); } } diff --git a/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java b/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java index 115bbd4d4..b62a024ba 100644 --- a/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java +++ b/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java @@ -15,71 +15,75 @@ */ package com.palantir.docker.compose; -import static java.util.stream.Collectors.toList; -import static org.joda.time.Duration.standardMinutes; - -import com.palantir.docker.compose.connection.Container; -import com.palantir.docker.compose.connection.ContainerCache; +import com.palantir.docker.compose.ImmutableDockerComposeRule.Builder; +import com.palantir.docker.compose.configuration.DockerComposeFiles; +import com.palantir.docker.compose.configuration.ProjectName; +import com.palantir.docker.compose.connection.DockerMachine; import com.palantir.docker.compose.connection.waiting.MultiServiceHealthCheck; -import com.palantir.docker.compose.connection.waiting.ServiceWait; import com.palantir.docker.compose.connection.waiting.SingleServiceHealthCheck; import com.palantir.docker.compose.execution.DockerCompose; -import com.palantir.docker.compose.execution.RetryingDockerCompose; -import com.palantir.docker.compose.logging.DoNothingLogCollector; -import com.palantir.docker.compose.logging.FileLogCollector; -import com.palantir.docker.compose.logging.LogCollector; -import java.util.ArrayList; import java.util.List; import org.joda.time.Duration; public class DockerCompositionBuilder { - private static final Duration DEFAULT_TIMEOUT = standardMinutes(2); - public static final int DEFAULT_RETRY_ATTEMPTS = 2; - - private final List serviceWaits = new ArrayList<>(); - private final DockerCompose dockerCompose; - private final ContainerCache containers; - private LogCollector logCollector = new DoNothingLogCollector(); - private int numRetryAttempts = DEFAULT_RETRY_ATTEMPTS; + private final Builder builder; - public DockerCompositionBuilder(DockerCompose dockerCompose) { - this.dockerCompose = dockerCompose; - this.containers = new ContainerCache(dockerCompose); + public DockerCompositionBuilder() { + this.builder = DockerComposeRule.builder(); } public DockerCompositionBuilder waitingForService(String serviceName, SingleServiceHealthCheck check) { - return waitingForService(serviceName, check, DEFAULT_TIMEOUT); + builder.waitingForService(serviceName, check); + return this; } public DockerCompositionBuilder waitingForServices(List services, MultiServiceHealthCheck check) { - return waitingForServices(services, check, DEFAULT_TIMEOUT); + builder.waitingForServices(services, check); + return this; } public DockerCompositionBuilder waitingForServices(List services, MultiServiceHealthCheck check, Duration timeout) { - List containersToWaitFor = services.stream() - .map(containers::get) - .collect(toList()); - serviceWaits.add(new ServiceWait(containersToWaitFor, check, timeout)); + builder.waitingForServices(services, check, timeout); return this; } public DockerCompositionBuilder waitingForService(String serviceName, SingleServiceHealthCheck check, Duration timeout) { - serviceWaits.add(new ServiceWait(containers.get(serviceName), check, timeout)); + builder.waitingForService(serviceName, check, timeout); + return this; + } + + public DockerCompositionBuilder files(DockerComposeFiles files) { + builder.files(files); + return this; + } + + public DockerCompositionBuilder machine(DockerMachine machine) { + builder.machine(machine); + return this; + } + + public DockerCompositionBuilder projectName(ProjectName name) { + builder.projectName(name); + return this; + } + + public DockerCompositionBuilder dockerCompose(DockerCompose compose) { + builder.dockerCompose(compose); return this; } public DockerCompositionBuilder saveLogsTo(String path) { - this.logCollector = FileLogCollector.fromPath(path); + builder.saveLogsTo(path); return this; } public DockerCompositionBuilder retryAttempts(int retryAttempts) { - this.numRetryAttempts = retryAttempts; + builder.retryAttempts(retryAttempts); return this; } public DockerComposition build() { - DockerCompose retryingDockerCompose = new RetryingDockerCompose(numRetryAttempts, dockerCompose); - return new DockerComposition(retryingDockerCompose, serviceWaits, logCollector, containers); + DockerComposeRule rule = builder.build(); + return new DockerComposition(rule); } } diff --git a/src/main/java/com/palantir/docker/compose/connection/Cluster.java b/src/main/java/com/palantir/docker/compose/connection/Cluster.java new file mode 100644 index 000000000..9726db4d4 --- /dev/null +++ b/src/main/java/com/palantir/docker/compose/connection/Cluster.java @@ -0,0 +1,11 @@ +/* + * Copyright 2016 Palantir Technologies, Inc. All rights reserved. + */ + +package com.palantir.docker.compose.connection; + +public interface Cluster { + + Container container(String name); + +} diff --git a/src/main/java/com/palantir/docker/compose/connection/ContainerCache.java b/src/main/java/com/palantir/docker/compose/connection/ContainerCache.java index d3c9b702e..838f51012 100644 --- a/src/main/java/com/palantir/docker/compose/connection/ContainerCache.java +++ b/src/main/java/com/palantir/docker/compose/connection/ContainerCache.java @@ -19,7 +19,7 @@ import java.util.HashMap; import java.util.Map; -public class ContainerCache { +public class ContainerCache implements Cluster { private final Map containers = new HashMap<>(); private final DockerCompose dockerCompose; @@ -28,7 +28,8 @@ public ContainerCache(DockerCompose dockerCompose) { this.dockerCompose = dockerCompose; } - public Container get(String containerName) { + @Override + public Container container(String containerName) { containers.putIfAbsent(containerName, dockerCompose.container(containerName)); return containers.get(containerName); } diff --git a/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterWait.java b/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterWait.java new file mode 100644 index 000000000..835ccdd77 --- /dev/null +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterWait.java @@ -0,0 +1,14 @@ +/* + * Copyright 2016 Palantir Technologies, Inc. All rights reserved. + */ + +package com.palantir.docker.compose.connection.waiting; + +import com.palantir.docker.compose.connection.Cluster; + +@FunctionalInterface +public interface ClusterWait { + + void waitUntilReady(Cluster cluster); + +} diff --git a/src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceWait.java b/src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceWait.java new file mode 100644 index 000000000..3c45e7039 --- /dev/null +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceWait.java @@ -0,0 +1,40 @@ +/* + * Copyright 2016 Palantir Technologies, Inc. All rights reserved. + */ + +package com.palantir.docker.compose.connection.waiting; + +import static java.util.stream.Collectors.toList; + +import com.palantir.docker.compose.connection.Cluster; +import com.palantir.docker.compose.connection.Container; +import java.util.List; +import org.immutables.value.Value; +import org.joda.time.Duration; + +@Value.Immutable +public abstract class MultiServiceWait implements ClusterWait { + + @Value.Parameter + protected abstract List containerNames(); + + @Value.Parameter + protected abstract MultiServiceHealthCheck healthcheck(); + + @Value.Parameter + protected abstract Duration timeout(); + + public static MultiServiceWait of(List serviceNames, MultiServiceHealthCheck healthCheck, Duration timeout) { + return ImmutableMultiServiceWait.of(serviceNames, healthCheck, timeout); + } + + @Override + public void waitUntilReady(Cluster containers) { + List containersToWaitFor = containerNames().stream() + .map(containers::container) + .collect(toList()); + ServiceWait serviceWait = new ServiceWait(containersToWaitFor, healthcheck(), timeout()); + serviceWait.waitTillServiceIsUp(); + } + +} diff --git a/src/main/java/com/palantir/docker/compose/connection/waiting/SingleServiceWait.java b/src/main/java/com/palantir/docker/compose/connection/waiting/SingleServiceWait.java new file mode 100644 index 000000000..7db6012c9 --- /dev/null +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/SingleServiceWait.java @@ -0,0 +1,35 @@ +/* + * Copyright 2016 Palantir Technologies, Inc. All rights reserved. + */ + +package com.palantir.docker.compose.connection.waiting; + +import com.palantir.docker.compose.connection.Cluster; +import com.palantir.docker.compose.connection.Container; +import org.immutables.value.Value; +import org.joda.time.Duration; + +@Value.Immutable +public abstract class SingleServiceWait implements ClusterWait { + + @Value.Parameter + protected abstract String containerName(); + + @Value.Parameter + protected abstract SingleServiceHealthCheck healthCheck(); + + @Value.Parameter + protected abstract Duration timeout(); + + public static SingleServiceWait of(String serviceName, SingleServiceHealthCheck healthCheck, Duration timeout) { + return ImmutableSingleServiceWait.of(serviceName, healthCheck, timeout); + } + + @Override + public void waitUntilReady(Cluster containers) { + Container container = containers.container(containerName()); + ServiceWait serviceWait = new ServiceWait(container, healthCheck(), timeout()); + serviceWait.waitTillServiceIsUp(); + } + +} diff --git a/src/main/java/com/palantir/docker/compose/execution/DockerCompose.java b/src/main/java/com/palantir/docker/compose/execution/DockerCompose.java index 5e386dcd5..baaf1e1f1 100644 --- a/src/main/java/com/palantir/docker/compose/execution/DockerCompose.java +++ b/src/main/java/com/palantir/docker/compose/execution/DockerCompose.java @@ -15,13 +15,14 @@ */ package com.palantir.docker.compose.execution; +import com.palantir.docker.compose.connection.Cluster; import com.palantir.docker.compose.connection.Container; import com.palantir.docker.compose.connection.ContainerNames; import com.palantir.docker.compose.connection.Ports; import java.io.IOException; import java.io.OutputStream; -public interface DockerCompose { +public interface DockerCompose extends Cluster { void build() throws IOException, InterruptedException; void up() throws IOException, InterruptedException; void down() throws IOException, InterruptedException; diff --git a/src/main/java/com/palantir/docker/compose/execution/DockerComposeVersion.java b/src/main/java/com/palantir/docker/compose/execution/DockerComposeVersion.java index a1f27bde5..b68f40641 100644 --- a/src/main/java/com/palantir/docker/compose/execution/DockerComposeVersion.java +++ b/src/main/java/com/palantir/docker/compose/execution/DockerComposeVersion.java @@ -19,6 +19,7 @@ public final class DockerComposeVersion { private DockerComposeVersion() {} + //docker-compose version format is like 1.7.0rc1, which can't be parsed by java-semver //here we only pass 1.7.0 to java-semver public static Version parseFromDockerComposeVersion(String versionOutput) { diff --git a/src/test/java/com/palantir/docker/compose/DockerCompositionTest.java b/src/test/java/com/palantir/docker/compose/DockerComposeRuleTest.java similarity index 82% rename from src/test/java/com/palantir/docker/compose/DockerCompositionTest.java rename to src/test/java/com/palantir/docker/compose/DockerComposeRuleTest.java index 6a2e6e6ef..4fd6dafec 100644 --- a/src/test/java/com/palantir/docker/compose/DockerCompositionTest.java +++ b/src/test/java/com/palantir/docker/compose/DockerComposeRuleTest.java @@ -30,9 +30,11 @@ import static org.mockito.Mockito.when; import com.google.common.collect.ImmutableList; +import com.palantir.docker.compose.configuration.DockerComposeFiles; import com.palantir.docker.compose.configuration.MockDockerEnvironment; import com.palantir.docker.compose.connection.Container; import com.palantir.docker.compose.connection.ContainerNames; +import com.palantir.docker.compose.connection.DockerMachine; import com.palantir.docker.compose.connection.DockerPort; import com.palantir.docker.compose.connection.waiting.MultiServiceHealthCheck; import com.palantir.docker.compose.connection.waiting.SingleServiceHealthCheck; @@ -51,7 +53,7 @@ import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; -public class DockerCompositionTest { +public class DockerComposeRuleTest { private static final String IP = "127.0.0.1"; @@ -62,18 +64,20 @@ public class DockerCompositionTest { private final DockerCompose dockerCompose = mock(DockerCompose.class); private final MockDockerEnvironment env = new MockDockerEnvironment(dockerCompose); - private final DockerCompositionBuilder dockerComposition = DockerComposition.of(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(); @Test public void docker_compose_build_and_up_is_called_before_tests_are_run() throws IOException, InterruptedException { - dockerComposition.build().before(); + rule.before(); verify(dockerCompose).build(); verify(dockerCompose).up(); } @Test public void docker_compose_kill_and_rm_are_called_after_tests_are_run() throws IOException, InterruptedException { - dockerComposition.build().after(); + rule.after(); verify(dockerCompose).kill(); verify(dockerCompose).rm(); } @@ -83,7 +87,7 @@ public void docker_compose_wait_for_service_passes_when_check_is_true() throws I AtomicInteger timesCheckCalled = new AtomicInteger(0); withComposeExecutableReturningContainerFor("db"); SingleServiceHealthCheck checkCalledOnce = (container) -> SuccessOrFailure.fromBoolean(timesCheckCalled.incrementAndGet() == 1, "not called once yet"); - dockerComposition.waitingForService("db", checkCalledOnce).build().before(); + DockerComposeRule.builder().from(rule).waitingForService("db", checkCalledOnce).build().before(); assertThat(timesCheckCalled.get(), is(1)); } @@ -96,7 +100,8 @@ public void docker_compose_wait_for_service_waits_multiple_services() throws IOE MultiServiceHealthCheck healthCheck = mock(MultiServiceHealthCheck.class); when(healthCheck.areServicesUp(containers)).thenReturn(SuccessOrFailure.success()); - dockerComposition.waitingForServices(ImmutableList.of("db1", "db2"), healthCheck).build().before(); + + DockerComposeRule.builder().from(rule).waitingForServices(ImmutableList.of("db1", "db2"), healthCheck).build().before(); verify(healthCheck).areServicesUp(containers); } @@ -106,7 +111,7 @@ public void docker_compose_wait_for_service_passes_when_check_is_true_after_bein AtomicInteger timesCheckCalled = new AtomicInteger(0); withComposeExecutableReturningContainerFor("db"); SingleServiceHealthCheck checkCalledTwice = (container) -> SuccessOrFailure.fromBoolean(timesCheckCalled.incrementAndGet() == 2, "not called twice yet"); - dockerComposition.waitingForService("db", checkCalledTwice).build().before(); + DockerComposeRule.builder().from(rule).waitingForService("db", checkCalledTwice).build().before(); assertThat(timesCheckCalled.get(), is(2)); } @@ -117,7 +122,7 @@ public void throws_if_a_wait_for_service_check_remains_false_till_the_timeout() exception.expect(IllegalStateException.class); exception.expectMessage("Container 'db' failed to pass startup check:\noops"); - dockerComposition.waitingForService("db", (container) -> SuccessOrFailure.failure("oops"), millis(200)).build().before(); + DockerComposeRule.builder().from(rule).waitingForService("db", (container) -> SuccessOrFailure.failure("oops"), millis(200)).build().before(); } @Test @@ -125,7 +130,7 @@ public void port_for_container_can_be_retrieved_by_external_mapping() throws IOE DockerPort expectedPort = env.port("db", IP, 5433, 5432); withComposeExecutableReturningContainerFor("db"); - DockerPort actualPort = dockerComposition.build().portOnContainerWithExternalMapping("db", 5433); + DockerPort actualPort = rule.containers().container("db").portMappedExternallyTo(5433); assertThat(actualPort, is(expectedPort)); } @@ -135,7 +140,7 @@ public void port_for_container_can_be_retrieved_by_internal_mapping() throws IOE DockerPort expectedPort = env.port("db", IP, 5433, 5432); withComposeExecutableReturningContainerFor("db"); - DockerPort actualPort = dockerComposition.build().portOnContainerWithInternalMapping("db", 5432); + DockerPort actualPort = rule.containers().container("db").portMappedInternallyTo(5432); assertThat(actualPort, is(expectedPort)); } @@ -144,10 +149,9 @@ public void port_for_container_can_be_retrieved_by_internal_mapping() throws IOE public void when_two_external_ports_on_a_container_are_requested_docker_compose_ps_is_only_executed_once() throws Exception { env.ports("db", IP, 5432, 8080); withComposeExecutableReturningContainerFor("db"); - DockerComposition composition = dockerComposition.build(); - composition.portOnContainerWithInternalMapping("db", 5432); - composition.portOnContainerWithInternalMapping("db", 8080); + rule.containers().container("db").portMappedInternallyTo(5432); + rule.containers().container("db").portMappedInternallyTo(8080); verify(dockerCompose, times(1)).ports("db"); } @@ -162,14 +166,14 @@ public void waiting_for_service_that_does_not_exist_results_in_an_illegal_state_ exception.expect(IllegalStateException.class); exception.expectMessage(nonExistentContainer); - dockerComposition.waitingForService(nonExistentContainer, toHaveAllPortsOpen()).build().before(); + DockerComposeRule.builder().from(rule).waitingForService(nonExistentContainer, toHaveAllPortsOpen()).build().before(); } @SuppressWarnings("unchecked") @Test public void logs_can_be_saved_to_a_directory_while_containers_are_running() throws IOException, InterruptedException { File logLocation = logFolder.newFolder(); - DockerComposition loggingComposition = dockerComposition.saveLogsTo(logLocation.getAbsolutePath()).build(); + DockerComposeRule loggingComposition = DockerComposeRule.builder().from(rule).saveLogsTo(logLocation.getAbsolutePath()).build(); when(dockerCompose.ps()).thenReturn(new ContainerNames("db")); CountDownLatch latch = new CountDownLatch(1); when(dockerCompose.writeLogs(eq("db"), any(OutputStream.class))).thenAnswer((args) -> { diff --git a/src/test/java/com/palantir/docker/compose/connection/ContainerCacheTest.java b/src/test/java/com/palantir/docker/compose/connection/ContainerCacheTest.java index e4d7eb57c..9e6b7a374 100644 --- a/src/test/java/com/palantir/docker/compose/connection/ContainerCacheTest.java +++ b/src/test/java/com/palantir/docker/compose/connection/ContainerCacheTest.java @@ -39,14 +39,14 @@ public void setup() { @Test public void getting_a_new_container_returns_a_container_with_the_specified_name() { - Container container = containers.get(CONTAINER_NAME); + Container container = containers.container(CONTAINER_NAME); assertThat(container, is(new Container(CONTAINER_NAME, dockerCompose))); } @Test public void getting_a_container_twice_returns_the_same_object() { - Container container = containers.get(CONTAINER_NAME); - Container sameContainer = containers.get(CONTAINER_NAME); + Container container = containers.container(CONTAINER_NAME); + Container sameContainer = containers.container(CONTAINER_NAME); assertThat(container, is(sameInstance(sameContainer))); } diff --git a/src/test/java/com/palantir/docker/compose/connection/waiting/SingleServiceWaitTest.java b/src/test/java/com/palantir/docker/compose/connection/waiting/SingleServiceWaitTest.java new file mode 100644 index 000000000..1bd4ab5b1 --- /dev/null +++ b/src/test/java/com/palantir/docker/compose/connection/waiting/SingleServiceWaitTest.java @@ -0,0 +1,43 @@ +/* + * Copyright 2016 Palantir Technologies, Inc. All rights reserved. + */ + +package com.palantir.docker.compose.connection.waiting; + +import static com.palantir.docker.compose.DockerComposeRule.DEFAULT_TIMEOUT; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.palantir.docker.compose.connection.Cluster; +import com.palantir.docker.compose.connection.Container; +import org.junit.Before; +import org.junit.Test; + +public class SingleServiceWaitTest { + + private Cluster containerAccessor = mock(Cluster.class); + private SingleServiceHealthCheck healthCheck = mock(SingleServiceHealthCheck.class); + private Container someContainer = mock(Container.class); + private SingleServiceWait wait = SingleServiceWait.of("somecontainer", healthCheck, DEFAULT_TIMEOUT); + + @Before + public void before() { + when(containerAccessor.container("somecontainer")).thenReturn(someContainer); + when(healthCheck.isServiceUp(any())).thenReturn(SuccessOrFailure.success()); + } + + @Test + public void isReadyLooksUpContainer() { + wait.waitUntilReady(containerAccessor); + verify(containerAccessor, times(1)).container("somecontainer"); + } + + @Test + public void isReadyDelegatesToServiceWait() { + wait.waitUntilReady(containerAccessor); + verify(healthCheck, times(1)).isServiceUp(someContainer); + } +}