Skip to content

Commit cc3aadc

Browse files
committed
Add SSL to TarantoolCartridgeContainer
1 parent d5b221f commit cc3aadc

File tree

10 files changed

+225
-66
lines changed

10 files changed

+225
-66
lines changed

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,12 @@
9090
<groupId>org.slf4j</groupId>
9191
<artifactId>slf4j-api</artifactId>
9292
</dependency>
93+
<dependency>
94+
<groupId>org.projectlombok</groupId>
95+
<artifactId>lombok</artifactId>
96+
<version>1.18.28</version>
97+
<scope>provided</scope>
98+
</dependency>
9399

94100
<!-- Test dependencies -->
95101
<dependency>
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package org.testcontainers.containers;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.EqualsAndHashCode;
5+
import lombok.Getter;
6+
import lombok.NoArgsConstructor;
7+
import lombok.NonNull;
8+
import lombok.ToString;
9+
10+
@Getter
11+
@NoArgsConstructor(staticName = "getSslContext")
12+
@AllArgsConstructor(staticName = "getSslContext")
13+
@ToString(includeFieldNames=true)
14+
@EqualsAndHashCode
15+
public class SslContext {
16+
@NonNull
17+
private String keyFile;
18+
@NonNull
19+
private String certFile;
20+
21+
// public SslContext(@NonNull String keyFile, @NonNull String certFile) {
22+
// if (keyFile.isEmpty()) {
23+
// throw new RuntimeException("Parameter keyFile can not be empty String");
24+
// }
25+
// if (certFile.isEmpty()) {
26+
// throw new RuntimeException("Parameter certFile can not be empty String");
27+
// }
28+
//
29+
// this.keyFile = keyFile;
30+
// this.certFile = certFile;
31+
// }
32+
}

src/main/java/org/testcontainers/containers/TarantoolCartridgeContainer.java

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,7 @@ public class TarantoolCartridgeContainer extends GenericContainer<TarantoolCartr
120120
private String directoryResourcePath = SCRIPT_RESOURCE_DIRECTORY;
121121
private String instanceDir = INSTANCE_DIR;
122122
private String topologyConfigurationFile;
123-
private Boolean sslIsActive = false;
124-
private String keyFile = "";
125-
private String certFile = "";
123+
private SslContext sslContext ;
126124

