55import com .github .dockerjava .api .command .InspectContainerResponse ;
66import com .github .dockerjava .api .exception .DockerException ;
77import 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 ;
159import com .redislabs .testcontainers .support .enterprise .RestAPI ;
10+ import com .redislabs .testcontainers .support .enterprise .rest .Database ;
11+ import com .redislabs .testcontainers .support .enterprise .rest .DatabaseCreateResponse ;
1612import lombok .AccessLevel ;
1713import lombok .AllArgsConstructor ;
18- import lombok .Getter ;
1914import lombok .SneakyThrows ;
2015import lombok .Value ;
2116import lombok .extern .slf4j .Slf4j ;
2217import org .testcontainers .DockerClientFactory ;
23- import org .testcontainers .containers .ContainerLaunchException ;
2418import org .testcontainers .containers .GenericContainer ;
2519import org .testcontainers .containers .output .FrameConsumerResultCallback ;
2620import org .testcontainers .containers .output .OutputFrame ;
2721import org .testcontainers .containers .output .ToStringConsumer ;
2822import 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 ;
3024import org .testcontainers .utility .DockerImageName ;
3125import org .testcontainers .utility .TestEnvironment ;
3226
33- import java .io .ByteArrayOutputStream ;
3427import java .io .IOException ;
35- import java .io .InputStream ;
36- import java .net .URL ;
3728import java .nio .charset .StandardCharsets ;
3829import 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
5133public 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