diff --git a/modules/testcontainers/src/main/java/org/microshed/testing/testcontainers/ApplicationContainer.java b/modules/testcontainers/src/main/java/org/microshed/testing/testcontainers/ApplicationContainer.java index e8e496ab..842a06a1 100644 --- a/modules/testcontainers/src/main/java/org/microshed/testing/testcontainers/ApplicationContainer.java +++ b/modules/testcontainers/src/main/java/org/microshed/testing/testcontainers/ApplicationContainer.java @@ -47,6 +47,7 @@ import org.microshed.testing.ManuallyStartedConfiguration; import org.microshed.testing.testcontainers.config.HollowTestcontainersConfiguration; import org.microshed.testing.testcontainers.config.TestcontainersConfiguration; +import org.microshed.testing.testcontainers.internal.HollowContainerInspection; import org.microshed.testing.testcontainers.internal.ImageFromDockerfile; import org.microshed.testing.testcontainers.spi.ServerAdapter; import org.slf4j.Logger; @@ -54,10 +55,12 @@ import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.containers.wait.strategy.WaitStrategy; import org.testcontainers.utility.Base58; +import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.command.InspectImageResponse; import com.github.dockerjava.api.model.ExposedPort; @@ -223,10 +226,12 @@ private void commonInit() { withAppContextRoot("/"); if (isHollow) { setContainerIpAddress(ManuallyStartedConfiguration.getHostname()); - if (ManuallyStartedConfiguration.getHttpsPort() != -1) - setFirstMappedPort(ManuallyStartedConfiguration.getHttpsPort()); - else - setFirstMappedPort(ManuallyStartedConfiguration.getHttpPort()); + List ports = new ArrayList<>(2); + ports.add(ManuallyStartedConfiguration.getHttpsPort()); + ports.add(ManuallyStartedConfiguration.getHttpPort()); + ports.removeIf(p -> p == -1); + setExposedPorts(ports); + setFirstMappedPort(ports.get(0)); withAppContextRoot(ManuallyStartedConfiguration.getBasePath()); } } @@ -267,6 +272,8 @@ protected void doStart() { Map env = getEnvMap(); if (env.size() > 0) getServerAdapter().setConfigProperties(env); + configure(); + waitUntilContainerStarted(); lateBind_started = true; return; } @@ -317,6 +324,15 @@ public Integer getMappedPort(int originalPort) { } } + @Override + public List getExposedPorts() { + if (isHollow) { + return Collections.singletonList(lateBind_port); + } else { + return super.getExposedPorts(); + } + } + /** * @param appContextRoot the application context root. The protocol, hostname, and port do not need to be * included in the appContextRoot parameter. For example, an application @@ -357,8 +373,13 @@ public ApplicationContainer withReadinessPath(String readinessUrl) { public ApplicationContainer withReadinessPath(String readinessUrl, int timeoutSeconds) { Objects.requireNonNull(readinessUrl); readinessUrl = buildPath(readinessUrl); - waitingFor(Wait.forHttp(readinessUrl) - .withStartupTimeout(Duration.ofSeconds(timeoutSeconds))); + HttpWaitStrategy strat = Wait.forHttp(readinessUrl); + strat.withStartupTimeout(Duration.ofSeconds(timeoutSeconds)); + getExposedPorts() + .stream() + .findFirst() + .ifPresent(p -> strat.forPort(p)); + waitingFor(strat); return this; } @@ -464,6 +485,22 @@ public String getBaseURL() { return "http://" + getContainerIpAddress() + ':' + getFirstMappedPort(); } + @Override + public InspectContainerResponse getContainerInfo() { + if (isHollow) + return new HollowContainerInspection(this); + else + return super.getContainerInfo(); + } + + @Override + public String getDockerImageName() { + if (isHollow) + return "HollowApplicationContainer"; + else + return super.getDockerImageName(); + } + /** * @return The {@link ServerAdapter} that is currently applied for this instance */ diff --git a/modules/testcontainers/src/main/java/org/microshed/testing/testcontainers/config/HollowTestcontainersConfiguration.java b/modules/testcontainers/src/main/java/org/microshed/testing/testcontainers/config/HollowTestcontainersConfiguration.java index 4cc9b304..72685bad 100644 --- a/modules/testcontainers/src/main/java/org/microshed/testing/testcontainers/config/HollowTestcontainersConfiguration.java +++ b/modules/testcontainers/src/main/java/org/microshed/testing/testcontainers/config/HollowTestcontainersConfiguration.java @@ -81,7 +81,7 @@ public void applyConfiguration(Class testClass) { Map fixedExposedPorts = new HashMap<>(); for (GenericContainer c : allContainers()) { for (Integer p : c.getExposedPorts()) { - if (fixedExposedPorts.containsKey(p)) { + if (fixedExposedPorts.containsKey(p) && !(c instanceof ApplicationContainer)) { throw new ExtensionConfigurationException("Cannot expose port " + p + " for " + c.getDockerImageName() + " because another container (" + fixedExposedPorts.get(p) + ") is already using it."); diff --git a/modules/testcontainers/src/main/java/org/microshed/testing/testcontainers/internal/HollowContainerInspection.java b/modules/testcontainers/src/main/java/org/microshed/testing/testcontainers/internal/HollowContainerInspection.java new file mode 100644 index 00000000..9d89a251 --- /dev/null +++ b/modules/testcontainers/src/main/java/org/microshed/testing/testcontainers/internal/HollowContainerInspection.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2020 IBM Corporation and others + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * 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 org.microshed.testing.testcontainers.internal; + +import org.microshed.testing.testcontainers.ApplicationContainer; + +import com.github.dockerjava.api.command.InspectContainerResponse; + +public class HollowContainerInspection extends InspectContainerResponse { + + private final ApplicationContainer app; + + public HollowContainerInspection(ApplicationContainer app) { + this.app = app; + } + + @Override + public String getName() { + return app.getDockerImageName(); + } + +} diff --git a/modules/testcontainers/src/test/java/org/microshed/testing/testcontainers/config/HollowTestcontainersConfigurationTest.java b/modules/testcontainers/src/test/java/org/microshed/testing/testcontainers/config/HollowTestcontainersConfigurationTest.java index 27d69806..43ad5c69 100644 --- a/modules/testcontainers/src/test/java/org/microshed/testing/testcontainers/config/HollowTestcontainersConfigurationTest.java +++ b/modules/testcontainers/src/test/java/org/microshed/testing/testcontainers/config/HollowTestcontainersConfigurationTest.java @@ -22,6 +22,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.nio.file.Paths; +import java.time.Duration; import java.util.Map; import org.junit.jupiter.api.Test; @@ -29,11 +30,15 @@ import org.microshed.testing.jupiter.MicroShedTest; import org.microshed.testing.testcontainers.ApplicationContainer; import org.testcontainers.containers.MockServerContainer; +import org.testcontainers.containers.wait.strategy.WaitStrategy; +import org.testcontainers.containers.wait.strategy.WaitStrategyTarget; import org.testcontainers.junit.jupiter.Container; @MicroShedTest public class HollowTestcontainersConfigurationTest { + private static boolean waitedForStartup = false; + // This cointainer never actually gets started, since we are running in hollow mode @Container public static ApplicationContainer app = new ApplicationContainer(Paths.get("src", "test", "resources", "Dockerfile")) @@ -45,7 +50,18 @@ public class HollowTestcontainersConfigurationTest { .withEnv("SVC_URL4", "http://mockserver:1080/hello/world") .withEnv("SVC_URL5", "http://mockserver:1080/hello/mockserver") .withEnv("SVC_URL6", oldValue -> "http://mockserver:1080") - .withMpRestClient("com.foo.ExampleClass", "http://mockserver:1080"); + .withMpRestClient("com.foo.ExampleClass", "http://mockserver:1080") + .waitingFor(new WaitStrategy() { + @Override + public WaitStrategy withStartupTimeout(Duration startupTimeout) { + return this; + } + + @Override + public void waitUntilReady(WaitStrategyTarget waitStrategyTarget) { + waitedForStartup = true; + } + }); @Container public static MockServerContainer mockServer = new MockServerContainer() @@ -91,4 +107,9 @@ public void testApplicationURL() { assertEquals("http://localhost:9080/", ApplicationEnvironment.Resolver.load().getApplicationURL()); } + @Test + public void testWaitFor() { + assertTrue(waitedForStartup, "The ApplicationContainer did not wait for startup in hollow mode"); + } + } \ No newline at end of file diff --git a/modules/testcontainers/src/test/java/org/microshed/testing/testcontainers/config/HollowTestcontainersConfigurationTest2.java b/modules/testcontainers/src/test/java/org/microshed/testing/testcontainers/config/HollowTestcontainersConfigurationTest2.java index 7b482a1b..9ef556ed 100644 --- a/modules/testcontainers/src/test/java/org/microshed/testing/testcontainers/config/HollowTestcontainersConfigurationTest2.java +++ b/modules/testcontainers/src/test/java/org/microshed/testing/testcontainers/config/HollowTestcontainersConfigurationTest2.java @@ -22,11 +22,14 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.nio.file.Paths; +import java.time.Duration; import org.junit.jupiter.api.Test; import org.microshed.testing.ApplicationEnvironment; import org.microshed.testing.jupiter.MicroShedTest; import org.microshed.testing.testcontainers.ApplicationContainer; +import org.testcontainers.containers.wait.strategy.WaitStrategy; +import org.testcontainers.containers.wait.strategy.WaitStrategyTarget; import org.testcontainers.junit.jupiter.Container; @MicroShedTest @@ -35,8 +38,17 @@ public class HollowTestcontainersConfigurationTest2 { // This cointainer never actually gets started, since we are running in hollow mode @Container public static ApplicationContainer app = new ApplicationContainer(Paths.get("src", "test", "resources", "Dockerfile")) - .withExposedPorts(9443) - .withAppContextRoot("/myservice"); + .withAppContextRoot("/myservice") + .waitingFor(new WaitStrategy() { + @Override + public WaitStrategy withStartupTimeout(Duration startupTimeout) { + return this; + } + + @Override + public void waitUntilReady(WaitStrategyTarget waitStrategyTarget) { + } + }); @Test public void testCorrectEnvironment() {