127125
/**
128126
* Create a container with default image and specified instances file from the classpath resources. Assumes that
@@ -193,6 +191,21 @@ public TarantoolCartridgeContainer(String dockerFile, String buildImageName, Str
193191
this(buildImage(dockerFile, buildImageName), instancesFile, topologyConfigurationFile, buildArgs);
194192
}
195193

194+
/**
195+
* Create a container with specified image and specified instances file from the classpath resources. By providing
196+
* the result Cartridge container image name, you can cache the image and avoid rebuilding on each test run (the
197+
* image is tagged with the provided name and not deleted after tests finishing).
198+
*
199+
* @param tarantoolImageParams params for cached image creating
200+
* @param instancesFile URL resource path to instances.yml relative in the classpath
201+
* @param topologyConfigurationFile URL resource path to a topology bootstrap script in the classpath
202+
*/
203+
public TarantoolCartridgeContainer(TarantoolImageParams tarantoolImageParams, String instancesFile,
204+
String topologyConfigurationFile) {
205+
this(new ImageFromDockerfile(TarantoolContainerImageHelper.getImage(tarantoolImageParams)), instancesFile,
206+
topologyConfigurationFile, tarantoolImageParams.getBuildArgs());
207+
}
208+
196209
private TarantoolCartridgeContainer(ImageFromDockerfile image, String instancesFile, String topologyConfigurationFile,
197210
Map<String, String> buildArgs) {
198211
super(withBuildArgs(image, buildArgs));
@@ -251,29 +264,15 @@ private static ImageFromDockerfile buildImage(String dockerFile, String buildIma
251264
}
252265

253266
/**
254-
* Specify SSL as connection transport.
255-
* Warning! SSL must be set as default transport on your tarantool cluster.
256-
* Supported only in Tarantool Enterprise
257-
*
258-
* @return this container instance
259-
*/
260-
public TarantoolCartridgeContainer withSsl() {
261-
checkNotRunning();
262-
this.sslIsActive = true;
263-
return this;
264-
}
265-
266-
/**
267-
* Specify path to key and cert files inside your container for SSL connection.
267+
* Specify SSL as connection transport. And path to key and cert files inside your container for mTLS connection
268268
* Warning! SSL must be set as default transport on your tarantool cluster.
269269
* Supported only in Tarantool Enterprise
270270
*
271271
* @return this container instance
272272
*/
273-
public TarantoolCartridgeContainer withKeyAndCertFiles(String keyFile, String certFile) {
273+
public TarantoolCartridgeContainer withSslContext(SslContext sslContext) {
274274
checkNotRunning();
275-
this.keyFile = keyFile;
276-
this.certFile = certFile;
275+
this.sslContext = sslContext;
277276
return this;
278277
}
279278

@@ -634,21 +633,21 @@ private boolean isCartridgeHealthy() {
634633

635634
@Override
636635
public ExecResult executeScript(String scriptResourcePath) throws Exception {
637-
return clientHelper.executeScript(scriptResourcePath, this.sslIsActive, this.keyFile, this.certFile);
636+
return clientHelper.executeScript(scriptResourcePath, this.sslContext);
638637
}
639638

640639
@Override
641640
public <T> T executeScriptDecoded(String scriptResourcePath) throws Exception {
642-
return clientHelper.executeScriptDecoded(scriptResourcePath, this.sslIsActive, this.keyFile, this.certFile);
641+
return clientHelper.executeScriptDecoded(scriptResourcePath, this.sslContext);
643642
}
644643

645644
@Override
646645
public ExecResult executeCommand(String command) throws Exception {
647-
return clientHelper.executeCommand(command, this.sslIsActive, this.keyFile, this.certFile);
646+
return clientHelper.executeCommand(command, this.sslContext);
648647
}
649648

650649
@Override
651650
public <T> T executeCommandDecoded(String command) throws Exception {
652-
return clientHelper.executeCommandDecoded(command, this.sslIsActive, this.keyFile, this.certFile);
651+
return clientHelper.executeCommandDecoded(command, this.sslContext);
653652
}
654653
}

src/main/java/org/testcontainers/containers/TarantoolContainer.java

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,13 @@ public class TarantoolContainer extends GenericContainer<TarantoolContainer>
3737
private String password = API_PASSWORD;
3838
private String host = DEFAULT_HOST;
3939
private Integer port = DEFAULT_PORT;
40-
private Boolean sslIsActive = false;
41-
private String keyFile = "";
42-
private String certFile = "";
4340
private TarantoolLogLevel logLevel = LOG_LEVEL;
4441
private Integer memtxMemory = MEMTX_MEMORY;
4542
private String directoryResourcePath = SCRIPT_RESOURCE_DIRECTORY;
4643
private String scriptFileName = SCRIPT_FILENAME;
4744
private String instanceDir = INSTANCE_DIR;
4845
private boolean useFixedPorts = false;
46+
private SslContext sslContext;
4947

5048
private final TarantoolContainerClientHelper clientHelper;
5149

@@ -171,29 +169,15 @@ public TarantoolContainer withPassword(String password) {
171169

172170

173171
/**
174-
* Specify SSL as connection transport.
172+
* Specify SSL as connection transport. And path to key and cert files inside your container for mTLS connection
175173
* Warning! SSL must be set as default transport on your tarantool cluster.
176174
* Supported only in Tarantool Enterprise
177175
*
178176
* @return this container instance
179177
*/
180-
public TarantoolContainer withSsl() {
178+
public TarantoolContainer withSslContext(SslContext sslContext) {
181179
checkNotRunning();
182-
this.sslIsActive = true;
183-
return this;
184-
}
185-
186-
/**
187-
* Specify path to key and cert files inside your container for SSL connection.
188-
* Warning! SSL must be set as default transport on your tarantool cluster.
189-
* Supported only in Tarantool Enterprise
190-
*
191-
* @return this container instance
192-
*/
193-
public TarantoolContainer withKeyAndCertFiles(String keyFile, String certFile) {
194-
checkNotRunning();
195-
this.keyFile = keyFile;
196-
this.certFile = certFile;
180+
this.sslContext = sslContext;
197181
return this;
198182
}
199183

@@ -370,21 +354,21 @@ protected void containerIsStopping(InspectContainerResponse containerInfo) {
370354

371355
@Override
372356
public Container.ExecResult executeScript(String scriptResourcePath) throws Exception {
373-
return clientHelper.executeScript(scriptResourcePath, this.sslIsActive, this.keyFile, this.certFile);
357+
return clientHelper.executeScript(scriptResourcePath, this.sslContext);
374358
}
375359

376360
@Override
377361
public <T> T executeScriptDecoded(String scriptResourcePath) throws Exception {
378-
return clientHelper.executeScriptDecoded(scriptResourcePath, this.sslIsActive, this.keyFile, this.certFile);
362+
return clientHelper.executeScriptDecoded(scriptResourcePath, this.sslContext);
379363
}
380364

381365
@Override
382366
public Container.ExecResult executeCommand(String command) throws Exception {
383-
return clientHelper.executeCommand(command, this.sslIsActive, this.keyFile, this.certFile);
367+
return clientHelper.executeCommand(command, this.sslContext);
384368
}
385369

386370
@Override
387371
public <T> T executeCommandDecoded(String command) throws Exception {
388-
return clientHelper.executeCommandDecoded(command, this.sslIsActive, this.keyFile, this.certFile);
372+
return clientHelper.executeCommandDecoded(command, this.sslContext);
389373
}
390374
}

src/main/java/org/testcontainers/containers/TarantoolContainerClientHelper.java

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -60,19 +60,19 @@ public final class TarantoolContainerClientHelper {
6060
this.container = container;
6161
}
6262

63-
public Container.ExecResult executeScript(String scriptResourcePath, Boolean sslIsActive, String keyFile, String certFile) throws IOException, InterruptedException {
63+
public Container.ExecResult executeScript(String scriptResourcePath, SslContext sslContext) throws IOException, InterruptedException {
6464
if (!container.isRunning()) {
6565
throw new IllegalStateException("Cannot execute scripts in stopped container");
6666
}
6767

6868
String scriptName = Paths.get(scriptResourcePath).getFileName().toString();
6969
String containerPath = normalizePath(Paths.get(TMP_DIR, scriptName));
7070
container.copyFileToContainer(MountableFile.forClasspathResource(scriptResourcePath), containerPath);
71-
return executeCommand(String.format("return dofile('%s')", containerPath), sslIsActive, keyFile, certFile);
71+
return executeCommand(String.format("return dofile('%s')", containerPath), sslContext);
7272
}
7373

74-
public <T> T executeScriptDecoded(String scriptResourcePath, Boolean sslIsActive, String keyFile, String certFile) throws IOException, InterruptedException, ExecutionException {
75-
Container.ExecResult result = executeScript(scriptResourcePath, sslIsActive, keyFile, certFile);
74+
public <T> T executeScriptDecoded(String scriptResourcePath, SslContext sslContext) throws IOException, InterruptedException, ExecutionException {
75+
Container.ExecResult result = executeScript(scriptResourcePath, sslContext);
7676

7777
if (result.getExitCode() != 0) {
7878

@@ -91,7 +91,7 @@ public <T> T executeScriptDecoded(String scriptResourcePath, Boolean sslIsActive
9191
return yaml.load(result.getStdout());
9292
}
9393

94-
public Container.ExecResult executeCommand(String command, Boolean sslIsActive, String keyFile, String certFile) throws IOException, InterruptedException {
94+
public Container.ExecResult executeCommand(String command, SslContext sslContext) throws IOException, InterruptedException {
9595
if (!container.isRunning()) {
9696
throw new IllegalStateException("Cannot execute commands in stopped container");
9797
}
@@ -100,20 +100,24 @@ public Container.ExecResult executeCommand(String command, Boolean sslIsActive,
100100
command = command.replace("\'", "\\\'");
101101

102102
String bashCommand;
103-
if (!keyFile.isEmpty() && !certFile.isEmpty()) {
103+
// No SSL
104+
if (sslContext == null) {
105+
bashCommand = String.format(COMMAND_TEMPLATE,
106+
container.getHost(), container.getInternalPort(),
107+
container.getUsername(), container.getPassword(),
108+
command
109+
);
110+
// mTLS
111+
} else if (sslContext.getKeyFile() != null && sslContext.getCertFile() != null) {
104112
bashCommand = String.format(MTLS_COMMAND_TEMPLATE,
105113
container.getHost(), container.getInternalPort(),
106-
keyFile, certFile,
114+
sslContext.getKeyFile(), sslContext.getCertFile(),
107115
container.getUsername(), container.getPassword(),
108116
command
109117
);
118+
// SSL
110119
} else {
111-
String commandTemplate = COMMAND_TEMPLATE;
112-
if (sslIsActive) {
113-
commandTemplate = SSL_COMMAND_TEMPLATE;
114-
}
115-
116-
bashCommand = String.format(commandTemplate,
120+
bashCommand = String.format(SSL_COMMAND_TEMPLATE,
117121
container.getHost(), container.getInternalPort(),
118122
container.getUsername(), container.getPassword(),
119123
command
@@ -123,8 +127,8 @@ public Container.ExecResult executeCommand(String command, Boolean sslIsActive,
123127
return container.execInContainer("sh", "-c", bashCommand);
124128
}
125129

126-
public <T> T executeCommandDecoded(String command, Boolean sslIsActive, String keyFile, String certFile) throws IOException, InterruptedException {
127-
Container.ExecResult result = executeCommand(command, sslIsActive, keyFile, certFile);
130+
public <T> T executeCommandDecoded(String command, SslContext sslContext) throws IOException, InterruptedException {
131+
Container.ExecResult result = executeCommand(command, sslContext);
128132

129133
if (result.getExitCode() != 0) {
130134
throw new IllegalStateException(String.format(EXECUTE_COMMAND_ERROR_TEMPLATE,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package org.testcontainers.containers.Enterprise;
2+
3+
import org.junit.jupiter.api.Assertions;
4+
import org.junit.jupiter.api.BeforeAll;
5+
import org.junit.jupiter.api.Test;
6+
import org.slf4j.LoggerFactory;
7+
import org.testcontainers.containers.CartridgeContainerTestUtils;
8+
import org.testcontainers.containers.Container.ExecResult;
9+
import org.testcontainers.containers.SslContext;
10+
import org.testcontainers.containers.TarantoolCartridgeBootstrapFromYamlTest;
11+
import org.testcontainers.containers.TarantoolCartridgeContainer;
12+
import org.testcontainers.containers.TarantoolContainer;
13+
import org.testcontainers.containers.TarantoolImageParams;
14+
import org.testcontainers.containers.output.Slf4jLogConsumer;
15+
import org.testcontainers.junit.jupiter.Container;
16+
import org.testcontainers.junit.jupiter.Testcontainers;
17+
import org.testcontainers.utility.MountableFile;
18+
19+
import java.io.File;
20+
import java.time.Duration;
21+
import java.util.HashMap;
22+
import java.util.Map;
23+
24+
import static org.junit.jupiter.api.Assertions.assertEquals;
25+
26+
/**
27+
* @author Ivan Dneprov
28+
*/
29+
@Testcontainers
30+
public class TarantoolCartridgeMtlsContainerTestEnterprise {
31+
32+
private static TarantoolCartridgeContainer containerWithSsl;
33+
34+
@BeforeAll
35+
public static void setUp() throws Exception {
36+
final File dockerfile = new File(
37+
TarantoolMTlsContainerTestEnterprise.class.getClassLoader()
38+
.getResource("enterprise/ssl/mtls/Dockerfile").toURI()
39+
);
40+
final Map<String, String> buildArgs = new HashMap<>();
41+
buildArgs.put("DOWNLOAD_SDK_URI", System.getenv("DOWNLOAD_SDK_URI"));
42+
buildArgs.put("SDK_VERSION", System.getenv("SDK_VERSION"));
43+
44+
containerWithSsl = new TarantoolCartridgeContainer(
45+
new TarantoolImageParams("tarantool-enterprise", dockerfile, buildArgs),
46+
"cartridge/instances.yml", "cartridge/replicasets.yml")
47+
.withCopyFileToContainer(MountableFile.forClasspathResource("cartridge"), "/app")
48+
.withCopyFileToContainer(MountableFile.forClasspathResource("enterprise/ssl"), "/app")
49+
.withStartupTimeout(Duration.ofSeconds(300))
50+
.withLogConsumer(new Slf4jLogConsumer(
51+
LoggerFactory.getLogger(TarantoolCartridgeMtlsContainerTestEnterprise.class)));
52+
53+
if (!containerWithSsl.isRunning()) {
54+
containerWithSsl.start();
55+
}
56+
}
57+
58+
@Test
59+
public void test_clientWithSsl_shouldWork() throws Exception {
60+
HashMap result = containerWithSsl.executeCommandDecoded("box.cfg.listen");
61+
HashMap params = (HashMap) result.get("params");
62+
assertEquals("ssl", params.get("transport"));
63+
assertEquals("server.key", params.get("ssl_key_file"));
64+
assertEquals("server.crt", params.get("ssl_cert_file"));
65+
assertEquals("ca.crt", params.get("ssl_ca_file"));
66+
}
67+
68+
@Test
69+
public void test_StaticClusterContainer_StartsSuccessfully_ifFilesAreCopied() throws Exception {
70+
CartridgeContainerTestUtils.executeProfileReplaceSmokeTest(containerWithSsl);
71+
}
72+
}

src/test/java/org/testcontainers/containers/Enterprise/TarantoolMTlsContainerTestEnterprise.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import org.junit.jupiter.api.Test;
55
import org.slf4j.Logger;
66
import org.slf4j.LoggerFactory;
7+
import org.testcontainers.containers.SslContext;
78
import org.testcontainers.containers.TarantoolContainer;
89
import org.testcontainers.containers.TarantoolImageParams;
910
import org.testcontainers.containers.output.Slf4jLogConsumer;
@@ -40,8 +41,7 @@ public static void setUp() throws Exception {
4041
.withPassword("test_password")
4142
.withMemtxMemory(256 * 1024 * 1024)
4243
.withDirectoryBinding("enterprise/ssl/mtls")
43-
.withSsl()
44-
.withKeyAndCertFiles("/app/ca.key", "/app/ca.crt")
44+
.withSslContext(SslContext.getSslContext("/app/ca.key", "/app/ca.crt"))
4545
.withLogConsumer(new Slf4jLogConsumer(log));
4646

4747
if (!containerWithSsl.isRunning()) {

src/test/java/org/testcontainers/containers/Enterprise/TarantoolSslContainerTestEnterprise.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import org.junit.jupiter.api.Test;
55
import org.slf4j.Logger;
66
import org.slf4j.LoggerFactory;
7+
import org.testcontainers.containers.SslContext;
78
import org.testcontainers.containers.TarantoolContainer;
89
import org.testcontainers.containers.TarantoolImageParams;
910
import org.testcontainers.containers.output.Slf4jLogConsumer;
@@ -40,7 +41,7 @@ public static void setUp() throws Exception {
4041
.withPassword("test_password")
4142
.withMemtxMemory(256 * 1024 * 1024)
4243
.withDirectoryBinding("enterprise/ssl")
43-
.withSsl()
44+
.withSslContext(SslContext.getSslContext())
4445
.withLogConsumer(new Slf4jLogConsumer(log));
4546

4647
if (!containerWithSsl.isRunning()) {

0 commit comments

Comments
 (0)