From aab17d2a38c994d476c33bcc62cdfeebe8c10e1a Mon Sep 17 00:00:00 2001 From: Dan Fox Date: Tue, 26 Apr 2016 20:27:17 +0100 Subject: [PATCH 01/12] ImmutableDockerComposeRule seems pretty sweet --- .../docker/compose/DockerComposeRule.java | 128 ++++++++++++++++++ .../docker/compose/DockerComposition.java | 70 ++++------ .../compose/DockerCompositionBuilder.java | 70 ++++------ .../docker/compose/DockerService.java | 22 +++ .../DockerCompositionIntegrationTest.java | 3 +- .../docker/compose/DockerCompositionTest.java | 25 +--- 6 files changed, 214 insertions(+), 104 deletions(-) create mode 100644 src/main/java/com/palantir/docker/compose/DockerComposeRule.java create mode 100644 src/main/java/com/palantir/docker/compose/DockerService.java diff --git a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java new file mode 100644 index 000000000..515c068cb --- /dev/null +++ b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java @@ -0,0 +1,128 @@ +/* + * Copyright 2016 Palantir Technologies, Inc. All rights reserved. + */ +package com.palantir.docker.compose; + +import static java.util.stream.Collectors.toList; + +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.ContainerCache; +import com.palantir.docker.compose.connection.DockerMachine; +import com.palantir.docker.compose.connection.waiting.ServiceWait; +import com.palantir.docker.compose.connection.waiting.SingleServiceHealthCheck; +import com.palantir.docker.compose.execution.DefaultDockerCompose; +import com.palantir.docker.compose.execution.DockerCompose; +import com.palantir.docker.compose.execution.DockerComposeExecutable; +import com.palantir.docker.compose.execution.RetryingDockerCompose; +import com.palantir.docker.compose.logging.DoNothingLogCollector; +import com.palantir.docker.compose.logging.FileLogCollector; +import com.palantir.docker.compose.logging.LogCollector; +import java.io.IOException; +import java.util.List; +import org.immutables.value.Value; +import org.joda.time.Duration; +import org.junit.rules.ExternalResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Value.Immutable +public abstract class DockerComposeRule extends ExternalResource { + private static final Logger log = LoggerFactory.getLogger(DockerComposeRule.class); + + public abstract DockerComposeFiles files(); + + @Value.Default + protected DockerMachine machine() { + return DockerMachine.localMachine().build(); + } + + @Value.Default + public ProjectName projectName() { + return ProjectName.random(); + } + + @Value.Default + public DockerComposeExecutable executable() { + return DockerComposeExecutable.builder() + .dockerComposeFiles(files()) + .dockerConfiguration(machine()) + .projectName(projectName()) + .build(); + } + + @Value.Default + protected DockerCompose dockerCompose() { + DockerCompose dockerCompose = new DefaultDockerCompose(executable(), machine()); + return new RetryingDockerCompose(DockerCompositionBuilder.DEFAULT_RETRY_ATTEMPTS, dockerCompose); + } + + @Value.Default + public ContainerCache containers() { + return new ContainerCache(dockerCompose()); + } + + protected abstract List services(); + + @Value.Lazy + protected List serviceWaits() { + return services().stream() + .map(service -> { + Container container = containers().get(service.serviceName()); + SingleServiceHealthCheck singlecheck = service.healthCheck(); + return new ServiceWait(container, singlecheck, timeout()); + }).collect(toList()); + } + + @Value.Default + protected Duration timeout() { + return Duration.standardMinutes(5); + } + + @Value.Default + protected LogCollector logCollector() { + return new DoNothingLogCollector(); + } + + @Override + public void before() throws IOException, InterruptedException { + log.debug("Starting docker-compose cluster"); + dockerCompose().build(); + dockerCompose().up(); + + log.debug("Starting log collection"); + + logCollector().startCollecting(dockerCompose()); + log.debug("Waiting for services"); + serviceWaits().forEach(ServiceWait::waitTillServiceIsUp); + log.debug("docker-compose cluster started"); + } + + @Override + public void after() { + try { + log.debug("Killing docker-compose cluster"); + dockerCompose().down(); + dockerCompose().kill(); + dockerCompose().rm(); + logCollector().stopCollecting(); + } catch (IOException | InterruptedException e) { + throw new RuntimeException("Error cleaning up docker compose cluster", e); + } + } + + public static ImmutableDockerComposeRule.Builder builder() { + return ImmutableDockerComposeRule.builder(); + } + + public abstract static class Builder { + + public abstract ImmutableDockerComposeRule.Builder logCollector(LogCollector logCollector); + + public ImmutableDockerComposeRule.Builder saveLogsTo(String path) { + return logCollector(FileLogCollector.fromPath(path)); + } + + } +} diff --git a/src/main/java/com/palantir/docker/compose/DockerComposition.java b/src/main/java/com/palantir/docker/compose/DockerComposition.java index e49670a08..28a404758 100644 --- a/src/main/java/com/palantir/docker/compose/DockerComposition.java +++ b/src/main/java/com/palantir/docker/compose/DockerComposition.java @@ -15,101 +15,83 @@ */ package com.palantir.docker.compose; -import com.google.common.collect.ImmutableList; import com.palantir.docker.compose.configuration.DockerComposeFiles; import com.palantir.docker.compose.configuration.ProjectName; import com.palantir.docker.compose.connection.ContainerCache; import com.palantir.docker.compose.connection.DockerMachine; import com.palantir.docker.compose.connection.DockerPort; import com.palantir.docker.compose.connection.waiting.ServiceWait; -import com.palantir.docker.compose.execution.DefaultDockerCompose; import com.palantir.docker.compose.execution.DockerCompose; import com.palantir.docker.compose.logging.LogCollector; import java.io.IOException; import java.util.List; import org.junit.rules.ExternalResource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class DockerComposition extends ExternalResource { - private static final Logger log = LoggerFactory.getLogger(DockerComposition.class); - - private final DockerCompose dockerCompose; - private final ContainerCache containers; - private final List serviceWaits; - private final LogCollector logCollector; + private DockerComposeRule rule; public DockerComposition( DockerCompose dockerCompose, List serviceWaits, LogCollector logCollector, ContainerCache containers) { - this.dockerCompose = dockerCompose; - this.serviceWaits = ImmutableList.copyOf(serviceWaits); - this.logCollector = logCollector; - this.containers = containers; + this.rule = null; + } + + public DockerComposition(DockerComposeRule rule) { + this.rule = rule; } @Override public void before() throws IOException, InterruptedException { - log.debug("Starting docker-compose cluster"); - dockerCompose.build(); - dockerCompose.up(); - - log.debug("Starting log collection"); - - logCollector.startCollecting(dockerCompose); - log.debug("Waiting for services"); - serviceWaits.forEach(ServiceWait::waitTillServiceIsUp); - log.debug("docker-compose cluster started"); + rule.before(); } @Override public void after() { - try { - log.debug("Killing docker-compose cluster"); - dockerCompose.down(); - dockerCompose.kill(); - dockerCompose.rm(); - logCollector.stopCollecting(); - } catch (IOException | InterruptedException e) { - throw new RuntimeException("Error cleaning up docker compose cluster", e); - } + rule.after(); } public DockerPort portOnContainerWithExternalMapping(String container, int portNumber) throws IOException, InterruptedException { - return containers.get(container) - .portMappedExternallyTo(portNumber); + return rule.containers().get(container).portMappedExternallyTo(portNumber); } public DockerPort portOnContainerWithInternalMapping(String container, int portNumber) throws IOException, InterruptedException { - return containers.get(container) - .portMappedInternallyTo(portNumber); + return rule.containers().get(container).portMappedInternallyTo(portNumber); } public static DockerCompositionBuilder of(String dockerComposeFile) { - return of(DockerComposeFiles.from(dockerComposeFile)); + return new DockerCompositionBuilder() + .files(DockerComposeFiles.from(dockerComposeFile)); } public static DockerCompositionBuilder of(DockerComposeFiles dockerComposeFiles) { - return of(dockerComposeFiles, DockerMachine.localMachine().build()); + return new DockerCompositionBuilder() + .files(dockerComposeFiles); } public static DockerCompositionBuilder of(String dockerComposeFile, DockerMachine dockerMachine) { - return of(DockerComposeFiles.from(dockerComposeFile), dockerMachine); + return new DockerCompositionBuilder() + .files(DockerComposeFiles.from(dockerComposeFile)) + .machine(dockerMachine); } public static DockerCompositionBuilder of(DockerComposeFiles dockerComposeFiles, DockerMachine dockerMachine) { - return of(new DefaultDockerCompose(dockerComposeFiles, dockerMachine, ProjectName.random())); + return new DockerCompositionBuilder() + .files(dockerComposeFiles) + .machine(dockerMachine); } public static DockerCompositionBuilder of(DockerComposeFiles dockerComposeFiles, DockerMachine dockerMachine, String projectName) { - return of(new DefaultDockerCompose(dockerComposeFiles, dockerMachine, ProjectName.fromString(projectName))); + return new DockerCompositionBuilder() + .files(dockerComposeFiles) + .machine(dockerMachine) + .projectName(ProjectName.fromString(projectName)); } - public static DockerCompositionBuilder of(DockerCompose executable) { - return new DockerCompositionBuilder(executable); + public static DockerCompositionBuilder of(DockerCompose compose) { + return new DockerCompositionBuilder().dockerCompose(compose); } } diff --git a/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java b/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java index 115bbd4d4..4a37c3ea2 100644 --- a/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java +++ b/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java @@ -15,71 +15,61 @@ */ package com.palantir.docker.compose; -import static java.util.stream.Collectors.toList; -import static org.joda.time.Duration.standardMinutes; - -import com.palantir.docker.compose.connection.Container; -import com.palantir.docker.compose.connection.ContainerCache; -import com.palantir.docker.compose.connection.waiting.MultiServiceHealthCheck; -import com.palantir.docker.compose.connection.waiting.ServiceWait; +import com.palantir.docker.compose.ImmutableDockerComposeRule.Builder; +import com.palantir.docker.compose.configuration.DockerComposeFiles; +import com.palantir.docker.compose.configuration.ProjectName; +import com.palantir.docker.compose.connection.DockerMachine; import com.palantir.docker.compose.connection.waiting.SingleServiceHealthCheck; import com.palantir.docker.compose.execution.DockerCompose; -import com.palantir.docker.compose.execution.RetryingDockerCompose; -import com.palantir.docker.compose.logging.DoNothingLogCollector; -import com.palantir.docker.compose.logging.FileLogCollector; -import com.palantir.docker.compose.logging.LogCollector; -import java.util.ArrayList; -import java.util.List; import org.joda.time.Duration; public class DockerCompositionBuilder { - private static final Duration DEFAULT_TIMEOUT = standardMinutes(2); + private final Builder builder; + public static final int DEFAULT_RETRY_ATTEMPTS = 2; - private final List serviceWaits = new ArrayList<>(); - private final DockerCompose dockerCompose; - private final ContainerCache containers; - private LogCollector logCollector = new DoNothingLogCollector(); - private int numRetryAttempts = DEFAULT_RETRY_ATTEMPTS; + public DockerCompositionBuilder() { + this.builder = DockerComposeRule.builder(); + } - public DockerCompositionBuilder(DockerCompose dockerCompose) { - this.dockerCompose = dockerCompose; - this.containers = new ContainerCache(dockerCompose); + public DockerCompositionBuilder waitingForService(String serviceName, SingleServiceHealthCheck healthCheck) { + builder.addServices(DockerService.of(serviceName, healthCheck)); + return this; } - public DockerCompositionBuilder waitingForService(String serviceName, SingleServiceHealthCheck check) { - return waitingForService(serviceName, check, DEFAULT_TIMEOUT); + public DockerComposition build() { + DockerComposeRule rule = builder.build(); + return new DockerComposition(rule); } - public DockerCompositionBuilder waitingForServices(List services, MultiServiceHealthCheck check) { - return waitingForServices(services, check, DEFAULT_TIMEOUT); + public DockerCompositionBuilder dockerCompose(DockerCompose compose) { + builder.dockerCompose(compose); + return this; } - public DockerCompositionBuilder waitingForServices(List services, MultiServiceHealthCheck check, Duration timeout) { - List containersToWaitFor = services.stream() - .map(containers::get) - .collect(toList()); - serviceWaits.add(new ServiceWait(containersToWaitFor, check, timeout)); + public DockerCompositionBuilder files(DockerComposeFiles files) { + builder.files(files); return this; } - public DockerCompositionBuilder waitingForService(String serviceName, SingleServiceHealthCheck check, Duration timeout) { - serviceWaits.add(new ServiceWait(containers.get(serviceName), check, timeout)); + public DockerCompositionBuilder machine(DockerMachine machine) { + builder.machine(machine); return this; } - public DockerCompositionBuilder saveLogsTo(String path) { - this.logCollector = FileLogCollector.fromPath(path); + public DockerCompositionBuilder projectName(ProjectName name) { + builder.projectName(name); return this; } - public DockerCompositionBuilder retryAttempts(int retryAttempts) { - this.numRetryAttempts = retryAttempts; + public DockerCompositionBuilder saveLogsTo(String absolutePath) { + builder.saveLogsTo(absolutePath); return this; } - public DockerComposition build() { - DockerCompose retryingDockerCompose = new RetryingDockerCompose(numRetryAttempts, dockerCompose); - return new DockerComposition(retryingDockerCompose, serviceWaits, logCollector, containers); + public DockerCompositionBuilder timeout(Duration timeout) { + builder.timeout(timeout); + return this; } + } diff --git a/src/main/java/com/palantir/docker/compose/DockerService.java b/src/main/java/com/palantir/docker/compose/DockerService.java new file mode 100644 index 000000000..e4681c2c0 --- /dev/null +++ b/src/main/java/com/palantir/docker/compose/DockerService.java @@ -0,0 +1,22 @@ +/* + * Copyright 2016 Palantir Technologies, Inc. All rights reserved. + */ + +package com.palantir.docker.compose; + +import com.palantir.docker.compose.connection.waiting.SingleServiceHealthCheck; +import org.immutables.value.Value; + +@Value.Immutable +public interface DockerService { + + @Value.Parameter + String serviceName(); + + @Value.Parameter + SingleServiceHealthCheck healthCheck(); + + static DockerService of(String serviceName, SingleServiceHealthCheck healthCheck) { + return ImmutableDockerService.of(serviceName, healthCheck); + } +} diff --git a/src/test/java/com/palantir/docker/compose/DockerCompositionIntegrationTest.java b/src/test/java/com/palantir/docker/compose/DockerCompositionIntegrationTest.java index fb96ad46c..7c5f1f241 100644 --- a/src/test/java/com/palantir/docker/compose/DockerCompositionIntegrationTest.java +++ b/src/test/java/com/palantir/docker/compose/DockerCompositionIntegrationTest.java @@ -40,7 +40,8 @@ public class DockerCompositionIntegrationTest { public DockerComposition composition = DockerComposition.of("src/test/resources/docker-compose.yaml") .waitingForService("db", toHaveAllPortsOpen()) .waitingForService("db2", toHaveAllPortsOpen()) - .waitingForServices(ImmutableList.of("db3", "db4"), toAllHaveAllPortsOpen()) + .waitingForService("db3", toHaveAllPortsOpen()) + .waitingForService("db4", toHaveAllPortsOpen()) .build(); private MultiServiceHealthCheck toAllHaveAllPortsOpen() { diff --git a/src/test/java/com/palantir/docker/compose/DockerCompositionTest.java b/src/test/java/com/palantir/docker/compose/DockerCompositionTest.java index 6a2e6e6ef..51b66445f 100644 --- a/src/test/java/com/palantir/docker/compose/DockerCompositionTest.java +++ b/src/test/java/com/palantir/docker/compose/DockerCompositionTest.java @@ -29,19 +29,18 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import com.google.common.collect.ImmutableList; +import com.palantir.docker.compose.configuration.DockerComposeFiles; import com.palantir.docker.compose.configuration.MockDockerEnvironment; import com.palantir.docker.compose.connection.Container; import com.palantir.docker.compose.connection.ContainerNames; +import com.palantir.docker.compose.connection.DockerMachine; import com.palantir.docker.compose.connection.DockerPort; -import com.palantir.docker.compose.connection.waiting.MultiServiceHealthCheck; import com.palantir.docker.compose.connection.waiting.SingleServiceHealthCheck; import com.palantir.docker.compose.connection.waiting.SuccessOrFailure; import com.palantir.docker.compose.execution.DockerCompose; import java.io.File; import java.io.IOException; import java.io.OutputStream; -import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -62,7 +61,9 @@ public class DockerCompositionTest { private final DockerCompose dockerCompose = mock(DockerCompose.class); private final MockDockerEnvironment env = new MockDockerEnvironment(dockerCompose); - private final DockerCompositionBuilder dockerComposition = DockerComposition.of(dockerCompose); + private DockerComposeFiles mockFiles = mock(DockerComposeFiles.class); + private DockerMachine machine = mock(DockerMachine.class); + private final DockerCompositionBuilder dockerComposition = DockerComposition.of(dockerCompose).files(mockFiles).machine(machine); @Test public void docker_compose_build_and_up_is_called_before_tests_are_run() throws IOException, InterruptedException { @@ -87,20 +88,6 @@ public void docker_compose_wait_for_service_passes_when_check_is_true() throws I assertThat(timesCheckCalled.get(), is(1)); } - @Test - public void docker_compose_wait_for_service_waits_multiple_services() throws IOException, InterruptedException { - Container db1 = withComposeExecutableReturningContainerFor("db1"); - Container db2 = withComposeExecutableReturningContainerFor("db2"); - List containers = ImmutableList.of(db1, db2); - - MultiServiceHealthCheck healthCheck = mock(MultiServiceHealthCheck.class); - when(healthCheck.areServicesUp(containers)).thenReturn(SuccessOrFailure.success()); - - dockerComposition.waitingForServices(ImmutableList.of("db1", "db2"), healthCheck).build().before(); - - verify(healthCheck).areServicesUp(containers); - } - @Test public void docker_compose_wait_for_service_passes_when_check_is_true_after_being_false() throws IOException, InterruptedException { AtomicInteger timesCheckCalled = new AtomicInteger(0); @@ -117,7 +104,7 @@ public void throws_if_a_wait_for_service_check_remains_false_till_the_timeout() exception.expect(IllegalStateException.class); exception.expectMessage("Container 'db' failed to pass startup check:\noops"); - dockerComposition.waitingForService("db", (container) -> SuccessOrFailure.failure("oops"), millis(200)).build().before(); + dockerComposition.timeout(millis(200)).waitingForService("db", (container) -> SuccessOrFailure.failure("oops")).build().before(); } @Test From 85f1c0ca17591ffa422317a4c3a831953ff32f47 Mon Sep 17 00:00:00 2001 From: Dan Fox Date: Tue, 26 Apr 2016 20:30:41 +0100 Subject: [PATCH 02/12] Delete MultiServiceHealthCheck --- .../waiting/MultiServiceHealthCheck.java | 35 ------------ .../connection/waiting/ServiceWait.java | 33 +++--------- .../DockerCompositionIntegrationTest.java | 13 ----- .../MultiServiceHealthCheckShould.java | 54 ------------------- 4 files changed, 8 insertions(+), 127 deletions(-) delete mode 100644 src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceHealthCheck.java delete mode 100644 src/test/java/com/palantir/docker/compose/connection/waiting/MultiServiceHealthCheckShould.java 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 09a1a74d1..000000000 --- a/src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceHealthCheck.java +++ /dev/null @@ -1,35 +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 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/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java b/src/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java index 5adf18b1d..296f9d201 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,13 +15,9 @@ */ 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; 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; @@ -32,24 +28,18 @@ public class ServiceWait { private static final Logger log = LoggerFactory.getLogger(ServiceWait.class); - private final List containers; - private final MultiServiceHealthCheck healthCheck; + private final Container container; + private final SingleServiceHealthCheck 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(List containers, MultiServiceHealthCheck healthCheck, Duration timeout) { - this.containers = containers; + public ServiceWait(Container container, SingleServiceHealthCheck healthCheck, Duration timeout) { + this.container = container; this.healthCheck = healthCheck; this.timeout = timeout; } public void waitTillServiceIsUp() { - log.debug("Waiting for services [{}]", containerNames()); + log.debug("Waiting for service [{}]", container.getContainerName()); final AtomicReference> lastSuccessOrFailure = new AtomicReference<>(Optional.empty()); try { Awaitility.await() @@ -63,7 +53,7 @@ public void waitTillServiceIsUp() { private Callable weHaveSuccess(AtomicReference> lastSuccessOrFailure) { return () -> { - SuccessOrFailure successOrFailure = healthCheck.areServicesUp(containers); + SuccessOrFailure successOrFailure = healthCheck.isServiceUp(container); lastSuccessOrFailure.set(Optional.of(successOrFailure)); return successOrFailure.succeeded(); }; @@ -75,15 +65,8 @@ private String serviceDidNotStartupExceptionMessage(AtomicReference 1 ? "Containers" : "Container", - containerNames(), + "Container", + container.getContainerName(), healthcheckFailureMessage); } - - private String containerNames() { - return containers.stream() - .map(Container::getContainerName) - .collect(joining(", ")); - - } } diff --git a/src/test/java/com/palantir/docker/compose/DockerCompositionIntegrationTest.java b/src/test/java/com/palantir/docker/compose/DockerCompositionIntegrationTest.java index 7c5f1f241..b9e86fdae 100644 --- a/src/test/java/com/palantir/docker/compose/DockerCompositionIntegrationTest.java +++ b/src/test/java/com/palantir/docker/compose/DockerCompositionIntegrationTest.java @@ -21,9 +21,6 @@ import static org.hamcrest.core.Is.is; 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.SuccessOrFailure; import java.io.IOException; import java.util.List; import java.util.function.Consumer; @@ -44,16 +41,6 @@ public class DockerCompositionIntegrationTest { .waitingForService("db4", toHaveAllPortsOpen()) .build(); - private MultiServiceHealthCheck toAllHaveAllPortsOpen() { - return containers -> { - boolean healthy = containers.stream() - .map(Container::areAllPortsOpen) - .allMatch(SuccessOrFailure::succeeded); - - return SuccessOrFailure.fromBoolean(healthy, ""); - }; - } - @Rule public ExpectedException exception = ExpectedException.none(); @Rule 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 da649de6f03300e419390a6d94be908234b0e3bf Mon Sep 17 00:00:00 2001 From: Dan Fox Date: Fri, 29 Apr 2016 12:02:51 +0100 Subject: [PATCH 03/12] Put back MultiServiceHealthCheck --- .../docker/compose/DockerComposeRule.java | 16 +++++- .../compose/DockerCompositionBuilder.java | 9 +++- .../waiting/MultiServiceHealthCheck.java | 35 ++++++++++++ .../connection/waiting/ServiceWait.java | 33 +++++++++--- .../DockerCompositionIntegrationTest.java | 16 +++++- .../docker/compose/DockerCompositionTest.java | 19 +++++++ .../MultiServiceHealthCheckShould.java | 54 +++++++++++++++++++ 7 files changed, 169 insertions(+), 13 deletions(-) create mode 100644 src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceHealthCheck.java create mode 100644 src/test/java/com/palantir/docker/compose/connection/waiting/MultiServiceHealthCheckShould.java diff --git a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java index 515c068cb..57e925286 100644 --- a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java +++ b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java @@ -5,11 +5,13 @@ import static java.util.stream.Collectors.toList; +import com.google.common.collect.ImmutableList; import com.palantir.docker.compose.configuration.DockerComposeFiles; import com.palantir.docker.compose.configuration.ProjectName; import com.palantir.docker.compose.connection.Container; import com.palantir.docker.compose.connection.ContainerCache; import com.palantir.docker.compose.connection.DockerMachine; +import com.palantir.docker.compose.connection.waiting.MultiServiceHealthCheck; import com.palantir.docker.compose.connection.waiting.ServiceWait; import com.palantir.docker.compose.connection.waiting.SingleServiceHealthCheck; import com.palantir.docker.compose.execution.DefaultDockerCompose; @@ -28,6 +30,7 @@ import org.slf4j.LoggerFactory; @Value.Immutable +@Value.Style(depluralize = true) public abstract class DockerComposeRule extends ExternalResource { private static final Logger log = LoggerFactory.getLogger(DockerComposeRule.class); @@ -118,11 +121,20 @@ public static ImmutableDockerComposeRule.Builder builder() { public abstract static class Builder { - public abstract ImmutableDockerComposeRule.Builder logCollector(LogCollector logCollector); + public abstract Builder logCollector(LogCollector logCollector); - public ImmutableDockerComposeRule.Builder saveLogsTo(String path) { + public Builder saveLogsTo(String path) { return logCollector(FileLogCollector.fromPath(path)); } + public abstract Builder addService(DockerService element); + + public Builder waitingForService(String serviceName, SingleServiceHealthCheck healthCheck) { + return addService(DockerService.of(serviceName, healthCheck)); + } + + public Builder waitingForServices(ImmutableList services, MultiServiceHealthCheck healthCheck) { + return this; + } } } diff --git a/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java b/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java index 4a37c3ea2..b126bff60 100644 --- a/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java +++ b/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java @@ -15,10 +15,12 @@ */ package com.palantir.docker.compose; +import com.google.common.collect.ImmutableList; import com.palantir.docker.compose.ImmutableDockerComposeRule.Builder; import com.palantir.docker.compose.configuration.DockerComposeFiles; import com.palantir.docker.compose.configuration.ProjectName; import com.palantir.docker.compose.connection.DockerMachine; +import com.palantir.docker.compose.connection.waiting.MultiServiceHealthCheck; import com.palantir.docker.compose.connection.waiting.SingleServiceHealthCheck; import com.palantir.docker.compose.execution.DockerCompose; import org.joda.time.Duration; @@ -33,7 +35,12 @@ public DockerCompositionBuilder() { } public DockerCompositionBuilder waitingForService(String serviceName, SingleServiceHealthCheck healthCheck) { - builder.addServices(DockerService.of(serviceName, healthCheck)); + builder.waitingForService(serviceName, healthCheck); + return this; + } + + public DockerCompositionBuilder waitingForServices(ImmutableList services, MultiServiceHealthCheck healthCheck) { + builder.waitingForServices(services, healthCheck); return this; } 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 new file mode 100644 index 000000000..09a1a74d1 --- /dev/null +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceHealthCheck.java @@ -0,0 +1,35 @@ +/* + * 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.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/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java b/src/main/java/com/palantir/docker/compose/connection/waiting/ServiceWait.java index 296f9d201..5adf18b1d 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,9 +15,13 @@ */ 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; 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; @@ -28,18 +32,24 @@ public class ServiceWait { private static final Logger log = LoggerFactory.getLogger(ServiceWait.class); - private final Container container; - private final SingleServiceHealthCheck healthCheck; + private final List containers; + private final MultiServiceHealthCheck healthCheck; private final Duration timeout; - public ServiceWait(Container container, SingleServiceHealthCheck healthCheck, Duration timeout) { - this.container = container; + public ServiceWait(Container service, SingleServiceHealthCheck healthCheck, Duration timeout) { + this.containers = ImmutableList.of(service); + this.healthCheck = MultiServiceHealthCheck.fromSingleServiceHealthCheck(healthCheck); + this.timeout = timeout; + } + + public ServiceWait(List containers, MultiServiceHealthCheck healthCheck, Duration timeout) { + this.containers = containers; this.healthCheck = healthCheck; this.timeout = timeout; } public void waitTillServiceIsUp() { - log.debug("Waiting for service [{}]", container.getContainerName()); + log.debug("Waiting for services [{}]", containerNames()); final AtomicReference> lastSuccessOrFailure = new AtomicReference<>(Optional.empty()); try { Awaitility.await() @@ -53,7 +63,7 @@ public void waitTillServiceIsUp() { private Callable weHaveSuccess(AtomicReference> lastSuccessOrFailure) { return () -> { - SuccessOrFailure successOrFailure = healthCheck.isServiceUp(container); + SuccessOrFailure successOrFailure = healthCheck.areServicesUp(containers); lastSuccessOrFailure.set(Optional.of(successOrFailure)); return successOrFailure.succeeded(); }; @@ -65,8 +75,15 @@ private String serviceDidNotStartupExceptionMessage(AtomicReference 1 ? "Containers" : "Container", + containerNames(), healthcheckFailureMessage); } + + private String containerNames() { + return containers.stream() + .map(Container::getContainerName) + .collect(joining(", ")); + + } } diff --git a/src/test/java/com/palantir/docker/compose/DockerCompositionIntegrationTest.java b/src/test/java/com/palantir/docker/compose/DockerCompositionIntegrationTest.java index b9e86fdae..fb96ad46c 100644 --- a/src/test/java/com/palantir/docker/compose/DockerCompositionIntegrationTest.java +++ b/src/test/java/com/palantir/docker/compose/DockerCompositionIntegrationTest.java @@ -21,6 +21,9 @@ import static org.hamcrest.core.Is.is; 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.SuccessOrFailure; import java.io.IOException; import java.util.List; import java.util.function.Consumer; @@ -37,10 +40,19 @@ public class DockerCompositionIntegrationTest { public DockerComposition composition = DockerComposition.of("src/test/resources/docker-compose.yaml") .waitingForService("db", toHaveAllPortsOpen()) .waitingForService("db2", toHaveAllPortsOpen()) - .waitingForService("db3", toHaveAllPortsOpen()) - .waitingForService("db4", toHaveAllPortsOpen()) + .waitingForServices(ImmutableList.of("db3", "db4"), toAllHaveAllPortsOpen()) .build(); + private MultiServiceHealthCheck toAllHaveAllPortsOpen() { + return containers -> { + boolean healthy = containers.stream() + .map(Container::areAllPortsOpen) + .allMatch(SuccessOrFailure::succeeded); + + return SuccessOrFailure.fromBoolean(healthy, ""); + }; + } + @Rule public ExpectedException exception = ExpectedException.none(); @Rule diff --git a/src/test/java/com/palantir/docker/compose/DockerCompositionTest.java b/src/test/java/com/palantir/docker/compose/DockerCompositionTest.java index 51b66445f..9cb56cc5b 100644 --- a/src/test/java/com/palantir/docker/compose/DockerCompositionTest.java +++ b/src/test/java/com/palantir/docker/compose/DockerCompositionTest.java @@ -29,22 +29,26 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.google.common.collect.ImmutableList; import com.palantir.docker.compose.configuration.DockerComposeFiles; import com.palantir.docker.compose.configuration.MockDockerEnvironment; import com.palantir.docker.compose.connection.Container; import com.palantir.docker.compose.connection.ContainerNames; import com.palantir.docker.compose.connection.DockerMachine; import com.palantir.docker.compose.connection.DockerPort; +import com.palantir.docker.compose.connection.waiting.MultiServiceHealthCheck; import com.palantir.docker.compose.connection.waiting.SingleServiceHealthCheck; import com.palantir.docker.compose.connection.waiting.SuccessOrFailure; import com.palantir.docker.compose.execution.DockerCompose; import java.io.File; import java.io.IOException; import java.io.OutputStream; +import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.apache.commons.io.IOUtils; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -88,6 +92,21 @@ public void docker_compose_wait_for_service_passes_when_check_is_true() throws I assertThat(timesCheckCalled.get(), is(1)); } + @Ignore + @Test + public void docker_compose_wait_for_service_waits_multiple_services() throws IOException, InterruptedException { + Container db1 = withComposeExecutableReturningContainerFor("db1"); + Container db2 = withComposeExecutableReturningContainerFor("db2"); + List containers = ImmutableList.of(db1, db2); + + MultiServiceHealthCheck healthCheck = mock(MultiServiceHealthCheck.class); + when(healthCheck.areServicesUp(containers)).thenReturn(SuccessOrFailure.success()); + + dockerComposition.waitingForServices(ImmutableList.of("db1", "db2"), healthCheck).build().before(); + + verify(healthCheck).areServicesUp(containers); + } + @Test public void docker_compose_wait_for_service_passes_when_check_is_true_after_being_false() throws IOException, InterruptedException { AtomicInteger timesCheckCalled = new AtomicInteger(0); 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 new file mode 100644 index 000000000..897465089 --- /dev/null +++ b/src/test/java/com/palantir/docker/compose/connection/waiting/MultiServiceHealthCheckShould.java @@ -0,0 +1,54 @@ +/* + * 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 a25be578b8a5ed3d8dcb0dca1453fe04dc102be6 Mon Sep 17 00:00:00 2001 From: Dan Fox Date: Fri, 29 Apr 2016 12:05:27 +0100 Subject: [PATCH 04/12] Cache and DockerCompose both implement ContainerAccessor interface --- .../palantir/docker/compose/DockerComposeRule.java | 5 +++-- .../palantir/docker/compose/DockerComposition.java | 4 ++-- .../docker/compose/connection/ContainerAccessor.java | 11 +++++++++++ .../docker/compose/connection/ContainerCache.java | 5 +++-- .../docker/compose/execution/DockerCompose.java | 3 ++- .../docker/compose/connection/ContainerCacheTest.java | 6 +++--- 6 files changed, 24 insertions(+), 10 deletions(-) create mode 100644 src/main/java/com/palantir/docker/compose/connection/ContainerAccessor.java diff --git a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java index 57e925286..e1e521326 100644 --- a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java +++ b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java @@ -9,6 +9,7 @@ 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.ContainerAccessor; import com.palantir.docker.compose.connection.ContainerCache; import com.palantir.docker.compose.connection.DockerMachine; import com.palantir.docker.compose.connection.waiting.MultiServiceHealthCheck; @@ -62,7 +63,7 @@ protected DockerCompose dockerCompose() { } @Value.Default - public ContainerCache containers() { + public ContainerAccessor containers() { return new ContainerCache(dockerCompose()); } @@ -72,7 +73,7 @@ public ContainerCache containers() { protected List serviceWaits() { return services().stream() .map(service -> { - Container container = containers().get(service.serviceName()); + Container container = containers().container(service.serviceName()); SingleServiceHealthCheck singlecheck = service.healthCheck(); return new ServiceWait(container, singlecheck, timeout()); }).collect(toList()); diff --git a/src/main/java/com/palantir/docker/compose/DockerComposition.java b/src/main/java/com/palantir/docker/compose/DockerComposition.java index 28a404758..0b3813a25 100644 --- a/src/main/java/com/palantir/docker/compose/DockerComposition.java +++ b/src/main/java/com/palantir/docker/compose/DockerComposition.java @@ -54,11 +54,11 @@ public void after() { } public DockerPort portOnContainerWithExternalMapping(String container, int portNumber) throws IOException, InterruptedException { - return rule.containers().get(container).portMappedExternallyTo(portNumber); + return rule.containers().container(container).portMappedExternallyTo(portNumber); } public DockerPort portOnContainerWithInternalMapping(String container, int portNumber) throws IOException, InterruptedException { - return rule.containers().get(container).portMappedInternallyTo(portNumber); + return rule.containers().container(container).portMappedInternallyTo(portNumber); } public static DockerCompositionBuilder of(String dockerComposeFile) { diff --git a/src/main/java/com/palantir/docker/compose/connection/ContainerAccessor.java b/src/main/java/com/palantir/docker/compose/connection/ContainerAccessor.java new file mode 100644 index 000000000..9f6e3e236 --- /dev/null +++ b/src/main/java/com/palantir/docker/compose/connection/ContainerAccessor.java @@ -0,0 +1,11 @@ +/* + * Copyright 2016 Palantir Technologies, Inc. All rights reserved. + */ + +package com.palantir.docker.compose.connection; + +public interface ContainerAccessor { + + Container container(String name); + +} diff --git a/src/main/java/com/palantir/docker/compose/connection/ContainerCache.java b/src/main/java/com/palantir/docker/compose/connection/ContainerCache.java index d3c9b702e..f86c8ed66 100644 --- a/src/main/java/com/palantir/docker/compose/connection/ContainerCache.java +++ b/src/main/java/com/palantir/docker/compose/connection/ContainerCache.java @@ -19,7 +19,7 @@ import java.util.HashMap; import java.util.Map; -public class ContainerCache { +public class ContainerCache implements ContainerAccessor { private final Map containers = new HashMap<>(); private final DockerCompose dockerCompose; @@ -28,7 +28,8 @@ public ContainerCache(DockerCompose dockerCompose) { this.dockerCompose = dockerCompose; } - public Container get(String containerName) { + @Override + public Container container(String containerName) { containers.putIfAbsent(containerName, dockerCompose.container(containerName)); return containers.get(containerName); } diff --git a/src/main/java/com/palantir/docker/compose/execution/DockerCompose.java b/src/main/java/com/palantir/docker/compose/execution/DockerCompose.java index bc1486159..6929f73ea 100644 --- a/src/main/java/com/palantir/docker/compose/execution/DockerCompose.java +++ b/src/main/java/com/palantir/docker/compose/execution/DockerCompose.java @@ -16,12 +16,13 @@ package com.palantir.docker.compose.execution; import com.palantir.docker.compose.connection.Container; +import com.palantir.docker.compose.connection.ContainerAccessor; import com.palantir.docker.compose.connection.ContainerNames; import com.palantir.docker.compose.connection.Ports; import java.io.IOException; import java.io.OutputStream; -public interface DockerCompose { +public interface DockerCompose extends ContainerAccessor { void build() throws IOException, InterruptedException; void up() throws IOException, InterruptedException; void down() throws IOException, InterruptedException; diff --git a/src/test/java/com/palantir/docker/compose/connection/ContainerCacheTest.java b/src/test/java/com/palantir/docker/compose/connection/ContainerCacheTest.java index e4d7eb57c..9e6b7a374 100644 --- a/src/test/java/com/palantir/docker/compose/connection/ContainerCacheTest.java +++ b/src/test/java/com/palantir/docker/compose/connection/ContainerCacheTest.java @@ -39,14 +39,14 @@ public void setup() { @Test public void getting_a_new_container_returns_a_container_with_the_specified_name() { - Container container = containers.get(CONTAINER_NAME); + Container container = containers.container(CONTAINER_NAME); assertThat(container, is(new Container(CONTAINER_NAME, dockerCompose))); } @Test public void getting_a_container_twice_returns_the_same_object() { - Container container = containers.get(CONTAINER_NAME); - Container sameContainer = containers.get(CONTAINER_NAME); + Container container = containers.container(CONTAINER_NAME); + Container sameContainer = containers.container(CONTAINER_NAME); assertThat(container, is(sameInstance(sameContainer))); } From c6f148eaf0acc1cac3b18979725c0392dce36b01 Mon Sep 17 00:00:00 2001 From: Dan Fox Date: Fri, 29 Apr 2016 13:43:08 +0100 Subject: [PATCH 05/12] SingleServiceWait ready for ClusterWait interface --- .../docker/compose/DockerComposeRule.java | 7 +-- .../docker/compose/DockerService.java | 22 ---------- .../connection/waiting/SingleServiceWait.java | 31 +++++++++++++ .../waiting/SingleServiceWaitTest.java | 43 +++++++++++++++++++ 4 files changed, 78 insertions(+), 25 deletions(-) delete mode 100644 src/main/java/com/palantir/docker/compose/DockerService.java create mode 100644 src/main/java/com/palantir/docker/compose/connection/waiting/SingleServiceWait.java create 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 e1e521326..e8207d466 100644 --- a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java +++ b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java @@ -15,6 +15,7 @@ import com.palantir.docker.compose.connection.waiting.MultiServiceHealthCheck; import com.palantir.docker.compose.connection.waiting.ServiceWait; import com.palantir.docker.compose.connection.waiting.SingleServiceHealthCheck; +import com.palantir.docker.compose.connection.waiting.SingleServiceWait; import com.palantir.docker.compose.execution.DefaultDockerCompose; import com.palantir.docker.compose.execution.DockerCompose; import com.palantir.docker.compose.execution.DockerComposeExecutable; @@ -67,7 +68,7 @@ public ContainerAccessor containers() { return new ContainerCache(dockerCompose()); } - protected abstract List services(); + protected abstract List services(); @Value.Lazy protected List serviceWaits() { @@ -128,10 +129,10 @@ public Builder saveLogsTo(String path) { return logCollector(FileLogCollector.fromPath(path)); } - public abstract Builder addService(DockerService element); + public abstract Builder addService(SingleServiceWait element); public Builder waitingForService(String serviceName, SingleServiceHealthCheck healthCheck) { - return addService(DockerService.of(serviceName, healthCheck)); + return addService(SingleServiceWait.of(serviceName, healthCheck)); } public Builder waitingForServices(ImmutableList services, MultiServiceHealthCheck healthCheck) { diff --git a/src/main/java/com/palantir/docker/compose/DockerService.java b/src/main/java/com/palantir/docker/compose/DockerService.java deleted file mode 100644 index e4681c2c0..000000000 --- a/src/main/java/com/palantir/docker/compose/DockerService.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2016 Palantir Technologies, Inc. All rights reserved. - */ - -package com.palantir.docker.compose; - -import com.palantir.docker.compose.connection.waiting.SingleServiceHealthCheck; -import org.immutables.value.Value; - -@Value.Immutable -public interface DockerService { - - @Value.Parameter - String serviceName(); - - @Value.Parameter - SingleServiceHealthCheck healthCheck(); - - static DockerService of(String serviceName, SingleServiceHealthCheck healthCheck) { - return ImmutableDockerService.of(serviceName, healthCheck); - } -} diff --git a/src/main/java/com/palantir/docker/compose/connection/waiting/SingleServiceWait.java b/src/main/java/com/palantir/docker/compose/connection/waiting/SingleServiceWait.java new file mode 100644 index 000000000..6fe21f1a3 --- /dev/null +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/SingleServiceWait.java @@ -0,0 +1,31 @@ +/* + * Copyright 2016 Palantir Technologies, Inc. All rights reserved. + */ + +package com.palantir.docker.compose.connection.waiting; + +import com.palantir.docker.compose.connection.Container; +import com.palantir.docker.compose.connection.ContainerAccessor; +import org.immutables.value.Value; +import org.joda.time.Duration; + +@Value.Immutable +public abstract class SingleServiceWait { + + @Value.Parameter + public abstract String serviceName(); + + @Value.Parameter + public abstract SingleServiceHealthCheck healthCheck(); + + public static SingleServiceWait of(String serviceName, SingleServiceHealthCheck healthCheck) { + return ImmutableSingleServiceWait.of(serviceName, healthCheck); + } + + public void waitUntilReady(ContainerAccessor containers, Duration timeout) { + Container container = containers.container(serviceName()); + ServiceWait serviceWait = new ServiceWait(container, healthCheck(), timeout); + serviceWait.waitTillServiceIsUp(); + } + +} diff --git a/src/test/java/com/palantir/docker/compose/connection/waiting/SingleServiceWaitTest.java b/src/test/java/com/palantir/docker/compose/connection/waiting/SingleServiceWaitTest.java new file mode 100644 index 000000000..ce8922778 --- /dev/null +++ b/src/test/java/com/palantir/docker/compose/connection/waiting/SingleServiceWaitTest.java @@ -0,0 +1,43 @@ +/* + * Copyright 2016 Palantir Technologies, Inc. All rights reserved. + */ + +package com.palantir.docker.compose.connection.waiting; + +import static 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.Container; +import com.palantir.docker.compose.connection.ContainerAccessor; +import org.joda.time.Duration; +import org.junit.Before; +import org.junit.Test; + +public class SingleServiceWaitTest { + + private ContainerAccessor containerAccessor = mock(ContainerAccessor.class); + private SingleServiceHealthCheck healthCheck = mock(SingleServiceHealthCheck.class); + private Container someContainer = mock(Container.class); + private SingleServiceWait wait = SingleServiceWait.of("somecontainer", healthCheck); + + @Before + public void before() { + when(containerAccessor.container("somecontainer")).thenReturn(someContainer); + when(healthCheck.isServiceUp(any())).thenReturn(SuccessOrFailure.success()); + } + + @Test + public void isReadyLooksUpContainer() { + wait.waitUntilReady(containerAccessor, Duration.millis(100)); + verify(containerAccessor, times(1)).container("somecontainer"); + } + + @Test + public void isReadyDelegatesToServiceWait() { + wait.waitUntilReady(containerAccessor, Duration.millis(100)); + verify(healthCheck, times(1)).isServiceUp(someContainer); + } +} From d89e280869bc6ded46e134ad4a64598b0e41d811 Mon Sep 17 00:00:00 2001 From: Dan Fox Date: Fri, 29 Apr 2016 13:47:29 +0100 Subject: [PATCH 06/12] DCR takes List --- .../docker/compose/DockerComposeRule.java | 23 ++++--------------- .../connection/waiting/ClusterWait.java | 15 ++++++++++++ .../connection/waiting/SingleServiceWait.java | 9 ++++---- 3 files changed, 25 insertions(+), 22 deletions(-) create mode 100644 src/main/java/com/palantir/docker/compose/connection/waiting/ClusterWait.java diff --git a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java index e8207d466..9bd95ff9f 100644 --- a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java +++ b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java @@ -3,17 +3,14 @@ */ package com.palantir.docker.compose; -import static java.util.stream.Collectors.toList; - import com.google.common.collect.ImmutableList; import com.palantir.docker.compose.configuration.DockerComposeFiles; import com.palantir.docker.compose.configuration.ProjectName; -import com.palantir.docker.compose.connection.Container; import com.palantir.docker.compose.connection.ContainerAccessor; import com.palantir.docker.compose.connection.ContainerCache; import com.palantir.docker.compose.connection.DockerMachine; +import com.palantir.docker.compose.connection.waiting.ClusterWait; import com.palantir.docker.compose.connection.waiting.MultiServiceHealthCheck; -import com.palantir.docker.compose.connection.waiting.ServiceWait; import com.palantir.docker.compose.connection.waiting.SingleServiceHealthCheck; import com.palantir.docker.compose.connection.waiting.SingleServiceWait; import com.palantir.docker.compose.execution.DefaultDockerCompose; @@ -68,17 +65,7 @@ public ContainerAccessor containers() { return new ContainerCache(dockerCompose()); } - protected abstract List services(); - - @Value.Lazy - protected List serviceWaits() { - return services().stream() - .map(service -> { - Container container = containers().container(service.serviceName()); - SingleServiceHealthCheck singlecheck = service.healthCheck(); - return new ServiceWait(container, singlecheck, timeout()); - }).collect(toList()); - } + protected abstract List clusterWaits(); @Value.Default protected Duration timeout() { @@ -100,7 +87,7 @@ public void before() throws IOException, InterruptedException { logCollector().startCollecting(dockerCompose()); log.debug("Waiting for services"); - serviceWaits().forEach(ServiceWait::waitTillServiceIsUp); + clusterWaits().forEach(clusterWait -> clusterWait.waitUntilReady(containers(), timeout())); log.debug("docker-compose cluster started"); } @@ -129,10 +116,10 @@ public Builder saveLogsTo(String path) { return logCollector(FileLogCollector.fromPath(path)); } - public abstract Builder addService(SingleServiceWait element); + public abstract Builder addClusterWait(ClusterWait clusterWait); public Builder waitingForService(String serviceName, SingleServiceHealthCheck healthCheck) { - return addService(SingleServiceWait.of(serviceName, healthCheck)); + return addClusterWait(SingleServiceWait.of(serviceName, healthCheck)); } public Builder waitingForServices(ImmutableList services, MultiServiceHealthCheck healthCheck) { diff --git a/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterWait.java b/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterWait.java new file mode 100644 index 000000000..368842d1a --- /dev/null +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/ClusterWait.java @@ -0,0 +1,15 @@ +/* + * Copyright 2016 Palantir Technologies, Inc. All rights reserved. + */ + +package com.palantir.docker.compose.connection.waiting; + +import com.palantir.docker.compose.connection.ContainerAccessor; +import org.joda.time.Duration; + +@FunctionalInterface +public interface ClusterWait { + + void waitUntilReady(ContainerAccessor containers, Duration timeout); + +} 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 6fe21f1a3..937f14ac8 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 @@ -10,20 +10,21 @@ import org.joda.time.Duration; @Value.Immutable -public abstract class SingleServiceWait { +public abstract class SingleServiceWait implements ClusterWait { @Value.Parameter - public abstract String serviceName(); + protected abstract String containerName(); @Value.Parameter - public abstract SingleServiceHealthCheck healthCheck(); + protected abstract SingleServiceHealthCheck healthCheck(); public static SingleServiceWait of(String serviceName, SingleServiceHealthCheck healthCheck) { return ImmutableSingleServiceWait.of(serviceName, healthCheck); } + @Override public void waitUntilReady(ContainerAccessor containers, Duration timeout) { - Container container = containers.container(serviceName()); + Container container = containers.container(containerName()); ServiceWait serviceWait = new ServiceWait(container, healthCheck(), timeout); serviceWait.waitTillServiceIsUp(); } From 98a7128e2f81c2ab07b6394746caab05e2d0f252 Mon Sep 17 00:00:00 2001 From: Dan Fox Date: Fri, 29 Apr 2016 13:59:50 +0100 Subject: [PATCH 07/12] MultiServiceWait allows MultiServiceHealthChecks --- .../docker/compose/DockerComposeRule.java | 14 +++---- .../connection/waiting/MultiServiceWait.java | 37 +++++++++++++++++++ .../docker/compose/DockerCompositionTest.java | 2 - 3 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceWait.java diff --git a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java index 9bd95ff9f..cf25a446c 100644 --- a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java +++ b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java @@ -3,7 +3,6 @@ */ package com.palantir.docker.compose; -import com.google.common.collect.ImmutableList; import com.palantir.docker.compose.configuration.DockerComposeFiles; import com.palantir.docker.compose.configuration.ProjectName; import com.palantir.docker.compose.connection.ContainerAccessor; @@ -11,6 +10,7 @@ import com.palantir.docker.compose.connection.DockerMachine; import com.palantir.docker.compose.connection.waiting.ClusterWait; import com.palantir.docker.compose.connection.waiting.MultiServiceHealthCheck; +import com.palantir.docker.compose.connection.waiting.MultiServiceWait; import com.palantir.docker.compose.connection.waiting.SingleServiceHealthCheck; import com.palantir.docker.compose.connection.waiting.SingleServiceWait; import com.palantir.docker.compose.execution.DefaultDockerCompose; @@ -110,20 +110,20 @@ public static ImmutableDockerComposeRule.Builder builder() { public abstract static class Builder { - public abstract Builder logCollector(LogCollector logCollector); + public abstract ImmutableDockerComposeRule.Builder logCollector(LogCollector logCollector); - public Builder saveLogsTo(String path) { + public ImmutableDockerComposeRule.Builder saveLogsTo(String path) { return logCollector(FileLogCollector.fromPath(path)); } - public abstract Builder addClusterWait(ClusterWait clusterWait); + public abstract ImmutableDockerComposeRule.Builder addClusterWait(ClusterWait clusterWait); - public Builder waitingForService(String serviceName, SingleServiceHealthCheck healthCheck) { + public ImmutableDockerComposeRule.Builder waitingForService(String serviceName, SingleServiceHealthCheck healthCheck) { return addClusterWait(SingleServiceWait.of(serviceName, healthCheck)); } - public Builder waitingForServices(ImmutableList services, MultiServiceHealthCheck healthCheck) { - return this; + public ImmutableDockerComposeRule.Builder waitingForServices(List services, MultiServiceHealthCheck healthCheck) { + return addClusterWait(MultiServiceWait.of(services, healthCheck)); } } } diff --git a/src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceWait.java b/src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceWait.java new file mode 100644 index 000000000..8eb9baac2 --- /dev/null +++ b/src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceWait.java @@ -0,0 +1,37 @@ +/* + * Copyright 2016 Palantir Technologies, Inc. All rights reserved. + */ + +package com.palantir.docker.compose.connection.waiting; + +import static java.util.stream.Collectors.toList; + +import com.palantir.docker.compose.connection.Container; +import com.palantir.docker.compose.connection.ContainerAccessor; +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(); + + public static MultiServiceWait of(List serviceNames, MultiServiceHealthCheck healthCheck) { + return ImmutableMultiServiceWait.of(serviceNames, healthCheck); + } + + @Override + public void waitUntilReady(ContainerAccessor containers, Duration timeout) { + List containersToWaitFor = containerNames().stream() + .map(containers::container) + .collect(toList()); + ServiceWait serviceWait = new ServiceWait(containersToWaitFor, healthcheck(), timeout); + serviceWait.waitTillServiceIsUp(); + } + +} diff --git a/src/test/java/com/palantir/docker/compose/DockerCompositionTest.java b/src/test/java/com/palantir/docker/compose/DockerCompositionTest.java index 9cb56cc5b..07b260f5a 100644 --- a/src/test/java/com/palantir/docker/compose/DockerCompositionTest.java +++ b/src/test/java/com/palantir/docker/compose/DockerCompositionTest.java @@ -48,7 +48,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.apache.commons.io.IOUtils; -import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -92,7 +91,6 @@ public void docker_compose_wait_for_service_passes_when_check_is_true() throws I assertThat(timesCheckCalled.get(), is(1)); } - @Ignore @Test public void docker_compose_wait_for_service_waits_multiple_services() throws IOException, InterruptedException { Container db1 = withComposeExecutableReturningContainerFor("db1"); From 7c38ed1c37a9367329a28c667dac31740de735bf Mon Sep 17 00:00:00 2001 From: Dan Fox Date: Fri, 29 Apr 2016 14:16:08 +0100 Subject: [PATCH 08/12] Remove all logic from the old DockerCompositionBuilder --- .../docker/compose/DockerComposeRule.java | 28 +++++++++++-------- .../docker/compose/DockerComposition.java | 12 -------- .../compose/DockerCompositionBuilder.java | 18 +++++++----- .../connection/waiting/ClusterWait.java | 3 +- .../connection/waiting/MultiServiceWait.java | 11 +++++--- .../connection/waiting/SingleServiceWait.java | 11 +++++--- .../docker/compose/DockerCompositionTest.java | 2 +- .../waiting/SingleServiceWaitTest.java | 8 +++--- 8 files changed, 48 insertions(+), 45 deletions(-) diff --git a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java index cf25a446c..0d3ed151a 100644 --- a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java +++ b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java @@ -31,10 +31,15 @@ @Value.Immutable @Value.Style(depluralize = true) public abstract class DockerComposeRule extends ExternalResource { + public static final Duration DEFAULT_TIMEOUT = Duration.standardMinutes(2); + public static final int DEFAULT_RETRY_ATTEMPTS = 2; + private static final Logger log = LoggerFactory.getLogger(DockerComposeRule.class); public abstract DockerComposeFiles files(); + protected abstract List clusterWaits(); + @Value.Default protected DockerMachine machine() { return DockerMachine.localMachine().build(); @@ -57,7 +62,7 @@ public DockerComposeExecutable executable() { @Value.Default protected DockerCompose dockerCompose() { DockerCompose dockerCompose = new DefaultDockerCompose(executable(), machine()); - return new RetryingDockerCompose(DockerCompositionBuilder.DEFAULT_RETRY_ATTEMPTS, dockerCompose); + return new RetryingDockerCompose(DEFAULT_RETRY_ATTEMPTS, dockerCompose); } @Value.Default @@ -65,13 +70,6 @@ public ContainerAccessor containers() { return new ContainerCache(dockerCompose()); } - protected abstract List clusterWaits(); - - @Value.Default - protected Duration timeout() { - return Duration.standardMinutes(5); - } - @Value.Default protected LogCollector logCollector() { return new DoNothingLogCollector(); @@ -87,7 +85,7 @@ public void before() throws IOException, InterruptedException { logCollector().startCollecting(dockerCompose()); log.debug("Waiting for services"); - clusterWaits().forEach(clusterWait -> clusterWait.waitUntilReady(containers(), timeout())); + clusterWaits().forEach(clusterWait -> clusterWait.waitUntilReady(containers())); log.debug("docker-compose cluster started"); } @@ -119,11 +117,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)); + return addClusterWait(SingleServiceWait.of(serviceName, healthCheck, DEFAULT_TIMEOUT)); + } + + public ImmutableDockerComposeRule.Builder waitingForService(String serviceName, SingleServiceHealthCheck healthCheck, Duration timeout) { + return addClusterWait(SingleServiceWait.of(serviceName, healthCheck, timeout)); } public ImmutableDockerComposeRule.Builder waitingForServices(List services, MultiServiceHealthCheck healthCheck) { - return addClusterWait(MultiServiceWait.of(services, healthCheck)); + return addClusterWait(MultiServiceWait.of(services, healthCheck, DEFAULT_TIMEOUT)); + } + + public ImmutableDockerComposeRule.Builder waitingForServices(List services, MultiServiceHealthCheck healthCheck, Duration timeout) { + return addClusterWait(MultiServiceWait.of(services, healthCheck, timeout)); } } } diff --git a/src/main/java/com/palantir/docker/compose/DockerComposition.java b/src/main/java/com/palantir/docker/compose/DockerComposition.java index 0b3813a25..5df8583a4 100644 --- a/src/main/java/com/palantir/docker/compose/DockerComposition.java +++ b/src/main/java/com/palantir/docker/compose/DockerComposition.java @@ -17,28 +17,16 @@ import com.palantir.docker.compose.configuration.DockerComposeFiles; import com.palantir.docker.compose.configuration.ProjectName; -import com.palantir.docker.compose.connection.ContainerCache; import com.palantir.docker.compose.connection.DockerMachine; import com.palantir.docker.compose.connection.DockerPort; -import com.palantir.docker.compose.connection.waiting.ServiceWait; import com.palantir.docker.compose.execution.DockerCompose; -import com.palantir.docker.compose.logging.LogCollector; import java.io.IOException; -import java.util.List; import org.junit.rules.ExternalResource; public class DockerComposition extends ExternalResource { private DockerComposeRule rule; - public DockerComposition( - DockerCompose dockerCompose, - List serviceWaits, - LogCollector logCollector, - ContainerCache containers) { - this.rule = null; - } - public DockerComposition(DockerComposeRule rule) { this.rule = rule; } diff --git a/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java b/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java index b126bff60..960bf5761 100644 --- a/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java +++ b/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java @@ -23,13 +23,12 @@ 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; public class DockerCompositionBuilder { private final Builder builder; - public static final int DEFAULT_RETRY_ATTEMPTS = 2; - public DockerCompositionBuilder() { this.builder = DockerComposeRule.builder(); } @@ -44,6 +43,16 @@ public DockerCompositionBuilder waitingForServices(ImmutableList service return this; } + public DockerCompositionBuilder waitingForServices(List services, MultiServiceHealthCheck check, Duration timeout) { + builder.waitingForServices(services, check, timeout); + return this; + } + + public DockerCompositionBuilder waitingForService(String serviceName, SingleServiceHealthCheck check, Duration timeout) { + builder.waitingForService(serviceName, check, timeout); + return this; + } + public DockerComposition build() { DockerComposeRule rule = builder.build(); return new DockerComposition(rule); @@ -74,9 +83,4 @@ public DockerCompositionBuilder saveLogsTo(String absolutePath) { return this; } - public DockerCompositionBuilder timeout(Duration timeout) { - builder.timeout(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 368842d1a..928a7a245 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 @@ -5,11 +5,10 @@ package com.palantir.docker.compose.connection.waiting; import com.palantir.docker.compose.connection.ContainerAccessor; -import org.joda.time.Duration; @FunctionalInterface public interface ClusterWait { - void waitUntilReady(ContainerAccessor containers, Duration timeout); + void waitUntilReady(ContainerAccessor containers); } 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 8eb9baac2..541beb85d 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 @@ -21,16 +21,19 @@ public abstract class MultiServiceWait implements ClusterWait { @Value.Parameter protected abstract MultiServiceHealthCheck healthcheck(); - public static MultiServiceWait of(List serviceNames, MultiServiceHealthCheck healthCheck) { - return ImmutableMultiServiceWait.of(serviceNames, 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(ContainerAccessor containers, Duration timeout) { + public void waitUntilReady(ContainerAccessor containers) { List containersToWaitFor = containerNames().stream() .map(containers::container) .collect(toList()); - ServiceWait serviceWait = new ServiceWait(containersToWaitFor, healthcheck(), timeout); + ServiceWait serviceWait = new ServiceWait(containersToWaitFor, healthcheck(), timeout()); serviceWait.waitTillServiceIsUp(); } diff --git a/src/main/java/com/palantir/docker/compose/connection/waiting/SingleServiceWait.java b/src/main/java/com/palantir/docker/compose/connection/waiting/SingleServiceWait.java index 937f14ac8..183b42430 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 @@ -18,14 +18,17 @@ public abstract class SingleServiceWait implements ClusterWait { @Value.Parameter protected abstract SingleServiceHealthCheck healthCheck(); - public static SingleServiceWait of(String serviceName, SingleServiceHealthCheck healthCheck) { - return ImmutableSingleServiceWait.of(serviceName, 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(ContainerAccessor containers, Duration timeout) { + public void waitUntilReady(ContainerAccessor containers) { Container container = containers.container(containerName()); - ServiceWait serviceWait = new ServiceWait(container, healthCheck(), timeout); + ServiceWait serviceWait = new ServiceWait(container, healthCheck(), timeout()); serviceWait.waitTillServiceIsUp(); } diff --git a/src/test/java/com/palantir/docker/compose/DockerCompositionTest.java b/src/test/java/com/palantir/docker/compose/DockerCompositionTest.java index 07b260f5a..cfeba8715 100644 --- a/src/test/java/com/palantir/docker/compose/DockerCompositionTest.java +++ b/src/test/java/com/palantir/docker/compose/DockerCompositionTest.java @@ -121,7 +121,7 @@ public void throws_if_a_wait_for_service_check_remains_false_till_the_timeout() exception.expect(IllegalStateException.class); exception.expectMessage("Container 'db' failed to pass startup check:\noops"); - dockerComposition.timeout(millis(200)).waitingForService("db", (container) -> SuccessOrFailure.failure("oops")).build().before(); + dockerComposition.waitingForService("db", (container) -> SuccessOrFailure.failure("oops"), millis(200)).build().before(); } @Test 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 ce8922778..1ce5deba8 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 @@ -4,6 +4,7 @@ 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; @@ -12,7 +13,6 @@ import com.palantir.docker.compose.connection.Container; import com.palantir.docker.compose.connection.ContainerAccessor; -import org.joda.time.Duration; import org.junit.Before; import org.junit.Test; @@ -21,7 +21,7 @@ public class SingleServiceWaitTest { private ContainerAccessor containerAccessor = mock(ContainerAccessor.class); private SingleServiceHealthCheck healthCheck = mock(SingleServiceHealthCheck.class); private Container someContainer = mock(Container.class); - private SingleServiceWait wait = SingleServiceWait.of("somecontainer", healthCheck); + private SingleServiceWait wait = SingleServiceWait.of("somecontainer", healthCheck, DEFAULT_TIMEOUT); @Before public void before() { @@ -31,13 +31,13 @@ public void before() { @Test public void isReadyLooksUpContainer() { - wait.waitUntilReady(containerAccessor, Duration.millis(100)); + wait.waitUntilReady(containerAccessor); verify(containerAccessor, times(1)).container("somecontainer"); } @Test public void isReadyDelegatesToServiceWait() { - wait.waitUntilReady(containerAccessor, Duration.millis(100)); + wait.waitUntilReady(containerAccessor); verify(healthCheck, times(1)).isServiceUp(someContainer); } } From 8dcd6bb4f118e9a1a8d9089dd98623482f781dd7 Mon Sep 17 00:00:00 2001 From: Dan Fox Date: Fri, 29 Apr 2016 14:19:13 +0100 Subject: [PATCH 09/12] Put back retryAttempts --- .../palantir/docker/compose/DockerComposeRule.java | 7 ++++++- .../docker/compose/DockerCompositionBuilder.java | 13 +++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java index 0d3ed151a..10adb4374 100644 --- a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java +++ b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java @@ -59,10 +59,15 @@ public DockerComposeExecutable executable() { .build(); } + @Value.Default + protected int retryAttempts() { + return DEFAULT_RETRY_ATTEMPTS; + } + @Value.Default protected DockerCompose dockerCompose() { DockerCompose dockerCompose = new DefaultDockerCompose(executable(), machine()); - return new RetryingDockerCompose(DEFAULT_RETRY_ATTEMPTS, dockerCompose); + return new RetryingDockerCompose(retryAttempts(), dockerCompose); } @Value.Default diff --git a/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java b/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java index 960bf5761..fb5ce3d46 100644 --- a/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java +++ b/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java @@ -33,13 +33,13 @@ public DockerCompositionBuilder() { this.builder = DockerComposeRule.builder(); } - public DockerCompositionBuilder waitingForService(String serviceName, SingleServiceHealthCheck healthCheck) { - builder.waitingForService(serviceName, healthCheck); + public DockerCompositionBuilder waitingForService(String serviceName, SingleServiceHealthCheck check) { + builder.waitingForService(serviceName, check); return this; } - public DockerCompositionBuilder waitingForServices(ImmutableList services, MultiServiceHealthCheck healthCheck) { - builder.waitingForServices(services, healthCheck); + public DockerCompositionBuilder waitingForServices(ImmutableList services, MultiServiceHealthCheck check) { + builder.waitingForServices(services, check); return this; } @@ -73,6 +73,11 @@ public DockerCompositionBuilder machine(DockerMachine machine) { return this; } + public DockerCompositionBuilder retryAttempts(int retryAttempts) { + builder.retryAttempts(retryAttempts); + return this; + } + public DockerCompositionBuilder projectName(ProjectName name) { builder.projectName(name); return this; From 6d952a13707dc8344a363ff4eb848c65f1b59cce Mon Sep 17 00:00:00 2001 From: Dan Fox Date: Fri, 29 Apr 2016 14:23:48 +0100 Subject: [PATCH 10/12] Merge in master --- README.md | 13 +++++ build.gradle | 1 + gradle.properties | 1 + .../docker/compose/DockerComposeRule.java | 7 +++ .../docker/compose/DockerComposition.java | 7 +++ .../execution/DefaultDockerCompose.java | 27 +++++++++++ .../compose/execution/DockerCompose.java | 1 + .../execution/DockerComposeExecArgument.java | 36 ++++++++++++++ .../execution/DockerComposeExecOption.java | 36 ++++++++++++++ .../execution/DockerComposeVersion.java | 38 +++++++++++++++ .../execution/RetryingDockerCompose.java | 6 +++ .../compose/execution/DockerComposeTest.java | 19 ++++++++ .../execution/DockerComposeVersionTest.java | 48 +++++++++++++++++++ .../RetryingDockerComposeShould.java | 15 ++++++ 14 files changed, 255 insertions(+) create mode 100644 src/main/java/com/palantir/docker/compose/execution/DockerComposeExecArgument.java create mode 100644 src/main/java/com/palantir/docker/compose/execution/DockerComposeExecOption.java create mode 100644 src/main/java/com/palantir/docker/compose/execution/DockerComposeVersion.java create mode 100644 src/test/java/com/palantir/docker/compose/execution/DockerComposeVersionTest.java diff --git a/README.md b/README.md index be0f2466a..b451844b0 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,19 @@ DockerPort portOnContainerWithInternalMapping(String container, int portNumber) In both cases the port in the Docker compose file must be referenced. Using the latter method no external port needs to be declared, this will be allocated by Docker at runtime and the DockerPort object contains the dynamic port and IP assignment. +Run docker-compose exec +--------------------------------------------------------- + +We support docker-compose exec command which runs a new command in a running container. + + +```java +dockerCompose.exec(dockerComposeExecOption, containerName, dockerComposeExecArgument) +``` + +Jut be aware that you need at least docker-compose 1.7 to run docker-compose exec + + Collecting logs --------------- diff --git a/build.gradle b/build.gradle index de3c24c9d..4a7a74813 100644 --- a/build.gradle +++ b/build.gradle @@ -54,6 +54,7 @@ dependencies { compile "commons-io:commons-io:2.4" compile "com.google.guava:guava:$guavaVersion" compile "joda-time:joda-time:$jodaVersion" + compile "com.github.zafarkhaja:java-semver:$javaSemverVersion" compile "junit:junit:$junitVersion" compile 'com.jayway.awaitility:awaitility:1.6.5' diff --git a/gradle.properties b/gradle.properties index 4920aa807..2082f4a15 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,6 +6,7 @@ guavaVersion=18.0 jodaVersion=2.9.1 log4jVersion=2.4 slf4jVersion=1.7.12 +javaSemverVersion=0.9.0 # Test junitVersion=4.12 diff --git a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java index 10adb4374..67764c8c9 100644 --- a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java +++ b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java @@ -15,6 +15,8 @@ import com.palantir.docker.compose.connection.waiting.SingleServiceWait; import com.palantir.docker.compose.execution.DefaultDockerCompose; import com.palantir.docker.compose.execution.DockerCompose; +import com.palantir.docker.compose.execution.DockerComposeExecArgument; +import com.palantir.docker.compose.execution.DockerComposeExecOption; import com.palantir.docker.compose.execution.DockerComposeExecutable; import com.palantir.docker.compose.execution.RetryingDockerCompose; import com.palantir.docker.compose.logging.DoNothingLogCollector; @@ -107,6 +109,11 @@ public void after() { } } + public void exec(DockerComposeExecOption options, String containerName, + DockerComposeExecArgument arguments) throws IOException, InterruptedException { + dockerCompose().exec(options, containerName, arguments); + } + public static ImmutableDockerComposeRule.Builder builder() { return ImmutableDockerComposeRule.builder(); } diff --git a/src/main/java/com/palantir/docker/compose/DockerComposition.java b/src/main/java/com/palantir/docker/compose/DockerComposition.java index 5df8583a4..f2ec766c4 100644 --- a/src/main/java/com/palantir/docker/compose/DockerComposition.java +++ b/src/main/java/com/palantir/docker/compose/DockerComposition.java @@ -20,6 +20,8 @@ import com.palantir.docker.compose.connection.DockerMachine; import com.palantir.docker.compose.connection.DockerPort; import com.palantir.docker.compose.execution.DockerCompose; +import com.palantir.docker.compose.execution.DockerComposeExecArgument; +import com.palantir.docker.compose.execution.DockerComposeExecOption; import java.io.IOException; import org.junit.rules.ExternalResource; @@ -82,4 +84,9 @@ public static DockerCompositionBuilder of(DockerCompose compose) { return new DockerCompositionBuilder().dockerCompose(compose); } + public void exec(DockerComposeExecOption options, String containerName, DockerComposeExecArgument arguments) + throws IOException, InterruptedException { + rule.exec(options, containerName, arguments); + } + } diff --git a/src/main/java/com/palantir/docker/compose/execution/DefaultDockerCompose.java b/src/main/java/com/palantir/docker/compose/execution/DefaultDockerCompose.java index eceb7fa62..26d4d6c3c 100644 --- a/src/main/java/com/palantir/docker/compose/execution/DefaultDockerCompose.java +++ b/src/main/java/com/palantir/docker/compose/execution/DefaultDockerCompose.java @@ -20,7 +20,9 @@ import static org.apache.commons.lang3.Validate.validState; import static org.joda.time.Duration.standardMinutes; +import com.github.zafarkhaja.semver.Version; import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; import com.palantir.docker.compose.configuration.DockerComposeFiles; import com.palantir.docker.compose.configuration.ProjectName; import com.palantir.docker.compose.connection.Container; @@ -83,6 +85,31 @@ public void rm() throws IOException, InterruptedException { executeDockerComposeCommand(throwingOnError(), "rm", "-f"); } + @Override + public void exec(DockerComposeExecOption dockerComposeExecOption, String containerName, + DockerComposeExecArgument dockerComposeExecArgument) throws IOException, InterruptedException { + verifyDockerComposeVersionAtLeast(Version.valueOf("1.7.0")); + String[] fullArgs = constructFullDockerComposeExecArguments(dockerComposeExecOption, containerName, dockerComposeExecArgument); + executeDockerComposeCommand(throwingOnError(), fullArgs); + } + + //Current docker-compose version output format: docker-compose version 1.7.0rc1, build 1ad8866 + private void verifyDockerComposeVersionAtLeast(Version targetVersion) throws IOException, InterruptedException { + String versionOutput = executeDockerComposeCommand(throwingOnError(), "-v"); + Version version = DockerComposeVersion.parseFromDockerComposeVersion(versionOutput); + validState(version.compareTo(targetVersion) >= 0, "You need at least docker-compose 1.7 to run docker-compose exec"); + } + + private String[] constructFullDockerComposeExecArguments(DockerComposeExecOption dockerComposeExecOption, + String containerName, DockerComposeExecArgument dockerComposeExecArgument) { + ImmutableList fullArgs = new ImmutableList.Builder().add("exec") + .addAll(dockerComposeExecOption.asList()) + .add(containerName) + .addAll(dockerComposeExecArgument.asList()) + .build(); + return fullArgs.toArray(new String[fullArgs.size()]); + } + @Override public ContainerNames ps() throws IOException, InterruptedException { String psOutput = executeDockerComposeCommand(throwingOnError(), "ps"); 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 6929f73ea..995ca9522 100644 --- a/src/main/java/com/palantir/docker/compose/execution/DockerCompose.java +++ b/src/main/java/com/palantir/docker/compose/execution/DockerCompose.java @@ -28,6 +28,7 @@ public interface DockerCompose extends ContainerAccessor { void down() throws IOException, InterruptedException; void kill() throws IOException, InterruptedException; void rm() throws IOException, InterruptedException; + void exec(DockerComposeExecOption dockerComposeExecOption, String containerName, DockerComposeExecArgument dockerComposeExecArgument) throws IOException, InterruptedException; ContainerNames ps() throws IOException, InterruptedException; Container container(String containerName); boolean writeLogs(String container, OutputStream output) throws IOException; diff --git a/src/main/java/com/palantir/docker/compose/execution/DockerComposeExecArgument.java b/src/main/java/com/palantir/docker/compose/execution/DockerComposeExecArgument.java new file mode 100644 index 000000000..e7fbe5132 --- /dev/null +++ b/src/main/java/com/palantir/docker/compose/execution/DockerComposeExecArgument.java @@ -0,0 +1,36 @@ +/* + * 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.execution; + +import java.util.Arrays; +import java.util.List; + +import org.immutables.value.Value; + +@Value.Immutable +public abstract class DockerComposeExecArgument { + + @Value.Parameter + protected abstract String[] arguments(); + + public static DockerComposeExecArgument arguments(String... arguments) { + return ImmutableDockerComposeExecArgument.of(arguments); + } + + public List asList() { + return Arrays.asList(arguments()); + } +} diff --git a/src/main/java/com/palantir/docker/compose/execution/DockerComposeExecOption.java b/src/main/java/com/palantir/docker/compose/execution/DockerComposeExecOption.java new file mode 100644 index 000000000..5cbf747f7 --- /dev/null +++ b/src/main/java/com/palantir/docker/compose/execution/DockerComposeExecOption.java @@ -0,0 +1,36 @@ +/* + * 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.execution; + +import java.util.Arrays; +import java.util.List; + +import org.immutables.value.Value; + +@Value.Immutable +public abstract class DockerComposeExecOption { + + @Value.Parameter + protected abstract String[] options(); + + public static DockerComposeExecOption options(String... options) { + return ImmutableDockerComposeExecOption.of(options); + } + + public List asList() { + return Arrays.asList(options()); + } +} diff --git a/src/main/java/com/palantir/docker/compose/execution/DockerComposeVersion.java b/src/main/java/com/palantir/docker/compose/execution/DockerComposeVersion.java new file mode 100644 index 000000000..b68f40641 --- /dev/null +++ b/src/main/java/com/palantir/docker/compose/execution/DockerComposeVersion.java @@ -0,0 +1,38 @@ +/* + * 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.execution; +import com.github.zafarkhaja.semver.Version; + +public final class DockerComposeVersion { + + private DockerComposeVersion() {} + + //docker-compose version format is like 1.7.0rc1, which can't be parsed by java-semver + //here we only pass 1.7.0 to java-semver + public static Version parseFromDockerComposeVersion(String versionOutput) { + String[] splitOnSeparator = versionOutput.split(" "); + String version = splitOnSeparator[2]; + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < version.length(); i++) { + if (version.charAt(i) >= '0' && version.charAt(i) <= '9' || version.charAt(i) == '.') { + builder.append(version.charAt(i)); + } else { + return Version.valueOf(builder.toString()); + } + } + return Version.valueOf(builder.toString()); + } +} diff --git a/src/main/java/com/palantir/docker/compose/execution/RetryingDockerCompose.java b/src/main/java/com/palantir/docker/compose/execution/RetryingDockerCompose.java index aeccfe029..64e5deb60 100644 --- a/src/main/java/com/palantir/docker/compose/execution/RetryingDockerCompose.java +++ b/src/main/java/com/palantir/docker/compose/execution/RetryingDockerCompose.java @@ -62,6 +62,12 @@ public void rm() throws IOException, InterruptedException { dockerCompose.rm(); } + @Override + public void exec(DockerComposeExecOption dockerComposeExecOption, String containerName, + DockerComposeExecArgument dockerComposeExecArgument) throws IOException, InterruptedException { + dockerCompose.exec(dockerComposeExecOption, containerName, dockerComposeExecArgument); + } + @Override public ContainerNames ps() throws IOException, InterruptedException { return retryer.runWithRetries(dockerCompose::ps); diff --git a/src/test/java/com/palantir/docker/compose/execution/DockerComposeTest.java b/src/test/java/com/palantir/docker/compose/execution/DockerComposeTest.java index 427b40686..89f420a05 100644 --- a/src/test/java/com/palantir/docker/compose/execution/DockerComposeTest.java +++ b/src/test/java/com/palantir/docker/compose/execution/DockerComposeTest.java @@ -15,11 +15,14 @@ */ package com.palantir.docker.compose.execution; +import static com.palantir.docker.compose.execution.DockerComposeExecArgument.arguments; +import static com.palantir.docker.compose.execution.DockerComposeExecOption.options; import static org.apache.commons.io.IOUtils.toInputStream; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; import static org.mockito.Matchers.anyVararg; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -35,6 +38,7 @@ import org.junit.Test; import org.junit.rules.ExpectedException; + public class DockerComposeTest { @Rule @@ -123,4 +127,19 @@ public void when_there_is_no_container_found_for_ports_an_i_s_e_is_thrown() thro compose.ports("db"); } + @Test + public void docker_compose_exec_passes_concatenated_arguments_to_executor() throws IOException, InterruptedException { + when(executedProcess.getInputStream()).thenReturn(toInputStream("docker-compose version 1.7.0rc1, build 1ad8866")); + compose.exec(options("-d"), "container_1", arguments("ls")); + verify(executor, times(1)).execute("exec", "-d", "container_1", "ls"); + } + + @Test + public void docker_compose_exec_fails_if_docker_compose_version_is_prior_1_7() throws IOException, InterruptedException { + when(executedProcess.getInputStream()).thenReturn(toInputStream("docker-compose version 1.5.6, build 1ad8866")); + exception.expect(IllegalStateException.class); + exception.expectMessage("You need at least docker-compose 1.7 to run docker-compose exec"); + compose.exec(options("-d"), "container_1", arguments("ls")); + } + } diff --git a/src/test/java/com/palantir/docker/compose/execution/DockerComposeVersionTest.java b/src/test/java/com/palantir/docker/compose/execution/DockerComposeVersionTest.java new file mode 100644 index 000000000..6828ad1a7 --- /dev/null +++ b/src/test/java/com/palantir/docker/compose/execution/DockerComposeVersionTest.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.execution; + +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.lessThan; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +import com.github.zafarkhaja.semver.Version; +import org.junit.Test; + + +public class DockerComposeVersionTest { + + @Test + public void compare_major_versions_first() throws Exception { + assertThat(Version.valueOf("2.1.0").compareTo(Version.valueOf("1.2.1")), greaterThan(0)); + } + + @Test + public void compare_minor_versions_when_major_versions_are_the_same() throws Exception { + assertThat(Version.valueOf("2.1.7").compareTo(Version.valueOf("2.3.2")), lessThan(0)); + } + + @Test + public void return_equals_for_the_same_version_strings() throws Exception { + assertEquals(Version.valueOf("2.1.2").compareTo(Version.valueOf("2.1.2")), 0); + } + + @Test + public void remove_non_digits_when_passing_version_string() { + assertEquals(Version.valueOf("1.7.0"), DockerComposeVersion.parseFromDockerComposeVersion("docker-compose version 1.7.0rc1, build 1ad8866")); + } +} diff --git a/src/test/java/com/palantir/docker/compose/execution/RetryingDockerComposeShould.java b/src/test/java/com/palantir/docker/compose/execution/RetryingDockerComposeShould.java index 99220344e..ca95b4e6c 100644 --- a/src/test/java/com/palantir/docker/compose/execution/RetryingDockerComposeShould.java +++ b/src/test/java/com/palantir/docker/compose/execution/RetryingDockerComposeShould.java @@ -15,10 +15,13 @@ */ package com.palantir.docker.compose.execution; +import static com.palantir.docker.compose.execution.DockerComposeExecArgument.arguments; +import static com.palantir.docker.compose.execution.DockerComposeExecOption.options; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; 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.verifyNoMoreInteractions; import static org.mockito.Mockito.when; @@ -33,6 +36,7 @@ public class RetryingDockerComposeShould { private final Retryer retryer = mock(Retryer.class); private final RetryingDockerCompose retryingDockerCompose = new RetryingDockerCompose(retryer, dockerCompose); private final ContainerNames someContainerNames = new ContainerNames("hey"); + private static final String CONTAINER_NAME = "container"; @Before public void before() throws IOException, InterruptedException { @@ -69,4 +73,15 @@ public void call_ps_on_the_underlying_docker_compose_and_returns_the_same_value( private void verifyRetryerWasUsed() throws IOException, InterruptedException { verify(retryer).runWithRetries(any(Retryer.RetryableDockerComposeOperation.class)); } + + private void verifyRetryerWasNotUsed() throws IOException, InterruptedException { + verify(retryer, times(0)).runWithRetries(any(Retryer.RetryableDockerComposeOperation.class)); + } + + @Test + public void calls_exec_on_the_underlying_docker_compose_and_not_invoke_retryer() throws IOException, InterruptedException { + retryingDockerCompose.exec(options("-d"), CONTAINER_NAME, arguments("ls")); + verifyRetryerWasNotUsed(); + verify(dockerCompose).exec(options("-d"), CONTAINER_NAME, arguments("ls")); + } } From 69ca023e4c8ec346850e8511aadf9bb39af7403d Mon Sep 17 00:00:00 2001 From: Dan Fox Date: Fri, 29 Apr 2016 14:29:22 +0100 Subject: [PATCH 11/12] Re-order to minimise diff --- .../compose/DockerCompositionBuilder.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java b/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java index fb5ce3d46..9466fc147 100644 --- a/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java +++ b/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java @@ -53,16 +53,6 @@ public DockerCompositionBuilder waitingForService(String serviceName, SingleServ return this; } - public DockerComposition build() { - DockerComposeRule rule = builder.build(); - return new DockerComposition(rule); - } - - public DockerCompositionBuilder dockerCompose(DockerCompose compose) { - builder.dockerCompose(compose); - return this; - } - public DockerCompositionBuilder files(DockerComposeFiles files) { builder.files(files); return this; @@ -73,19 +63,29 @@ public DockerCompositionBuilder machine(DockerMachine machine) { return this; } - public DockerCompositionBuilder retryAttempts(int retryAttempts) { - builder.retryAttempts(retryAttempts); + public DockerCompositionBuilder projectName(ProjectName name) { + builder.projectName(name); return this; } - public DockerCompositionBuilder projectName(ProjectName name) { - builder.projectName(name); + public DockerCompositionBuilder dockerCompose(DockerCompose compose) { + builder.dockerCompose(compose); + return this; + } + + public DockerCompositionBuilder saveLogsTo(String path) { + builder.saveLogsTo(path); return this; } - public DockerCompositionBuilder saveLogsTo(String absolutePath) { - builder.saveLogsTo(absolutePath); + public DockerCompositionBuilder retryAttempts(int retryAttempts) { + builder.retryAttempts(retryAttempts); return this; } + public DockerComposition build() { + DockerComposeRule rule = builder.build(); + return new DockerComposition(rule); + } + } From 4916c5700f4a392f47f781143321b987c8c0b83c Mon Sep 17 00:00:00 2001 From: Dan Fox Date: Fri, 29 Apr 2016 15:15:07 +0100 Subject: [PATCH 12/12] DockerCompositionTest -> DockerComposeRuleTest --- .../docker/compose/DockerComposeRule.java | 24 +++++++++------ .../compose/DockerCompositionBuilder.java | 4 +-- .../{ContainerAccessor.java => Cluster.java} | 2 +- .../compose/connection/ContainerCache.java | 2 +- .../connection/waiting/ClusterWait.java | 4 +-- .../connection/waiting/MultiServiceWait.java | 4 +-- .../connection/waiting/SingleServiceWait.java | 4 +-- .../compose/execution/DockerCompose.java | 4 +-- ...onTest.java => DockerComposeRuleTest.java} | 30 +++++++++---------- .../waiting/SingleServiceWaitTest.java | 4 +-- 10 files changed, 43 insertions(+), 39 deletions(-) rename src/main/java/com/palantir/docker/compose/connection/{ContainerAccessor.java => Cluster.java} (81%) rename src/test/java/com/palantir/docker/compose/{DockerCompositionTest.java => DockerComposeRuleTest.java} (85%) diff --git a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java index 67764c8c9..fdca2955d 100644 --- a/src/main/java/com/palantir/docker/compose/DockerComposeRule.java +++ b/src/main/java/com/palantir/docker/compose/DockerComposeRule.java @@ -5,7 +5,7 @@ import com.palantir.docker.compose.configuration.DockerComposeFiles; import com.palantir.docker.compose.configuration.ProjectName; -import com.palantir.docker.compose.connection.ContainerAccessor; +import com.palantir.docker.compose.connection.Cluster; import com.palantir.docker.compose.connection.ContainerCache; import com.palantir.docker.compose.connection.DockerMachine; import com.palantir.docker.compose.connection.waiting.ClusterWait; @@ -43,7 +43,7 @@ public abstract class DockerComposeRule extends ExternalResource { protected abstract List clusterWaits(); @Value.Default - protected DockerMachine machine() { + public DockerMachine machine() { return DockerMachine.localMachine().build(); } @@ -62,21 +62,21 @@ public DockerComposeExecutable executable() { } @Value.Default - protected int retryAttempts() { - return DEFAULT_RETRY_ATTEMPTS; - } - - @Value.Default - protected DockerCompose dockerCompose() { + public DockerCompose dockerCompose() { DockerCompose dockerCompose = new DefaultDockerCompose(executable(), machine()); return new RetryingDockerCompose(retryAttempts(), dockerCompose); } @Value.Default - public ContainerAccessor containers() { + public Cluster containers() { return new ContainerCache(dockerCompose()); } + @Value.Default + protected int retryAttempts() { + return DEFAULT_RETRY_ATTEMPTS; + } + @Value.Default protected LogCollector logCollector() { return new DoNothingLogCollector(); @@ -120,6 +120,12 @@ public static ImmutableDockerComposeRule.Builder builder() { public abstract static class Builder { + public abstract ImmutableDockerComposeRule.Builder files(DockerComposeFiles files); + + public ImmutableDockerComposeRule.Builder file(String dockerComposeYmlFile) { + return files(DockerComposeFiles.from(dockerComposeYmlFile)); + } + public abstract ImmutableDockerComposeRule.Builder logCollector(LogCollector logCollector); public ImmutableDockerComposeRule.Builder saveLogsTo(String path) { diff --git a/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java b/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java index 9466fc147..b62a024ba 100644 --- a/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java +++ b/src/main/java/com/palantir/docker/compose/DockerCompositionBuilder.java @@ -15,7 +15,6 @@ */ package com.palantir.docker.compose; -import com.google.common.collect.ImmutableList; import com.palantir.docker.compose.ImmutableDockerComposeRule.Builder; import com.palantir.docker.compose.configuration.DockerComposeFiles; import com.palantir.docker.compose.configuration.ProjectName; @@ -38,7 +37,7 @@ public DockerCompositionBuilder waitingForService(String serviceName, SingleServ return this; } - public DockerCompositionBuilder waitingForServices(ImmutableList services, MultiServiceHealthCheck check) { + public DockerCompositionBuilder waitingForServices(List services, MultiServiceHealthCheck check) { builder.waitingForServices(services, check); return this; } @@ -87,5 +86,4 @@ public DockerComposition build() { DockerComposeRule rule = builder.build(); return new DockerComposition(rule); } - } diff --git a/src/main/java/com/palantir/docker/compose/connection/ContainerAccessor.java b/src/main/java/com/palantir/docker/compose/connection/Cluster.java similarity index 81% rename from src/main/java/com/palantir/docker/compose/connection/ContainerAccessor.java rename to src/main/java/com/palantir/docker/compose/connection/Cluster.java index 9f6e3e236..9726db4d4 100644 --- a/src/main/java/com/palantir/docker/compose/connection/ContainerAccessor.java +++ b/src/main/java/com/palantir/docker/compose/connection/Cluster.java @@ -4,7 +4,7 @@ package com.palantir.docker.compose.connection; -public interface ContainerAccessor { +public interface Cluster { Container container(String name); diff --git a/src/main/java/com/palantir/docker/compose/connection/ContainerCache.java b/src/main/java/com/palantir/docker/compose/connection/ContainerCache.java index f86c8ed66..838f51012 100644 --- a/src/main/java/com/palantir/docker/compose/connection/ContainerCache.java +++ b/src/main/java/com/palantir/docker/compose/connection/ContainerCache.java @@ -19,7 +19,7 @@ import java.util.HashMap; import java.util.Map; -public class ContainerCache implements ContainerAccessor { +public class ContainerCache implements Cluster { private final Map containers = new HashMap<>(); private final DockerCompose dockerCompose; 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 928a7a245..835ccdd77 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 @@ -4,11 +4,11 @@ package com.palantir.docker.compose.connection.waiting; -import com.palantir.docker.compose.connection.ContainerAccessor; +import com.palantir.docker.compose.connection.Cluster; @FunctionalInterface public interface ClusterWait { - void waitUntilReady(ContainerAccessor containers); + void waitUntilReady(Cluster cluster); } diff --git a/src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceWait.java b/src/main/java/com/palantir/docker/compose/connection/waiting/MultiServiceWait.java index 541beb85d..3c45e7039 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 @@ -6,8 +6,8 @@ import static java.util.stream.Collectors.toList; +import com.palantir.docker.compose.connection.Cluster; import com.palantir.docker.compose.connection.Container; -import com.palantir.docker.compose.connection.ContainerAccessor; import java.util.List; import org.immutables.value.Value; import org.joda.time.Duration; @@ -29,7 +29,7 @@ public static MultiServiceWait of(List serviceNames, MultiServiceHealthC } @Override - public void waitUntilReady(ContainerAccessor containers) { + public void waitUntilReady(Cluster containers) { List containersToWaitFor = containerNames().stream() .map(containers::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 183b42430..7db6012c9 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 @@ -4,8 +4,8 @@ package com.palantir.docker.compose.connection.waiting; +import com.palantir.docker.compose.connection.Cluster; import com.palantir.docker.compose.connection.Container; -import com.palantir.docker.compose.connection.ContainerAccessor; import org.immutables.value.Value; import org.joda.time.Duration; @@ -26,7 +26,7 @@ public static SingleServiceWait of(String serviceName, SingleServiceHealthCheck } @Override - public void waitUntilReady(ContainerAccessor containers) { + public void waitUntilReady(Cluster containers) { Container container = containers.container(containerName()); ServiceWait serviceWait = new ServiceWait(container, healthCheck(), timeout()); serviceWait.waitTillServiceIsUp(); diff --git a/src/main/java/com/palantir/docker/compose/execution/DockerCompose.java b/src/main/java/com/palantir/docker/compose/execution/DockerCompose.java index 995ca9522..baaf1e1f1 100644 --- a/src/main/java/com/palantir/docker/compose/execution/DockerCompose.java +++ b/src/main/java/com/palantir/docker/compose/execution/DockerCompose.java @@ -15,14 +15,14 @@ */ package com.palantir.docker.compose.execution; +import com.palantir.docker.compose.connection.Cluster; import com.palantir.docker.compose.connection.Container; -import com.palantir.docker.compose.connection.ContainerAccessor; 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 ContainerAccessor { +public interface DockerCompose extends Cluster { void build() throws IOException, InterruptedException; void up() throws IOException, InterruptedException; void down() throws IOException, InterruptedException; diff --git a/src/test/java/com/palantir/docker/compose/DockerCompositionTest.java b/src/test/java/com/palantir/docker/compose/DockerComposeRuleTest.java similarity index 85% rename from src/test/java/com/palantir/docker/compose/DockerCompositionTest.java rename to src/test/java/com/palantir/docker/compose/DockerComposeRuleTest.java index cfeba8715..4fd6dafec 100644 --- a/src/test/java/com/palantir/docker/compose/DockerCompositionTest.java +++ b/src/test/java/com/palantir/docker/compose/DockerComposeRuleTest.java @@ -53,7 +53,7 @@ import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; -public class DockerCompositionTest { +public class DockerComposeRuleTest { private static final String IP = "127.0.0.1"; @@ -66,18 +66,18 @@ public class DockerCompositionTest { private final MockDockerEnvironment env = new MockDockerEnvironment(dockerCompose); private DockerComposeFiles mockFiles = mock(DockerComposeFiles.class); private DockerMachine machine = mock(DockerMachine.class); - private final DockerCompositionBuilder dockerComposition = DockerComposition.of(dockerCompose).files(mockFiles).machine(machine); + private final DockerComposeRule rule = DockerComposeRule.builder().dockerCompose(dockerCompose).files(mockFiles).machine(machine).build(); @Test public void docker_compose_build_and_up_is_called_before_tests_are_run() throws IOException, InterruptedException { - dockerComposition.build().before(); + rule.before(); verify(dockerCompose).build(); verify(dockerCompose).up(); } @Test public void docker_compose_kill_and_rm_are_called_after_tests_are_run() throws IOException, InterruptedException { - dockerComposition.build().after(); + rule.after(); verify(dockerCompose).kill(); verify(dockerCompose).rm(); } @@ -87,7 +87,7 @@ public void docker_compose_wait_for_service_passes_when_check_is_true() throws I AtomicInteger timesCheckCalled = new AtomicInteger(0); withComposeExecutableReturningContainerFor("db"); SingleServiceHealthCheck checkCalledOnce = (container) -> SuccessOrFailure.fromBoolean(timesCheckCalled.incrementAndGet() == 1, "not called once yet"); - dockerComposition.waitingForService("db", checkCalledOnce).build().before(); + DockerComposeRule.builder().from(rule).waitingForService("db", checkCalledOnce).build().before(); assertThat(timesCheckCalled.get(), is(1)); } @@ -100,7 +100,8 @@ public void docker_compose_wait_for_service_waits_multiple_services() throws IOE MultiServiceHealthCheck healthCheck = mock(MultiServiceHealthCheck.class); when(healthCheck.areServicesUp(containers)).thenReturn(SuccessOrFailure.success()); - dockerComposition.waitingForServices(ImmutableList.of("db1", "db2"), healthCheck).build().before(); + + DockerComposeRule.builder().from(rule).waitingForServices(ImmutableList.of("db1", "db2"), healthCheck).build().before(); verify(healthCheck).areServicesUp(containers); } @@ -110,7 +111,7 @@ public void docker_compose_wait_for_service_passes_when_check_is_true_after_bein AtomicInteger timesCheckCalled = new AtomicInteger(0); withComposeExecutableReturningContainerFor("db"); SingleServiceHealthCheck checkCalledTwice = (container) -> SuccessOrFailure.fromBoolean(timesCheckCalled.incrementAndGet() == 2, "not called twice yet"); - dockerComposition.waitingForService("db", checkCalledTwice).build().before(); + DockerComposeRule.builder().from(rule).waitingForService("db", checkCalledTwice).build().before(); assertThat(timesCheckCalled.get(), is(2)); } @@ -121,7 +122,7 @@ public void throws_if_a_wait_for_service_check_remains_false_till_the_timeout() exception.expect(IllegalStateException.class); exception.expectMessage("Container 'db' failed to pass startup check:\noops"); - dockerComposition.waitingForService("db", (container) -> SuccessOrFailure.failure("oops"), millis(200)).build().before(); + DockerComposeRule.builder().from(rule).waitingForService("db", (container) -> SuccessOrFailure.failure("oops"), millis(200)).build().before(); } @Test @@ -129,7 +130,7 @@ public void port_for_container_can_be_retrieved_by_external_mapping() throws IOE DockerPort expectedPort = env.port("db", IP, 5433, 5432); withComposeExecutableReturningContainerFor("db"); - DockerPort actualPort = dockerComposition.build().portOnContainerWithExternalMapping("db", 5433); + DockerPort actualPort = rule.containers().container("db").portMappedExternallyTo(5433); assertThat(actualPort, is(expectedPort)); } @@ -139,7 +140,7 @@ public void port_for_container_can_be_retrieved_by_internal_mapping() throws IOE DockerPort expectedPort = env.port("db", IP, 5433, 5432); withComposeExecutableReturningContainerFor("db"); - DockerPort actualPort = dockerComposition.build().portOnContainerWithInternalMapping("db", 5432); + DockerPort actualPort = rule.containers().container("db").portMappedInternallyTo(5432); assertThat(actualPort, is(expectedPort)); } @@ -148,10 +149,9 @@ public void port_for_container_can_be_retrieved_by_internal_mapping() throws IOE public void when_two_external_ports_on_a_container_are_requested_docker_compose_ps_is_only_executed_once() throws Exception { env.ports("db", IP, 5432, 8080); withComposeExecutableReturningContainerFor("db"); - DockerComposition composition = dockerComposition.build(); - composition.portOnContainerWithInternalMapping("db", 5432); - composition.portOnContainerWithInternalMapping("db", 8080); + rule.containers().container("db").portMappedInternallyTo(5432); + rule.containers().container("db").portMappedInternallyTo(8080); verify(dockerCompose, times(1)).ports("db"); } @@ -166,14 +166,14 @@ public void waiting_for_service_that_does_not_exist_results_in_an_illegal_state_ exception.expect(IllegalStateException.class); exception.expectMessage(nonExistentContainer); - dockerComposition.waitingForService(nonExistentContainer, toHaveAllPortsOpen()).build().before(); + DockerComposeRule.builder().from(rule).waitingForService(nonExistentContainer, toHaveAllPortsOpen()).build().before(); } @SuppressWarnings("unchecked") @Test public void logs_can_be_saved_to_a_directory_while_containers_are_running() throws IOException, InterruptedException { File logLocation = logFolder.newFolder(); - DockerComposition loggingComposition = dockerComposition.saveLogsTo(logLocation.getAbsolutePath()).build(); + DockerComposeRule loggingComposition = DockerComposeRule.builder().from(rule).saveLogsTo(logLocation.getAbsolutePath()).build(); when(dockerCompose.ps()).thenReturn(new ContainerNames("db")); CountDownLatch latch = new CountDownLatch(1); when(dockerCompose.writeLogs(eq("db"), any(OutputStream.class))).thenAnswer((args) -> { diff --git a/src/test/java/com/palantir/docker/compose/connection/waiting/SingleServiceWaitTest.java b/src/test/java/com/palantir/docker/compose/connection/waiting/SingleServiceWaitTest.java index 1ce5deba8..1bd4ab5b1 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 @@ -11,14 +11,14 @@ 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 com.palantir.docker.compose.connection.ContainerAccessor; import org.junit.Before; import org.junit.Test; public class SingleServiceWaitTest { - private ContainerAccessor containerAccessor = mock(ContainerAccessor.class); + private Cluster containerAccessor = mock(Cluster.class); private SingleServiceHealthCheck healthCheck = mock(SingleServiceHealthCheck.class); private Container someContainer = mock(Container.class); private SingleServiceWait wait = SingleServiceWait.of("somecontainer", healthCheck, DEFAULT_TIMEOUT);