Skip to content

Instantly share code, notes, and snippets.

@ldoguin
Created July 26, 2016 12:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ldoguin/5573d45f6431ad464eb1698c229c6e4c to your computer and use it in GitHub Desktop.
Save ldoguin/5573d45f6431ad464eb1698c229c6e4c to your computer and use it in GitHub Desktop.
Custom wait strategy, waits for first node to be healthy. For the TestContainers project.
package org.testcontainers.containers;
import com.couchbase.client.deps.com.fasterxml.jackson.databind.JsonNode;
import com.couchbase.client.deps.com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Strings;
import com.google.common.io.BaseEncoding;
import org.rnorth.ducttape.TimeoutException;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import static org.rnorth.ducttape.unreliables.Unreliables.retryUntilSuccess;
/**
* Created by ldoguin on 19/07/16.
*/
public class CouchbaseWaitStrategy extends GenericContainer.AbstractWaitStrategy {
/**
* Authorization HTTP header.
*/
private static final String HEADER_AUTHORIZATION = "Authorization";
/**
* Basic Authorization scheme prefix.
*/
private static final String AUTH_BASIC = "Basic ";
private String path = "/pools/default";
private int statusCode = HttpURLConnection.HTTP_OK;
private boolean tlsEnabled;
private String username;
private String password;
private ObjectMapper om = new ObjectMapper();
/**
* Indicates that the status check should use HTTPS.
*
* @return this
*/
public CouchbaseWaitStrategy usingTls() {
this.tlsEnabled = true;
return this;
}
/**
* Authenticate with HTTP Basic Authorization credentials.
*
* @param username the username
* @param password the password
* @return this
*/
public CouchbaseWaitStrategy withBasicCredentials(String username, String password) {
this.username = username;
this.password = password;
return this;
}
@Override
protected void waitUntilReady() {
final Integer livenessCheckPort = getLivenessCheckPort();
if (null == livenessCheckPort) {
logger().warn("No exposed ports or mapped ports - cannot wait for status");
return;
}
final String uri = buildLivenessUri(livenessCheckPort).toString();
logger().info("Waiting for {} seconds for URL: {}", startupTimeout.getSeconds(), uri);
// try to connect to the URL
try {
retryUntilSuccess((int) startupTimeout.getSeconds(), TimeUnit.SECONDS, () -> {
getRateLimiter().doWhenReady(() -> {
try {
final HttpURLConnection connection = (HttpURLConnection) new URL(uri).openConnection();
// authenticate
if (!Strings.isNullOrEmpty(username)) {
connection.setRequestProperty(HEADER_AUTHORIZATION, buildAuthString(username, password));
connection.setUseCaches(false);
}
connection.setRequestMethod("GET");
connection.connect();
if (statusCode != connection.getResponseCode()) {
throw new RuntimeException(String.format("HTTP response code was: %s",
connection.getResponseCode()));
}
// Specific Couchbase wait strategy to be sure the node is online and healthy
JsonNode node = om.readTree(connection.getInputStream());
JsonNode statusNode = node.at("/nodes/0/status");
String status = statusNode.asText();
if (!"healthy".equals(status)){
throw new RuntimeException(String.format("Couchbase Node status was: %s", status));
}
} catch (IOException e) {
throw new RuntimeException(e);
}
});
return true;
});
} catch (TimeoutException e) {
throw new ContainerLaunchException(String.format(
"Timed out waiting for URL to be accessible (%s should return HTTP %s)", uri, statusCode));
}
}
/**
* Build the URI on which to check if the container is ready.
*
* @param livenessCheckPort the liveness port
* @return the liveness URI
*/
private URI buildLivenessUri(int livenessCheckPort) {
final String scheme = (tlsEnabled ? "https" : "http") + "://";
final String host = container.getContainerIpAddress();
final String portSuffix;
if ((tlsEnabled && 443 == livenessCheckPort) || (!tlsEnabled && 80 == livenessCheckPort)) {
portSuffix = "";
} else {
portSuffix = ":" + String.valueOf(livenessCheckPort);
}
return URI.create(scheme + host + portSuffix + path);
}
/**
* @param username the username
* @param password the password
* @return a basic authentication string for the given credentials
*/
private String buildAuthString(String username, String password) {
return AUTH_BASIC + BaseEncoding.base64().encode((username + ":" + password).getBytes());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment