Skip to content

Commit

Permalink
couchbase: wait until all services are part of the config
Browse files Browse the repository at this point in the history
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
  • Loading branch information
daschl committed Jul 17, 2020
1 parent 977058d commit f7caad2
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package org.testcontainers.couchbase;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.dockerjava.api.command.InspectContainerResponse;
Expand All @@ -36,9 +37,11 @@
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
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 @@ -241,15 +244,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 @@ -350,10 +348,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 @@ -450,4 +449,41 @@ 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
@SuppressWarnings("unchecked")
public boolean test(final String rawConfig) {
try {
Map<String, Object> parsedConfig = MAPPER.readValue(rawConfig, new TypeReference<Map<String, Object>>() {});

for (Map<String, Object> node : (List<Map<String, Object>>) parsedConfig.get("nodesExt")) {
final Map<String, Integer> nodeServices = (Map<String, Integer>) node.get("services");
for (CouchbaseService enabledService : enabledServices) {
boolean found = false;
for (String nodeService : nodeServices.keySet()) {
if (nodeService.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);
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 f7caad2

Please sign in to comment.