From 8c9102d03f2337196af216ba4fb08db6dc466873 Mon Sep 17 00:00:00 2001 From: ctayeb Date: Mon, 19 Jun 2017 01:50:01 +0200 Subject: [PATCH 01/18] First implementation --- .../couchbase/AbstractCouchbaseTest.java | 56 ++++ .../couchbase/CouchbaseContainer.java | 286 ++++++++++++++++++ .../CouchbaseQueryServiceWaitStrategy.java | 53 ++++ .../couchbase/CouchbaseWaitStrategy.java | 160 ++++++++++ .../couchbase/CouchbaseContainerTest.java | 41 +++ test/resources/logback-test.xml | 29 ++ 6 files changed, 625 insertions(+) create mode 100644 main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java create mode 100644 main/java/org/testcontainers/couchbase/CouchbaseContainer.java create mode 100644 main/java/org/testcontainers/couchbase/CouchbaseQueryServiceWaitStrategy.java create mode 100644 main/java/org/testcontainers/couchbase/CouchbaseWaitStrategy.java create mode 100644 test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java create mode 100644 test/resources/logback-test.xml diff --git a/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java b/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java new file mode 100644 index 00000000000..b8b998aadea --- /dev/null +++ b/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java @@ -0,0 +1,56 @@ +package org.testcontainers.couchbase; + +import com.couchbase.client.java.Bucket; +import com.couchbase.client.java.CouchbaseCluster; +import com.couchbase.client.java.bucket.BucketType; +import com.couchbase.client.java.cluster.DefaultBucketSettings; +import com.couchbase.client.java.query.N1qlParams; +import com.couchbase.client.java.query.N1qlQuery; +import com.couchbase.client.java.query.consistency.ScanConsistency; +import lombok.Getter; +import org.junit.After; + +/** + * @author ctayeb + */ +public abstract class AbstractCouchbaseTest { + + public static final String DEFAULT_BUCKET = "default"; + + @Getter(lazy = true) + private final static CouchbaseContainer couchbaseContainer = initCouchbaseContainer(); + + @Getter(lazy = true) + private final static Bucket bucket = openBucket(DEFAULT_BUCKET); + + @After + public void clear() { + if (getCouchbaseContainer().isIndex() && getCouchbaseContainer().isQuery() && getCouchbaseContainer().isPrimaryIndex()) { + getBucket().query( + N1qlQuery.simple(String.format("DELETE FROM `%s`", getBucket().name()), + N1qlParams.build().consistency(ScanConsistency.STATEMENT_PLUS))); + } else { + getBucket().bucketManager().flush(); + } + } + + private static CouchbaseContainer initCouchbaseContainer() { + CouchbaseContainer couchbaseContainer = new CouchbaseContainer() + .withNewBucket(DefaultBucketSettings.builder() + .enableFlush(true) + .name(DEFAULT_BUCKET) + .quota(100) + .replicas(0) + .type(BucketType.COUCHBASE) + .build()); + couchbaseContainer.start(); + return couchbaseContainer; + } + + private static Bucket openBucket(String bucketName) { + CouchbaseCluster cluster = getCouchbaseContainer().getCouchbaseCluster(); + Bucket bucket = cluster.openBucket(bucketName); + Runtime.getRuntime().addShutdownHook(new Thread(bucket::close)); + return bucket; + } +} diff --git a/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/main/java/org/testcontainers/couchbase/CouchbaseContainer.java new file mode 100644 index 00000000000..01eb77decac --- /dev/null +++ b/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2016 Couchbase, Inc. + * + * 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.testcontainers.couchbase; + +import com.couchbase.client.core.utils.Base64; +import com.couchbase.client.java.Bucket; +import com.couchbase.client.java.CouchbaseCluster; +import com.couchbase.client.java.cluster.BucketSettings; +import com.couchbase.client.java.env.CouchbaseEnvironment; +import com.couchbase.client.java.env.DefaultCouchbaseEnvironment; +import com.couchbase.client.java.query.Index; +import lombok.Getter; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.HttpWaitStrategy; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.List; + +/** + * Based on Laurent Doguin version + * + * Optimized by ctayeb + */ +public class CouchbaseContainer> extends GenericContainer { + + private String memoryQuota = "300"; + + private String indexMemoryQuota = "300"; + + private String clusterUsername = "Administrator"; + + private String clusterPassword = "password"; + + private boolean keyValue = true; + + @Getter + private boolean query = true; + + @Getter + private boolean index = true; + + @Getter + private boolean primaryIndex = false; + + @Getter + private boolean fts = false; + + private boolean beerSample = false; + + private boolean travelSample = false; + + private boolean gamesIMSample = false; + + private CouchbaseEnvironment couchbaseEnvironment; + + private CouchbaseCluster couchbaseCluster; + + private List newBuckets = new ArrayList<>(); + + private String urlBase; + + public CouchbaseContainer() { + super("couchbase/server:latest"); + } + + public CouchbaseContainer(String containerName) { + super(containerName); + } + + @Override + protected Integer getLivenessCheckPort() { + return getMappedPort(8091); + } + + @Override + protected void configure() { + // Configurable ports + addExposedPorts(11210, 11207, 8091, 18091); + + // Non configurable ports + addFixedExposedPort(8092, 8092); + addFixedExposedPort(8093, 8093); + addFixedExposedPort(8094, 8094); + addFixedExposedPort(8095, 8095); + addFixedExposedPort(18092, 18092); + addFixedExposedPort(18093, 18093); + setWaitStrategy(new HttpWaitStrategy().forPath("/ui/index.html#/")); + } + + public CouchbaseEnvironment getCouchbaseEnvironment() { + if (couchbaseEnvironment == null) { + initCluster(); + couchbaseEnvironment = DefaultCouchbaseEnvironment.builder() + .bootstrapCarrierDirectPort(getMappedPort(11210)) + .bootstrapCarrierSslPort(getMappedPort(11207)) + .bootstrapHttpDirectPort(getMappedPort(8091)) + .bootstrapHttpSslPort(getMappedPort(18091)) + .build(); + } + return couchbaseEnvironment; + } + + public CouchbaseCluster getCouchbaseCluster() { + if (couchbaseCluster == null) { + couchbaseCluster = CouchbaseCluster.create(getCouchbaseEnvironment(), getContainerIpAddress()); + } + return couchbaseCluster; + } + + public SELF withClusterUsername(String username) { + this.clusterUsername = username; + return self(); + } + + public SELF withClusterPassword(String password) { + this.clusterPassword = password; + return self(); + } + + public SELF withMemoryQuota(String memoryQuota) { + this.memoryQuota = memoryQuota; + return self(); + } + + public SELF withIndexMemoryQuota(String indexMemoryQuota) { + this.indexMemoryQuota = indexMemoryQuota; + return self(); + } + + public SELF withKeyValue(boolean withKV) { + this.keyValue = withKV; + return self(); + } + + public SELF withIndex(boolean withIndex) { + this.index = withIndex; + return self(); + } + + public SELF withPrimaryIndex(boolean primaryIndex) { + this.primaryIndex = primaryIndex; + return self(); + } + + public SELF withQuery(boolean withQuery) { + this.query = withQuery; + return self(); + } + + public SELF withFTS(boolean withFTS) { + this.fts = withFTS; + return self(); + } + + public SELF withTravelSample() { + this.travelSample = true; + return self(); + } + + public SELF withBeerSample() { + this.beerSample = true; + return self(); + } + + public SELF withGamesIMSample() { + this.gamesIMSample = true; + return self(); + } + + public SELF withNewBucket(BucketSettings bucketSettings) { + newBuckets.add(bucketSettings); + return self(); + } + + + public void initCluster() { + urlBase = String.format("http://%s:%s", getContainerIpAddress(), getMappedPort(8091)); + try { + String poolURL = "/pools/default"; + String poolPayload = "memoryQuota=" + URLEncoder.encode(memoryQuota, "UTF-8") + "&indexMemoryQuota=" + URLEncoder.encode(indexMemoryQuota, "UTF-8"); + + String setupServicesURL = "/node/controller/setupServices"; + StringBuilder servicePayloadBuilder = new StringBuilder(); + if (keyValue) { + servicePayloadBuilder.append("kv,"); + } + if (query) { + servicePayloadBuilder.append("n1ql,"); + } + if (index) { + servicePayloadBuilder.append("index,"); + } + if (fts) { + servicePayloadBuilder.append("fts,"); + } + String setupServiceContent = "services=" + URLEncoder.encode(servicePayloadBuilder.toString(), "UTF-8"); + + String webSettingsURL = "/settings/web"; + String webSettingsContent = "username=" + URLEncoder.encode(clusterUsername, "UTF-8") + "&password=" + URLEncoder.encode(clusterPassword, "UTF-8") + "&port=8091"; + + String bucketURL = "/sampleBuckets/install"; + + StringBuilder sampleBucketPayloadBuilder = new StringBuilder(); + sampleBucketPayloadBuilder.append('['); + if (travelSample) { + sampleBucketPayloadBuilder.append("\"travel-sample\","); + } + if (beerSample) { + sampleBucketPayloadBuilder.append("\"beer-sample\","); + } + if (gamesIMSample) { + sampleBucketPayloadBuilder.append("\"gamesim-sample\","); + } + sampleBucketPayloadBuilder.append(']'); + + callCouchbaseRestAPI(poolURL, poolPayload); + callCouchbaseRestAPI(setupServicesURL, setupServiceContent); + callCouchbaseRestAPI(webSettingsURL, webSettingsContent); + callCouchbaseRestAPI(bucketURL, sampleBucketPayloadBuilder.toString()); + + CouchbaseWaitStrategy s = new CouchbaseWaitStrategy(); + s.withBasicCredentials(clusterUsername, clusterPassword); + s.waitUntilReady(this); + callCouchbaseRestAPI("/settings/indexes", "indexerThreads=0&logLevel=info&maxRollbackPoints=5&storageMode=memory_optimized"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public void createBucket(BucketSettings bucketSetting, boolean primaryIndex) { + BucketSettings bucketSettings = getCouchbaseCluster().clusterManager(clusterUsername, clusterPassword).insertBucket(bucketSetting); + if (index) { + Bucket bucket = getCouchbaseCluster().openBucket(bucketSettings.name()); + new CouchbaseQueryServiceWaitStrategy(bucket).waitUntilReady(this); + if (primaryIndex) { + bucket.query(Index.createPrimaryIndex().on(bucketSetting.name())); + } + } + } + + public void callCouchbaseRestAPI(String url, String payload) throws IOException { + String fullUrl = urlBase + url; + HttpURLConnection httpConnection = (HttpURLConnection) ((new URL(fullUrl).openConnection())); + httpConnection.setDoOutput(true); + httpConnection.setRequestMethod("POST"); + httpConnection.setRequestProperty("Content-Type", + "application/x-www-form-urlencoded"); + String encoded = Base64.encode((clusterUsername + ":" + clusterPassword).getBytes("UTF-8")); + httpConnection.setRequestProperty("Authorization", "Basic " + encoded); + DataOutputStream out = new DataOutputStream(httpConnection.getOutputStream()); + out.writeBytes(payload); + out.flush(); + out.close(); + httpConnection.getResponseCode(); + httpConnection.disconnect(); + } + + @Override + public void start() { + super.start(); + if (!newBuckets.isEmpty()) { + for (BucketSettings bucketSetting : newBuckets) { + createBucket(bucketSetting, primaryIndex); + } + } + } + +} diff --git a/main/java/org/testcontainers/couchbase/CouchbaseQueryServiceWaitStrategy.java b/main/java/org/testcontainers/couchbase/CouchbaseQueryServiceWaitStrategy.java new file mode 100644 index 00000000000..154813abcfd --- /dev/null +++ b/main/java/org/testcontainers/couchbase/CouchbaseQueryServiceWaitStrategy.java @@ -0,0 +1,53 @@ +package org.testcontainers.couchbase; + +import com.couchbase.client.core.message.cluster.GetClusterConfigRequest; +import com.couchbase.client.core.message.cluster.GetClusterConfigResponse; +import com.couchbase.client.core.service.ServiceType; +import com.couchbase.client.java.Bucket; +import org.rnorth.ducttape.TimeoutException; +import org.testcontainers.containers.ContainerLaunchException; +import org.testcontainers.containers.GenericContainer; + +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +import static org.rnorth.ducttape.unreliables.Unreliables.retryUntilSuccess; + +/** + * @author ctayeb + * Created on 06/06/2017 + */ +public class CouchbaseQueryServiceWaitStrategy extends GenericContainer.AbstractWaitStrategy { + + private final Bucket bucket; + + public CouchbaseQueryServiceWaitStrategy(Bucket bucket) { + this.bucket = bucket; + startupTimeout = Duration.ofSeconds(120); + } + + @Override + protected void waitUntilReady() { + logger().info("Waiting for {} seconds for QUERY service", startupTimeout.getSeconds()); + + // try to connect to the URL + try { + retryUntilSuccess((int) startupTimeout.getSeconds(), TimeUnit.SECONDS, () -> { + getRateLimiter().doWhenReady(() -> { + GetClusterConfigResponse clusterConfig = bucket.core() + .send(new GetClusterConfigRequest()) + .toBlocking().single(); + boolean queryServiceEnabled = clusterConfig.config() + .bucketConfig(bucket.name()) + .serviceEnabled(ServiceType.QUERY); + if (!queryServiceEnabled) { + throw new RuntimeException("Query service not ready yet"); + } + }); + return true; + }); + } catch (TimeoutException e) { + throw new ContainerLaunchException("Timed out waiting for QUERY service"); + } + } +} diff --git a/main/java/org/testcontainers/couchbase/CouchbaseWaitStrategy.java b/main/java/org/testcontainers/couchbase/CouchbaseWaitStrategy.java new file mode 100644 index 00000000000..61ea386861f --- /dev/null +++ b/main/java/org/testcontainers/couchbase/CouchbaseWaitStrategy.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2016 Couchbase, Inc. + * + * 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.testcontainers.couchbase; + +import com.couchbase.client.deps.com.fasterxml.jackson.databind.JsonNode; +import com.couchbase.client.deps.com.fasterxml.jackson.databind.ObjectMapper; +import org.rnorth.ducttape.TimeoutException; +import org.testcontainers.containers.ContainerLaunchException; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.shaded.com.google.common.base.Strings; +import org.testcontainers.shaded.com.google.common.io.BaseEncoding; + +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; + +/** + * @author ldoguin + * created on 18/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()); + } +} \ No newline at end of file diff --git a/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java b/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java new file mode 100644 index 00000000000..7daaf070acc --- /dev/null +++ b/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java @@ -0,0 +1,41 @@ +package org.testcontainers.couchbase; + +import com.couchbase.client.java.document.RawJsonDocument; +import com.couchbase.client.java.query.N1qlQuery; +import com.couchbase.client.java.query.N1qlQueryResult; +import com.couchbase.client.java.query.N1qlQueryRow; +import org.junit.Assert; +import org.junit.Test; + +import java.util.List; + +/** + * @author ctayeb + * created on 18/06/2017 + */ +public class CouchbaseContainerTest extends AbstractCouchbaseTest { + + private static final String ID = "toto"; + + private static final String DOCUMENT = "{\"name\":\"toto\"}"; + + @Test + public void should_insert_document() { + RawJsonDocument expected = RawJsonDocument.create(ID, DOCUMENT); + getBucket().upsert(expected); + RawJsonDocument result = getBucket().get(ID, RawJsonDocument.class); + Assert.assertEquals(expected.content(), result.content()); + } + + @Test + public void should_execute_n1ql() { + getBucket().query(N1qlQuery.simple("INSERT INTO " + DEFAULT_BUCKET + " (KEY, VALUE) VALUES ('" + ID + "', " + DOCUMENT + ")")); + + N1qlQueryResult query = getBucket().query(N1qlQuery.simple("SELECT * FROM " + DEFAULT_BUCKET + " USE KEYS '" + ID + "'")); + Assert.assertTrue(query.parseSuccess()); + Assert.assertTrue(query.finalSuccess()); + List n1qlQueryRows = query.allRows(); + Assert.assertEquals(1, n1qlQueryRows.size()); + Assert.assertEquals(DOCUMENT, n1qlQueryRows.get(0).value().get(DEFAULT_BUCKET).toString()); + } +} diff --git a/test/resources/logback-test.xml b/test/resources/logback-test.xml new file mode 100644 index 00000000000..fe273feb881 --- /dev/null +++ b/test/resources/logback-test.xml @@ -0,0 +1,29 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + + + + + + + + + + + PROFILER + DENY + + \ No newline at end of file From 3f84f46f5b2d7dc2a4b582b08186441a6c3d6cb1 Mon Sep 17 00:00:00 2001 From: ctayeb Date: Sat, 1 Jul 2017 17:19:15 +0200 Subject: [PATCH 02/18] Activate primary index by default --- main/java/org/testcontainers/couchbase/CouchbaseContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index 01eb77decac..6b314c26c7d 100644 --- a/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -58,7 +58,7 @@ public class CouchbaseContainer> extends G private boolean index = true; @Getter - private boolean primaryIndex = false; + private boolean primaryIndex = true; @Getter private boolean fts = false; From 7328c958d25b4dedbb1e41ac888148a004a8000c Mon Sep 17 00:00:00 2001 From: ctayeb Date: Fri, 19 Jan 2018 18:35:36 +0100 Subject: [PATCH 03/18] Add compatibility with Couchbase v5 --- .../couchbase/AbstractCouchbaseTest.java | 9 ++++++--- .../couchbase/CouchbaseContainer.java | 14 +++++++++++--- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java b/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java index b8b998aadea..3fad8fef2f8 100644 --- a/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java +++ b/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java @@ -17,11 +17,13 @@ public abstract class AbstractCouchbaseTest { public static final String DEFAULT_BUCKET = "default"; + public static final String DEFAULT_PASSWORD = "password"; + @Getter(lazy = true) private final static CouchbaseContainer couchbaseContainer = initCouchbaseContainer(); @Getter(lazy = true) - private final static Bucket bucket = openBucket(DEFAULT_BUCKET); + private final static Bucket bucket = openBucket(DEFAULT_BUCKET, DEFAULT_PASSWORD); @After public void clear() { @@ -39,6 +41,7 @@ private static CouchbaseContainer initCouchbaseContainer() { .withNewBucket(DefaultBucketSettings.builder() .enableFlush(true) .name(DEFAULT_BUCKET) + .password(DEFAULT_PASSWORD) .quota(100) .replicas(0) .type(BucketType.COUCHBASE) @@ -47,9 +50,9 @@ private static CouchbaseContainer initCouchbaseContainer() { return couchbaseContainer; } - private static Bucket openBucket(String bucketName) { + private static Bucket openBucket(String bucketName, String password) { CouchbaseCluster cluster = getCouchbaseContainer().getCouchbaseCluster(); - Bucket bucket = cluster.openBucket(bucketName); + Bucket bucket = cluster.openBucket(bucketName, password); Runtime.getRuntime().addShutdownHook(new Thread(bucket::close)); return bucket; } diff --git a/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index 6b314c26c7d..5a44e081bc7 100644 --- a/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -18,7 +18,7 @@ import com.couchbase.client.core.utils.Base64; import com.couchbase.client.java.Bucket; import com.couchbase.client.java.CouchbaseCluster; -import com.couchbase.client.java.cluster.BucketSettings; +import com.couchbase.client.java.cluster.*; import com.couchbase.client.java.env.CouchbaseEnvironment; import com.couchbase.client.java.env.DefaultCouchbaseEnvironment; import com.couchbase.client.java.query.Index; @@ -32,6 +32,7 @@ import java.net.URL; import java.net.URLEncoder; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -246,9 +247,16 @@ public void initCluster() { } public void createBucket(BucketSettings bucketSetting, boolean primaryIndex) { - BucketSettings bucketSettings = getCouchbaseCluster().clusterManager(clusterUsername, clusterPassword).insertBucket(bucketSetting); + ClusterManager clusterManager = getCouchbaseCluster().clusterManager(clusterUsername, clusterPassword); + // Insert Bucket + BucketSettings bucketSettings = clusterManager.insertBucket(bucketSetting); + // Insert Bucket admin user + UserSettings userSettings = UserSettings.build() + .password(bucketSetting.password()) + .roles(Collections.singletonList(new UserRole("bucket_admin", bucketSetting.name()))); + clusterManager.upsertUser(AuthDomain.LOCAL, bucketSetting.name(), userSettings); if (index) { - Bucket bucket = getCouchbaseCluster().openBucket(bucketSettings.name()); + Bucket bucket = getCouchbaseCluster().openBucket(bucketSettings.name(), bucketSettings.password()); new CouchbaseQueryServiceWaitStrategy(bucket).waitUntilReady(this); if (primaryIndex) { bucket.query(Index.createPrimaryIndex().on(bucketSetting.name())); From 16f9d4207c1b36684f1f5c51562c23aa4b63b7e7 Mon Sep 17 00:00:00 2001 From: ctayeb Date: Tue, 23 Jan 2018 20:00:16 +0100 Subject: [PATCH 04/18] Fix backward compatibility with older version --- .../testcontainers/couchbase/AbstractCouchbaseTest.java | 6 +++--- .../org/testcontainers/couchbase/CouchbaseContainer.java | 8 ++++++-- .../testcontainers/couchbase/CouchbaseContainerTest.java | 6 +++--- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java b/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java index 3fad8fef2f8..4262017770b 100644 --- a/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java +++ b/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java @@ -15,7 +15,7 @@ */ public abstract class AbstractCouchbaseTest { - public static final String DEFAULT_BUCKET = "default"; + public static final String TEST_BUCKET = "test"; public static final String DEFAULT_PASSWORD = "password"; @@ -23,7 +23,7 @@ public abstract class AbstractCouchbaseTest { private final static CouchbaseContainer couchbaseContainer = initCouchbaseContainer(); @Getter(lazy = true) - private final static Bucket bucket = openBucket(DEFAULT_BUCKET, DEFAULT_PASSWORD); + private final static Bucket bucket = openBucket(TEST_BUCKET, DEFAULT_PASSWORD); @After public void clear() { @@ -40,7 +40,7 @@ private static CouchbaseContainer initCouchbaseContainer() { CouchbaseContainer couchbaseContainer = new CouchbaseContainer() .withNewBucket(DefaultBucketSettings.builder() .enableFlush(true) - .name(DEFAULT_BUCKET) + .name(TEST_BUCKET) .password(DEFAULT_PASSWORD) .quota(100) .replicas(0) diff --git a/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index 5a44e081bc7..62932c5ff6e 100644 --- a/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -37,7 +37,7 @@ /** * Based on Laurent Doguin version - * + *

* Optimized by ctayeb */ public class CouchbaseContainer> extends GenericContainer { @@ -254,7 +254,11 @@ public void createBucket(BucketSettings bucketSetting, boolean primaryIndex) { UserSettings userSettings = UserSettings.build() .password(bucketSetting.password()) .roles(Collections.singletonList(new UserRole("bucket_admin", bucketSetting.name()))); - clusterManager.upsertUser(AuthDomain.LOCAL, bucketSetting.name(), userSettings); + try { + clusterManager.upsertUser(AuthDomain.LOCAL, bucketSetting.name(), userSettings); + } catch (Exception e) { + logger().warn("Unable to insert user '" + bucketSetting.name() + "', maybe you are using older version"); + } if (index) { Bucket bucket = getCouchbaseCluster().openBucket(bucketSettings.name(), bucketSettings.password()); new CouchbaseQueryServiceWaitStrategy(bucket).waitUntilReady(this); diff --git a/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java b/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java index 7daaf070acc..be2d4378277 100644 --- a/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java +++ b/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java @@ -29,13 +29,13 @@ public void should_insert_document() { @Test public void should_execute_n1ql() { - getBucket().query(N1qlQuery.simple("INSERT INTO " + DEFAULT_BUCKET + " (KEY, VALUE) VALUES ('" + ID + "', " + DOCUMENT + ")")); + getBucket().query(N1qlQuery.simple("INSERT INTO " + TEST_BUCKET + " (KEY, VALUE) VALUES ('" + ID + "', " + DOCUMENT + ")")); - N1qlQueryResult query = getBucket().query(N1qlQuery.simple("SELECT * FROM " + DEFAULT_BUCKET + " USE KEYS '" + ID + "'")); + N1qlQueryResult query = getBucket().query(N1qlQuery.simple("SELECT * FROM " + TEST_BUCKET + " USE KEYS '" + ID + "'")); Assert.assertTrue(query.parseSuccess()); Assert.assertTrue(query.finalSuccess()); List n1qlQueryRows = query.allRows(); Assert.assertEquals(1, n1qlQueryRows.size()); - Assert.assertEquals(DOCUMENT, n1qlQueryRows.get(0).value().get(DEFAULT_BUCKET).toString()); + Assert.assertEquals(DOCUMENT, n1qlQueryRows.get(0).value().get(TEST_BUCKET).toString()); } } From 39e3167a5b0f6af93ac61f17b7669f91119cce46 Mon Sep 17 00:00:00 2001 From: ctayeb Date: Mon, 7 May 2018 22:02:06 +0200 Subject: [PATCH 05/18] Use more lombok annotations to reduce boilerplate code --- .../couchbase/CouchbaseContainer.java | 115 +++++------------- 1 file changed, 32 insertions(+), 83 deletions(-) diff --git a/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index 62932c5ff6e..c8d499fdb28 100644 --- a/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -22,7 +22,9 @@ import com.couchbase.client.java.env.CouchbaseEnvironment; import com.couchbase.client.java.env.DefaultCouchbaseEnvironment; import com.couchbase.client.java.query.Index; +import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.experimental.Wither; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.HttpWaitStrategy; @@ -40,39 +42,54 @@ *

* Optimized by ctayeb */ +@AllArgsConstructor public class CouchbaseContainer> extends GenericContainer { + @Wither private String memoryQuota = "300"; + @Wither private String indexMemoryQuota = "300"; + @Wither private String clusterUsername = "Administrator"; + @Wither private String clusterPassword = "password"; + @Wither private boolean keyValue = true; @Getter + @Wither private boolean query = true; @Getter + @Wither private boolean index = true; @Getter + @Wither private boolean primaryIndex = true; @Getter + @Wither private boolean fts = false; + @Wither private boolean beerSample = false; + @Wither private boolean travelSample = false; + @Wither private boolean gamesIMSample = false; - private CouchbaseEnvironment couchbaseEnvironment; + @Getter(lazy = true) + private final CouchbaseEnvironment couchbaseEnvironment = createCouchbaseEnvironment(); - private CouchbaseCluster couchbaseCluster; + @Getter(lazy = true) + private final CouchbaseCluster couchbaseCluster = createCouchbaseCluster(); private List newBuckets = new ArrayList<>(); @@ -106,92 +123,11 @@ protected void configure() { setWaitStrategy(new HttpWaitStrategy().forPath("/ui/index.html#/")); } - public CouchbaseEnvironment getCouchbaseEnvironment() { - if (couchbaseEnvironment == null) { - initCluster(); - couchbaseEnvironment = DefaultCouchbaseEnvironment.builder() - .bootstrapCarrierDirectPort(getMappedPort(11210)) - .bootstrapCarrierSslPort(getMappedPort(11207)) - .bootstrapHttpDirectPort(getMappedPort(8091)) - .bootstrapHttpSslPort(getMappedPort(18091)) - .build(); - } - return couchbaseEnvironment; - } - - public CouchbaseCluster getCouchbaseCluster() { - if (couchbaseCluster == null) { - couchbaseCluster = CouchbaseCluster.create(getCouchbaseEnvironment(), getContainerIpAddress()); - } - return couchbaseCluster; - } - - public SELF withClusterUsername(String username) { - this.clusterUsername = username; - return self(); - } - - public SELF withClusterPassword(String password) { - this.clusterPassword = password; - return self(); - } - - public SELF withMemoryQuota(String memoryQuota) { - this.memoryQuota = memoryQuota; - return self(); - } - - public SELF withIndexMemoryQuota(String indexMemoryQuota) { - this.indexMemoryQuota = indexMemoryQuota; - return self(); - } - - public SELF withKeyValue(boolean withKV) { - this.keyValue = withKV; - return self(); - } - - public SELF withIndex(boolean withIndex) { - this.index = withIndex; - return self(); - } - - public SELF withPrimaryIndex(boolean primaryIndex) { - this.primaryIndex = primaryIndex; - return self(); - } - - public SELF withQuery(boolean withQuery) { - this.query = withQuery; - return self(); - } - - public SELF withFTS(boolean withFTS) { - this.fts = withFTS; - return self(); - } - - public SELF withTravelSample() { - this.travelSample = true; - return self(); - } - - public SELF withBeerSample() { - this.beerSample = true; - return self(); - } - - public SELF withGamesIMSample() { - this.gamesIMSample = true; - return self(); - } - public SELF withNewBucket(BucketSettings bucketSettings) { newBuckets.add(bucketSettings); return self(); } - public void initCluster() { urlBase = String.format("http://%s:%s", getContainerIpAddress(), getMappedPort(8091)); try { @@ -295,4 +231,17 @@ public void start() { } } + private CouchbaseCluster createCouchbaseCluster() { + return CouchbaseCluster.create(getCouchbaseEnvironment(), getContainerIpAddress()); + } + + private DefaultCouchbaseEnvironment createCouchbaseEnvironment() { + initCluster(); + return DefaultCouchbaseEnvironment.builder() + .bootstrapCarrierDirectPort(getMappedPort(11210)) + .bootstrapCarrierSslPort(getMappedPort(11207)) + .bootstrapHttpDirectPort(getMappedPort(8091)) + .bootstrapHttpSslPort(getMappedPort(18091)) + .build(); + } } From 6add3293b3374f853db2d9770b2779d30fe5e0ed Mon Sep 17 00:00:00 2001 From: ctayeb Date: Mon, 7 May 2018 22:44:43 +0200 Subject: [PATCH 06/18] Move src to modules/couchbase --- .../java/org/testcontainers/couchbase/AbstractCouchbaseTest.java | 0 .../java/org/testcontainers/couchbase/CouchbaseContainer.java | 0 .../couchbase/CouchbaseQueryServiceWaitStrategy.java | 0 .../java/org/testcontainers/couchbase/CouchbaseWaitStrategy.java | 0 .../java/org/testcontainers/couchbase/CouchbaseContainerTest.java | 0 {test => modules/couchbase/src/test}/resources/logback-test.xml | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename {main => modules/couchbase/src/main}/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java (100%) rename {main => modules/couchbase/src/main}/java/org/testcontainers/couchbase/CouchbaseContainer.java (100%) rename {main => modules/couchbase/src/main}/java/org/testcontainers/couchbase/CouchbaseQueryServiceWaitStrategy.java (100%) rename {main => modules/couchbase/src/main}/java/org/testcontainers/couchbase/CouchbaseWaitStrategy.java (100%) rename {test => modules/couchbase/src/test}/java/org/testcontainers/couchbase/CouchbaseContainerTest.java (100%) rename {test => modules/couchbase/src/test}/resources/logback-test.xml (100%) diff --git a/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java similarity index 100% rename from main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java rename to modules/couchbase/src/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java diff --git a/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java similarity index 100% rename from main/java/org/testcontainers/couchbase/CouchbaseContainer.java rename to modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java diff --git a/main/java/org/testcontainers/couchbase/CouchbaseQueryServiceWaitStrategy.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseQueryServiceWaitStrategy.java similarity index 100% rename from main/java/org/testcontainers/couchbase/CouchbaseQueryServiceWaitStrategy.java rename to modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseQueryServiceWaitStrategy.java diff --git a/main/java/org/testcontainers/couchbase/CouchbaseWaitStrategy.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseWaitStrategy.java similarity index 100% rename from main/java/org/testcontainers/couchbase/CouchbaseWaitStrategy.java rename to modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseWaitStrategy.java diff --git a/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java similarity index 100% rename from test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java rename to modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java diff --git a/test/resources/logback-test.xml b/modules/couchbase/src/test/resources/logback-test.xml similarity index 100% rename from test/resources/logback-test.xml rename to modules/couchbase/src/test/resources/logback-test.xml From 5d015536f166abacdbdd56841c0e37bdd2ba0080 Mon Sep 17 00:00:00 2001 From: ctayeb Date: Sun, 13 May 2018 19:20:32 +0200 Subject: [PATCH 07/18] Add build with gradle --- modules/couchbase/build.gradle | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 modules/couchbase/build.gradle diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle new file mode 100644 index 00000000000..218adce2d5a --- /dev/null +++ b/modules/couchbase/build.gradle @@ -0,0 +1,6 @@ +description = "Testcontainers :: Couchbase" + +dependencies { + compile project(':testcontainers') + compile 'com.couchbase.client:java-client:2.5.7' +} From 8934841ac181ad0c661b173690b90856b9160900 Mon Sep 17 00:00:00 2001 From: ctayeb Date: Sun, 13 May 2018 19:21:24 +0200 Subject: [PATCH 08/18] Replace shaded libraries with real ones --- .../org/testcontainers/couchbase/CouchbaseWaitStrategy.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseWaitStrategy.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseWaitStrategy.java index 61ea386861f..e9bc1336f64 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseWaitStrategy.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseWaitStrategy.java @@ -17,11 +17,11 @@ 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 org.testcontainers.containers.ContainerLaunchException; import org.testcontainers.containers.GenericContainer; -import org.testcontainers.shaded.com.google.common.base.Strings; -import org.testcontainers.shaded.com.google.common.io.BaseEncoding; import java.io.IOException; import java.net.HttpURLConnection; @@ -157,4 +157,4 @@ private URI buildLivenessUri(int livenessCheckPort) { private String buildAuthString(String username, String password) { return AUTH_BASIC + BaseEncoding.base64().encode((username + ":" + password).getBytes()); } -} \ No newline at end of file +} From bc45a31e754623f9f513eb20ef1f8080684a8aa6 Mon Sep 17 00:00:00 2001 From: ctayeb Date: Sun, 13 May 2018 19:27:59 +0200 Subject: [PATCH 09/18] Fix waiting for Couchbase start --- .../org/testcontainers/couchbase/CouchbaseContainer.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index c8d499fdb28..5abeb61725e 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -25,6 +25,7 @@ import lombok.AllArgsConstructor; import lombok.Getter; import lombok.experimental.Wither; +import org.apache.commons.compress.utils.Sets; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.HttpWaitStrategy; @@ -36,6 +37,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Set; /** * Based on Laurent Doguin version @@ -108,6 +110,11 @@ protected Integer getLivenessCheckPort() { return getMappedPort(8091); } + @Override + public Set getLivenessCheckPortNumbers() { + return Sets.newHashSet(getLivenessCheckPort()); + } + @Override protected void configure() { // Configurable ports From 96f71c8c26fa538166a46e9ce4f0e059f3a5a4bd Mon Sep 17 00:00:00 2001 From: ctayeb Date: Sun, 13 May 2018 19:28:20 +0200 Subject: [PATCH 10/18] Add AUTHORS and README --- modules/couchbase/AUTHORS | 1 + modules/couchbase/README.md | 59 +++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 modules/couchbase/AUTHORS create mode 100644 modules/couchbase/README.md diff --git a/modules/couchbase/AUTHORS b/modules/couchbase/AUTHORS new file mode 100644 index 00000000000..db292de72f1 --- /dev/null +++ b/modules/couchbase/AUTHORS @@ -0,0 +1 @@ +Tayeb Chlyah diff --git a/modules/couchbase/README.md b/modules/couchbase/README.md new file mode 100644 index 00000000000..0106f416662 --- /dev/null +++ b/modules/couchbase/README.md @@ -0,0 +1,59 @@ + + +# TestContainers Couchbase Module +Testcontainers module for Couchbase. [Couchbase](https://www.couchbase.com/) is a Document oriented NoSQL database. + +## Usage example + +Running Couchbase as a stand-in in a test: + +### Create you own bucket + +```java +public class SomeTest { + + @Rule + public CouchbaseContainer couchbase = new CouchbaseContainer() + .withNewBucket(DefaultBucketSettings.builder() + .enableFlush(true) + .name('bucket-name') + .quota(100) + .type(BucketType.COUCHBASE) + .build()); + + @Test + public void someTestMethod() { + Bucket bucket = couchbase.getCouchbaseCluster().openBucket('bucket-name') + + ... interact with client as if using Couchbase normally +``` + +### Use preconfigured default bucket + +Bucket is cleared after each test + +```java +public class SomeTest extends AbstractCouchbaseTest { + + @Test + public void someTestMethod() { + Bucket bucket = getBucket(); + + ... interact with client as if using Couchbase normally +``` + +### Special consideration + +Couchbase testContainer is configured to use random available [ports](https://developer.couchbase.com/documentation/server/current/install/install-ports.html) for some ports only, as [Couchbase Java SDK](https://developer.couchbase.com/documentation/server/current/sdk/java/start-using-sdk.html) permit to configure only some ports : +- **8091** : REST/HTTP traffic ([bootstrapHttpDirectPort](http://docs.couchbase.com/sdk-api/couchbase-java-client-2.4.6/com/couchbase/client/java/env/DefaultCouchbaseEnvironment.Builder.html#bootstrapCarrierDirectPort-int-)) +- **18091** : REST/HTTP traffic with SSL ([bootstrapHttpSslPort](http://docs.couchbase.com/sdk-api/couchbase-java-client-2.4.6/com/couchbase/client/java/env/DefaultCouchbaseEnvironment.Builder.html#bootstrapCarrierSslPort-int-)) +- **11210** : memcached ([bootstrapCarrierDirectPort](http://docs.couchbase.com/sdk-api/couchbase-java-client-2.4.6/com/couchbase/client/java/env/DefaultCouchbaseEnvironment.Builder.html#bootstrapCarrierDirectPort-int-)) +- **11207** : memcached SSL ([bootstrapCarrierSslPort](http://docs.couchbase.com/sdk-api/couchbase-java-client-2.4.6/com/couchbase/client/java/env/DefaultCouchbaseEnvironment.Builder.html#bootstrapCarrierSslPort-int-)) + +All other ports cannot be changed by Java SDK, there are sadly fixed : +- **8092** : Queries, views, XDCR +- **8093** : REST/HTTP Query service +- **8094** : REST/HTTP Search Service +- **8095** : REST/HTTP Analytic service + +So if you disable Query, Search and Analytic service, you can run multiple instance of this container, otherwise, you're stuck with one instance, for now. From ffc24881d1e2a19b37f052028155af7090159158 Mon Sep 17 00:00:00 2001 From: ctayeb Date: Sun, 13 May 2018 22:08:02 +0200 Subject: [PATCH 11/18] Fix Couchbase version --- .../org/testcontainers/couchbase/CouchbaseContainer.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index 5abeb61725e..034fdd8cd32 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -47,6 +47,8 @@ @AllArgsConstructor public class CouchbaseContainer> extends GenericContainer { + public static final String VERSION = "5.1.0"; + @Wither private String memoryQuota = "300"; @@ -98,7 +100,7 @@ public class CouchbaseContainer> extends G private String urlBase; public CouchbaseContainer() { - super("couchbase/server:latest"); + super("couchbase/server:" + VERSION); } public CouchbaseContainer(String containerName) { @@ -223,9 +225,7 @@ public void callCouchbaseRestAPI(String url, String payload) throws IOException DataOutputStream out = new DataOutputStream(httpConnection.getOutputStream()); out.writeBytes(payload); out.flush(); - out.close(); httpConnection.getResponseCode(); - httpConnection.disconnect(); } @Override From 50eeb6f384b649ead9decf955c24f6ea4cad09ad Mon Sep 17 00:00:00 2001 From: ctayeb Date: Sun, 13 May 2018 22:10:20 +0200 Subject: [PATCH 12/18] Add Cleanup lombok annotation to auto close resources --- .../org/testcontainers/couchbase/CouchbaseContainer.java | 9 ++++++--- .../testcontainers/couchbase/CouchbaseWaitStrategy.java | 2 ++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index 034fdd8cd32..b87e20aaf40 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -23,6 +23,7 @@ import com.couchbase.client.java.env.DefaultCouchbaseEnvironment; import com.couchbase.client.java.query.Index; import lombok.AllArgsConstructor; +import lombok.Cleanup; import lombok.Getter; import lombok.experimental.Wither; import org.apache.commons.compress.utils.Sets; @@ -40,9 +41,9 @@ import java.util.Set; /** - * Based on Laurent Doguin version - *

- * Optimized by ctayeb + * Based on Laurent Doguin version, + * + * optimized by Tayeb Chlyah */ @AllArgsConstructor public class CouchbaseContainer> extends GenericContainer { @@ -215,6 +216,7 @@ public void createBucket(BucketSettings bucketSetting, boolean primaryIndex) { public void callCouchbaseRestAPI(String url, String payload) throws IOException { String fullUrl = urlBase + url; + @Cleanup("disconnect") HttpURLConnection httpConnection = (HttpURLConnection) ((new URL(fullUrl).openConnection())); httpConnection.setDoOutput(true); httpConnection.setRequestMethod("POST"); @@ -222,6 +224,7 @@ public void callCouchbaseRestAPI(String url, String payload) throws IOException "application/x-www-form-urlencoded"); String encoded = Base64.encode((clusterUsername + ":" + clusterPassword).getBytes("UTF-8")); httpConnection.setRequestProperty("Authorization", "Basic " + encoded); + @Cleanup DataOutputStream out = new DataOutputStream(httpConnection.getOutputStream()); out.writeBytes(payload); out.flush(); diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseWaitStrategy.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseWaitStrategy.java index e9bc1336f64..b9b620f3e14 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseWaitStrategy.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseWaitStrategy.java @@ -19,6 +19,7 @@ import com.couchbase.client.deps.com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Strings; import com.google.common.io.BaseEncoding; +import lombok.Cleanup; import org.rnorth.ducttape.TimeoutException; import org.testcontainers.containers.ContainerLaunchException; import org.testcontainers.containers.GenericContainer; @@ -92,6 +93,7 @@ protected void waitUntilReady() { retryUntilSuccess((int) startupTimeout.getSeconds(), TimeUnit.SECONDS, () -> { getRateLimiter().doWhenReady(() -> { try { + @Cleanup("disconnect") final HttpURLConnection connection = (HttpURLConnection) new URL(uri).openConnection(); // authenticate From 00232fe3ca6c22a3fe938255b47be6c40edd1a89 Mon Sep 17 00:00:00 2001 From: ctayeb Date: Sun, 13 May 2018 22:10:48 +0200 Subject: [PATCH 13/18] Update README --- modules/couchbase/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/README.md b/modules/couchbase/README.md index 0106f416662..7a18a235eb4 100644 --- a/modules/couchbase/README.md +++ b/modules/couchbase/README.md @@ -44,7 +44,7 @@ public class SomeTest extends AbstractCouchbaseTest { ### Special consideration -Couchbase testContainer is configured to use random available [ports](https://developer.couchbase.com/documentation/server/current/install/install-ports.html) for some ports only, as [Couchbase Java SDK](https://developer.couchbase.com/documentation/server/current/sdk/java/start-using-sdk.html) permit to configure only some ports : +Couchbase container is configured to use random available [ports](https://developer.couchbase.com/documentation/server/current/install/install-ports.html) for some ports only, as [Couchbase Java SDK](https://developer.couchbase.com/documentation/server/current/sdk/java/start-using-sdk.html) permit to configure only some ports : - **8091** : REST/HTTP traffic ([bootstrapHttpDirectPort](http://docs.couchbase.com/sdk-api/couchbase-java-client-2.4.6/com/couchbase/client/java/env/DefaultCouchbaseEnvironment.Builder.html#bootstrapCarrierDirectPort-int-)) - **18091** : REST/HTTP traffic with SSL ([bootstrapHttpSslPort](http://docs.couchbase.com/sdk-api/couchbase-java-client-2.4.6/com/couchbase/client/java/env/DefaultCouchbaseEnvironment.Builder.html#bootstrapCarrierSslPort-int-)) - **11210** : memcached ([bootstrapCarrierDirectPort](http://docs.couchbase.com/sdk-api/couchbase-java-client-2.4.6/com/couchbase/client/java/env/DefaultCouchbaseEnvironment.Builder.html#bootstrapCarrierDirectPort-int-)) From a1ae86b185b0b3d925967cfcc62c22c338b4dcc0 Mon Sep 17 00:00:00 2001 From: ctayeb Date: Mon, 14 May 2018 18:34:45 +0200 Subject: [PATCH 14/18] Use new AbstractWaitStrategy implementation --- .../CouchbaseQueryServiceWaitStrategy.java | 7 +++++-- .../couchbase/CouchbaseWaitStrategy.java | 19 +++++++++++-------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseQueryServiceWaitStrategy.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseQueryServiceWaitStrategy.java index 154813abcfd..e27c1b2e771 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseQueryServiceWaitStrategy.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseQueryServiceWaitStrategy.java @@ -4,9 +4,11 @@ import com.couchbase.client.core.message.cluster.GetClusterConfigResponse; import com.couchbase.client.core.service.ServiceType; import com.couchbase.client.java.Bucket; +import lombok.extern.slf4j.Slf4j; import org.rnorth.ducttape.TimeoutException; import org.testcontainers.containers.ContainerLaunchException; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy; import java.time.Duration; import java.util.concurrent.TimeUnit; @@ -17,7 +19,8 @@ * @author ctayeb * Created on 06/06/2017 */ -public class CouchbaseQueryServiceWaitStrategy extends GenericContainer.AbstractWaitStrategy { +@Slf4j +public class CouchbaseQueryServiceWaitStrategy extends AbstractWaitStrategy { private final Bucket bucket; @@ -28,7 +31,7 @@ public CouchbaseQueryServiceWaitStrategy(Bucket bucket) { @Override protected void waitUntilReady() { - logger().info("Waiting for {} seconds for QUERY service", startupTimeout.getSeconds()); + log.info("Waiting for {} seconds for QUERY service", startupTimeout.getSeconds()); // try to connect to the URL try { diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseWaitStrategy.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseWaitStrategy.java index b9b620f3e14..5c760e1e0b1 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseWaitStrategy.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseWaitStrategy.java @@ -20,14 +20,16 @@ import com.google.common.base.Strings; import com.google.common.io.BaseEncoding; import lombok.Cleanup; +import lombok.extern.slf4j.Slf4j; import org.rnorth.ducttape.TimeoutException; import org.testcontainers.containers.ContainerLaunchException; -import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URI; import java.net.URL; +import java.util.Optional; import java.util.concurrent.TimeUnit; import static org.rnorth.ducttape.unreliables.Unreliables.retryUntilSuccess; @@ -36,7 +38,8 @@ * @author ldoguin * created on 18/07/16. */ -public class CouchbaseWaitStrategy extends GenericContainer.AbstractWaitStrategy { +@Slf4j +public class CouchbaseWaitStrategy extends AbstractWaitStrategy { /** * Authorization HTTP header. */ @@ -79,14 +82,14 @@ public CouchbaseWaitStrategy withBasicCredentials(String username, String passwo @Override protected void waitUntilReady() { - final Integer livenessCheckPort = getLivenessCheckPort(); - if (null == livenessCheckPort) { - logger().warn("No exposed ports or mapped ports - cannot wait for status"); + Optional livenessCheckPort = waitStrategyTarget.getLivenessCheckPortNumbers().stream().findFirst(); + if (!livenessCheckPort.isPresent()) { + log.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); + final String uri = livenessCheckPort.map(this::buildLivenessUri).get().toString(); + log.info("Waiting for {} seconds for URL: {}", startupTimeout.getSeconds(), uri); // try to connect to the URL try { @@ -139,7 +142,7 @@ protected void waitUntilReady() { */ private URI buildLivenessUri(int livenessCheckPort) { final String scheme = (tlsEnabled ? "https" : "http") + "://"; - final String host = container.getContainerIpAddress(); + final String host = waitStrategyTarget.getContainerIpAddress(); final String portSuffix; if ((tlsEnabled && 443 == livenessCheckPort) || (!tlsEnabled && 80 == livenessCheckPort)) { From 666f092e4034abc6a3ed8ab0024dd1cfc7328b98 Mon Sep 17 00:00:00 2001 From: ctayeb Date: Sun, 13 May 2018 22:51:15 +0200 Subject: [PATCH 15/18] Respect Codacy quality standards --- .../couchbase/AbstractCouchbaseTest.java | 20 ++-- .../couchbase/CouchbaseContainer.java | 102 +++++++++--------- .../CouchbaseQueryServiceWaitStrategy.java | 11 +- .../couchbase/CouchbaseWaitStrategy.java | 15 ++- .../couchbase/CouchbaseContainerTest.java | 4 +- 5 files changed, 74 insertions(+), 78 deletions(-) diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java index 4262017770b..b8cdaa14cb2 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/AbstractCouchbaseTest.java @@ -29,8 +29,8 @@ public abstract class AbstractCouchbaseTest { public void clear() { if (getCouchbaseContainer().isIndex() && getCouchbaseContainer().isQuery() && getCouchbaseContainer().isPrimaryIndex()) { getBucket().query( - N1qlQuery.simple(String.format("DELETE FROM `%s`", getBucket().name()), - N1qlParams.build().consistency(ScanConsistency.STATEMENT_PLUS))); + N1qlQuery.simple(String.format("DELETE FROM `%s`", getBucket().name()), + N1qlParams.build().consistency(ScanConsistency.STATEMENT_PLUS))); } else { getBucket().bucketManager().flush(); } @@ -38,14 +38,14 @@ public void clear() { private static CouchbaseContainer initCouchbaseContainer() { CouchbaseContainer couchbaseContainer = new CouchbaseContainer() - .withNewBucket(DefaultBucketSettings.builder() - .enableFlush(true) - .name(TEST_BUCKET) - .password(DEFAULT_PASSWORD) - .quota(100) - .replicas(0) - .type(BucketType.COUCHBASE) - .build()); + .withNewBucket(DefaultBucketSettings.builder() + .enableFlush(true) + .name(TEST_BUCKET) + .password(DEFAULT_PASSWORD) + .quota(100) + .replicas(0) + .type(BucketType.COUCHBASE) + .build()); couchbaseContainer.start(); return couchbaseContainer; } diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index b87e20aaf40..2329e21a279 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -25,6 +25,7 @@ import lombok.AllArgsConstructor; import lombok.Cleanup; import lombok.Getter; +import lombok.SneakyThrows; import lombok.experimental.Wither; import org.apache.commons.compress.utils.Sets; import org.testcontainers.containers.GenericContainer; @@ -138,58 +139,55 @@ public SELF withNewBucket(BucketSettings bucketSettings) { return self(); } + @SneakyThrows public void initCluster() { urlBase = String.format("http://%s:%s", getContainerIpAddress(), getMappedPort(8091)); - try { - String poolURL = "/pools/default"; - String poolPayload = "memoryQuota=" + URLEncoder.encode(memoryQuota, "UTF-8") + "&indexMemoryQuota=" + URLEncoder.encode(indexMemoryQuota, "UTF-8"); + String poolURL = "/pools/default"; + String poolPayload = "memoryQuota=" + URLEncoder.encode(memoryQuota, "UTF-8") + "&indexMemoryQuota=" + URLEncoder.encode(indexMemoryQuota, "UTF-8"); - String setupServicesURL = "/node/controller/setupServices"; - StringBuilder servicePayloadBuilder = new StringBuilder(); - if (keyValue) { - servicePayloadBuilder.append("kv,"); - } - if (query) { - servicePayloadBuilder.append("n1ql,"); - } - if (index) { - servicePayloadBuilder.append("index,"); - } - if (fts) { - servicePayloadBuilder.append("fts,"); - } - String setupServiceContent = "services=" + URLEncoder.encode(servicePayloadBuilder.toString(), "UTF-8"); + String setupServicesURL = "/node/controller/setupServices"; + StringBuilder servicePayloadBuilder = new StringBuilder(); + if (keyValue) { + servicePayloadBuilder.append("kv,"); + } + if (query) { + servicePayloadBuilder.append("n1ql,"); + } + if (index) { + servicePayloadBuilder.append("index,"); + } + if (fts) { + servicePayloadBuilder.append("fts,"); + } + String setupServiceContent = "services=" + URLEncoder.encode(servicePayloadBuilder.toString(), "UTF-8"); - String webSettingsURL = "/settings/web"; - String webSettingsContent = "username=" + URLEncoder.encode(clusterUsername, "UTF-8") + "&password=" + URLEncoder.encode(clusterPassword, "UTF-8") + "&port=8091"; + String webSettingsURL = "/settings/web"; + String webSettingsContent = "username=" + URLEncoder.encode(clusterUsername, "UTF-8") + "&password=" + URLEncoder.encode(clusterPassword, "UTF-8") + "&port=8091"; - String bucketURL = "/sampleBuckets/install"; + String bucketURL = "/sampleBuckets/install"; - StringBuilder sampleBucketPayloadBuilder = new StringBuilder(); - sampleBucketPayloadBuilder.append('['); - if (travelSample) { - sampleBucketPayloadBuilder.append("\"travel-sample\","); - } - if (beerSample) { - sampleBucketPayloadBuilder.append("\"beer-sample\","); - } - if (gamesIMSample) { - sampleBucketPayloadBuilder.append("\"gamesim-sample\","); - } - sampleBucketPayloadBuilder.append(']'); + StringBuilder sampleBucketPayloadBuilder = new StringBuilder(); + sampleBucketPayloadBuilder.append('['); + if (travelSample) { + sampleBucketPayloadBuilder.append("\"travel-sample\","); + } + if (beerSample) { + sampleBucketPayloadBuilder.append("\"beer-sample\","); + } + if (gamesIMSample) { + sampleBucketPayloadBuilder.append("\"gamesim-sample\","); + } + sampleBucketPayloadBuilder.append(']'); - callCouchbaseRestAPI(poolURL, poolPayload); - callCouchbaseRestAPI(setupServicesURL, setupServiceContent); - callCouchbaseRestAPI(webSettingsURL, webSettingsContent); - callCouchbaseRestAPI(bucketURL, sampleBucketPayloadBuilder.toString()); + callCouchbaseRestAPI(poolURL, poolPayload); + callCouchbaseRestAPI(setupServicesURL, setupServiceContent); + callCouchbaseRestAPI(webSettingsURL, webSettingsContent); + callCouchbaseRestAPI(bucketURL, sampleBucketPayloadBuilder.toString()); - CouchbaseWaitStrategy s = new CouchbaseWaitStrategy(); - s.withBasicCredentials(clusterUsername, clusterPassword); - s.waitUntilReady(this); - callCouchbaseRestAPI("/settings/indexes", "indexerThreads=0&logLevel=info&maxRollbackPoints=5&storageMode=memory_optimized"); - } catch (Exception e) { - throw new RuntimeException(e); - } + CouchbaseWaitStrategy s = new CouchbaseWaitStrategy(); + s.withBasicCredentials(clusterUsername, clusterPassword); + s.waitUntilReady(this); + callCouchbaseRestAPI("/settings/indexes", "indexerThreads=0&logLevel=info&maxRollbackPoints=5&storageMode=memory_optimized"); } public void createBucket(BucketSettings bucketSetting, boolean primaryIndex) { @@ -198,8 +196,8 @@ public void createBucket(BucketSettings bucketSetting, boolean primaryIndex) { BucketSettings bucketSettings = clusterManager.insertBucket(bucketSetting); // Insert Bucket admin user UserSettings userSettings = UserSettings.build() - .password(bucketSetting.password()) - .roles(Collections.singletonList(new UserRole("bucket_admin", bucketSetting.name()))); + .password(bucketSetting.password()) + .roles(Collections.singletonList(new UserRole("bucket_admin", bucketSetting.name()))); try { clusterManager.upsertUser(AuthDomain.LOCAL, bucketSetting.name(), userSettings); } catch (Exception e) { @@ -221,7 +219,7 @@ public void callCouchbaseRestAPI(String url, String payload) throws IOException httpConnection.setDoOutput(true); httpConnection.setRequestMethod("POST"); httpConnection.setRequestProperty("Content-Type", - "application/x-www-form-urlencoded"); + "application/x-www-form-urlencoded"); String encoded = Base64.encode((clusterUsername + ":" + clusterPassword).getBytes("UTF-8")); httpConnection.setRequestProperty("Authorization", "Basic " + encoded); @Cleanup @@ -248,10 +246,10 @@ private CouchbaseCluster createCouchbaseCluster() { private DefaultCouchbaseEnvironment createCouchbaseEnvironment() { initCluster(); return DefaultCouchbaseEnvironment.builder() - .bootstrapCarrierDirectPort(getMappedPort(11210)) - .bootstrapCarrierSslPort(getMappedPort(11207)) - .bootstrapHttpDirectPort(getMappedPort(8091)) - .bootstrapHttpSslPort(getMappedPort(18091)) - .build(); + .bootstrapCarrierDirectPort(getMappedPort(11210)) + .bootstrapCarrierSslPort(getMappedPort(11207)) + .bootstrapHttpDirectPort(getMappedPort(8091)) + .bootstrapHttpSslPort(getMappedPort(18091)) + .build(); } } diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseQueryServiceWaitStrategy.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseQueryServiceWaitStrategy.java index e27c1b2e771..853dd7d8eb7 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseQueryServiceWaitStrategy.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseQueryServiceWaitStrategy.java @@ -7,7 +7,6 @@ import lombok.extern.slf4j.Slf4j; import org.rnorth.ducttape.TimeoutException; import org.testcontainers.containers.ContainerLaunchException; -import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy; import java.time.Duration; @@ -38,13 +37,13 @@ protected void waitUntilReady() { retryUntilSuccess((int) startupTimeout.getSeconds(), TimeUnit.SECONDS, () -> { getRateLimiter().doWhenReady(() -> { GetClusterConfigResponse clusterConfig = bucket.core() - .send(new GetClusterConfigRequest()) - .toBlocking().single(); + .send(new GetClusterConfigRequest()) + .toBlocking().single(); boolean queryServiceEnabled = clusterConfig.config() - .bucketConfig(bucket.name()) - .serviceEnabled(ServiceType.QUERY); + .bucketConfig(bucket.name()) + .serviceEnabled(ServiceType.QUERY); if (!queryServiceEnabled) { - throw new RuntimeException("Query service not ready yet"); + throw new ContainerLaunchException("Query service not ready yet"); } }); return true; diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseWaitStrategy.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseWaitStrategy.java index 5c760e1e0b1..346b60a8cc4 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseWaitStrategy.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseWaitStrategy.java @@ -96,8 +96,7 @@ protected void waitUntilReady() { retryUntilSuccess((int) startupTimeout.getSeconds(), TimeUnit.SECONDS, () -> { getRateLimiter().doWhenReady(() -> { try { - @Cleanup("disconnect") - final HttpURLConnection connection = (HttpURLConnection) new URL(uri).openConnection(); + @Cleanup("disconnect") final HttpURLConnection connection = (HttpURLConnection) new URL(uri).openConnection(); // authenticate if (!Strings.isNullOrEmpty(username)) { @@ -109,20 +108,20 @@ protected void waitUntilReady() { connection.connect(); if (statusCode != connection.getResponseCode()) { - throw new RuntimeException(String.format("HTTP response code was: %s", - connection.getResponseCode())); + throw new ContainerLaunchException(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)); + if (!"healthy".equals(status)) { + throw new ContainerLaunchException(String.format("Couchbase Node status was: %s", status)); } } catch (IOException e) { - throw new RuntimeException(e); + throw new ContainerLaunchException("Unable to check Couchbase Node status", e); } }); return true; @@ -130,7 +129,7 @@ protected void waitUntilReady() { } catch (TimeoutException e) { throw new ContainerLaunchException(String.format( - "Timed out waiting for URL to be accessible (%s should return HTTP %s)", uri, statusCode)); + "Timed out waiting for URL to be accessible (%s should return HTTP %s)", uri, statusCode)); } } diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java index be2d4378277..52660787796 100644 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java @@ -20,7 +20,7 @@ public class CouchbaseContainerTest extends AbstractCouchbaseTest { private static final String DOCUMENT = "{\"name\":\"toto\"}"; @Test - public void should_insert_document() { + public void shouldInsertDocument() { RawJsonDocument expected = RawJsonDocument.create(ID, DOCUMENT); getBucket().upsert(expected); RawJsonDocument result = getBucket().get(ID, RawJsonDocument.class); @@ -28,7 +28,7 @@ public void should_insert_document() { } @Test - public void should_execute_n1ql() { + public void shouldExecuteN1ql() { getBucket().query(N1qlQuery.simple("INSERT INTO " + TEST_BUCKET + " (KEY, VALUE) VALUES ('" + ID + "', " + DOCUMENT + ")")); N1qlQueryResult query = getBucket().query(N1qlQuery.simple("SELECT * FROM " + TEST_BUCKET + " USE KEYS '" + ID + "'")); From 6e45640ece67274ba9bc8f760ee6c0ee1911dc7f Mon Sep 17 00:00:00 2001 From: ctayeb Date: Thu, 17 May 2018 22:53:21 +0200 Subject: [PATCH 16/18] Some optimizations --- .../org/testcontainers/couchbase/CouchbaseContainer.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index 2329e21a279..59cbf8f40cc 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -22,6 +22,7 @@ import com.couchbase.client.java.env.CouchbaseEnvironment; import com.couchbase.client.java.env.DefaultCouchbaseEnvironment; import com.couchbase.client.java.query.Index; +import com.github.dockerjava.api.command.InspectContainerResponse; import lombok.AllArgsConstructor; import lombok.Cleanup; import lombok.Getter; @@ -47,7 +48,7 @@ * optimized by Tayeb Chlyah */ @AllArgsConstructor -public class CouchbaseContainer> extends GenericContainer { +public class CouchbaseContainer extends GenericContainer { public static final String VERSION = "5.1.0"; @@ -134,7 +135,7 @@ protected void configure() { setWaitStrategy(new HttpWaitStrategy().forPath("/ui/index.html#/")); } - public SELF withNewBucket(BucketSettings bucketSettings) { + public CouchbaseContainer withNewBucket(BucketSettings bucketSettings) { newBuckets.add(bucketSettings); return self(); } @@ -230,8 +231,7 @@ public void callCouchbaseRestAPI(String url, String payload) throws IOException } @Override - public void start() { - super.start(); + protected void containerIsStarted(InspectContainerResponse containerInfo) { if (!newBuckets.isEmpty()) { for (BucketSettings bucketSetting : newBuckets) { createBucket(bucketSetting, primaryIndex); From 3739a514a49697a32f339553f84252020de71be3 Mon Sep 17 00:00:00 2001 From: ctayeb Date: Thu, 17 May 2018 22:53:28 +0200 Subject: [PATCH 17/18] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c256d198e95..0d9c062692b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file. ### Fixed ### Changed +- Added Couchbase module ([\#688](https://github.com/testcontainers/testcontainers-java/pull/688)) ## [1.7.2] - 2018-04-30 From cc51bcef7f7290c1a37ce6533a8c0c4753ccaa1d Mon Sep 17 00:00:00 2001 From: ctayeb Date: Mon, 4 Jun 2018 00:50:15 +0200 Subject: [PATCH 18/18] Replace CouchbaseWaitStrategy by HttpWaitStrategy and ResponsePredicate --- .../couchbase/CouchbaseContainer.java | 39 ++++- .../couchbase/CouchbaseWaitStrategy.java | 164 ------------------ 2 files changed, 30 insertions(+), 173 deletions(-) delete mode 100644 modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseWaitStrategy.java diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index 59cbf8f40cc..356bbc91fea 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -22,6 +22,8 @@ import com.couchbase.client.java.env.CouchbaseEnvironment; import com.couchbase.client.java.env.DefaultCouchbaseEnvironment; import com.couchbase.client.java.query.Index; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import com.github.dockerjava.api.command.InspectContainerResponse; import lombok.AllArgsConstructor; import lombok.Cleanup; @@ -29,28 +31,29 @@ import lombok.SneakyThrows; import lombok.experimental.Wither; import org.apache.commons.compress.utils.Sets; +import org.jetbrains.annotations.NotNull; import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.wait.HttpWaitStrategy; +import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import java.io.DataOutputStream; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; +import java.util.*; + +import static java.net.HttpURLConnection.HTTP_OK; /** * Based on Laurent Doguin version, - * + *

* optimized by Tayeb Chlyah */ @AllArgsConstructor public class CouchbaseContainer extends GenericContainer { public static final String VERSION = "5.1.0"; + public static final ObjectMapper MAPPER = new ObjectMapper(); @Wither private String memoryQuota = "300"; @@ -185,12 +188,30 @@ public void initCluster() { callCouchbaseRestAPI(webSettingsURL, webSettingsContent); callCouchbaseRestAPI(bucketURL, sampleBucketPayloadBuilder.toString()); - CouchbaseWaitStrategy s = new CouchbaseWaitStrategy(); - s.withBasicCredentials(clusterUsername, clusterPassword); - s.waitUntilReady(this); + createNodeWaitStrategy().waitUntilReady(this); callCouchbaseRestAPI("/settings/indexes", "indexerThreads=0&logLevel=info&maxRollbackPoints=5&storageMode=memory_optimized"); } + @NotNull + private HttpWaitStrategy createNodeWaitStrategy() { + return new HttpWaitStrategy() + .forPath("/pools/default/") + .withBasicCredentials(clusterUsername, clusterPassword) + .forStatusCode(HTTP_OK) + .forResponsePredicate(response -> { + try { + return Optional.of(MAPPER.readTree(response)) + .map(n -> n.at("/nodes/0/status")) + .map(JsonNode::asText) + .map("healthy"::equals) + .orElse(false); + } catch (IOException e) { + logger().error("Unable to parse response {}", response); + return false; + } + }); + } + public void createBucket(BucketSettings bucketSetting, boolean primaryIndex) { ClusterManager clusterManager = getCouchbaseCluster().clusterManager(clusterUsername, clusterPassword); // Insert Bucket diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseWaitStrategy.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseWaitStrategy.java deleted file mode 100644 index 346b60a8cc4..00000000000 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseWaitStrategy.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (c) 2016 Couchbase, Inc. - * - * 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.testcontainers.couchbase; - -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 lombok.Cleanup; -import lombok.extern.slf4j.Slf4j; -import org.rnorth.ducttape.TimeoutException; -import org.testcontainers.containers.ContainerLaunchException; -import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy; - -import java.io.IOException; -import java.net.HttpURLConnection; -import java.net.URI; -import java.net.URL; -import java.util.Optional; -import java.util.concurrent.TimeUnit; - -import static org.rnorth.ducttape.unreliables.Unreliables.retryUntilSuccess; - -/** - * @author ldoguin - * created on 18/07/16. - */ -@Slf4j -public class CouchbaseWaitStrategy extends 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() { - Optional livenessCheckPort = waitStrategyTarget.getLivenessCheckPortNumbers().stream().findFirst(); - if (!livenessCheckPort.isPresent()) { - log.warn("No exposed ports or mapped ports - cannot wait for status"); - return; - } - - final String uri = livenessCheckPort.map(this::buildLivenessUri).get().toString(); - log.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 { - @Cleanup("disconnect") 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 ContainerLaunchException(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 ContainerLaunchException(String.format("Couchbase Node status was: %s", status)); - } - - } catch (IOException e) { - throw new ContainerLaunchException("Unable to check Couchbase Node status", 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 = waitStrategyTarget.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()); - } -}