From 0aeaec32714027ef4b0815780787243576e9c735 Mon Sep 17 00:00:00 2001 From: Sijie Guo Date: Fri, 18 May 2018 11:34:42 -0700 Subject: [PATCH] Allow testcontainer to wait for a specific port (#703) * Allow testcontainer to wait for a specific port *Motivation* A container might expose multiple ports for different purposes. for example, it can have a port for http endpoint, while having another port for grpc endpoint. So when doing a liveness check, it would be better to allow configuring checking a specific port, rather than checking the first exposed port. *Modification* Modify the `HttpWaitStrategy` to allow configuring the liveness port. * - change logging from info to trace - add an item in CHANGELOG.md * Update CHANGELOG.md --- CHANGELOG.md | 1 + .../wait/strategy/HttpWaitStrategy.java | 34 ++++++++++++++++--- .../strategy/AbstractWaitStrategyTest.java | 6 +++- .../wait/strategy/HttpWaitStrategyTest.java | 20 ++++++++++- 4 files changed, 54 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39f4105097d..65ff3441cb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. ### Fixed ### Changed +- Allow `HttpWaitStrategy` to wait for a specific port ([\#703](https://github.com/testcontainers/testcontainers-java/pull/703)) ## [1.7.3] - 2018-05-16 diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java index f74b51c602b..29f5a64b6df 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java @@ -2,6 +2,7 @@ import com.google.common.base.Strings; import com.google.common.io.BaseEncoding; +import java.util.Optional; import lombok.extern.slf4j.Slf4j; import org.rnorth.ducttape.TimeoutException; import org.testcontainers.containers.ContainerLaunchException; @@ -43,6 +44,7 @@ public class HttpWaitStrategy extends AbstractWaitStrategy { if (statusCodes.isEmpty() && HttpURLConnection.HTTP_OK == responseCode) return true; return statusCodes.contains(responseCode); }; + private Optional livenessPort = Optional.empty(); /** * Waits for the given status code. @@ -76,6 +78,17 @@ public HttpWaitStrategy forPath(String path) { return this; } + /** + * Wait for the given port. + * + * @param port the given port + * @return this + */ + public HttpWaitStrategy forPort(int port) { + this.livenessPort = Optional.of(port); + return this; + } + /** * Indicates that the status check should use HTTPS. * @@ -112,13 +125,19 @@ public HttpWaitStrategy forResponsePredicate(Predicate responsePredicate @Override protected void waitUntilReady() { final String containerName = waitStrategyTarget.getContainerInfo().getName(); - final Set livenessCheckPorts = getLivenessCheckPorts(); - if (livenessCheckPorts == null || livenessCheckPorts.isEmpty()) { - log.warn("{}: No exposed ports or mapped ports - cannot wait for status", containerName); + + final Integer livenessCheckPort = livenessPort.map(waitStrategyTarget::getMappedPort).orElseGet(() -> { + final Set livenessCheckPorts = getLivenessCheckPorts(); + if (livenessCheckPorts == null || livenessCheckPorts.isEmpty()) { + log.warn("{}: No exposed ports or mapped ports - cannot wait for status", containerName); + return -1; + } + return livenessCheckPorts.iterator().next(); + }); + + if (null == livenessCheckPort || -1 == livenessCheckPort) { return; } - - final Integer livenessCheckPort = livenessCheckPorts.iterator().next(); final String uri = buildLivenessUri(livenessCheckPort).toString(); log.info("{}: Waiting for {} seconds for URL: {}", containerName, startupTimeout.getSeconds(), uri); @@ -138,6 +157,8 @@ protected void waitUntilReady() { connection.setRequestMethod("GET"); connection.connect(); + log.trace("Get response code {}", connection.getResponseCode()); + if (!statusCodePredicate.test(connection.getResponseCode())) { throw new RuntimeException(String.format("HTTP response code was: %s", connection.getResponseCode())); @@ -145,6 +166,9 @@ protected void waitUntilReady() { if(responsePredicate != null) { String responseBody = getResponseBody(connection); + + log.trace("Get response {}", responseBody); + if(!responsePredicate.test(responseBody)) { throw new RuntimeException(String.format("Response: %s did not match predicate", responseBody)); diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java index 574cef5e2df..3d3c2f0606d 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java @@ -62,9 +62,13 @@ private GenericContainer startContainerWithCommand(String shellCommand) { * @return the (unstarted) container */ protected GenericContainer startContainerWithCommand(String shellCommand, WaitStrategy waitStrategy) { + return startContainerWithCommand(shellCommand, waitStrategy, 8080); + } + + protected GenericContainer startContainerWithCommand(String shellCommand, WaitStrategy waitStrategy, Integer... ports) { // apply WaitStrategy to container return new GenericContainer(IMAGE_NAME) - .withExposedPorts(8080) + .withExposedPorts(ports) .withCommand("sh", "-c", shellCommand) .waitingFor(waitStrategy.withStartupTimeout(Duration.ofMillis(WAIT_TIMEOUT_MILLIS))); } diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java index 56c58fa0b2d..65a693f514f 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java @@ -102,6 +102,20 @@ public void testWaitUntilReadyWithTimeoutAndBadResponseBody() { waitUntilReadyAndTimeout(createShellCommand("200 OK", "Bad Response")); } + + /** + * Expects the WaitStrategy probing the right port. + */ + @Test + public void testWaitUntilReadyWithSpecificPort() { + waitUntilReadyAndSucceed(startContainerWithCommand( + createShellCommand("200 OK", GOOD_RESPONSE_BODY, 9090), + createHttpWaitStrategy(ready) + .forPort(9090), + 7070, 8080, 9090 + )); + } + /** * @param ready the AtomicBoolean on which to indicate success * @return the WaitStrategy under test @@ -129,10 +143,14 @@ protected void waitUntilReady() { } private String createShellCommand(String header, String responseBody) { + return createShellCommand(header, responseBody, 8080); + } + + private String createShellCommand(String header, String responseBody, int port) { int length = responseBody.getBytes().length; return "while true; do { echo -e \"HTTP/1.1 "+header+NEWLINE+ "Content-Type: text/html"+NEWLINE+ "Content-Length: "+length +NEWLINE+ "\";" - +" echo \""+responseBody+"\";} | nc -lp 8080; done"; + +" echo \""+responseBody+"\";} | nc -lp " + port + "; done"; } }