Skip to content

Commit

Permalink
couchbase: wait until all services are part of the config (testcontai…
Browse files Browse the repository at this point in the history
…ners#3003)

This changeset adds a predicate to the wait strategy to make sure
that it not only returns a 200, but also that every enabled service
is actually already exposed in the config. Since we are polling
the server during bootstrap here, not all of them might show up
at the same time.

Also, while not contributing to the fix we poll the terse bucket
http config "b" instead of the verbose one "buckets" since it is
a little more efficient on the server side and actually the config
the client internally works with.

fixes testcontainers#2993

Co-authored-by: Sergei Egorov <bsideup@gmail.com>
  • Loading branch information
daschl and bsideup committed Feb 6, 2021
1 parent 7688d1b commit ed8386c
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
Expand Down Expand Up @@ -181,7 +183,7 @@ protected void configure() {
.map("healthy"::equals)
.orElse(false);
} catch (IOException e) {
logger().error("Unable to parse response {}", response, e);
logger().error("Unable to parse response: {}", response, e);
return false;
}
})
Expand Down Expand Up @@ -254,15 +256,10 @@ private void renameNode() {
private void initializeServices() {
logger().debug("Initializing couchbase services on host: {}", enabledServices);

final String services = enabledServices.stream().map(s -> {
switch (s) {
case KV: return "kv";
case QUERY: return "n1ql";
case INDEX: return "index";
case SEARCH: return "fts";
default: throw new IllegalStateException("Unknown service!");
}
}).collect(Collectors.joining(","));
final String services = enabledServices
.stream()
.map(CouchbaseService::getIdentifier)
.collect(Collectors.joining(","));

@Cleanup Response response = doHttpRequest(MGMT_PORT, "/node/controller/setupServices", "POST", new FormBody.Builder()
.add("services", services)
Expand Down Expand Up @@ -363,10 +360,11 @@ private void createBuckets() {
checkSuccessfulResponse(response, "Could not create bucket " + bucket.getName());

new HttpWaitStrategy()
.forPath("/pools/default/buckets/" + bucket.getName())
.forPath("/pools/default/b/" + bucket.getName())
.forPort(MGMT_PORT)
.withBasicCredentials(username, password)
.forStatusCode(200)
.forResponsePredicate(new AllServicesEnabledPredicate())
.waitUntilReady(this);

if (enabledServices.contains(CouchbaseService.QUERY)) {
Expand Down Expand Up @@ -472,4 +470,38 @@ private Response doHttpRequest(final int port, final String path, final String m
throw new RuntimeException("Could not perform request against couchbase HTTP endpoint ", ex);
}
}

/**
* In addition to getting a 200, we need to make sure that all services we need are enabled and available on
* the bucket.
* <p>
* Fixes the issue observed in https://github.com/testcontainers/testcontainers-java/issues/2993
*/
private class AllServicesEnabledPredicate implements Predicate<String> {

@Override
public boolean test(final String rawConfig) {
try {
for (JsonNode node : MAPPER.readTree(rawConfig).at("/nodesExt")) {
for (CouchbaseService enabledService : enabledServices) {
boolean found = false;
Iterator<String> fieldNames = node.get("services").fieldNames();
while (fieldNames.hasNext()) {
if (fieldNames.next().startsWith(enabledService.getIdentifier())) {
found = true;
}
}
if (!found) {
logger().trace("Service {} not yet part of config, retrying.", enabledService);
return false;
}
}
}
return true;
} catch (IOException ex) {
logger().error("Unable to parse response: {}", rawConfig, ex);
return false;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,30 @@ public enum CouchbaseService {
/**
* Key-Value service.
*/
KV,
KV("kv"),

/**
* Query (N1QL) service.
*/
QUERY,
QUERY("n1ql"),

/**
* Search (FTS) service.
*/
SEARCH,
SEARCH("fts"),

/**
* Indexing service (needed if QUERY is also used!).
*/
INDEX
INDEX("index");

private final String identifier;

CouchbaseService(String identifier) {
this.identifier = identifier;
}

String getIdentifier() {
return identifier;
}
}

0 comments on commit ed8386c

Please sign in to comment.