diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 86a98586c7f..14124d88c28 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -103,6 +103,8 @@ public class DockerComposeContainer> private final Map waitStrategyMap = new ConcurrentHashMap<>(); + private Duration startupTimeout = Duration.ofMinutes(30); + private final SocatContainer ambassadorContainer = new SocatContainer(); private final Map>> logConsumers = new ConcurrentHashMap<>(); @@ -433,14 +435,14 @@ private String getServiceInstanceName(String serviceName) { /* * can have multiple wait strategies for a single container, e.g. if waiting on several ports * if no wait strategy is defined, the WaitAllStrategy will return immediately. - * The WaitAllStrategy uses an long timeout, because timeouts should be handled by the inner strategies. + * The WaitAllStrategy uses the startup timeout for everything as a global maximum, but we expect timeouts to be handled by the inner strategies. */ private void addWaitStrategy(String serviceInstanceName, @NonNull WaitStrategy waitStrategy) { final WaitAllStrategy waitAllStrategy = waitStrategyMap.computeIfAbsent( serviceInstanceName, __ -> { return new WaitAllStrategy(WaitAllStrategy.Mode.WITH_MAXIMUM_OUTER_TIMEOUT) - .withStartupTimeout(Duration.ofMinutes(30)); + .withStartupTimeout(startupTimeout); } ); waitAllStrategy.withStrategy(waitStrategy); @@ -598,6 +600,16 @@ public SELF withRemoveImages(RemoveImages removeImages) { return self(); } + /** + * Set the maximum startup timeout all the waits set are bounded to. + * + * @return this instance. for chaining + */ + public SELF withStartupTimeout(Duration startupTimeout) { + this.startupTimeout = startupTimeout; + return self(); + } + public Optional getContainerByServiceName(String serviceName) { return Optional.ofNullable(serviceInstanceMap.get(serviceName)); } diff --git a/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java b/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java index 8f056de98fa..305de496cc9 100644 --- a/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java +++ b/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java @@ -1,12 +1,15 @@ package org.testcontainers.containers; import org.junit.Test; +import org.testcontainers.containers.wait.strategy.Wait; import java.io.File; +import java.time.Duration; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; +import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; public class DockerComposeContainerWithServicesTest { @@ -87,6 +90,21 @@ public void testScaleInComposeFileIsRespected() { } } + @Test + public void testStartupTimeoutSetsTheHighestTimeout() { + assertThrows("We expect a timeout from the startup timeout", org.rnorth.ducttape.TimeoutException.class, + () -> { + try ( + DockerComposeContainer compose = new DockerComposeContainer<>(SIMPLE_COMPOSE_FILE) + .withServices("redis") + .withStartupTimeout(Duration.ofMillis(1)) + .withExposedService("redis", 80, Wait.forListeningPort().withStartupTimeout(Duration.ofMinutes(1))); + ) { + compose.start(); + } + }); + } + private void verifyStartedContainers(final DockerComposeContainer compose, final String... names) { final List containerNames = compose .listChildContainers()