Skip to content

Commit

Permalink
Allow testcontainer to wait for a specific port (#703)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
sijie authored and bsideup committed May 18, 2018
1 parent b44c558 commit 0aeaec3
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -43,6 +44,7 @@ public class HttpWaitStrategy extends AbstractWaitStrategy {
if (statusCodes.isEmpty() && HttpURLConnection.HTTP_OK == responseCode) return true;
return statusCodes.contains(responseCode);
};
private Optional<Integer> livenessPort = Optional.empty();

/**
* Waits for the given status code.
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -112,13 +125,19 @@ public HttpWaitStrategy forResponsePredicate(Predicate<String> responsePredicate
@Override
protected void waitUntilReady() {
final String containerName = waitStrategyTarget.getContainerInfo().getName();
final Set<Integer> 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<Integer> 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);

Expand All @@ -138,13 +157,18 @@ 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()));
}

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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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";
}
}

0 comments on commit 0aeaec3

Please sign in to comment.