From d858341bd86c97ea84dc3352d8ca07d963271c9b Mon Sep 17 00:00:00 2001 From: Joe Lea Date: Thu, 5 May 2016 17:27:51 +0100 Subject: [PATCH 01/24] Add ip address to Cluster --- .../palantir/docker/compose/DockerComposeRule.java | 6 +++++- .../palantir/docker/compose/connection/Cluster.java | 12 ++++++++++-- .../docker/compose/connection/ContainerCache.java | 3 +-- .../docker/compose/execution/DockerCompose.java | 3 +-- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java index 2579bcaf6..476f24978 100644 --- a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java +++ b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java @@ -8,6 +8,7 @@ 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.ImmutableCluster; import com.palantir.docker.compose.connection.waiting.ClusterWait; import com.palantir.docker.compose.connection.waiting.MultiServiceHealthCheck; import com.palantir.docker.compose.connection.waiting.MultiServiceWait; @@ -69,7 +70,10 @@ public DockerCompose dockerCompose() { @Value.Default public Cluster containers() { - return new ContainerCache(dockerCompose()); + return ImmutableCluster.builder() + .ip(machine().getIp()) + .containers(new ContainerCache(dockerCompose())) + .build(); } @Value.Default diff --git a/src/main/java/com/palantir/docker/compose/connection/Cluster.java b/src/main/java/com/palantir/docker/compose/connection/Cluster.java index 9726db4d4..fa58df66f 100644 --- a/src/main/java/com/palantir/docker/compose/connection/Cluster.java +++ b/src/main/java/com/palantir/docker/compose/connection/Cluster.java @@ -4,8 +4,16 @@ package com.palantir.docker.compose.connection; -public interface Cluster { +import org.immutables.value.Value; - Container container(String name); +@Value.Immutable +public abstract class Cluster { + + public abstract String ip(); + public abstract ContainerCache containers(); + + public Container container(String name) { + return containers().container(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 838f51012..1f8584e38 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 implements Cluster { +public class ContainerCache { private final Map containers = new HashMap<>(); private final DockerCompose dockerCompose; @@ -28,7 +28,6 @@ public ContainerCache(DockerCompose dockerCompose) { this.dockerCompose = dockerCompose; } - @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/execution/DockerCompose.java b/src/main/java/com/palantir/docker/compose/execution/DockerCompose.java index baaf1e1f1..5e386dcd5 100644 --- a/src/main/java/com/palantir/docker/compose/execution/DockerCompose.java +++ b/src/main/java/com/palantir/docker/compose/execution/DockerCompose.java @@ -15,14 +15,13 @@ */ 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 extends Cluster { +public interface DockerCompose { void build() throws IOException, InterruptedException; void up() throws IOException, InterruptedException; void down() throws IOException, InterruptedException; From a810695fb7b119441135547f6563fd92f14eb90c Mon Sep 17 00:00:00 2001 From: Joe Lea Date: Fri, 6 May 2016 14:15:46 +0100 Subject: [PATCH 02/24] Add cluster as an argument to service waits --- .../docker/compose/connection/waiting/MultiServiceWait.java | 6 +++--- .../docker/compose/connection/waiting/ServiceWait.java | 3 ++- .../compose/connection/waiting/SingleServiceWait.java | 6 +++--- 3 files changed, 8 insertions(+), 7 deletions(-) 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 index 3c45e7039..1d0338948 100644 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceWait.java +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceWait.java @@ -29,12 +29,12 @@ public static MultiServiceWait of(List serviceNames, MultiServiceHealthC } @Override - public void waitUntilReady(Cluster containers) { + public void waitUntilReady(Cluster cluster) { List containersToWaitFor = containerNames().stream() - .map(containers::container) + .map(cluster::container) .collect(toList()); ServiceWait serviceWait = new ServiceWait(containersToWaitFor, healthcheck(), timeout()); - serviceWait.waitTillServiceIsUp(); + serviceWait.waitTillServiceIsUp(cluster); } } diff --git a/src/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java b/src/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java index 5adf18b1d..e26964d10 100644 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java @@ -20,6 +20,7 @@ import com.google.common.collect.ImmutableList; import com.jayway.awaitility.Awaitility; import com.jayway.awaitility.core.ConditionTimeoutException; +import com.palantir.docker.compose.connection.Cluster; import com.palantir.docker.compose.connection.Container; import java.util.List; import java.util.Optional; @@ -48,7 +49,7 @@ public ServiceWait(List containers, MultiServiceHealthCheck healthChe this.timeout = timeout; } - public void waitTillServiceIsUp() { + public void waitTillServiceIsUp(Cluster cluster) { log.debug("Waiting for services [{}]", containerNames()); final AtomicReference> lastSuccessOrFailure = new AtomicReference<>(Optional.empty()); try { 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 index 7db6012c9..9f7d79a30 100644 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/SingleServiceWait.java +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/SingleServiceWait.java @@ -26,10 +26,10 @@ public static SingleServiceWait of(String serviceName, SingleServiceHealthCheck } @Override - public void waitUntilReady(Cluster containers) { - Container container = containers.container(containerName()); + public void waitUntilReady(Cluster cluster) { + Container container = cluster.container(containerName()); ServiceWait serviceWait = new ServiceWait(container, healthCheck(), timeout()); - serviceWait.waitTillServiceIsUp(); + serviceWait.waitTillServiceIsUp(cluster); } } From be4d6dc74057b97ccca1c3f9a25095e9227e384f Mon Sep 17 00:00:00 2001 From: Joe Lea Date: Fri, 6 May 2016 14:17:44 +0100 Subject: [PATCH 03/24] Have the service wait know about containerNames --- .../compose/connection/waiting/ServiceWait.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java b/src/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java index e26964d10..f2d2934e7 100644 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java @@ -27,6 +27,7 @@ import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; import org.joda.time.Duration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,19 +35,21 @@ public class ServiceWait { private static final Logger log = LoggerFactory.getLogger(ServiceWait.class); private final List containers; + private final List containerNames; private final MultiServiceHealthCheck healthCheck; private final Duration timeout; - public ServiceWait(Container service, SingleServiceHealthCheck healthCheck, Duration timeout) { - this.containers = ImmutableList.of(service); - this.healthCheck = MultiServiceHealthCheck.fromSingleServiceHealthCheck(healthCheck); - this.timeout = timeout; + public ServiceWait(Container container, SingleServiceHealthCheck healthCheck, Duration timeout) { + this(ImmutableList.of(container), MultiServiceHealthCheck.fromSingleServiceHealthCheck(healthCheck), timeout); } public ServiceWait(List containers, MultiServiceHealthCheck healthCheck, Duration timeout) { this.containers = containers; this.healthCheck = healthCheck; this.timeout = timeout; + containerNames = containers.stream() + .map(Container::getContainerName) + .collect(Collectors.toList()); } public void waitTillServiceIsUp(Cluster cluster) { From e83ce24607700a40d4e6c4f7d4efb12642ce1d04 Mon Sep 17 00:00:00 2001 From: Joe Lea Date: Fri, 6 May 2016 14:20:13 +0100 Subject: [PATCH 04/24] Remove unused code --- .../compose/connection/waiting/ServiceWait.java | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java b/src/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java index f2d2934e7..b662b8b18 100644 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java @@ -15,8 +15,6 @@ */ package com.palantir.docker.compose.connection.waiting; -import static java.util.stream.Collectors.joining; - import com.google.common.collect.ImmutableList; import com.jayway.awaitility.Awaitility; import com.jayway.awaitility.core.ConditionTimeoutException; @@ -53,7 +51,7 @@ public ServiceWait(List containers, MultiServiceHealthCheck healthChe } public void waitTillServiceIsUp(Cluster cluster) { - log.debug("Waiting for services [{}]", containerNames()); + log.debug("Waiting for services [{}]", containerNames); final AtomicReference> lastSuccessOrFailure = new AtomicReference<>(Optional.empty()); try { Awaitility.await() @@ -80,14 +78,8 @@ private String serviceDidNotStartupExceptionMessage(AtomicReference 1 ? "Containers" : "Container", - containerNames(), + containerNames, healthcheckFailureMessage); } - private String containerNames() { - return containers.stream() - .map(Container::getContainerName) - .collect(joining(", ")); - - } } From f620b7418089519a0142cea937bc353117ffb929 Mon Sep 17 00:00:00 2001 From: Joe Lea Date: Fri, 6 May 2016 14:26:58 +0100 Subject: [PATCH 05/24] Remove containers field --- .../compose/connection/waiting/ServiceWait.java | 16 ++++++++-------- .../waiting/SingleServiceWaitTest.java | 15 +++++---------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java b/src/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java index b662b8b18..01a60e4e8 100644 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java @@ -15,6 +15,8 @@ */ package com.palantir.docker.compose.connection.waiting; +import static java.util.stream.Collectors.toList; + import com.google.common.collect.ImmutableList; import com.jayway.awaitility.Awaitility; import com.jayway.awaitility.core.ConditionTimeoutException; @@ -25,14 +27,12 @@ import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Collectors; import org.joda.time.Duration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ServiceWait { private static final Logger log = LoggerFactory.getLogger(ServiceWait.class); - private final List containers; private final List containerNames; private final MultiServiceHealthCheck healthCheck; private final Duration timeout; @@ -42,30 +42,30 @@ public ServiceWait(Container container, SingleServiceHealthCheck healthCheck, Du } public ServiceWait(List containers, MultiServiceHealthCheck healthCheck, Duration timeout) { - this.containers = containers; this.healthCheck = healthCheck; this.timeout = timeout; containerNames = containers.stream() .map(Container::getContainerName) - .collect(Collectors.toList()); + .collect(toList()); } public void waitTillServiceIsUp(Cluster cluster) { log.debug("Waiting for services [{}]", containerNames); final AtomicReference> lastSuccessOrFailure = new AtomicReference<>(Optional.empty()); + List containersToCheck = containerNames.stream().map(cluster::container).collect(toList()); try { Awaitility.await() .pollInterval(50, TimeUnit.MILLISECONDS) .atMost(timeout.getMillis(), TimeUnit.MILLISECONDS) - .until(weHaveSuccess(lastSuccessOrFailure)); + .until(weHaveSuccess(containersToCheck, lastSuccessOrFailure)); } catch (ConditionTimeoutException e) { throw new IllegalStateException(serviceDidNotStartupExceptionMessage(lastSuccessOrFailure)); } } - private Callable weHaveSuccess(AtomicReference> lastSuccessOrFailure) { + private Callable weHaveSuccess(List containersToCheck, AtomicReference> lastSuccessOrFailure) { return () -> { - SuccessOrFailure successOrFailure = healthCheck.areServicesUp(containers); + SuccessOrFailure successOrFailure = healthCheck.areServicesUp(containersToCheck); lastSuccessOrFailure.set(Optional.of(successOrFailure)); return successOrFailure.succeeded(); }; @@ -77,7 +77,7 @@ private String serviceDidNotStartupExceptionMessage(AtomicReference 1 ? "Containers" : "Container", + containerNames.size() > 1 ? "Containers" : "Container", containerNames, healthcheckFailureMessage); } 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 index 1bd4ab5b1..d54f07451 100644 --- a/src/test/java/com/palantir/docker/compose/connection/waiting/SingleServiceWaitTest.java +++ b/src/test/java/com/palantir/docker/compose/connection/waiting/SingleServiceWaitTest.java @@ -18,26 +18,21 @@ public class SingleServiceWaitTest { - private Cluster containerAccessor = mock(Cluster.class); + private Cluster cluster = mock(Cluster.class); private SingleServiceHealthCheck healthCheck = mock(SingleServiceHealthCheck.class); - private Container someContainer = mock(Container.class); + private Container someContainer = mock(Container.class, "somecontainer"); private SingleServiceWait wait = SingleServiceWait.of("somecontainer", healthCheck, DEFAULT_TIMEOUT); @Before public void before() { - when(containerAccessor.container("somecontainer")).thenReturn(someContainer); + when(someContainer.getContainerName()).thenReturn("somecontainer"); + when(cluster.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); + wait.waitUntilReady(cluster); verify(healthCheck, times(1)).isServiceUp(someContainer); } } From 8082abc33be1edff4f1696e6e2496672d73cee8a Mon Sep 17 00:00:00 2001 From: Joe Lea Date: Fri, 6 May 2016 14:27:55 +0100 Subject: [PATCH 06/24] Remove unnecessary conversion to containers --- .../compose/connection/waiting/MultiServiceWait.java | 8 +------- .../docker/compose/connection/waiting/ServiceWait.java | 10 ++++------ .../compose/connection/waiting/SingleServiceWait.java | 4 +--- 3 files changed, 6 insertions(+), 16 deletions(-) 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 index 1d0338948..02089e798 100644 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceWait.java +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceWait.java @@ -4,10 +4,7 @@ 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; @@ -30,10 +27,7 @@ public static MultiServiceWait of(List serviceNames, MultiServiceHealthC @Override public void waitUntilReady(Cluster cluster) { - List containersToWaitFor = containerNames().stream() - .map(cluster::container) - .collect(toList()); - ServiceWait serviceWait = new ServiceWait(containersToWaitFor, healthcheck(), timeout()); + ServiceWait serviceWait = new ServiceWait(containerNames(), healthcheck(), timeout()); serviceWait.waitTillServiceIsUp(cluster); } diff --git a/src/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java b/src/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java index 01a60e4e8..3587c874c 100644 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java @@ -37,16 +37,14 @@ public class ServiceWait { private final MultiServiceHealthCheck healthCheck; private final Duration timeout; - public ServiceWait(Container container, SingleServiceHealthCheck healthCheck, Duration timeout) { - this(ImmutableList.of(container), MultiServiceHealthCheck.fromSingleServiceHealthCheck(healthCheck), timeout); + public ServiceWait(String containerName, SingleServiceHealthCheck healthCheck, Duration timeout) { + this(ImmutableList.of(containerName), MultiServiceHealthCheck.fromSingleServiceHealthCheck(healthCheck), timeout); } - public ServiceWait(List containers, MultiServiceHealthCheck healthCheck, Duration timeout) { + public ServiceWait(List containerNames, MultiServiceHealthCheck healthCheck, Duration timeout) { + this.containerNames = containerNames; this.healthCheck = healthCheck; this.timeout = timeout; - containerNames = containers.stream() - .map(Container::getContainerName) - .collect(toList()); } public void waitTillServiceIsUp(Cluster cluster) { 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 index 9f7d79a30..a68b71817 100644 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/SingleServiceWait.java +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/SingleServiceWait.java @@ -5,7 +5,6 @@ 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; @@ -27,8 +26,7 @@ public static SingleServiceWait of(String serviceName, SingleServiceHealthCheck @Override public void waitUntilReady(Cluster cluster) { - Container container = cluster.container(containerName()); - ServiceWait serviceWait = new ServiceWait(container, healthCheck(), timeout()); + ServiceWait serviceWait = new ServiceWait(containerName(), healthCheck(), timeout()); serviceWait.waitTillServiceIsUp(cluster); } From 5f0b65d8c210b3fed4ced8302f49d4095670b4fc Mon Sep 17 00:00:00 2001 From: Joe Lea Date: Fri, 6 May 2016 14:29:53 +0100 Subject: [PATCH 07/24] Make ServiceWait implement ClusterWait --- .../docker/compose/connection/waiting/MultiServiceWait.java | 2 +- .../docker/compose/connection/waiting/ServiceWait.java | 4 ++-- .../docker/compose/connection/waiting/SingleServiceWait.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) 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 index 02089e798..9455aff6d 100644 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceWait.java +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceWait.java @@ -28,7 +28,7 @@ public static MultiServiceWait of(List serviceNames, MultiServiceHealthC @Override public void waitUntilReady(Cluster cluster) { ServiceWait serviceWait = new ServiceWait(containerNames(), healthcheck(), timeout()); - serviceWait.waitTillServiceIsUp(cluster); + serviceWait.waitUntilReady(cluster); } } diff --git a/src/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java b/src/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java index 3587c874c..00260e435 100644 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java @@ -31,7 +31,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class ServiceWait { +public class ServiceWait implements ClusterWait { private static final Logger log = LoggerFactory.getLogger(ServiceWait.class); private final List containerNames; private final MultiServiceHealthCheck healthCheck; @@ -47,7 +47,7 @@ public ServiceWait(List containerNames, MultiServiceHealthCheck healthCh this.timeout = timeout; } - public void waitTillServiceIsUp(Cluster cluster) { + public void waitUntilReady(Cluster cluster) { log.debug("Waiting for services [{}]", containerNames); final AtomicReference> lastSuccessOrFailure = new AtomicReference<>(Optional.empty()); List containersToCheck = containerNames.stream().map(cluster::container).collect(toList()); 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 index a68b71817..cae70694c 100644 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/SingleServiceWait.java +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/SingleServiceWait.java @@ -27,7 +27,7 @@ public static SingleServiceWait of(String serviceName, SingleServiceHealthCheck @Override public void waitUntilReady(Cluster cluster) { ServiceWait serviceWait = new ServiceWait(containerName(), healthCheck(), timeout()); - serviceWait.waitTillServiceIsUp(cluster); + serviceWait.waitUntilReady(cluster); } } From 1f4316cb009f6557d6d0a8a80617ba831bbad8a1 Mon Sep 17 00:00:00 2001 From: Joe Lea Date: Fri, 6 May 2016 14:38:30 +0100 Subject: [PATCH 08/24] Fix tests missing a mocked return value --- .../docker/compose/DockerComposeRuleTest.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/palantir/docker/compose/DockerComposeRuleTest.java b/src/test/java/com/palantir/docker/compose/DockerComposeRuleTest.java index 2b1353a3e..d6298c4d9 100644 --- a/src/test/java/com/palantir/docker/compose/DockerComposeRuleTest.java +++ b/src/test/java/com/palantir/docker/compose/DockerComposeRuleTest.java @@ -50,6 +50,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.apache.commons.io.IOUtils; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -69,7 +70,13 @@ public class DockerComposeRuleTest { private DockerComposeFiles mockFiles = mock(DockerComposeFiles.class); private DockerMachine machine = mock(DockerMachine.class); private LogCollector logCollector = mock(LogCollector.class); - private final DockerComposeRule rule = defaultBuilder().build(); + private DockerComposeRule rule; + + @Before public void + setup() { + when(machine.getIp()).thenReturn(IP); + rule = defaultBuilder().build(); + } @Test public void docker_compose_build_and_up_is_called_before_tests_are_run() throws IOException, InterruptedException { @@ -123,7 +130,7 @@ public void throws_if_a_wait_for_service_check_remains_false_till_the_timeout() withComposeExecutableReturningContainerFor("db"); exception.expect(IllegalStateException.class); - exception.expectMessage("Container 'db' failed to pass startup check:\noops"); + exception.expectMessage("Container '[db]' failed to pass startup check:\noops"); DockerComposeRule.builder().from(rule).waitingForService("db", (container) -> SuccessOrFailure.failure("oops"), millis(200)).build().before(); } From 835affd1a3b82453aaf570981ece26f0a3b352a6 Mon Sep 17 00:00:00 2001 From: Joe Lea Date: Fri, 6 May 2016 14:43:02 +0100 Subject: [PATCH 09/24] Remove unused code --- .../docker/compose/DockerComposeRule.java | 11 +++--- .../connection/waiting/MultiServiceWait.java | 34 ----------------- .../connection/waiting/SingleServiceWait.java | 33 ---------------- .../waiting/SingleServiceWaitTest.java | 38 ------------------- 4 files changed, 5 insertions(+), 111 deletions(-) delete mode 100644 src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceWait.java delete mode 100644 src/main/java/com/palantir/docker/compose/connection/waiting/SingleServiceWait.java delete mode 100644 src/test/java/com/palantir/docker/compose/connection/waiting/SingleServiceWaitTest.java diff --git a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java index 476f24978..fa1d0fdd0 100644 --- a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java +++ b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java @@ -11,9 +11,8 @@ import com.palantir.docker.compose.connection.ImmutableCluster; 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.ServiceWait; 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; @@ -154,19 +153,19 @@ public ImmutableDockerComposeRule.Builder saveLogsTo(String path) { public abstract ImmutableDockerComposeRule.Builder addClusterWait(ClusterWait clusterWait); public ImmutableDockerComposeRule.Builder waitingForService(String serviceName, SingleServiceHealthCheck healthCheck) { - return addClusterWait(SingleServiceWait.of(serviceName, healthCheck, DEFAULT_TIMEOUT)); + return addClusterWait(new ServiceWait(serviceName, healthCheck, DEFAULT_TIMEOUT)); } public ImmutableDockerComposeRule.Builder waitingForService(String serviceName, SingleServiceHealthCheck healthCheck, Duration timeout) { - return addClusterWait(SingleServiceWait.of(serviceName, healthCheck, timeout)); + return addClusterWait(new ServiceWait(serviceName, healthCheck, timeout)); } public ImmutableDockerComposeRule.Builder waitingForServices(List services, MultiServiceHealthCheck healthCheck) { - return addClusterWait(MultiServiceWait.of(services, healthCheck, DEFAULT_TIMEOUT)); + return addClusterWait(new ServiceWait(services, healthCheck, DEFAULT_TIMEOUT)); } public ImmutableDockerComposeRule.Builder waitingForServices(List services, MultiServiceHealthCheck healthCheck, Duration timeout) { - return addClusterWait(MultiServiceWait.of(services, healthCheck, timeout)); + return addClusterWait(new ServiceWait(services, healthCheck, timeout)); } } } 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 deleted file mode 100644 index 9455aff6d..000000000 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceWait.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2016 Palantir Technologies, Inc. All rights reserved. - */ - -package com.palantir.docker.compose.connection.waiting; - -import com.palantir.docker.compose.connection.Cluster; -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 cluster) { - ServiceWait serviceWait = new ServiceWait(containerNames(), healthcheck(), timeout()); - serviceWait.waitUntilReady(cluster); - } - -} 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 deleted file mode 100644 index cae70694c..000000000 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/SingleServiceWait.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2016 Palantir Technologies, Inc. All rights reserved. - */ - -package com.palantir.docker.compose.connection.waiting; - -import com.palantir.docker.compose.connection.Cluster; -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 cluster) { - ServiceWait serviceWait = new ServiceWait(containerName(), healthCheck(), timeout()); - serviceWait.waitUntilReady(cluster); - } - -} 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 deleted file mode 100644 index d54f07451..000000000 --- a/src/test/java/com/palantir/docker/compose/connection/waiting/SingleServiceWaitTest.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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 cluster = mock(Cluster.class); - private SingleServiceHealthCheck healthCheck = mock(SingleServiceHealthCheck.class); - private Container someContainer = mock(Container.class, "somecontainer"); - private SingleServiceWait wait = SingleServiceWait.of("somecontainer", healthCheck, DEFAULT_TIMEOUT); - - @Before - public void before() { - when(someContainer.getContainerName()).thenReturn("somecontainer"); - when(cluster.container("somecontainer")).thenReturn(someContainer); - when(healthCheck.isServiceUp(any())).thenReturn(SuccessOrFailure.success()); - } - - @Test - public void isReadyDelegatesToServiceWait() { - wait.waitUntilReady(cluster); - verify(healthCheck, times(1)).isServiceUp(someContainer); - } -} From 4bb74a5cceb07777c5d3418f05dc364b3511407a Mon Sep 17 00:00:00 2001 From: Joe Lea Date: Fri, 6 May 2016 14:44:11 +0100 Subject: [PATCH 10/24] Replace ClusterWait with ServiceWait --- .../docker/compose/DockerComposeRule.java | 11 ++- .../connection/waiting/ClusterWait.java | 77 ++++++++++++++++- .../connection/waiting/ServiceWait.java | 83 ------------------- 3 files changed, 78 insertions(+), 93 deletions(-) delete mode 100644 src/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java diff --git a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java index fa1d0fdd0..995ecbee1 100644 --- a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java +++ b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java @@ -9,9 +9,8 @@ import com.palantir.docker.compose.connection.ContainerCache; import com.palantir.docker.compose.connection.DockerMachine; import com.palantir.docker.compose.connection.ImmutableCluster; -import com.palantir.docker.compose.connection.waiting.ClusterWait; import com.palantir.docker.compose.connection.waiting.MultiServiceHealthCheck; -import com.palantir.docker.compose.connection.waiting.ServiceWait; +import com.palantir.docker.compose.connection.waiting.ClusterWait; import com.palantir.docker.compose.connection.waiting.SingleServiceHealthCheck; import com.palantir.docker.compose.execution.DefaultDockerCompose; import com.palantir.docker.compose.execution.DockerCompose; @@ -153,19 +152,19 @@ public ImmutableDockerComposeRule.Builder saveLogsTo(String path) { public abstract ImmutableDockerComposeRule.Builder addClusterWait(ClusterWait clusterWait); public ImmutableDockerComposeRule.Builder waitingForService(String serviceName, SingleServiceHealthCheck healthCheck) { - return addClusterWait(new ServiceWait(serviceName, healthCheck, DEFAULT_TIMEOUT)); + return addClusterWait(new ClusterWait(serviceName, healthCheck, DEFAULT_TIMEOUT)); } public ImmutableDockerComposeRule.Builder waitingForService(String serviceName, SingleServiceHealthCheck healthCheck, Duration timeout) { - return addClusterWait(new ServiceWait(serviceName, healthCheck, timeout)); + return addClusterWait(new ClusterWait(serviceName, healthCheck, timeout)); } public ImmutableDockerComposeRule.Builder waitingForServices(List services, MultiServiceHealthCheck healthCheck) { - return addClusterWait(new ServiceWait(services, healthCheck, DEFAULT_TIMEOUT)); + return addClusterWait(new ClusterWait(services, healthCheck, DEFAULT_TIMEOUT)); } public ImmutableDockerComposeRule.Builder waitingForServices(List services, MultiServiceHealthCheck healthCheck, Duration timeout) { - return addClusterWait(new ServiceWait(services, healthCheck, timeout)); + return addClusterWait(new ClusterWait(services, healthCheck, timeout)); } } } 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 index 835ccdd77..36356d467 100644 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterWait.java +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterWait.java @@ -1,14 +1,83 @@ /* * Copyright 2016 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - package com.palantir.docker.compose.connection.waiting; +import static java.util.stream.Collectors.toList; + +import com.google.common.collect.ImmutableList; +import com.jayway.awaitility.Awaitility; +import com.jayway.awaitility.core.ConditionTimeoutException; import com.palantir.docker.compose.connection.Cluster; +import com.palantir.docker.compose.connection.Container; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import org.joda.time.Duration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ClusterWait { + private static final Logger log = LoggerFactory.getLogger(ClusterWait.class); + private final List containerNames; + private final MultiServiceHealthCheck healthCheck; + private final Duration timeout; + + public ClusterWait(String containerName, SingleServiceHealthCheck healthCheck, Duration timeout) { + this(ImmutableList.of(containerName), MultiServiceHealthCheck.fromSingleServiceHealthCheck(healthCheck), timeout); + } + + public ClusterWait(List containerNames, MultiServiceHealthCheck healthCheck, Duration timeout) { + this.containerNames = containerNames; + this.healthCheck = healthCheck; + this.timeout = timeout; + } + + public void waitUntilReady(Cluster cluster) { + log.debug("Waiting for services [{}]", containerNames); + final AtomicReference> lastSuccessOrFailure = new AtomicReference<>(Optional.empty()); + List containersToCheck = containerNames.stream().map(cluster::container).collect(toList()); + try { + Awaitility.await() + .pollInterval(50, TimeUnit.MILLISECONDS) + .atMost(timeout.getMillis(), TimeUnit.MILLISECONDS) + .until(weHaveSuccess(containersToCheck, lastSuccessOrFailure)); + } catch (ConditionTimeoutException e) { + throw new IllegalStateException(serviceDidNotStartupExceptionMessage(lastSuccessOrFailure)); + } + } + + private Callable weHaveSuccess(List containersToCheck, AtomicReference> lastSuccessOrFailure) { + return () -> { + SuccessOrFailure successOrFailure = healthCheck.areServicesUp(containersToCheck); + lastSuccessOrFailure.set(Optional.of(successOrFailure)); + return successOrFailure.succeeded(); + }; + } -@FunctionalInterface -public interface ClusterWait { + private String serviceDidNotStartupExceptionMessage(AtomicReference> lastSuccessOrFailure) { + String healthcheckFailureMessage = lastSuccessOrFailure.get() + .flatMap(SuccessOrFailure::toOptionalFailureMessage) + .orElse("The healthcheck did not finish before the timeout"); - void waitUntilReady(Cluster cluster); + return String.format("%s '%s' failed to pass startup check:%n%s", + containerNames.size() > 1 ? "Containers" : "Container", + containerNames, + healthcheckFailureMessage); + } } diff --git a/src/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java b/src/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java deleted file mode 100644 index 00260e435..000000000 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2016 Palantir Technologies, Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.palantir.docker.compose.connection.waiting; - -import static java.util.stream.Collectors.toList; - -import com.google.common.collect.ImmutableList; -import com.jayway.awaitility.Awaitility; -import com.jayway.awaitility.core.ConditionTimeoutException; -import com.palantir.docker.compose.connection.Cluster; -import com.palantir.docker.compose.connection.Container; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.Callable; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; -import org.joda.time.Duration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ServiceWait implements ClusterWait { - private static final Logger log = LoggerFactory.getLogger(ServiceWait.class); - private final List containerNames; - private final MultiServiceHealthCheck healthCheck; - private final Duration timeout; - - public ServiceWait(String containerName, SingleServiceHealthCheck healthCheck, Duration timeout) { - this(ImmutableList.of(containerName), MultiServiceHealthCheck.fromSingleServiceHealthCheck(healthCheck), timeout); - } - - public ServiceWait(List containerNames, MultiServiceHealthCheck healthCheck, Duration timeout) { - this.containerNames = containerNames; - this.healthCheck = healthCheck; - this.timeout = timeout; - } - - public void waitUntilReady(Cluster cluster) { - log.debug("Waiting for services [{}]", containerNames); - final AtomicReference> lastSuccessOrFailure = new AtomicReference<>(Optional.empty()); - List containersToCheck = containerNames.stream().map(cluster::container).collect(toList()); - try { - Awaitility.await() - .pollInterval(50, TimeUnit.MILLISECONDS) - .atMost(timeout.getMillis(), TimeUnit.MILLISECONDS) - .until(weHaveSuccess(containersToCheck, lastSuccessOrFailure)); - } catch (ConditionTimeoutException e) { - throw new IllegalStateException(serviceDidNotStartupExceptionMessage(lastSuccessOrFailure)); - } - } - - private Callable weHaveSuccess(List containersToCheck, AtomicReference> lastSuccessOrFailure) { - return () -> { - SuccessOrFailure successOrFailure = healthCheck.areServicesUp(containersToCheck); - lastSuccessOrFailure.set(Optional.of(successOrFailure)); - return successOrFailure.succeeded(); - }; - } - - private String serviceDidNotStartupExceptionMessage(AtomicReference> lastSuccessOrFailure) { - String healthcheckFailureMessage = lastSuccessOrFailure.get() - .flatMap(SuccessOrFailure::toOptionalFailureMessage) - .orElse("The healthcheck did not finish before the timeout"); - - return String.format("%s '%s' failed to pass startup check:%n%s", - containerNames.size() > 1 ? "Containers" : "Container", - containerNames, - healthcheckFailureMessage); - } - -} From f4a8fef12313d182ad713b5b6ec599330d378953 Mon Sep 17 00:00:00 2001 From: Joe Lea Date: Fri, 6 May 2016 14:57:40 +0100 Subject: [PATCH 11/24] Allow using ClusterHealthChecks --- .../waiting/ClusterHealthCheck.java | 23 ++++++++ .../connection/waiting/ClusterWait.java | 55 +++++++++++++------ .../connection/waiting/ClusterWaitShould.java | 48 ++++++++++++++++ 3 files changed, 110 insertions(+), 16 deletions(-) create mode 100644 src/main/java/com/palantir/docker/compose/connection/waiting/ClusterHealthCheck.java create mode 100644 src/test/java/com/palantir/docker/compose/connection/waiting/ClusterWaitShould.java diff --git a/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterHealthCheck.java b/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterHealthCheck.java new file mode 100644 index 000000000..f072d68d7 --- /dev/null +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterHealthCheck.java @@ -0,0 +1,23 @@ +/* + * Copyright 2016 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.palantir.docker.compose.connection.waiting; + +import com.palantir.docker.compose.connection.Cluster; + +@FunctionalInterface +public interface ClusterHealthCheck { + SuccessOrFailure isClusterHealthy(Cluster cluster); +} 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 index 36356d467..f5d401cf3 100644 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterWait.java +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterWait.java @@ -33,12 +33,14 @@ public class ClusterWait { private static final Logger log = LoggerFactory.getLogger(ClusterWait.class); - private final List containerNames; - private final MultiServiceHealthCheck healthCheck; - private final Duration timeout; + private ClusterHealthCheck clusterHealthCheck; + private List containerNames; + private MultiServiceHealthCheck healthCheck; + private Duration timeout; public ClusterWait(String containerName, SingleServiceHealthCheck healthCheck, Duration timeout) { - this(ImmutableList.of(containerName), MultiServiceHealthCheck.fromSingleServiceHealthCheck(healthCheck), timeout); + this(ImmutableList.of(containerName), MultiServiceHealthCheck.fromSingleServiceHealthCheck(healthCheck), + timeout); } public ClusterWait(List containerNames, MultiServiceHealthCheck healthCheck, Duration timeout) { @@ -47,37 +49,58 @@ public ClusterWait(List containerNames, MultiServiceHealthCheck healthCh this.timeout = timeout; } + public ClusterWait(ClusterHealthCheck clusterHealthCheck, Duration timeout) { + this.clusterHealthCheck = clusterHealthCheck; + this.timeout = timeout; + } + public void waitUntilReady(Cluster cluster) { + final AtomicReference> lastSuccessOrFailure = new AtomicReference<>( + Optional.empty()); + final Callable evaluator = weHaveSuccess(cluster, lastSuccessOrFailure); + log.debug("Waiting for services [{}]", containerNames); - final AtomicReference> lastSuccessOrFailure = new AtomicReference<>(Optional.empty()); - List containersToCheck = containerNames.stream().map(cluster::container).collect(toList()); try { Awaitility.await() .pollInterval(50, TimeUnit.MILLISECONDS) .atMost(timeout.getMillis(), TimeUnit.MILLISECONDS) - .until(weHaveSuccess(containersToCheck, lastSuccessOrFailure)); + .until(evaluator); } catch (ConditionTimeoutException e) { throw new IllegalStateException(serviceDidNotStartupExceptionMessage(lastSuccessOrFailure)); } } - private Callable weHaveSuccess(List containersToCheck, AtomicReference> lastSuccessOrFailure) { - return () -> { - SuccessOrFailure successOrFailure = healthCheck.areServicesUp(containersToCheck); - lastSuccessOrFailure.set(Optional.of(successOrFailure)); - return successOrFailure.succeeded(); - }; + private List containersToCheck(Cluster cluster) { + return containerNames.stream().map(cluster::container).collect(toList()); + } + + private Callable weHaveSuccess(Cluster cluster, + AtomicReference> lastSuccessOrFailure) { + if (clusterHealthCheck == null) { + return () -> { + SuccessOrFailure successOrFailure = healthCheck.areServicesUp(containersToCheck(cluster)); + lastSuccessOrFailure.set(Optional.of(successOrFailure)); + return successOrFailure.succeeded(); + }; + } else { + return () -> { + SuccessOrFailure successOrFailure = clusterHealthCheck.isClusterHealthy(cluster); + lastSuccessOrFailure.set(Optional.of(successOrFailure)); + return successOrFailure.succeeded(); + }; + } } - private String serviceDidNotStartupExceptionMessage(AtomicReference> lastSuccessOrFailure) { + private String serviceDidNotStartupExceptionMessage( + AtomicReference> lastSuccessOrFailure) { String healthcheckFailureMessage = lastSuccessOrFailure.get() .flatMap(SuccessOrFailure::toOptionalFailureMessage) .orElse("The healthcheck did not finish before the timeout"); return String.format("%s '%s' failed to pass startup check:%n%s", - containerNames.size() > 1 ? "Containers" : "Container", + containerNames.size() > 1 ? "Containers" : "Container", containerNames, - healthcheckFailureMessage); + healthcheckFailureMessage); } } diff --git a/src/test/java/com/palantir/docker/compose/connection/waiting/ClusterWaitShould.java b/src/test/java/com/palantir/docker/compose/connection/waiting/ClusterWaitShould.java new file mode 100644 index 000000000..e33e8c528 --- /dev/null +++ b/src/test/java/com/palantir/docker/compose/connection/waiting/ClusterWaitShould.java @@ -0,0 +1,48 @@ +/* + * Copyright 2016 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.palantir.docker.compose.connection.waiting; + +import static com.palantir.docker.compose.connection.waiting.SuccessOrFailure.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.palantir.docker.compose.connection.Cluster; +import com.palantir.docker.compose.connection.ContainerCache; +import com.palantir.docker.compose.connection.ImmutableCluster; +import org.joda.time.Duration; +import org.junit.Test; + +public class ClusterWaitShould { + + private static final Duration DURATION = Duration.standardSeconds(1); + private static final String IP = "192.168.100.100"; + + private final ContainerCache containerCache = mock(ContainerCache.class); + private final ClusterHealthCheck clusterHealthCheck = mock(ClusterHealthCheck.class); + + private final Cluster cluster = ImmutableCluster.builder() + .containers(containerCache) + .ip(IP) + .build(); + + + @Test public void + return_when_a_cluster_is_ready() { + when(clusterHealthCheck.isClusterHealthy(cluster)).thenReturn(success()); + ClusterWait wait = new ClusterWait(clusterHealthCheck, DURATION); + wait.waitUntilReady(cluster); + } +} From 919e617d0249065efdb536622ef709a5e2c9fd4e Mon Sep 17 00:00:00 2001 From: Joe Lea Date: Fri, 6 May 2016 15:10:45 +0100 Subject: [PATCH 12/24] Cluster can fail to startup --- .../connection/waiting/ClusterWait.java | 5 +--- .../connection/waiting/ClusterWaitShould.java | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) 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 index f5d401cf3..09103525a 100644 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterWait.java +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterWait.java @@ -97,10 +97,7 @@ private String serviceDidNotStartupExceptionMessage( .flatMap(SuccessOrFailure::toOptionalFailureMessage) .orElse("The healthcheck did not finish before the timeout"); - return String.format("%s '%s' failed to pass startup check:%n%s", - containerNames.size() > 1 ? "Containers" : "Container", - containerNames, - healthcheckFailureMessage); + return "The cluster failed to pass a startup check: " + healthcheckFailureMessage; } } diff --git a/src/test/java/com/palantir/docker/compose/connection/waiting/ClusterWaitShould.java b/src/test/java/com/palantir/docker/compose/connection/waiting/ClusterWaitShould.java index e33e8c528..645b7a3b8 100644 --- a/src/test/java/com/palantir/docker/compose/connection/waiting/ClusterWaitShould.java +++ b/src/test/java/com/palantir/docker/compose/connection/waiting/ClusterWaitShould.java @@ -17,13 +17,17 @@ import static com.palantir.docker.compose.connection.waiting.SuccessOrFailure.*; 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.ContainerCache; import com.palantir.docker.compose.connection.ImmutableCluster; import org.joda.time.Duration; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; public class ClusterWaitShould { @@ -38,6 +42,8 @@ public class ClusterWaitShould { .ip(IP) .build(); + @Rule public ExpectedException exception = ExpectedException.none(); + @Test public void return_when_a_cluster_is_ready() { @@ -45,4 +51,24 @@ public class ClusterWaitShould { ClusterWait wait = new ClusterWait(clusterHealthCheck, DURATION); wait.waitUntilReady(cluster); } + + @Test public void + check_until_a_cluster_is_ready() { + when(clusterHealthCheck.isClusterHealthy(cluster)).thenReturn(failure("failure!"), success()); + ClusterWait wait = new ClusterWait(clusterHealthCheck, DURATION); + wait.waitUntilReady(cluster); + verify(clusterHealthCheck, times(2)).isClusterHealthy(cluster); + } + + @Test(timeout = 2000L) public void + timeout_if_the_cluster_is_not_healthy() { + when(clusterHealthCheck.isClusterHealthy(cluster)).thenReturn(failure("failure!")); + + exception.expect(IllegalStateException.class); + exception.expectMessage("failure!"); + + ClusterWait wait = new ClusterWait(clusterHealthCheck, DURATION); + + wait.waitUntilReady(cluster); + } } From 21cce4c06da72f0e4533c110e9de86462bd9e9eb Mon Sep 17 00:00:00 2001 From: Joe Lea Date: Fri, 6 May 2016 15:21:11 +0100 Subject: [PATCH 13/24] Remove unused code --- .../MultiServiceHealthCheckShould.java | 54 ------------------- 1 file changed, 54 deletions(-) delete mode 100644 src/test/java/com/palantir/docker/compose/connection/waiting/MultiServiceHealthCheckShould.java diff --git a/src/test/java/com/palantir/docker/compose/connection/waiting/MultiServiceHealthCheckShould.java b/src/test/java/com/palantir/docker/compose/connection/waiting/MultiServiceHealthCheckShould.java deleted file mode 100644 index 897465089..000000000 --- a/src/test/java/com/palantir/docker/compose/connection/waiting/MultiServiceHealthCheckShould.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2016 Palantir Technologies, Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.palantir.docker.compose.connection.waiting; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import com.google.common.collect.ImmutableList; -import com.palantir.docker.compose.connection.Container; -import org.junit.Test; - -public class MultiServiceHealthCheckShould { - - private static final Container CONTAINER = mock(Container.class); - private static final Container OTHER_CONTAINER = mock(Container.class); - - private final SingleServiceHealthCheck delegate = mock(SingleServiceHealthCheck.class); - private final MultiServiceHealthCheck healthCheck = MultiServiceHealthCheck.fromSingleServiceHealthCheck(delegate); - - @Test public void - delegate_to_the_wrapped_single_service_health_check() { - when(delegate.isServiceUp(CONTAINER)).thenReturn(SuccessOrFailure.success()); - - assertThat( - healthCheck.areServicesUp(ImmutableList.of(CONTAINER)), - is(delegate.isServiceUp(CONTAINER))); - } - - @Test(expected = IllegalArgumentException.class) public void - throw_an_error_when_a_wrapped_health_check_is_passed_more_than_1_argument() { - healthCheck.areServicesUp(ImmutableList.of(CONTAINER, OTHER_CONTAINER)); - } - - @Test(expected = IllegalArgumentException.class) public void - throw_an_error_when_a_wrapped_health_check_is_passed_0_arguments() { - healthCheck.areServicesUp(ImmutableList.of(CONTAINER, OTHER_CONTAINER)); - } - -} From b4132a09c3cbad1aaa4af54f1aeeee6bd63e17f4 Mon Sep 17 00:00:00 2001 From: Joe Lea Date: Fri, 6 May 2016 15:23:07 +0100 Subject: [PATCH 14/24] Remove unnecessary transforms --- .../docker/compose/DockerComposeRule.java | 2 +- .../docker/compose/connection/Cluster.java | 13 ++++++- .../waiting/ClusterHealthCheck.java | 9 +++++ .../connection/waiting/ClusterWait.java | 38 +++++-------------- .../waiting/MultiServiceHealthCheck.java | 9 ----- .../connection/waiting/ClusterWaitShould.java | 2 +- 6 files changed, 31 insertions(+), 42 deletions(-) diff --git a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java index 995ecbee1..e1bb1c140 100644 --- a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java +++ b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java @@ -70,7 +70,7 @@ public DockerCompose dockerCompose() { public Cluster containers() { return ImmutableCluster.builder() .ip(machine().getIp()) - .containers(new ContainerCache(dockerCompose())) + .containerCache(new ContainerCache(dockerCompose())) .build(); } diff --git a/src/main/java/com/palantir/docker/compose/connection/Cluster.java b/src/main/java/com/palantir/docker/compose/connection/Cluster.java index fa58df66f..6db82bb3d 100644 --- a/src/main/java/com/palantir/docker/compose/connection/Cluster.java +++ b/src/main/java/com/palantir/docker/compose/connection/Cluster.java @@ -4,16 +4,25 @@ package com.palantir.docker.compose.connection; +import static java.util.stream.Collectors.toList; + +import java.util.List; import org.immutables.value.Value; @Value.Immutable public abstract class Cluster { public abstract String ip(); - public abstract ContainerCache containers(); + public abstract ContainerCache containerCache(); public Container container(String name) { - return containers().container(name); + return containerCache().container(name); + } + + public List containers(List containerNames) { + return containerNames.stream() + .map(this::container) + .collect(toList()); } } diff --git a/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterHealthCheck.java b/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterHealthCheck.java index f072d68d7..c0714c163 100644 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterHealthCheck.java +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterHealthCheck.java @@ -16,8 +16,17 @@ package com.palantir.docker.compose.connection.waiting; import com.palantir.docker.compose.connection.Cluster; +import java.util.List; @FunctionalInterface public interface ClusterHealthCheck { + static ClusterHealthCheck serviceHealthCheck(List containerNames, MultiServiceHealthCheck delegate) { + return cluster -> delegate.areServicesUp(cluster.containers(containerNames)); + } + + static ClusterHealthCheck serviceHealthCheck(String containerName, SingleServiceHealthCheck delegate) { + return cluster -> delegate.isServiceUp(cluster.container(containerName)); + } + SuccessOrFailure isClusterHealthy(Cluster cluster); } 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 index 09103525a..7d950a635 100644 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterWait.java +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterWait.java @@ -15,13 +15,11 @@ */ package com.palantir.docker.compose.connection.waiting; -import static java.util.stream.Collectors.toList; +import static com.palantir.docker.compose.connection.waiting.ClusterHealthCheck.*; -import com.google.common.collect.ImmutableList; import com.jayway.awaitility.Awaitility; import com.jayway.awaitility.core.ConditionTimeoutException; import com.palantir.docker.compose.connection.Cluster; -import com.palantir.docker.compose.connection.Container; import java.util.List; import java.util.Optional; import java.util.concurrent.Callable; @@ -33,20 +31,15 @@ public class ClusterWait { private static final Logger log = LoggerFactory.getLogger(ClusterWait.class); - private ClusterHealthCheck clusterHealthCheck; - private List containerNames; - private MultiServiceHealthCheck healthCheck; - private Duration timeout; + private final ClusterHealthCheck clusterHealthCheck; + private final Duration timeout; - public ClusterWait(String containerName, SingleServiceHealthCheck healthCheck, Duration timeout) { - this(ImmutableList.of(containerName), MultiServiceHealthCheck.fromSingleServiceHealthCheck(healthCheck), - timeout); + public ClusterWait(String service, SingleServiceHealthCheck healthCheck, Duration timeout) { + this(serviceHealthCheck(service, healthCheck), timeout); } - public ClusterWait(List containerNames, MultiServiceHealthCheck healthCheck, Duration timeout) { - this.containerNames = containerNames; - this.healthCheck = healthCheck; - this.timeout = timeout; + public ClusterWait(List services, MultiServiceHealthCheck healthCheck, Duration timeout) { + this(serviceHealthCheck(services, healthCheck), timeout); } public ClusterWait(ClusterHealthCheck clusterHealthCheck, Duration timeout) { @@ -57,38 +50,25 @@ public ClusterWait(ClusterHealthCheck clusterHealthCheck, Duration timeout) { public void waitUntilReady(Cluster cluster) { final AtomicReference> lastSuccessOrFailure = new AtomicReference<>( Optional.empty()); - final Callable evaluator = weHaveSuccess(cluster, lastSuccessOrFailure); - log.debug("Waiting for services [{}]", containerNames); + log.debug("Waiting for cluster to be healthy"); try { Awaitility.await() .pollInterval(50, TimeUnit.MILLISECONDS) .atMost(timeout.getMillis(), TimeUnit.MILLISECONDS) - .until(evaluator); + .until(weHaveSuccess(cluster, lastSuccessOrFailure)); } catch (ConditionTimeoutException e) { throw new IllegalStateException(serviceDidNotStartupExceptionMessage(lastSuccessOrFailure)); } } - private List containersToCheck(Cluster cluster) { - return containerNames.stream().map(cluster::container).collect(toList()); - } - private Callable weHaveSuccess(Cluster cluster, AtomicReference> lastSuccessOrFailure) { - if (clusterHealthCheck == null) { - return () -> { - SuccessOrFailure successOrFailure = healthCheck.areServicesUp(containersToCheck(cluster)); - lastSuccessOrFailure.set(Optional.of(successOrFailure)); - return successOrFailure.succeeded(); - }; - } else { return () -> { SuccessOrFailure successOrFailure = clusterHealthCheck.isClusterHealthy(cluster); lastSuccessOrFailure.set(Optional.of(successOrFailure)); return successOrFailure.succeeded(); }; - } } private String serviceDidNotStartupExceptionMessage( diff --git a/src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceHealthCheck.java b/src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceHealthCheck.java index 09a1a74d1..a1f79c1a8 100644 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceHealthCheck.java +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceHealthCheck.java @@ -16,20 +16,11 @@ package com.palantir.docker.compose.connection.waiting; -import static com.google.common.collect.Iterables.getOnlyElement; - -import com.google.common.base.Preconditions; import com.palantir.docker.compose.connection.Container; import java.util.List; @FunctionalInterface public interface MultiServiceHealthCheck { - static MultiServiceHealthCheck fromSingleServiceHealthCheck(SingleServiceHealthCheck healthCheck) { - return containers -> { - Preconditions.checkArgument(containers.size() == 1, "Trying to run a single container health check on containers " + containers); - return healthCheck.isServiceUp(getOnlyElement(containers)); - }; - } SuccessOrFailure areServicesUp(List containers); } diff --git a/src/test/java/com/palantir/docker/compose/connection/waiting/ClusterWaitShould.java b/src/test/java/com/palantir/docker/compose/connection/waiting/ClusterWaitShould.java index 645b7a3b8..0fb85c61a 100644 --- a/src/test/java/com/palantir/docker/compose/connection/waiting/ClusterWaitShould.java +++ b/src/test/java/com/palantir/docker/compose/connection/waiting/ClusterWaitShould.java @@ -38,7 +38,7 @@ public class ClusterWaitShould { private final ClusterHealthCheck clusterHealthCheck = mock(ClusterHealthCheck.class); private final Cluster cluster = ImmutableCluster.builder() - .containers(containerCache) + .containerCache(containerCache) .ip(IP) .build(); From d09f83dfd772446b48f91c660e05b88ede5bda3b Mon Sep 17 00:00:00 2001 From: Joe Lea Date: Fri, 6 May 2016 15:29:21 +0100 Subject: [PATCH 15/24] Remove SingleServiceHealthCheck in favour of generics --- .../com/palantir/docker/compose/DockerComposeRule.java | 9 +++++---- .../docker/compose/DockerCompositionBuilder.java | 7 ++++--- .../compose/connection/waiting/ClusterHealthCheck.java | 5 +++-- .../docker/compose/connection/waiting/ClusterWait.java | 5 +++-- .../{SingleServiceHealthCheck.java => HealthCheck.java} | 7 ++----- .../docker/compose/connection/waiting/HealthChecks.java | 4 ++-- .../palantir/docker/compose/DockerComposeRuleTest.java | 9 +++++---- .../connection/waiting/HttpHealthCheckShould.java | 4 ++-- .../connection/waiting/PortsHealthCheckShould.java | 6 +++--- 9 files changed, 29 insertions(+), 27 deletions(-) rename src/main/java/com/palantir/docker/compose/connection/waiting/{SingleServiceHealthCheck.java => HealthCheck.java} (81%) diff --git a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java index e1bb1c140..2bd10ff28 100644 --- a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java +++ b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java @@ -6,12 +6,13 @@ 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.Container; import com.palantir.docker.compose.connection.ContainerCache; import com.palantir.docker.compose.connection.DockerMachine; import com.palantir.docker.compose.connection.ImmutableCluster; -import com.palantir.docker.compose.connection.waiting.MultiServiceHealthCheck; import com.palantir.docker.compose.connection.waiting.ClusterWait; -import com.palantir.docker.compose.connection.waiting.SingleServiceHealthCheck; +import com.palantir.docker.compose.connection.waiting.HealthCheck; +import com.palantir.docker.compose.connection.waiting.MultiServiceHealthCheck; import com.palantir.docker.compose.execution.DefaultDockerCompose; import com.palantir.docker.compose.execution.DockerCompose; import com.palantir.docker.compose.execution.DockerComposeExecArgument; @@ -151,11 +152,11 @@ public ImmutableDockerComposeRule.Builder saveLogsTo(String path) { public abstract ImmutableDockerComposeRule.Builder addClusterWait(ClusterWait clusterWait); - public ImmutableDockerComposeRule.Builder waitingForService(String serviceName, SingleServiceHealthCheck healthCheck) { + public ImmutableDockerComposeRule.Builder waitingForService(String serviceName, HealthCheck healthCheck) { return addClusterWait(new ClusterWait(serviceName, healthCheck, DEFAULT_TIMEOUT)); } - public ImmutableDockerComposeRule.Builder waitingForService(String serviceName, SingleServiceHealthCheck healthCheck, Duration timeout) { + public ImmutableDockerComposeRule.Builder waitingForService(String serviceName, HealthCheck healthCheck, Duration timeout) { return addClusterWait(new ClusterWait(serviceName, healthCheck, timeout)); } diff --git a/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java b/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java index a39a5698d..9511c966a 100644 --- a/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java +++ b/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java @@ -18,9 +18,10 @@ 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.Container; import com.palantir.docker.compose.connection.DockerMachine; +import com.palantir.docker.compose.connection.waiting.HealthCheck; import com.palantir.docker.compose.connection.waiting.MultiServiceHealthCheck; -import com.palantir.docker.compose.connection.waiting.SingleServiceHealthCheck; import com.palantir.docker.compose.execution.DockerCompose; import java.util.List; import org.joda.time.Duration; @@ -32,7 +33,7 @@ public DockerCompositionBuilder() { this.builder = DockerComposeRule.builder(); } - public DockerCompositionBuilder waitingForService(String serviceName, SingleServiceHealthCheck check) { + public DockerCompositionBuilder waitingForService(String serviceName, HealthCheck check) { builder.waitingForService(serviceName, check); return this; } @@ -47,7 +48,7 @@ public DockerCompositionBuilder waitingForServices(List services, MultiS return this; } - public DockerCompositionBuilder waitingForService(String serviceName, SingleServiceHealthCheck check, Duration timeout) { + public DockerCompositionBuilder waitingForService(String serviceName, HealthCheck check, Duration timeout) { builder.waitingForService(serviceName, check, timeout); return this; } diff --git a/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterHealthCheck.java b/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterHealthCheck.java index c0714c163..8a9b132eb 100644 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterHealthCheck.java +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterHealthCheck.java @@ -16,6 +16,7 @@ package com.palantir.docker.compose.connection.waiting; import com.palantir.docker.compose.connection.Cluster; +import com.palantir.docker.compose.connection.Container; import java.util.List; @FunctionalInterface @@ -24,8 +25,8 @@ static ClusterHealthCheck serviceHealthCheck(List containerNames, MultiS return cluster -> delegate.areServicesUp(cluster.containers(containerNames)); } - static ClusterHealthCheck serviceHealthCheck(String containerName, SingleServiceHealthCheck delegate) { - return cluster -> delegate.isServiceUp(cluster.container(containerName)); + static ClusterHealthCheck serviceHealthCheck(String containerName, HealthCheck delegate) { + return cluster -> delegate.isHealthy(cluster.container(containerName)); } SuccessOrFailure isClusterHealthy(Cluster cluster); 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 index 7d950a635..7a395ec40 100644 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterWait.java +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterWait.java @@ -15,11 +15,12 @@ */ package com.palantir.docker.compose.connection.waiting; -import static com.palantir.docker.compose.connection.waiting.ClusterHealthCheck.*; +import static com.palantir.docker.compose.connection.waiting.ClusterHealthCheck.serviceHealthCheck; import com.jayway.awaitility.Awaitility; import com.jayway.awaitility.core.ConditionTimeoutException; import com.palantir.docker.compose.connection.Cluster; +import com.palantir.docker.compose.connection.Container; import java.util.List; import java.util.Optional; import java.util.concurrent.Callable; @@ -34,7 +35,7 @@ public class ClusterWait { private final ClusterHealthCheck clusterHealthCheck; private final Duration timeout; - public ClusterWait(String service, SingleServiceHealthCheck healthCheck, Duration timeout) { + public ClusterWait(String service, HealthCheck healthCheck, Duration timeout) { this(serviceHealthCheck(service, healthCheck), timeout); } diff --git a/src/main/java/com/palantir/docker/compose/connection/waiting/SingleServiceHealthCheck.java b/src/main/java/com/palantir/docker/compose/connection/waiting/HealthCheck.java similarity index 81% rename from src/main/java/com/palantir/docker/compose/connection/waiting/SingleServiceHealthCheck.java rename to src/main/java/com/palantir/docker/compose/connection/waiting/HealthCheck.java index 22c3cd5fb..f9d894165 100644 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/SingleServiceHealthCheck.java +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/HealthCheck.java @@ -13,12 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.palantir.docker.compose.connection.waiting; -import com.palantir.docker.compose.connection.Container; - @FunctionalInterface -public interface SingleServiceHealthCheck { - SuccessOrFailure isServiceUp(Container container); +public interface HealthCheck { + SuccessOrFailure isHealthy(T target); } diff --git a/src/main/java/com/palantir/docker/compose/connection/waiting/HealthChecks.java b/src/main/java/com/palantir/docker/compose/connection/waiting/HealthChecks.java index 11e5abfc3..92cc04f3b 100644 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/HealthChecks.java +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/HealthChecks.java @@ -24,11 +24,11 @@ public final class HealthChecks { private HealthChecks() {} - public static SingleServiceHealthCheck toRespondOverHttp(int internalPort, Function urlFunction) { + public static HealthCheck toRespondOverHttp(int internalPort, Function urlFunction) { return container -> container.portIsListeningOnHttp(internalPort, urlFunction); } - public static SingleServiceHealthCheck toHaveAllPortsOpen() { + public static HealthCheck toHaveAllPortsOpen() { return Container::areAllPortsOpen; } } diff --git a/src/test/java/com/palantir/docker/compose/DockerComposeRuleTest.java b/src/test/java/com/palantir/docker/compose/DockerComposeRuleTest.java index d6298c4d9..a7a0ff81e 100644 --- a/src/test/java/com/palantir/docker/compose/DockerComposeRuleTest.java +++ b/src/test/java/com/palantir/docker/compose/DockerComposeRuleTest.java @@ -37,8 +37,8 @@ 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.HealthCheck; import com.palantir.docker.compose.connection.waiting.MultiServiceHealthCheck; -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; @@ -96,7 +96,7 @@ public void docker_compose_kill_and_rm_are_called_after_tests_are_run() throws I public void docker_compose_wait_for_service_passes_when_check_is_true() throws IOException, InterruptedException { AtomicInteger timesCheckCalled = new AtomicInteger(0); withComposeExecutableReturningContainerFor("db"); - SingleServiceHealthCheck checkCalledOnce = (container) -> SuccessOrFailure.fromBoolean(timesCheckCalled.incrementAndGet() == 1, "not called once yet"); + HealthCheck checkCalledOnce = (container) -> SuccessOrFailure.fromBoolean(timesCheckCalled.incrementAndGet() == 1, "not called once yet"); DockerComposeRule.builder().from(rule).waitingForService("db", checkCalledOnce).build().before(); assertThat(timesCheckCalled.get(), is(1)); } @@ -120,7 +120,7 @@ public void docker_compose_wait_for_service_waits_multiple_services() throws IOE public void docker_compose_wait_for_service_passes_when_check_is_true_after_being_false() throws IOException, InterruptedException { AtomicInteger timesCheckCalled = new AtomicInteger(0); withComposeExecutableReturningContainerFor("db"); - SingleServiceHealthCheck checkCalledTwice = (container) -> SuccessOrFailure.fromBoolean(timesCheckCalled.incrementAndGet() == 2, "not called twice yet"); + HealthCheck checkCalledTwice = (container) -> SuccessOrFailure.fromBoolean(timesCheckCalled.incrementAndGet() == 2, "not called twice yet"); DockerComposeRule.builder().from(rule).waitingForService("db", checkCalledTwice).build().before(); assertThat(timesCheckCalled.get(), is(2)); } @@ -130,7 +130,8 @@ public void throws_if_a_wait_for_service_check_remains_false_till_the_timeout() withComposeExecutableReturningContainerFor("db"); exception.expect(IllegalStateException.class); - exception.expectMessage("Container '[db]' failed to pass startup check:\noops"); + exception.expectMessage("failed to pass a startup check"); + exception.expectMessage("oops"); DockerComposeRule.builder().from(rule).waitingForService("db", (container) -> SuccessOrFailure.failure("oops"), millis(200)).build().before(); } diff --git a/src/test/java/com/palantir/docker/compose/connection/waiting/HttpHealthCheckShould.java b/src/test/java/com/palantir/docker/compose/connection/waiting/HttpHealthCheckShould.java index 56eb042e3..79c66289c 100644 --- a/src/test/java/com/palantir/docker/compose/connection/waiting/HttpHealthCheckShould.java +++ b/src/test/java/com/palantir/docker/compose/connection/waiting/HttpHealthCheckShould.java @@ -38,7 +38,7 @@ public void be_healthy_when_the_port_is_listening_over_http() { whenTheContainerIsListeningOnHttpTo(PORT, URL_FUNCTION); assertThat( - HealthChecks.toRespondOverHttp(PORT, URL_FUNCTION).isServiceUp(container), + HealthChecks.toRespondOverHttp(PORT, URL_FUNCTION).isHealthy(container), is(successful())); } @@ -47,7 +47,7 @@ public void be_unhealthy_when_all_ports_are_not_listening() { whenTheContainerIsNotListeningOnHttpTo(PORT, URL_FUNCTION); assertThat( - HealthChecks.toRespondOverHttp(PORT, URL_FUNCTION).isServiceUp(container), + HealthChecks.toRespondOverHttp(PORT, URL_FUNCTION).isHealthy(container), is(failure())); } diff --git a/src/test/java/com/palantir/docker/compose/connection/waiting/PortsHealthCheckShould.java b/src/test/java/com/palantir/docker/compose/connection/waiting/PortsHealthCheckShould.java index b1a50dce7..9c181b5ad 100644 --- a/src/test/java/com/palantir/docker/compose/connection/waiting/PortsHealthCheckShould.java +++ b/src/test/java/com/palantir/docker/compose/connection/waiting/PortsHealthCheckShould.java @@ -26,21 +26,21 @@ import org.junit.Test; public class PortsHealthCheckShould { - private final SingleServiceHealthCheck healthCheck = HealthChecks.toHaveAllPortsOpen(); + private final HealthCheck healthCheck = HealthChecks.toHaveAllPortsOpen(); private final Container container = mock(Container.class); @Test public void be_healthy_when_all_ports_are_listening() { whenTheContainerHasAllPortsOpen(); - assertThat(healthCheck.isServiceUp(container), is(successful())); + assertThat(healthCheck.isHealthy(container), is(successful())); } @Test public void be_unhealthy_when_all_ports_are_not_listening() { whenTheContainerDoesNotHaveAllPortsOpen(); - assertThat(healthCheck.isServiceUp(container), is(failure())); + assertThat(healthCheck.isHealthy(container), is(failure())); } private void whenTheContainerDoesNotHaveAllPortsOpen() { From ec996b5497d57840b7e92fc2a41f6609dbaccb96 Mon Sep 17 00:00:00 2001 From: Joe Lea Date: Fri, 6 May 2016 15:46:00 +0100 Subject: [PATCH 16/24] Generalise transforming cluster check --- .../connection/waiting/ClusterHealthCheck.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterHealthCheck.java b/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterHealthCheck.java index 8a9b132eb..c9423e8a3 100644 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterHealthCheck.java +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterHealthCheck.java @@ -18,6 +18,7 @@ import com.palantir.docker.compose.connection.Cluster; import com.palantir.docker.compose.connection.Container; import java.util.List; +import java.util.function.Function; @FunctionalInterface public interface ClusterHealthCheck { @@ -25,8 +26,15 @@ static ClusterHealthCheck serviceHealthCheck(List containerNames, MultiS return cluster -> delegate.areServicesUp(cluster.containers(containerNames)); } - static ClusterHealthCheck serviceHealthCheck(String containerName, HealthCheck delegate) { - return cluster -> delegate.isHealthy(cluster.container(containerName)); + static ClusterHealthCheck serviceHealthCheck(String containerName, HealthCheck containerCheck) { + return transformingHealthCheck(cluster -> cluster.container(containerName), containerCheck); + } + + static ClusterHealthCheck transformingHealthCheck(Function transform, HealthCheck healthCheck) { + return cluster -> { + T target = transform.apply(cluster); + return healthCheck.isHealthy(target); + }; } SuccessOrFailure isClusterHealthy(Cluster cluster); From 33316a2998a2033919c229c13fcca56bb88b0f9c Mon Sep 17 00:00:00 2001 From: Joe Lea Date: Fri, 6 May 2016 15:50:01 +0100 Subject: [PATCH 17/24] Use generalised form of waiting for multi service waits --- .../docker/compose/connection/waiting/ClusterHealthCheck.java | 4 ++-- .../compose/connection/waiting/MultiServiceHealthCheck.java | 4 ++-- .../com/palantir/docker/compose/DockerComposeRuleTest.java | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterHealthCheck.java b/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterHealthCheck.java index c9423e8a3..49912f88e 100644 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterHealthCheck.java +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterHealthCheck.java @@ -22,8 +22,8 @@ @FunctionalInterface public interface ClusterHealthCheck { - static ClusterHealthCheck serviceHealthCheck(List containerNames, MultiServiceHealthCheck delegate) { - return cluster -> delegate.areServicesUp(cluster.containers(containerNames)); + static ClusterHealthCheck serviceHealthCheck(List containerNames, HealthCheck> delegate) { + return transformingHealthCheck(cluster -> cluster.containers(containerNames), delegate); } static ClusterHealthCheck serviceHealthCheck(String containerName, HealthCheck containerCheck) { diff --git a/src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceHealthCheck.java b/src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceHealthCheck.java index a1f79c1a8..05a29d276 100644 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceHealthCheck.java +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceHealthCheck.java @@ -20,7 +20,7 @@ import java.util.List; @FunctionalInterface -public interface MultiServiceHealthCheck { +public interface MultiServiceHealthCheck extends HealthCheck> { - SuccessOrFailure areServicesUp(List containers); + SuccessOrFailure isHealthy(List containers); } diff --git a/src/test/java/com/palantir/docker/compose/DockerComposeRuleTest.java b/src/test/java/com/palantir/docker/compose/DockerComposeRuleTest.java index a7a0ff81e..608f08665 100644 --- a/src/test/java/com/palantir/docker/compose/DockerComposeRuleTest.java +++ b/src/test/java/com/palantir/docker/compose/DockerComposeRuleTest.java @@ -108,12 +108,12 @@ public void docker_compose_wait_for_service_waits_multiple_services() throws IOE List containers = ImmutableList.of(db1, db2); MultiServiceHealthCheck healthCheck = mock(MultiServiceHealthCheck.class); - when(healthCheck.areServicesUp(containers)).thenReturn(SuccessOrFailure.success()); + when(healthCheck.isHealthy(containers)).thenReturn(SuccessOrFailure.success()); DockerComposeRule.builder().from(rule).waitingForServices(ImmutableList.of("db1", "db2"), healthCheck).build().before(); - verify(healthCheck).areServicesUp(containers); + verify(healthCheck).isHealthy(containers); } @Test From 5bea2fb8fe93f198e35c6528bc3f0ed452029708 Mon Sep 17 00:00:00 2001 From: Joe Lea Date: Fri, 6 May 2016 15:54:05 +0100 Subject: [PATCH 18/24] Get rid of MultiServiceHealthCheck --- README.md | 2 +- .../docker/compose/DockerComposeRule.java | 5 ++-- .../compose/DockerCompositionBuilder.java | 5 ++-- .../connection/waiting/ClusterWait.java | 2 +- .../waiting/MultiServiceHealthCheck.java | 26 ------------------- .../docker/compose/DockerComposeRuleTest.java | 9 +++++-- .../DockerCompositionIntegrationTest.java | 4 +-- 7 files changed, 15 insertions(+), 38 deletions(-) delete mode 100644 src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceHealthCheck.java diff --git a/README.md b/README.md index ba8a98b9f..fa22b27fb 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ public class DockerCompositionTest { ``` The entrypoint method `waitingForService(String container, SingleServiceHealthCheck check[, Duration timeout])` will make sure the healthcheck passes for that container before the tests start. -The entrypoint method `waitingForServices(List containers, MultiServiceHealthCheck check[, Duration timeout])` will make sure the healthcheck passes for the cluster of containers before the tests start. +The entrypoint method `waitingForServices(List containers, HealthCheck> check[, Duration timeout])` will make sure the healthcheck passes for the cluster of containers before the tests start. We provide 2 default healthChecks in the HealthChecks class: diff --git a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java index 2bd10ff28..6288508f1 100644 --- a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java +++ b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java @@ -12,7 +12,6 @@ import com.palantir.docker.compose.connection.ImmutableCluster; import com.palantir.docker.compose.connection.waiting.ClusterWait; import com.palantir.docker.compose.connection.waiting.HealthCheck; -import com.palantir.docker.compose.connection.waiting.MultiServiceHealthCheck; import com.palantir.docker.compose.execution.DefaultDockerCompose; import com.palantir.docker.compose.execution.DockerCompose; import com.palantir.docker.compose.execution.DockerComposeExecArgument; @@ -160,11 +159,11 @@ public ImmutableDockerComposeRule.Builder waitingForService(String serviceName, return addClusterWait(new ClusterWait(serviceName, healthCheck, timeout)); } - public ImmutableDockerComposeRule.Builder waitingForServices(List services, MultiServiceHealthCheck healthCheck) { + public ImmutableDockerComposeRule.Builder waitingForServices(List services, HealthCheck> healthCheck) { return addClusterWait(new ClusterWait(services, healthCheck, DEFAULT_TIMEOUT)); } - public ImmutableDockerComposeRule.Builder waitingForServices(List services, MultiServiceHealthCheck healthCheck, Duration timeout) { + public ImmutableDockerComposeRule.Builder waitingForServices(List services, HealthCheck> healthCheck, Duration timeout) { return addClusterWait(new ClusterWait(services, healthCheck, timeout)); } } diff --git a/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java b/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java index 9511c966a..66a7e63bb 100644 --- a/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java +++ b/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java @@ -21,7 +21,6 @@ import com.palantir.docker.compose.connection.Container; import com.palantir.docker.compose.connection.DockerMachine; import com.palantir.docker.compose.connection.waiting.HealthCheck; -import com.palantir.docker.compose.connection.waiting.MultiServiceHealthCheck; import com.palantir.docker.compose.execution.DockerCompose; import java.util.List; import org.joda.time.Duration; @@ -38,12 +37,12 @@ public DockerCompositionBuilder waitingForService(String serviceName, HealthChec return this; } - public DockerCompositionBuilder waitingForServices(List services, MultiServiceHealthCheck check) { + public DockerCompositionBuilder waitingForServices(List services, HealthCheck> check) { builder.waitingForServices(services, check); return this; } - public DockerCompositionBuilder waitingForServices(List services, MultiServiceHealthCheck check, Duration timeout) { + public DockerCompositionBuilder waitingForServices(List services, HealthCheck> check, Duration timeout) { builder.waitingForServices(services, check, timeout); return this; } 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 index 7a395ec40..f0c60fb2c 100644 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterWait.java +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterWait.java @@ -39,7 +39,7 @@ public ClusterWait(String service, HealthCheck healthCheck, Duration this(serviceHealthCheck(service, healthCheck), timeout); } - public ClusterWait(List services, MultiServiceHealthCheck healthCheck, Duration timeout) { + public ClusterWait(List services, HealthCheck> healthCheck, Duration timeout) { this(serviceHealthCheck(services, healthCheck), timeout); } diff --git a/src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceHealthCheck.java b/src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceHealthCheck.java deleted file mode 100644 index 05a29d276..000000000 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceHealthCheck.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2016 Palantir Technologies, Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.palantir.docker.compose.connection.waiting; - -import com.palantir.docker.compose.connection.Container; -import java.util.List; - -@FunctionalInterface -public interface MultiServiceHealthCheck extends HealthCheck> { - - SuccessOrFailure isHealthy(List containers); -} diff --git a/src/test/java/com/palantir/docker/compose/DockerComposeRuleTest.java b/src/test/java/com/palantir/docker/compose/DockerComposeRuleTest.java index 608f08665..5a7e1d428 100644 --- a/src/test/java/com/palantir/docker/compose/DockerComposeRuleTest.java +++ b/src/test/java/com/palantir/docker/compose/DockerComposeRuleTest.java @@ -38,7 +38,6 @@ import com.palantir.docker.compose.connection.DockerMachine; import com.palantir.docker.compose.connection.DockerPort; import com.palantir.docker.compose.connection.waiting.HealthCheck; -import com.palantir.docker.compose.connection.waiting.MultiServiceHealthCheck; import com.palantir.docker.compose.connection.waiting.SuccessOrFailure; import com.palantir.docker.compose.execution.DockerCompose; import com.palantir.docker.compose.logging.LogCollector; @@ -55,7 +54,11 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +@RunWith(MockitoJUnitRunner.class) public class DockerComposeRuleTest { private static final String IP = "127.0.0.1"; @@ -65,6 +68,9 @@ public class DockerComposeRuleTest { @Rule public TemporaryFolder logFolder = new TemporaryFolder(); + @Mock + private HealthCheck> healthCheck; + private final DockerCompose dockerCompose = mock(DockerCompose.class); private final MockDockerEnvironment env = new MockDockerEnvironment(dockerCompose); private DockerComposeFiles mockFiles = mock(DockerComposeFiles.class); @@ -107,7 +113,6 @@ public void docker_compose_wait_for_service_waits_multiple_services() throws IOE Container db2 = withComposeExecutableReturningContainerFor("db2"); List containers = ImmutableList.of(db1, db2); - MultiServiceHealthCheck healthCheck = mock(MultiServiceHealthCheck.class); when(healthCheck.isHealthy(containers)).thenReturn(SuccessOrFailure.success()); diff --git a/src/test/java/com/palantir/docker/compose/DockerCompositionIntegrationTest.java b/src/test/java/com/palantir/docker/compose/DockerCompositionIntegrationTest.java index fb96ad46c..57c1800e4 100644 --- a/src/test/java/com/palantir/docker/compose/DockerCompositionIntegrationTest.java +++ b/src/test/java/com/palantir/docker/compose/DockerCompositionIntegrationTest.java @@ -22,7 +22,7 @@ import com.google.common.collect.ImmutableList; import com.palantir.docker.compose.connection.Container; -import com.palantir.docker.compose.connection.waiting.MultiServiceHealthCheck; +import com.palantir.docker.compose.connection.waiting.HealthCheck; import com.palantir.docker.compose.connection.waiting.SuccessOrFailure; import java.io.IOException; import java.util.List; @@ -43,7 +43,7 @@ public class DockerCompositionIntegrationTest { .waitingForServices(ImmutableList.of("db3", "db4"), toAllHaveAllPortsOpen()) .build(); - private MultiServiceHealthCheck toAllHaveAllPortsOpen() { + private HealthCheck> toAllHaveAllPortsOpen() { return containers -> { boolean healthy = containers.stream() .map(Container::areAllPortsOpen) From 551ca84b7f0d467cb5cec331c4fe601ba1635aab Mon Sep 17 00:00:00 2001 From: Joe Lea Date: Fri, 6 May 2016 15:58:58 +0100 Subject: [PATCH 19/24] Remove unused code --- .../palantir/docker/compose/DockerComposeRule.java | 13 +++++++++---- .../compose/connection/waiting/ClusterWait.java | 12 ------------ 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java index 6288508f1..753e64f90 100644 --- a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java +++ b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java @@ -3,6 +3,8 @@ */ package com.palantir.docker.compose; +import static com.palantir.docker.compose.connection.waiting.ClusterHealthCheck.*; + import com.palantir.docker.compose.configuration.DockerComposeFiles; import com.palantir.docker.compose.configuration.ProjectName; import com.palantir.docker.compose.connection.Cluster; @@ -10,6 +12,7 @@ import com.palantir.docker.compose.connection.ContainerCache; import com.palantir.docker.compose.connection.DockerMachine; import com.palantir.docker.compose.connection.ImmutableCluster; +import com.palantir.docker.compose.connection.waiting.ClusterHealthCheck; import com.palantir.docker.compose.connection.waiting.ClusterWait; import com.palantir.docker.compose.connection.waiting.HealthCheck; import com.palantir.docker.compose.execution.DefaultDockerCompose; @@ -152,19 +155,21 @@ public ImmutableDockerComposeRule.Builder saveLogsTo(String path) { public abstract ImmutableDockerComposeRule.Builder addClusterWait(ClusterWait clusterWait); public ImmutableDockerComposeRule.Builder waitingForService(String serviceName, HealthCheck healthCheck) { - return addClusterWait(new ClusterWait(serviceName, healthCheck, DEFAULT_TIMEOUT)); + return waitingForService(serviceName, healthCheck, DEFAULT_TIMEOUT); } public ImmutableDockerComposeRule.Builder waitingForService(String serviceName, HealthCheck healthCheck, Duration timeout) { - return addClusterWait(new ClusterWait(serviceName, healthCheck, timeout)); + ClusterHealthCheck clusterHealthCheck = serviceHealthCheck(serviceName, healthCheck); + return addClusterWait(new ClusterWait(clusterHealthCheck, timeout)); } public ImmutableDockerComposeRule.Builder waitingForServices(List services, HealthCheck> healthCheck) { - return addClusterWait(new ClusterWait(services, healthCheck, DEFAULT_TIMEOUT)); + return waitingForServices(services, healthCheck, DEFAULT_TIMEOUT); } public ImmutableDockerComposeRule.Builder waitingForServices(List services, HealthCheck> healthCheck, Duration timeout) { - return addClusterWait(new ClusterWait(services, healthCheck, timeout)); + ClusterHealthCheck clusterHealthCheck = serviceHealthCheck(services, healthCheck); + return addClusterWait(new ClusterWait(clusterHealthCheck, timeout)); } } } 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 index f0c60fb2c..365c86537 100644 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterWait.java +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterWait.java @@ -15,13 +15,9 @@ */ package com.palantir.docker.compose.connection.waiting; -import static com.palantir.docker.compose.connection.waiting.ClusterHealthCheck.serviceHealthCheck; - import com.jayway.awaitility.Awaitility; import com.jayway.awaitility.core.ConditionTimeoutException; import com.palantir.docker.compose.connection.Cluster; -import com.palantir.docker.compose.connection.Container; -import java.util.List; import java.util.Optional; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; @@ -35,14 +31,6 @@ public class ClusterWait { private final ClusterHealthCheck clusterHealthCheck; private final Duration timeout; - public ClusterWait(String service, HealthCheck healthCheck, Duration timeout) { - this(serviceHealthCheck(service, healthCheck), timeout); - } - - public ClusterWait(List services, HealthCheck> healthCheck, Duration timeout) { - this(serviceHealthCheck(services, healthCheck), timeout); - } - public ClusterWait(ClusterHealthCheck clusterHealthCheck, Duration timeout) { this.clusterHealthCheck = clusterHealthCheck; this.timeout = timeout; From 9abfd92d52c855de7963cd5b894867b530f28243 Mon Sep 17 00:00:00 2001 From: Joe Lea Date: Fri, 6 May 2016 16:02:53 +0100 Subject: [PATCH 20/24] Checkstyle --- .../com/palantir/docker/compose/DockerComposeRule.java | 2 +- .../docker/compose/connection/waiting/ClusterWait.java | 10 +++++----- .../compose/connection/waiting/ClusterWaitShould.java | 3 ++- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java index 753e64f90..66010d2a4 100644 --- a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java +++ b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java @@ -3,7 +3,7 @@ */ package com.palantir.docker.compose; -import static com.palantir.docker.compose.connection.waiting.ClusterHealthCheck.*; +import static com.palantir.docker.compose.connection.waiting.ClusterHealthCheck.serviceHealthCheck; import com.palantir.docker.compose.configuration.DockerComposeFiles; import com.palantir.docker.compose.configuration.ProjectName; 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 index 365c86537..8dae4e240 100644 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterWait.java +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterWait.java @@ -53,11 +53,11 @@ public void waitUntilReady(Cluster cluster) { private Callable weHaveSuccess(Cluster cluster, AtomicReference> lastSuccessOrFailure) { - return () -> { - SuccessOrFailure successOrFailure = clusterHealthCheck.isClusterHealthy(cluster); - lastSuccessOrFailure.set(Optional.of(successOrFailure)); - return successOrFailure.succeeded(); - }; + return () -> { + SuccessOrFailure successOrFailure = clusterHealthCheck.isClusterHealthy(cluster); + lastSuccessOrFailure.set(Optional.of(successOrFailure)); + return successOrFailure.succeeded(); + }; } private String serviceDidNotStartupExceptionMessage( diff --git a/src/test/java/com/palantir/docker/compose/connection/waiting/ClusterWaitShould.java b/src/test/java/com/palantir/docker/compose/connection/waiting/ClusterWaitShould.java index 0fb85c61a..152d0ca49 100644 --- a/src/test/java/com/palantir/docker/compose/connection/waiting/ClusterWaitShould.java +++ b/src/test/java/com/palantir/docker/compose/connection/waiting/ClusterWaitShould.java @@ -15,7 +15,8 @@ */ package com.palantir.docker.compose.connection.waiting; -import static com.palantir.docker.compose.connection.waiting.SuccessOrFailure.*; +import static com.palantir.docker.compose.connection.waiting.SuccessOrFailure.failure; +import static com.palantir.docker.compose.connection.waiting.SuccessOrFailure.success; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; From d8d8941f1a4497e03989c5449432cb0631c1487e Mon Sep 17 00:00:00 2001 From: Joe Lea Date: Fri, 6 May 2016 17:49:35 +0100 Subject: [PATCH 21/24] Add access to host networked ports --- .../docker/compose/DockerComposeRule.java | 15 +++++++ .../docker/compose/DockerComposition.java | 3 ++ .../compose/DockerCompositionBuilder.java | 11 +++++ .../HostNetworkedPortsIntegrationTest.java | 42 +++++++++++++++++++ .../host-networked-docker-compose.yaml | 12 ++++++ 5 files changed, 83 insertions(+) create mode 100644 src/test/java/com/palantir/docker/compose/HostNetworkedPortsIntegrationTest.java create mode 100644 src/test/resources/host-networked-docker-compose.yaml diff --git a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java index 66010d2a4..69a2dcf8e 100644 --- a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java +++ b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java @@ -4,6 +4,7 @@ package com.palantir.docker.compose; import static com.palantir.docker.compose.connection.waiting.ClusterHealthCheck.serviceHealthCheck; +import static com.palantir.docker.compose.connection.waiting.ClusterHealthCheck.transformingHealthCheck; import com.palantir.docker.compose.configuration.DockerComposeFiles; import com.palantir.docker.compose.configuration.ProjectName; @@ -11,6 +12,7 @@ import com.palantir.docker.compose.connection.Container; 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.ImmutableCluster; import com.palantir.docker.compose.connection.waiting.ClusterHealthCheck; import com.palantir.docker.compose.connection.waiting.ClusterWait; @@ -40,6 +42,10 @@ public abstract class DockerComposeRule extends ExternalResource { private static final Logger log = LoggerFactory.getLogger(DockerComposeRule.class); + public DockerPort hostNetworkedPort(int port) { + return new DockerPort(machine().getIp(), port, port); + } + public abstract DockerComposeFiles files(); protected abstract List clusterWaits(); @@ -171,5 +177,14 @@ public ImmutableDockerComposeRule.Builder waitingForServices(List servic ClusterHealthCheck clusterHealthCheck = serviceHealthCheck(services, healthCheck); return addClusterWait(new ClusterWait(clusterHealthCheck, timeout)); } + + public ImmutableDockerComposeRule.Builder waitingForHostNetworkedPort(int port, HealthCheck healthCheck) { + return waitingForHostNetworkedPort(port, healthCheck, DEFAULT_TIMEOUT); + } + + public ImmutableDockerComposeRule.Builder waitingForHostNetworkedPort(int port, HealthCheck healthCheck, Duration timeout) { + ClusterHealthCheck clusterHealthCheck = transformingHealthCheck(cluster -> new DockerPort(cluster.ip(), port, port), healthCheck); + return addClusterWait(new ClusterWait(clusterHealthCheck, timeout)); + } } } diff --git a/src/main/java/com/palantir/docker/compose/DockerComposition.java b/src/main/java/com/palantir/docker/compose/DockerComposition.java index f2ec766c4..566d6f932 100644 --- a/src/main/java/com/palantir/docker/compose/DockerComposition.java +++ b/src/main/java/com/palantir/docker/compose/DockerComposition.java @@ -89,4 +89,7 @@ public void exec(DockerComposeExecOption options, String containerName, DockerCo rule.exec(options, containerName, arguments); } + public DockerPort hostNetworkedPort(int port) { + return rule.hostNetworkedPort(port); + } } diff --git a/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java b/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java index 66a7e63bb..3728a034f 100644 --- a/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java +++ b/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java @@ -20,6 +20,7 @@ import com.palantir.docker.compose.configuration.ProjectName; import com.palantir.docker.compose.connection.Container; import com.palantir.docker.compose.connection.DockerMachine; +import com.palantir.docker.compose.connection.DockerPort; import com.palantir.docker.compose.connection.waiting.HealthCheck; import com.palantir.docker.compose.execution.DockerCompose; import java.util.List; @@ -52,6 +53,16 @@ public DockerCompositionBuilder waitingForService(String serviceName, HealthChec return this; } + public DockerCompositionBuilder waitingForHostNetworkedPort(int port, HealthCheck healthCheck, Duration timeout) { + builder.waitingForHostNetworkedPort(port, healthCheck, timeout); + return this; + } + + public DockerCompositionBuilder waitingForHostNetworkedPort(int port, HealthCheck healthCheck) { + builder.waitingForHostNetworkedPort(port, healthCheck); + return this; + } + public DockerCompositionBuilder files(DockerComposeFiles files) { builder.files(files); return this; diff --git a/src/test/java/com/palantir/docker/compose/HostNetworkedPortsIntegrationTest.java b/src/test/java/com/palantir/docker/compose/HostNetworkedPortsIntegrationTest.java new file mode 100644 index 000000000..904e25157 --- /dev/null +++ b/src/test/java/com/palantir/docker/compose/HostNetworkedPortsIntegrationTest.java @@ -0,0 +1,42 @@ +/* + * Copyright 2016 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.palantir.docker.compose; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; + +import com.palantir.docker.compose.connection.DockerPort; +import com.palantir.docker.compose.connection.waiting.HealthCheck; +import com.palantir.docker.compose.connection.waiting.SuccessOrFailure; +import org.junit.Rule; +import org.junit.Test; + +public class HostNetworkedPortsIntegrationTest { + @Rule + public DockerComposition composition = DockerComposition.of("src/test/resources/host-networked-docker-compose.yaml") + .waitingForHostNetworkedPort(5432, toBeOpen()) + .build(); + + private HealthCheck toBeOpen() { + return port -> SuccessOrFailure.fromBoolean(port.isListeningNow(), "" + port + "was not listening"); + } + + @Test public void + can_access_host_networked_ports() { + assertThat(composition.hostNetworkedPort(5432).getInternalPort(), is(5432)); + assertThat(composition.hostNetworkedPort(5432).getExternalPort(), is(5432)); + } +} diff --git a/src/test/resources/host-networked-docker-compose.yaml b/src/test/resources/host-networked-docker-compose.yaml new file mode 100644 index 000000000..7b98ee553 --- /dev/null +++ b/src/test/resources/host-networked-docker-compose.yaml @@ -0,0 +1,12 @@ +version: "2" + +services: + db: + image: kiasaki/alpine-postgres + environment: + - "POSTGRES_DB=source" + - "POSTGRES_USER=palantir" + - "POSTGRES_PASSWORD=palantir" + ports: + - "5433" + network_mode: host From c94f6648d1de8de9bfa96169267c156c3def856f Mon Sep 17 00:00:00 2001 From: Joe Lea Date: Fri, 6 May 2016 17:51:54 +0100 Subject: [PATCH 22/24] Update docs --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fa22b27fb..bc91d3dfa 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,7 @@ public class DockerCompositionTest { .waitingForService("web", HealthChecks.toRespondOverHttp(8080, (port) -> port.inFormat("https://$HOST:$EXTERNAL_PORT"))) .waitingForService("other", (container) -> customServiceCheck(container), Duration.standardMinutes(2)) .waitingForServices(ImmutableList.of("node1", "node2"), toBeHealthyAsACluster()) + .waitingForHostNetworkedPort(5432, toBeOpen()) .build(); @Test @@ -88,8 +89,9 @@ public class DockerCompositionTest { } ``` -The entrypoint method `waitingForService(String container, SingleServiceHealthCheck check[, Duration timeout])` will make sure the healthcheck passes for that container before the tests start. +The entrypoint method `waitingForService(String container, HealthCheck check[, Duration timeout])` will make sure the healthcheck passes for that container before the tests start. The entrypoint method `waitingForServices(List containers, HealthCheck> check[, Duration timeout])` will make sure the healthcheck passes for the cluster of containers before the tests start. +The entrypoint method `waitingForHostNetworkedPort(int portNumber, HealthCheck check[, Duration timeout])` will make sure the healthcheck passes for a particular host networked port. We provide 2 default healthChecks in the HealthChecks class: From 3d1536b12d20b4e10b53c0805aa0d72cd0ee7dd3 Mon Sep 17 00:00:00 2001 From: Joe Lea Date: Mon, 9 May 2016 14:36:24 +0100 Subject: [PATCH 23/24] Fix port clashes in tests --- .../docker/compose/DockerCompositionIntegrationTest.java | 4 ++-- src/test/resources/docker-compose.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/palantir/docker/compose/DockerCompositionIntegrationTest.java b/src/test/java/com/palantir/docker/compose/DockerCompositionIntegrationTest.java index 57c1800e4..53be4dc67 100644 --- a/src/test/java/com/palantir/docker/compose/DockerCompositionIntegrationTest.java +++ b/src/test/java/com/palantir/docker/compose/DockerCompositionIntegrationTest.java @@ -66,7 +66,7 @@ private void forEachContainer(Consumer consumer) { public void should_run_docker_compose_up_using_the_specified_docker_compose_file_to_bring_postgres_up() throws InterruptedException, IOException { forEachContainer((container) -> { try { - assertThat(composition.portOnContainerWithExternalMapping("db", 5433).isListeningNow(), is(true)); + assertThat(composition.portOnContainerWithExternalMapping("db", 5442).isListeningNow(), is(true)); } catch (IOException | InterruptedException e) { propagate(e); } @@ -79,7 +79,7 @@ public void after_test_is_executed_the_launched_postgres_container_is_no_longer_ forEachContainer(container -> { try { - assertThat(composition.portOnContainerWithExternalMapping("db", 5433).isListeningNow(), is(false)); + assertThat(composition.portOnContainerWithInternalMapping("db", 5432).isListeningNow(), is(false)); } catch (IOException | InterruptedException e) { propagate(e); } diff --git a/src/test/resources/docker-compose.yaml b/src/test/resources/docker-compose.yaml index b924c8da1..61dcf43a9 100644 --- a/src/test/resources/docker-compose.yaml +++ b/src/test/resources/docker-compose.yaml @@ -5,7 +5,7 @@ db: - "POSTGRES_USER=palantir" - "POSTGRES_PASSWORD=palantir" ports: - - "5433:5432" + - "5442:5432" db2: image: kiasaki/alpine-postgres From ab1c672984238cdb297518cd64f75b9e6a159124 Mon Sep 17 00:00:00 2001 From: Joe Lea Date: Mon, 9 May 2016 14:47:24 +0100 Subject: [PATCH 24/24] Use docker 1.10 in circle --- circle.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/circle.yml b/circle.yml index b237fa658..1316ab4a6 100644 --- a/circle.yml +++ b/circle.yml @@ -1,4 +1,6 @@ machine: + pre: + - curl -sSL https://s3.amazonaws.com/circle-downloads/install-circleci-docker.sh | bash -s -- 1.10.0 java: version: oraclejdk8 services: @@ -6,6 +8,10 @@ machine: environment: TERM: dumb +dependencies: + pre: + - sudo pip install docker-compose + test: pre: - ./gradlew findbugsMain findbugsTest checkstyleMain checkstyleTest javadoc --info