Skip to content

Commit 8269309

Browse files
committed
feat(testcontainers): add script execution method
1 parent d6a9ea4 commit 8269309

File tree

4 files changed

+212
-30
lines changed

4 files changed

+212
-30
lines changed

tarantool-core/src/test/java/io/tarantool/core/integration/MVCCStreamsTest.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import org.junit.jupiter.api.Timeout;
1818
import org.msgpack.value.ArrayValue;
1919
import org.msgpack.value.ValueFactory;
20-
import org.testcontainers.containers.tarantool.Tarantool3Container;
20+
import org.testcontainers.containers.tarantool.Tarantool2Container;
2121
import org.testcontainers.junit.jupiter.Container;
2222
import org.testcontainers.junit.jupiter.Testcontainers;
2323

@@ -59,10 +59,9 @@ public class MVCCStreamsTest extends BaseTest {
5959
private static final ArrayValue keyB = ValueFactory.newArray(ValueFactory.newString("key_d"));
6060

6161
@Container
62-
private static final Tarantool3Container tt =
63-
new Tarantool3Container(DockerImageName.parse("tarantool/tarantool"), "test-node")
64-
.withEnv(ENV_MAP);
65-
//.withScriptFileName("server-mvcc.lua");
62+
private static final Tarantool2Container tt =
63+
new Tarantool2Container.Builder(DockerImageName.parse("tarantool/tarantool"), "server-mvcc.lua")
64+
.build().withEnv(ENV_MAP);
6665

6766
@BeforeAll
6867
public static void setUp() throws Exception {

testcontainers/src/main/java/org/testcontainers/containers/tarantool/Tarantool2Container.java

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,13 @@
1414

1515
import com.github.dockerjava.api.command.InspectContainerResponse;
1616
import org.testcontainers.containers.BindMode;
17+
import org.testcontainers.containers.Container;
1718
import org.testcontainers.containers.ContainerLaunchException;
1819
import org.testcontainers.containers.GenericContainer;
1920
import org.testcontainers.containers.SelinuxContext;
2021
import org.testcontainers.containers.utils.Utils;
2122
import org.testcontainers.utility.DockerImageName;
2223

23-
/**
24-
* Testcontainers for Tarantool version 2.11.x.
25-
*
26-
* @implNote The implementation assumes that you will not configure the following parameters in the
27-
* init script (they adjusted automatically):
28-
* <ul>
29-
* <li>{@code listen}
30-
* <li>{@code memtx_dir}
31-
* <li>{@code wal_dir}
32-
* <li>{@code vinyl_dir}
33-
* </ul>
34-
*/
3524
public class Tarantool2Container extends GenericContainer<Tarantool2Container>
3625
implements TarantoolContainer<Tarantool2Container> {
3726

@@ -45,11 +34,14 @@ public class Tarantool2Container extends GenericContainer<Tarantool2Container>
4534

4635
private boolean configured;
4736

37+
private final TarantoolContainerLuaExecutor luaExecutor;
38+
4839
private Tarantool2Container(DockerImageName dockerImageName, String initScript, String node) {
4940
super(dockerImageName);
5041
this.node = node;
5142
this.initScript = initScript;
5243
this.mountPath = Utils.createTempDirectory(this.node);
44+
this.luaExecutor = new TarantoolContainerLuaExecutor(this, TarantoolContainer.DEFAULT_TARANTOOL_PORT);
5345
}
5446

5547
@Override
@@ -150,6 +142,30 @@ public InetSocketAddress internalAddress() {
150142
return new InetSocketAddress(this.node, TarantoolContainer.DEFAULT_TARANTOOL_PORT);
151143
}
152144

145+
public String getExecResult(String command) throws Exception {
146+
return this.luaExecutor.getExecResult(command);
147+
}
148+
149+
public Container.ExecResult executeCommand(String command) throws IOException, InterruptedException {
150+
return luaExecutor.executeCommand(command);
151+
}
152+
153+
public Container.ExecResult executeCommand(String command,
154+
org.testcontainers.containers.utils.SslContext sslContext)
155+
throws IOException, InterruptedException {
156+
return luaExecutor.executeCommand(command, sslContext);
157+
}
158+
159+
public <T> T executeCommandDecoded(String command) throws IOException, InterruptedException {
160+
return luaExecutor.executeCommandDecoded(command);
161+
}
162+
163+
public <T> T executeCommandDecoded(String command,
164+
org.testcontainers.containers.utils.SslContext sslContext)
165+
throws IOException, InterruptedException {
166+
return luaExecutor.executeCommandDecoded(command, sslContext);
167+
}
168+
153169
public static Builder builder(DockerImageName image, Path initScriptPath) {
154170
try {
155171
final String rawScript =

testcontainers/src/main/java/org/testcontainers/containers/tarantool/Tarantool3Container.java

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,15 @@
2121
import com.github.dockerjava.api.command.InspectContainerResponse;
2222
import org.slf4j.Logger;
2323
import org.slf4j.LoggerFactory;
24+
import java.io.IOException;
25+
2426
import org.testcontainers.containers.BindMode;
2527
import org.testcontainers.containers.Container;
2628
import org.testcontainers.containers.ContainerLaunchException;
2729
import org.testcontainers.containers.GenericContainer;
2830
import org.testcontainers.containers.SelinuxContext;
29-
import org.testcontainers.containers.TarantoolContainerClientHelper;
30-
import org.testcontainers.containers.TarantoolContainerOperations;
3131
import org.testcontainers.containers.output.Slf4jLogConsumer;
3232
import org.testcontainers.containers.utils.HttpHost;
33-
import org.testcontainers.containers.utils.SslContext;
3433
import org.testcontainers.containers.utils.Utils;
3534
import org.testcontainers.utility.DockerImageName;
3635
import org.testcontainers.utility.MountableFile;
@@ -60,6 +59,8 @@ public class Tarantool3Container extends GenericContainer<Tarantool3Container>
6059

6160
private boolean configured;
6261

62+
private final TarantoolContainerLuaExecutor luaExecutor;
63+
6364
public Tarantool3Container(DockerImageName dockerImageName, String node) {
6465
super(dockerImageName);
6566
this.instanceUuid = UUID.randomUUID();
@@ -70,6 +71,7 @@ public Tarantool3Container(DockerImageName dockerImageName, String node) {
7071
this.lock = new ReentrantLock();
7172
this.isClosed = new AtomicBoolean();
7273
this.etcdAddresses = new ArrayList<>(1);
74+
this.luaExecutor = new TarantoolContainerLuaExecutor(this, TarantoolContainer.DEFAULT_TARANTOOL_PORT);
7375
}
7476

7577
public Tarantool3Container withEtcdAddresses(HttpHost... etcdAddresses) {
@@ -93,16 +95,7 @@ public Tarantool3Container withEtcdPrefix(String etcdPrefix) {
9395
}
9496

9597
public String getExecResult(String command) throws Exception {
96-
ExecResult result = this.execInContainer(command);
97-
if (result.getExitCode() != 0) {
98-
throw new RuntimeException("Cannot execute script: " + command);
99-
}
100-
return result.getStdout()
101-
.trim()
102-
.replace("\n", "")
103-
.replace("...", "")
104-
.replace("--", "")
105-
.trim();
98+
return this.luaExecutor.getExecResult(command);
10699
}
107100

108101
@Override
@@ -278,6 +271,26 @@ public void stop() {
278271
}
279272
}
280273

274+
public Container.ExecResult executeCommand(String command) throws IOException, InterruptedException {
275+
return luaExecutor.executeCommand(command);
276+
}
277+
278+
public Container.ExecResult executeCommand(String command,
279+
org.testcontainers.containers.utils.SslContext sslContext)
280+
throws IOException, InterruptedException {
281+
return luaExecutor.executeCommand(command, sslContext);
282+
}
283+
284+
public <T> T executeCommandDecoded(String command) throws IOException, InterruptedException {
285+
return luaExecutor.executeCommandDecoded(command);
286+
}
287+
288+
public <T> T executeCommandDecoded(String command,
289+
org.testcontainers.containers.utils.SslContext sslContext)
290+
throws IOException, InterruptedException {
291+
return luaExecutor.executeCommandDecoded(command, sslContext);
292+
}
293+
281294
private static String joinEtcdAddresses(List<HttpHost> etcdAddresses) {
282295
if (etcdAddresses == null || etcdAddresses.isEmpty()) {
283296
return null;
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/*
2+
* Copyright (c) 2025 VK DIGITAL TECHNOLOGIES LIMITED LIABILITY COMPANY
3+
* All Rights Reserved.
4+
*/
5+
6+
package org.testcontainers.containers.tarantool;
7+
8+
import java.io.IOException;
9+
10+
import org.testcontainers.containers.Container;
11+
import org.testcontainers.containers.utils.SslContext;
12+
import org.yaml.snakeyaml.Yaml;
13+
14+
public final class TarantoolContainerLuaExecutor {
15+
16+
private static final String EXECUTE_COMMAND_ERROR_TEMPLATE =
17+
"Executed command \"%s\" with exit code %d, stderr: \"%s\", stdout: \"%s\"";
18+
private static final String MTLS_COMMAND_TEMPLATE =
19+
"echo \" " +
20+
" print(require('yaml').encode( " +
21+
" {require('net.box').connect( " +
22+
" { uri='%s:%d', params = { transport='ssl', ssl_key_file = '%s', ssl_cert_file = '%s'" +
23+
" }}, " +
24+
" { user = '%s', password = '%s' } " +
25+
" ):eval('%s')}) " +
26+
" ); " +
27+
" os.exit(); " +
28+
"\" > container-tmp.lua &&" +
29+
" tarantool container-tmp.lua";
30+
private static final String SSL_COMMAND_TEMPLATE =
31+
"echo \" " +
32+
" print(require('yaml').encode( " +
33+
" {require('net.box').connect( " +
34+
" { uri='%s:%d', params = { transport='ssl' }}, " +
35+
" { user = '%s', password = '%s' } " +
36+
" ):eval('%s')}) " +
37+
" ); " +
38+
" os.exit(); " +
39+
"\" > container-tmp.lua &&" +
40+
" tarantool container-tmp.lua";
41+
private static final String COMMAND_TEMPLATE = "echo \" " +
42+
" print(require('yaml').encode( " +
43+
" {require('net.box').connect( " +
44+
" '%s:%d', " +
45+
" { user = '%s', password = '%s' } " +
46+
" ):eval('%s')}) " +
47+
" ); " +
48+
" os.exit(); " +
49+
"\" > container-tmp.lua &&" +
50+
" tarantool container-tmp.lua";
51+
52+
private static final String ENV_USERNAME_CMD =
53+
"echo ${TARANTOOL_USER_NAME:-${TT_CLI_USERNAME:-guest}}";
54+
private static final String ENV_PASSWORD_CMD =
55+
"echo ${TARANTOOL_USER_PASSWORD:-${TT_CLI_PASSWORD:-}}";
56+
57+
private static final Yaml YAML = new Yaml();
58+
59+
private final Container<?> container;
60+
private final int port;
61+
62+
public TarantoolContainerLuaExecutor(Container<?> container, int port) {
63+
this.container = container;
64+
this.port = port;
65+
}
66+
67+
public String getExecResult(String command) throws Exception {
68+
Container.ExecResult result = this.container.execInContainer(command);
69+
if (result.getExitCode() != 0) {
70+
throw new RuntimeException("Cannot execute script: " + command);
71+
}
72+
return result.getStdout()
73+
.trim()
74+
.replace("\n", "")
75+
.replace("...", "")
76+
.replace("--", "")
77+
.trim();
78+
}
79+
80+
public Container.ExecResult executeCommand(String command) throws IOException, InterruptedException {
81+
return executeCommand(command, null);
82+
}
83+
84+
public Container.ExecResult executeCommand(String command, SslContext sslContext)
85+
throws IOException, InterruptedException {
86+
if (!container.isRunning()) {
87+
throw new IllegalStateException("Cannot execute commands in stopped container");
88+
}
89+
90+
command = command.replace("\"", "\\\"");
91+
command = command.replace("\'", "\\\'");
92+
93+
String username = getUsernameFromEnv();
94+
String password = getPasswordFromEnv();
95+
String host = "localhost";
96+
97+
String bashCommand;
98+
if (sslContext == null) {
99+
bashCommand = String.format(COMMAND_TEMPLATE,
100+
host, port,
101+
username, password,
102+
command
103+
);
104+
} else if (sslContext.getKeyFile() != null && sslContext.getCertFile() != null) {
105+
bashCommand = String.format(MTLS_COMMAND_TEMPLATE,
106+
host, port,
107+
sslContext.getKeyFile(), sslContext.getCertFile(),
108+
username, password,
109+
command
110+
);
111+
} else {
112+
bashCommand = String.format(SSL_COMMAND_TEMPLATE,
113+
host, port,
114+
username, password,
115+
command
116+
);
117+
}
118+
119+
return container.execInContainer("sh", "-c", bashCommand);
120+
}
121+
122+
public <T> T executeCommandDecoded(String command) throws IOException, InterruptedException {
123+
return executeCommandDecoded(command, null);
124+
}
125+
126+
public <T> T executeCommandDecoded(String command, SslContext sslContext)
127+
throws IOException, InterruptedException {
128+
Container.ExecResult result = executeCommand(command, sslContext);
129+
130+
if (result.getExitCode() != 0) {
131+
throw new IllegalStateException(String.format(EXECUTE_COMMAND_ERROR_TEMPLATE,
132+
command, result.getExitCode(), result.getStderr(), result.getStdout()));
133+
}
134+
135+
return YAML.load(result.getStdout());
136+
}
137+
138+
private String getUsernameFromEnv() throws IOException, InterruptedException {
139+
Container.ExecResult result = container.execInContainer("sh", "-c", ENV_USERNAME_CMD);
140+
if (result.getExitCode() != 0) {
141+
return "guest";
142+
}
143+
String username = result.getStdout().trim();
144+
return username.isEmpty() ? "guest" : username;
145+
}
146+
147+
private String getPasswordFromEnv() throws IOException, InterruptedException {
148+
Container.ExecResult result = container.execInContainer("sh", "-c", ENV_PASSWORD_CMD);
149+
if (result.getExitCode() != 0) {
150+
return "";
151+
}
152+
return result.getStdout().trim();
153+
}
154+
}

0 commit comments

Comments
 (0)