Skip to content

Commit 2b37aa8

Browse files
committed
created db provisioner for programmatic provisioning
1 parent 42aa1d5 commit 2b37aa8

File tree

14 files changed

+420
-279
lines changed

14 files changed

+420
-279
lines changed

src/main/java/com/redislabs/testcontainers/RedisClusterContainer.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package com.redislabs.testcontainers;
22

3-
import org.testcontainers.containers.FixedHostPortGenericContainer;
3+
import org.testcontainers.containers.GenericContainer;
44
import org.testcontainers.containers.wait.strategy.Wait;
55
import org.testcontainers.shaded.org.apache.commons.lang.ClassUtils;
66

7-
public class RedisClusterContainer extends FixedHostPortGenericContainer<RedisClusterContainer> implements RedisServer {
7+
public class RedisClusterContainer extends GenericContainer<RedisClusterContainer> implements RedisServer {
88

99
private static final String ENV_MASTERS = "MASTERS";
1010
private static final String ENV_SLAVES_PER_MASTER = "SLAVES_PER_MASTER";
@@ -54,7 +54,7 @@ private RedisClusterContainer update() {
5454
withEnv(ENV_MASTERS, String.valueOf(masters));
5555
withEnv(ENV_SLAVES_PER_MASTER, String.valueOf(slavesPerMaster));
5656
for (int port : ports()) {
57-
withFixedExposedPort(port, port);
57+
addFixedExposedPort(port, port);
5858
}
5959
return this;
6060
}

src/main/java/com/redislabs/testcontainers/RedisEnterpriseContainer.java

Lines changed: 44 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -5,98 +5,69 @@
55
import com.github.dockerjava.api.command.InspectContainerResponse;
66
import com.github.dockerjava.api.exception.DockerException;
77
import com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
8-
import com.redislabs.testcontainers.support.enterprise.ActionStatus;
9-
import com.redislabs.testcontainers.support.enterprise.Command;
10-
import com.redislabs.testcontainers.support.enterprise.CommandResponse;
11-
import com.redislabs.testcontainers.support.enterprise.Database;
12-
import com.redislabs.testcontainers.support.enterprise.DatabaseCreateResponse;
13-
import com.redislabs.testcontainers.support.enterprise.Module;
14-
import com.redislabs.testcontainers.support.enterprise.ModuleInstallResponse;
8+
import com.redislabs.testcontainers.support.enterprise.DatabaseProvisioner;
159
import com.redislabs.testcontainers.support.enterprise.RestAPI;
10+
import com.redislabs.testcontainers.support.enterprise.rest.Database;
11+
import com.redislabs.testcontainers.support.enterprise.rest.DatabaseCreateResponse;
1612
import lombok.AccessLevel;
1713
import lombok.AllArgsConstructor;
18-
import lombok.Getter;
1914
import lombok.SneakyThrows;
2015
import lombok.Value;
2116
import lombok.extern.slf4j.Slf4j;
2217
import org.testcontainers.DockerClientFactory;
23-
import org.testcontainers.containers.ContainerLaunchException;
2418
import org.testcontainers.containers.GenericContainer;
2519
import org.testcontainers.containers.output.FrameConsumerResultCallback;
2620
import org.testcontainers.containers.output.OutputFrame;
2721
import org.testcontainers.containers.output.ToStringConsumer;
2822
import org.testcontainers.containers.wait.strategy.Wait;
29-
import org.testcontainers.shaded.org.apache.commons.io.IOUtils;
23+
import org.testcontainers.shaded.org.apache.commons.lang.ClassUtils;
3024
import org.testcontainers.utility.DockerImageName;
3125
import org.testcontainers.utility.TestEnvironment;
3226

33-
import java.io.ByteArrayOutputStream;
3427
import java.io.IOException;
35-
import java.io.InputStream;
36-
import java.net.URL;
3728
import java.nio.charset.StandardCharsets;
3829
import java.time.Duration;
39-
import java.util.Arrays;
40-
import java.util.Collections;
41-
import java.util.HashSet;
42-
import java.util.List;
43-
import java.util.Map;
44-
import java.util.Set;
45-
import java.util.function.Function;
46-
import java.util.stream.Collectors;
47-
import java.util.stream.Stream;
48-
49-
@SuppressWarnings({"unused", "UnusedReturnValue", "BusyWait"})
30+
31+
@SuppressWarnings({"unused", "UnusedReturnValue"})
5032
@Slf4j
5133
public class RedisEnterpriseContainer extends GenericContainer<RedisEnterpriseContainer> implements RedisServer {
5234

35+
public static final String MODULE_BLOOM = "bf";
36+
public static final String MODULE_GEARS = "rg";
37+
public static final String MODULE_GRAPH = "graph";
38+
public static final String MODULE_JSON = "ReJSON";
39+
public static final String MODULE_SEARCH = "search";
40+
public static final String MODULE_TIMESERIES = "timeseries";
41+
5342
public static final String ADMIN_USERNAME = "testcontainers@redislabs.com";
5443
public static final String ADMIN_PASSWORD = "redislabs123";
55-
public static final String GEARS_MODULE_FILE = "redisgears.linux-bionic-x64.1.0.6.zip";
56-
public static final String URL_GEARS_MODULE = "https://redismodules.s3.amazonaws.com/redisgears/" + GEARS_MODULE_FILE;
5744
public static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("redislabs/redis");
58-
public static final List<Database.Regex> DEFAULT_SHARD_KEY_REGEX = Arrays.asList(Database.Regex.of(".*\\{(?<tag>.*)\\}.*"), Database.Regex.of("(?<tag>.*)"));
59-
private static final long DEFAULT_MODULE_INSTALLATION_TIMEOUT = 60000;
60-
private static final long DEFAULT_MODULE_INSTALLATION_CHECK_INTERVAL = 5000;
61-
private static final long DEFAULT_DATABASE_PING_TIMEOUT = 10000;
62-
private static final long DEFAULT_DATABASE_PING_RETRY_INTERVAL = 1000;
6345
private static final long DEFAULT_RLADMIN_WAIT_INTERVAL = 500;
6446
private static final String NODE_EXTERNAL_ADDR = "0.0.0.0";
65-
private static final int DEFAULT_CLUSTER_SHARD_COUNT = 3;
47+
private static final int DEFAULT_SHARD_COUNT = 2;
48+
private static final String DEFAULT_DATABASE_NAME = "testcontainers";
6649
public static int ADMIN_PORT = 8443;
6750
public static int ENDPOINT_PORT = 12000;
68-
private static final Map<String, RedisModule> MODULES = Stream.of(RedisModule.values()).collect(Collectors.toMap(RedisModule::getModuleName, Function.identity()));
69-
70-
private final Set<RedisModule> modules = new HashSet<>();
71-
private RestAPI restAPI = RestAPI.credentials(new UsernamePasswordCredentials(ADMIN_USERNAME, ADMIN_PASSWORD.toCharArray())).build();
72-
private Duration moduleInstallationTimeout = Duration.ofMillis(DEFAULT_MODULE_INSTALLATION_TIMEOUT);
73-
private Duration moduleInstallationCheckInterval = Duration.ofMillis(DEFAULT_MODULE_INSTALLATION_CHECK_INTERVAL);
74-
private Duration databasePingTimeout = Duration.ofMillis(DEFAULT_DATABASE_PING_TIMEOUT);
75-
private Database database = Database.builder().name("testcontainers").build();
76-
private Duration databasePingRetryInterval = Duration.ofMillis(DEFAULT_DATABASE_PING_RETRY_INTERVAL);
51+
52+
private DatabaseProvisioner.Options provisionerOptions = DatabaseProvisioner.Options.builder().build();
7753
private Duration rladminWaitDuration = Duration.ofMillis(DEFAULT_RLADMIN_WAIT_INTERVAL);
54+
private String databaseName = DEFAULT_DATABASE_NAME;
55+
private int shardCount = DEFAULT_SHARD_COUNT;
56+
private boolean ossCluster;
57+
private Database.Module[] modules = new Database.Module[0];
7858

7959
public RedisEnterpriseContainer() {
8060
super(DEFAULT_IMAGE_NAME);
61+
addFixedExposedPort(RestAPI.DEFAULT_PORT, RestAPI.DEFAULT_PORT);
8162
addFixedExposedPort(ADMIN_PORT, ADMIN_PORT);
8263
addFixedExposedPort(ENDPOINT_PORT, ENDPOINT_PORT);
8364
withPrivilegedMode(true);
8465
withPublishAllPorts(false);
8566
waitingFor(Wait.forLogMessage(".*success: job_scheduler entered RUNNING state, process has stayed up for.*\\n", 1));
8667
}
8768

88-
public RedisEnterpriseContainer withRestAPI(RestAPI restAPI) {
89-
this.restAPI = restAPI;
90-
return this;
91-
}
92-
93-
public RedisEnterpriseContainer withDatabase(Database database) {
94-
this.database = database;
95-
return this;
96-
}
97-
98-
public RedisEnterpriseContainer withModuleInstallationCheckInterval(Duration moduleInstallationCheckInterval) {
99-
this.moduleInstallationCheckInterval = moduleInstallationCheckInterval;
69+
public RedisEnterpriseContainer withProvisionerOptions(DatabaseProvisioner.Options options) {
70+
this.provisionerOptions = options;
10071
return this;
10172
}
10273

@@ -106,109 +77,40 @@ public RedisEnterpriseContainer withRladminWaitDuration(Duration rladminWaitDura
10677
}
10778

10879
public RedisEnterpriseContainer withShardCount(int shardCount) {
109-
this.database.setShardCount(shardCount);
110-
if (shardCount > 1) {
111-
this.database.setSharding(true);
112-
this.database.setShardKeyRegex(DEFAULT_SHARD_KEY_REGEX);
113-
}
80+
this.shardCount = shardCount;
11481
return this;
11582
}
11683

117-
public RedisEnterpriseContainer withModuleInstallationTimeout(Duration moduleInstallationTimeout) {
118-
this.moduleInstallationTimeout = moduleInstallationTimeout;
119-
return this;
120-
}
121-
122-
public RedisEnterpriseContainer withDatabasePingTimeout(Duration databasePingTimeout) {
123-
this.databasePingTimeout = databasePingTimeout;
124-
return this;
125-
}
126-
127-
public RedisEnterpriseContainer databasePingRetryInterval(Duration databasePingRetryInterval) {
128-
this.databasePingRetryInterval = databasePingRetryInterval;
129-
return this;
130-
}
131-
132-
public RedisEnterpriseContainer withOSSCluster() {
133-
this.database.setOssCluster(true);
134-
this.database.setProxyPolicy(Database.ProxyPolicy.ALL_MASTER_SHARDS);
135-
this.database.setOssClusterAPIPreferredIPType(Database.IPType.EXTERNAL);
136-
if (database.getShardCount() == null || database.getShardCount() < 2) {
137-
log.info("Setting shard count to {}", DEFAULT_CLUSTER_SHARD_COUNT);
138-
withShardCount(DEFAULT_CLUSTER_SHARD_COUNT);
139-
}
84+
public RedisEnterpriseContainer withModules(Database.Module... modules) {
85+
this.modules = modules;
14086
return this;
14187
}
14288

143-
public RedisEnterpriseContainer withModules(RedisModule... modules) {
144-
Collections.addAll(this.modules, modules);
89+
public RedisEnterpriseContainer withDatabaseName(String name) {
90+
this.databaseName = name;
14591
return this;
14692
}
14793

148-
@Override
149-
public void start() {
150-
addFixedExposedPort(restAPI.getPort(), restAPI.getPort());
151-
super.start();
152-
}
153-
15494
@SneakyThrows
15595
@Override
15696
protected void containerIsStarted(InspectContainerResponse containerInfo) {
15797
super.containerIsStarted(containerInfo);
98+
log.info("Creating Redis Enterprise cluster");
15899
execute("/opt/redislabs/bin/rladmin", "cluster", "create", "name", "cluster.local", "username", ADMIN_USERNAME, "password", ADMIN_PASSWORD);
159100
Thread.sleep(rladminWaitDuration.toMillis());
101+
log.info("Setting Redis Enterprise node external IP");
160102
execute("/opt/redislabs/bin/rladmin", "node", "1", "external_addr", "set", NODE_EXTERNAL_ADDR);
161103
Thread.sleep(rladminWaitDuration.toMillis());
162-
if (!modules.isEmpty()) {
163-
if (modules.contains(RedisModule.GEARS)) {
164-
installGears();
165-
}
166-
List<Module> availableModules = restAPI.modules();
167-
Map<RedisModule, String> moduleIds = availableModules.stream().collect(Collectors.toMap(m -> MODULES.get(m.getName()), Module::getId));
168-
database.setModuleList(modules.stream().map(m -> Database.Module.builder().id(moduleIds.get(m)).name(m.getModuleName()).build()).collect(Collectors.toList()));
169-
}
170-
DatabaseCreateResponse databaseCreateResponse = restAPI.create(database);
171-
if (canPingDatabase(databaseCreateResponse.getUid())) {
172-
log.info("Created database {} with UID {}", databaseCreateResponse.getName(), databaseCreateResponse.getUid());
173-
} else {
174-
throw new ContainerLaunchException("Could not ping database at " + getRedisURI());
175-
}
104+
RestAPI restAPI = RestAPI.credentials(new UsernamePasswordCredentials(ADMIN_USERNAME, ADMIN_PASSWORD.toCharArray())).build();
105+
DatabaseProvisioner provisioner = DatabaseProvisioner.restAPI(restAPI).options(provisionerOptions).build();
106+
Database database = Database.name(databaseName).port(ENDPOINT_PORT).shardCount(shardCount).ossCluster(ossCluster).modules(modules).build();
107+
DatabaseCreateResponse response = provisioner.create(database);
108+
log.info("Created database {} with UID {}", database.getName(), response.getUid());
176109
}
177110

178-
179-
private boolean canPingDatabase(long uid) throws Exception {
180-
long start = System.currentTimeMillis();
181-
do {
182-
try {
183-
CommandResponse response = restAPI.command(uid, Command.command("PING").build());
184-
if (response.getResponse().asBoolean()) {
185-
return true;
186-
}
187-
} catch (Exception e) {
188-
// ignore
189-
}
190-
Thread.sleep(databasePingRetryInterval.toMillis());
191-
} while (System.currentTimeMillis() - start < databasePingTimeout.toMillis());
192-
return false;
193-
}
194-
195-
private void installGears() throws Exception {
196-
try (InputStream zipInputStream = new URL(URL_GEARS_MODULE).openStream()) {
197-
ByteArrayOutputStream baos = new ByteArrayOutputStream();
198-
IOUtils.copy(zipInputStream, baos);
199-
ModuleInstallResponse installResponse = restAPI.module(GEARS_MODULE_FILE, baos.toByteArray());
200-
long start = System.currentTimeMillis();
201-
ActionStatus status;
202-
do {
203-
status = restAPI.actionStatus(installResponse.getActionUID());
204-
if ("completed".equals(status.getStatus())) {
205-
return;
206-
}
207-
// Wait before checking again
208-
Thread.sleep(moduleInstallationCheckInterval.toMillis());
209-
} while (System.currentTimeMillis() - start < moduleInstallationTimeout.toMillis());
210-
throw new ContainerLaunchException("Timed out waiting for module installation to complete. Action UID: " + installResponse.getActionUID());
211-
}
111+
public RedisEnterpriseContainer withOSSCluster() {
112+
this.ossCluster = true;
113+
return this;
212114
}
213115

214116
@Override
@@ -218,7 +120,11 @@ public String getRedisURI() {
218120

219121
@Override
220122
public boolean isCluster() {
221-
return Boolean.TRUE.equals(database.getOssCluster());
123+
return ossCluster;
124+
}
125+
126+
public String toString() {
127+
return ClassUtils.getShortClassName(this.getClass());
222128
}
223129

224130
@SuppressWarnings("deprecation")
@@ -278,18 +184,6 @@ private boolean isRunning(InspectContainerResponse containerInfo) {
278184
}
279185
}
280186

281-
public enum RedisModule {
282-
283-
JSON("ReJSON"), SEARCH("search"), TIMESERIES("timeseries"), BLOOM("bf"), GRAPH("graph"), GEARS("rg");
284-
285-
@Getter
286-
private final String moduleName;
287-
288-
RedisModule(String name) {
289-
this.moduleName = name;
290-
}
291-
292-
}
293187

294188
}
295189

0 commit comments

Comments
 (0)