Created
July 26, 2016 12:28
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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