Skip to content

Commit a9bc5b0

Browse files
vrogach2020wey1and
andauthored
Fix tests + refactoring of cartridge container bootstrap (#46)
* Start tarantool in containers under root by default - fixes troubles with mount permissions * Fix bootstrap from yaml errors checking * Refactor cartridge tests * Add checks before bootstrap of cartridge Co-authored-by: Oleg Kuznetsov <[email protected]>
1 parent cd55509 commit a9bc5b0

14 files changed

+183
-378
lines changed

pom.xml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,6 @@
9191
<version>1.2.3</version>
9292
<scope>test</scope>
9393
</dependency>
94-
<dependency>
95-
<groupId>com.github.docker-java</groupId>
96-
<artifactId>docker-java</artifactId>
97-
<version>3.2.13</version>
98-
</dependency>
9994
</dependencies>
10095

10196
<build>

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

Lines changed: 90 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@
77
import java.net.URL;
88
import java.util.Arrays;
99
import java.util.Collections;
10+
import java.util.HashMap;
1011
import java.util.List;
1112
import java.util.Map;
1213
import java.util.concurrent.CompletableFuture;
1314
import java.util.concurrent.ExecutionException;
14-
import java.util.concurrent.Future;
1515
import java.util.concurrent.TimeoutException;
16+
import java.util.function.Supplier;
1617

1718
import static org.testcontainers.containers.PathUtils.normalizePath;
1819

@@ -102,8 +103,11 @@ public class TarantoolCartridgeContainer extends GenericContainer<TarantoolCartr
102103
private static final String ENV_TARANTOOL_RUNDIR = "TARANTOOL_RUNDIR";
103104
private static final String ENV_TARANTOOL_DATADIR = "TARANTOOL_DATADIR";
104105
private static final String ENV_TARANTOOL_INSTANCES_FILE = "TARANTOOL_INSTANCES_FILE";
106+
105107
private final CartridgeConfigParser instanceFileParser;
106108
private final TarantoolContainerClientHelper clientHelper;
109+
private final String TARANTOOL_RUN_DIR;
110+
107111
private boolean useFixedPorts = false;
108112
private String routerHost = ROUTER_HOST;
109113
private int routerPort = ROUTER_PORT;
@@ -113,7 +117,6 @@ public class TarantoolCartridgeContainer extends GenericContainer<TarantoolCartr
113117
private String directoryResourcePath = SCRIPT_RESOURCE_DIRECTORY;
114118
private String instanceDir = INSTANCE_DIR;
115119
private String topologyConfigurationFile;
116-
private String replicasetsFileName;
117120

118121
/**
119122
* Create a container with default image and specified instances file from the classpath resources. Assumes that
@@ -181,35 +184,39 @@ public TarantoolCartridgeContainer(String dockerFile, String buildImageName,
181184
*/
182185
public TarantoolCartridgeContainer(String dockerFile, String buildImageName, String instancesFile,
183186
String topologyConfigurationFile, final Map<String, String> buildArgs) {
184-
this(withArguments(buildImage(dockerFile, buildImageName), instancesFile, buildArgs),
185-
instancesFile, topologyConfigurationFile);
187+
this(buildImage(dockerFile, buildImageName), instancesFile, topologyConfigurationFile, buildArgs);
186188
}
187189

190+
private TarantoolCartridgeContainer(ImageFromDockerfile image, String instancesFile, String topologyConfigurationFile,
191+
Map<String, String> buildArgs) {
192+
super(withBuildArgs(image, buildArgs));
193+
194+
TARANTOOL_RUN_DIR = mergeBuildArguments(buildArgs).getOrDefault(ENV_TARANTOOL_RUNDIR, "/tmp/run");
188195

189-
private TarantoolCartridgeContainer(Future<String> image, String instancesFile, String topologyConfigurationFile) {
190-
super(image);
191196
if (instancesFile == null || instancesFile.isEmpty()) {
192197
throw new IllegalArgumentException("Instance file name must not be null or empty");
193198
}
194199
if (topologyConfigurationFile == null || topologyConfigurationFile.isEmpty()) {
195200
throw new IllegalArgumentException("Topology configuration file must not be null or empty");
196201
}
197-
String fileType = topologyConfigurationFile.substring(topologyConfigurationFile.lastIndexOf('.') + 1);
198-
if (fileType.equals("lua")) {
199-
this.topologyConfigurationFile = topologyConfigurationFile;
200-
}else{
201-
this.replicasetsFileName = topologyConfigurationFile.substring(topologyConfigurationFile.lastIndexOf('/')+1);
202-
}
202+
this.topologyConfigurationFile = topologyConfigurationFile;
203203
this.instanceFileParser = new CartridgeConfigParser(instancesFile);
204204
this.clientHelper = new TarantoolContainerClientHelper(this);
205205
}
206206

207-
private static Future<String> withArguments(ImageFromDockerfile image, String instancesFile,
208-
final Map<String, String> buildArgs) {
209-
if (!buildArgs.isEmpty()) {
210-
image.withBuildArgs(buildArgs);
207+
private static ImageFromDockerfile withBuildArgs(ImageFromDockerfile image, Map<String, String> buildArgs) {
208+
Map<String, String> args = mergeBuildArguments(buildArgs);
209+
210+
if (!args.isEmpty()) {
211+
image.withBuildArgs(args);
211212
}
212213

214+
return image;
215+
}
216+
217+
private static Map<String, String> mergeBuildArguments(Map<String, String> buildArgs) {
218+
Map<String, String> args = new HashMap<>(buildArgs);
219+
213220
for (String envVariable : Arrays.asList(
214221
ENV_TARANTOOL_VERSION,
215222
ENV_TARANTOOL_SERVER_USER,
@@ -222,11 +229,11 @@ private static Future<String> withArguments(ImageFromDockerfile image, String in
222229
ENV_TARANTOOL_INSTANCES_FILE
223230
)) {
224231
String variableValue = System.getenv(envVariable);
225-
if (variableValue != null) {
226-
image.withBuildArg(envVariable, variableValue);
232+
if (variableValue != null && !args.containsKey(envVariable)) {
233+
args.put(envVariable, variableValue);
227234
}
228235
}
229-
return image;
236+
return args;
230237
}
231238

232239
private static ImageFromDockerfile buildImage(String dockerFile, String buildImageName) {
@@ -458,38 +465,25 @@ protected void containerIsStarting(InspectContainerResponse containerInfo) {
458465
}
459466

460467
private boolean setupTopology() {
461-
if (topologyConfigurationFile == null) {
462-
String runDirPath = null;
463-
try {
464-
Container.ExecResult envVariablesContainer = this.execInContainer("env");
465-
String stdout = envVariablesContainer.getStdout();
466-
int exitCode = envVariablesContainer.getExitCode();
467-
if (exitCode != 0) {
468-
logger().error("Failed to bootstrap replica sets topology: {}", stdout);
469-
}
470-
int startInd = stdout.lastIndexOf(ENV_TARANTOOL_RUNDIR + "=");
471-
try {
472-
runDirPath = stdout.substring(startInd,
473-
stdout.indexOf('\n', startInd)).split("=")[1];
474-
} catch (Exception e) {
475-
logger().error("Missing dir-run environment variable: {}", e.getMessage());
476-
}
468+
String fileType = topologyConfigurationFile.substring(topologyConfigurationFile.lastIndexOf('.') + 1);
477469

478-
} catch (Exception e) {
479-
logger().error("Failed to get environment variables: {}", e.getMessage());
480-
}
470+
if (fileType.equals("yml")) {
471+
String replicasetsFileName = topologyConfigurationFile
472+
.substring(topologyConfigurationFile.lastIndexOf('/') + 1);
481473

482474
try {
483-
Container.ExecResult lsResult = this.execInContainer("cartridge", "replicasets", "--run-dir=" + runDirPath,
484-
"--file=" + this.replicasetsFileName, "setup", "--bootstrap-vshard");
485-
String stdout = lsResult.getStdout();
486-
int exitCode = lsResult.getExitCode();
487-
if (exitCode != 0) {
488-
logger().error("Failed to bootstrap replica sets topology: {}", stdout);
475+
Container.ExecResult result = execInContainer("cartridge",
476+
"replicasets",
477+
"--run-dir=" + TARANTOOL_RUN_DIR,
478+
"--file=" + replicasetsFileName, "setup", "--bootstrap-vshard");
479+
if (result.getExitCode() != 0) {
480+
throw new RuntimeException("Failed to change the app topology via cartridge CLI: "
481+
+ result.getStdout());
489482
}
490483
} catch (Exception e) {
491-
logger().error("Failed to bootstrap replica sets topology: {}", e.getMessage());
484+
throw new RuntimeException("Failed to change the app topology: " + e.getMessage());
492485
}
486+
493487
} else {
494488
try {
495489
executeScript(topologyConfigurationFile).get();
@@ -539,14 +533,65 @@ private void bootstrapVshard() {
539533
protected void containerIsStarted(InspectContainerResponse containerInfo, boolean reused) {
540534
super.containerIsStarted(containerInfo, reused);
541535

536+
waitUntilRouterIsUp(60);
542537
retryingSetupTopology();
538+
// wait until Roles are configured
539+
waitUntilCartridgeIsHealthy(10);
543540
bootstrapVshard();
544541

545542
logger().info("Tarantool Cartridge cluster is started");
546543
logger().info("Tarantool Cartridge router is listening at {}:{}", getRouterHost(), getRouterPort());
547544
logger().info("Tarantool Cartridge HTTP API is available at {}:{}", getAPIHost(), getAPIPort());
548545
}
549546

547+
private void waitUntilRouterIsUp(int secondsToWait) {
548+
waitUntilTrue(secondsToWait, this::routerIsUp);
549+
}
550+
551+
private void waitUntilCartridgeIsHealthy(int secondsToWait) {
552+
waitUntilTrue(secondsToWait, this::isCartridgeHealthy);
553+
}
554+
555+
private void waitUntilTrue(int secondsToWait, Supplier<Boolean> waitFunc) {
556+
int secondsPassed = 0;
557+
boolean result = waitFunc.get();
558+
while (!result && secondsPassed < secondsToWait) {
559+
result = waitFunc.get();
560+
try {
561+
Thread.sleep(1000);
562+
} catch (InterruptedException e) {
563+
break;
564+
}
565+
}
566+
if (!result) {
567+
throw new RuntimeException("Failed to change the app topology after retry");
568+
}
569+
}
570+
571+
private boolean routerIsUp() {
572+
String healthyCmd = " local cartridge = package.loaded['cartridge']" +
573+
" return assert(cartridge ~= nil)";
574+
try {
575+
List<?> result = executeCommand(healthyCmd).get();
576+
return (Boolean) result.get(0);
577+
} catch (Exception e) {
578+
logger().warn("Error while waiting for router instance to be up: " + e.getMessage());
579+
return false;
580+
}
581+
}
582+
583+
private boolean isCartridgeHealthy() {
584+
String healthyCmd = " local cartridge = package.loaded['cartridge']" +
585+
" return assert(cartridge) and assert(cartridge.is_healthy())";
586+
try {
587+
List<?> result = executeCommand(healthyCmd).get();
588+
return (Boolean) result.get(0);
589+
} catch (Exception e) {
590+
logger().warn("Error while waiting for cartridge healthy state: " + e.getMessage());
591+
return false;
592+
}
593+
}
594+
550595
@Override
551596
public CompletableFuture<List<?>> executeScript(String scriptResourcePath) throws Exception {
552597
return clientHelper.executeScript(scriptResourcePath);

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ private TarantoolClient<TarantoolTuple, TarantoolResult<TarantoolTuple>> createC
3737
.withCredentials(container.getUsername(), container.getPassword())
3838
.withAddress(container.getHost(), container.getPort())
3939
.withRequestTimeout(5000)
40-
.withRetryingByNumberOfAttempts(10,
41-
TarantoolRequestRetryPolicies.retryNetworkErrors()
42-
.or(TarantoolRequestRetryPolicies.retryNetworkErrors()), b -> b.withDelay(100))
40+
.withRetryingByNumberOfAttempts(15,
41+
TarantoolRequestRetryPolicies.retryNetworkErrors(),
42+
b -> b.withDelay(100))
4343
.build();
4444
}
4545

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
import com.github.dockerjava.api.command.BuildImageCmd;
55
import com.github.dockerjava.api.command.BuildImageResultCallback;
66
import com.github.dockerjava.api.model.Image;
7-
import com.github.dockerjava.core.DockerClientBuilder;
8-
import org.apache.commons.lang3.StringUtils;
7+
import org.testcontainers.DockerClientFactory;
98

109
import java.util.Arrays;
1110
import java.util.Collection;
@@ -21,8 +20,6 @@
2120
*/
2221
class TarantoolContainerImageHelper {
2322

24-
private static final DockerClient dockerClient = DockerClientBuilder.getInstance().build();
25-
2623
private TarantoolContainerImageHelper() {
2724
}
2825

@@ -35,7 +32,7 @@ private TarantoolContainerImageHelper() {
3532
static String getImage(TarantoolImageParams imageParams) {
3633
final String tag = imageParams.getTag();
3734

38-
if (StringUtils.isEmpty(tag)) {
35+
if (tag == null || tag.isEmpty()) {
3936
throw new IllegalArgumentException("Image tag is null or empty!");
4037
}
4138

@@ -52,7 +49,7 @@ static String getImage(TarantoolImageParams imageParams) {
5249
* @param imageParams parameters for building tarantool image
5350
*/
5451
private static void buildImage(TarantoolImageParams imageParams) {
55-
final BuildImageCmd buildImageCmd = dockerClient.buildImageCmd(imageParams.getDockerfile());
52+
final BuildImageCmd buildImageCmd = getDockerClient().buildImageCmd(imageParams.getDockerfile());
5653

5754
final Map<String, String> buildArgs = imageParams.getBuildArgs();
5855
for (Map.Entry<String, String> entry : buildArgs.entrySet()) {
@@ -71,11 +68,16 @@ private static void buildImage(TarantoolImageParams imageParams) {
7168
* @return true if image exist and false if not
7269
*/
7370
private static boolean hasImage(String tag) {
74-
final List<Image> images = dockerClient.listImagesCmd().exec();
71+
final List<Image> images = getDockerClient().listImagesCmd().exec();
7572
return images.stream()
7673
.map(Image::getRepoTags)
7774
.map(Arrays::asList)
7875
.flatMap(Collection::stream)
7976
.anyMatch(repoTag -> repoTag.equals(tag));
8077
}
78+
79+
80+
private static DockerClient getDockerClient() {
81+
return DockerClientFactory.instance().client();
82+
}
8183
}

src/main/resources/Dockerfile

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
FROM centos:7 AS tarantool-base
22
ARG TARANTOOL_VERSION=2.8
3-
ARG TARANTOOL_SERVER_USER="tarantool"
4-
ARG TARANTOOL_SERVER_UID=1000
5-
ARG TARANTOOL_SERVER_GROUP="tarantool"
6-
ARG TARANTOOL_SERVER_GID=1000
3+
ARG TARANTOOL_SERVER_USER="root"
4+
ARG TARANTOOL_SERVER_GROUP="root"
75
ARG TARANTOOL_WORKDIR="/app"
86
ARG TARANTOOL_RUNDIR="/tmp/run"
97
ARG TARANTOOL_DATADIR="/tmp/data"
@@ -15,9 +13,7 @@ ENV TARANTOOL_INSTANCES_FILE=$TARANTOOL_INSTANCES_FILE
1513
RUN curl -L https://tarantool.io/installer.sh | VER=$TARANTOOL_VERSION /bin/bash -s -- --repo-only && \
1614
yum -y install cmake make gcc gcc-c++ git unzip tarantool tarantool-devel cartridge-cli && \
1715
yum clean all
18-
RUN groupadd -g $TARANTOOL_SERVER_GID $TARANTOOL_SERVER_GROUP && \
19-
useradd -u $TARANTOOL_SERVER_UID -g $TARANTOOL_SERVER_GID -m -s /bin/bash $TARANTOOL_SERVER_USER \
20-
|| true
16+
RUN groupadd $TARANTOOL_SERVER_GROUP && useradd -m -s /bin/bash $TARANTOOL_SERVER_USER || true
2117
USER $TARANTOOL_SERVER_USER:$TARANTOOL_SERVER_GROUP
2218
RUN cartridge version
2319

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package org.testcontainers.containers;
2+
3+
import org.junit.jupiter.api.Test;
4+
import org.slf4j.LoggerFactory;
5+
import org.testcontainers.containers.output.Slf4jLogConsumer;
6+
import org.testcontainers.junit.jupiter.Container;
7+
import org.testcontainers.junit.jupiter.Testcontainers;
8+
import org.testcontainers.utility.MountableFile;
9+
10+
import java.time.Duration;
11+
12+
/**
13+
* @author Alexey Kuzin
14+
*/
15+
@Testcontainers
16+
public class TarantoolCartridgeBootstrapFromLuaWithFixedPortsTest {
17+
18+
@Container
19+
private static final TarantoolCartridgeContainer container =
20+
new TarantoolCartridgeContainer(
21+
"Dockerfile",
22+
"cartridge/instances_fixedport.yml",
23+
"cartridge/topology_fixedport.lua")
24+
.withCopyFileToContainer(MountableFile.forClasspathResource("cartridge"), "/app")
25+
.withCopyFileToContainer(MountableFile.forClasspathResource("cartridge/instances_fixedport.yml"),"/app/instances.yml")
26+
.withStartupTimeout(Duration.ofSeconds(300))
27+
.withUseFixedPorts(true)
28+
.withAPIPort(18081)
29+
.withRouterPort(13301)
30+
.withLogConsumer(new Slf4jLogConsumer(
31+
LoggerFactory.getLogger(TarantoolCartridgeBootstrapFromLuaWithFixedPortsTest.class)));
32+
33+
@Test
34+
public void test_StaticClusterContainer_StartsSuccessfully_ifFilesAreCopied() throws Exception {
35+
CartridgeContainerTestUtils.executeProfileReplaceSmokeTest(container);
36+
}
37+
}

0 commit comments

Comments
 (0)