Skip to content

Fix problems with ports and directory bindings. #29

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Nov 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
Expand Down Expand Up @@ -98,6 +100,7 @@ public class TarantoolCartridgeContainer extends GenericContainer<TarantoolCartr
private static final String ENV_TARANTOOL_WORKDIR = "TARANTOOL_WORKDIR";
private static final String ENV_TARANTOOL_RUNDIR = "TARANTOOL_RUNDIR";
private static final String ENV_TARANTOOL_DATADIR = "TARANTOOL_DATADIR";
private boolean useFixedPorts = false;

private String routerHost = ROUTER_HOST;
private int routerPort = ROUTER_PORT;
Expand All @@ -106,7 +109,6 @@ public class TarantoolCartridgeContainer extends GenericContainer<TarantoolCartr
private String routerPassword = CARTRIDGE_PASSWORD;
private String directoryResourcePath = SCRIPT_RESOURCE_DIRECTORY;
private String instanceDir = INSTANCE_DIR;
private final String instancesFile;
private final CartridgeConfigParser instanceFileParser;
private final String topologyConfigurationFile;
private final TarantoolContainerClientHelper clientHelper;
Expand All @@ -119,7 +121,7 @@ public class TarantoolCartridgeContainer extends GenericContainer<TarantoolCartr
* @param topologyConfigurationFile path to a topology bootstrap script, relative to the classpath resources
*/
public TarantoolCartridgeContainer(String instancesFile, String topologyConfigurationFile) {
this(withArguments(buildImage()), instancesFile, topologyConfigurationFile);
this(DOCKERFILE, instancesFile, topologyConfigurationFile);
}

/**
Expand All @@ -130,7 +132,7 @@ public TarantoolCartridgeContainer(String instancesFile, String topologyConfigur
* @param topologyConfigurationFile path to a topology bootstrap script, relative to the classpath resources
*/
public TarantoolCartridgeContainer(String dockerFile, String instancesFile, String topologyConfigurationFile) {
this(withArguments(buildImage(dockerFile)), instancesFile, topologyConfigurationFile);
this(dockerFile, "", instancesFile, topologyConfigurationFile);
}

/**
Expand All @@ -145,21 +147,43 @@ public TarantoolCartridgeContainer(String dockerFile, String instancesFile, Stri
*/
public TarantoolCartridgeContainer(String dockerFile, String buildImageName,
String instancesFile, String topologyConfigurationFile) {
this(withArguments(buildImage(dockerFile, buildImageName)), instancesFile, topologyConfigurationFile);
this(dockerFile, buildImageName, instancesFile, topologyConfigurationFile, Collections.emptyMap());
}


/**
* Create a container with specified image and specified instances file from the classpath resources. By providing
* the result Cartridge container image name, you can cache the image and avoid rebuilding on each test run (the
* image is tagged with the provided name and not deleted after tests finishing).
*
* @param dockerFile URL resource path to a Dockerfile which configures Cartridge
* and other necessary services
* @param buildImageName Specify a stable image name for the test container to prevent rebuilds
* @param instancesFile URL resource path to instances.yml relative in the classpath
* @param topologyConfigurationFile URL resource path to a topology bootstrap script in the classpath
* @param buildArgs a map of arguments that will be passed to docker ARG commands on image build.
* This values can be overriden by environment.
*/
public TarantoolCartridgeContainer(String dockerFile, String buildImageName, String instancesFile,
String topologyConfigurationFile, final Map<String, String> buildArgs) {
this(withArguments(buildImage(dockerFile, buildImageName), buildArgs),
instancesFile, topologyConfigurationFile);
}

private TarantoolCartridgeContainer(Future<String> image, String instancesFile, String topologyConfigurationFile) {
super(image);
if (instancesFile == null || instancesFile.isEmpty()) {
throw new IllegalArgumentException("Instance file name must not be null or empty");
}
this.instancesFile = instancesFile;
this.instanceFileParser = new CartridgeConfigParser(instancesFile);
this.topologyConfigurationFile = topologyConfigurationFile;
this.clientHelper = new TarantoolContainerClientHelper(this);
}

private static Future<String> withArguments(ImageFromDockerfile image) {
private static Future<String> withArguments(ImageFromDockerfile image, final Map<String, String> buildArgs) {
if (!buildArgs.isEmpty()) {
image.withBuildArgs(buildArgs);
}
for (String envVariable : Arrays.asList(
ENV_TARANTOOL_VERSION,
ENV_TARANTOOL_SERVER_USER,
Expand All @@ -178,17 +202,12 @@ private static Future<String> withArguments(ImageFromDockerfile image) {
return image;
}

private static ImageFromDockerfile buildImage() {
return buildImage(DOCKERFILE);
}

private static ImageFromDockerfile buildImage(String dockerFile) {
return new ImageFromDockerfile().withFileFromClasspath("Dockerfile", dockerFile);
}

private static ImageFromDockerfile buildImage(String dockerFile, String buildImageName) {
return new ImageFromDockerfile(buildImageName, false)
.withFileFromClasspath("Dockerfile", dockerFile);
if (buildImageName != null && !buildImageName.isEmpty()) {
return new ImageFromDockerfile(buildImageName, false)
.withFileFromClasspath("Dockerfile", dockerFile);
}
return new ImageFromDockerfile().withFileFromClasspath("Dockerfile", dockerFile);
}

/**
Expand All @@ -206,6 +225,9 @@ public String getRouterHost() {
* @return router mapped port
*/
public int getRouterPort() {
if (useFixedPorts) {
return routerPort;
}
return getMappedPort(routerPort);
}

Expand Down Expand Up @@ -312,9 +334,23 @@ public TarantoolCartridgeContainer withDirectoryBinding(String directoryResource
* @return HTTP API port
*/
public int getAPIPort() {
if (useFixedPorts) {
return apiPort;
}
return getMappedPort(apiPort);
}

/**
* Use fixed ports binding.
* Defaults to false.
*
* @return HTTP API port
*/
public TarantoolCartridgeContainer withUseFixedPorts(boolean useFixedPorts) {
this.useFixedPorts = useFixedPorts;
return this;
}

/**
* Set Cartridge router hostname
*
Expand Down Expand Up @@ -377,8 +413,17 @@ public TarantoolCartridgeContainer withRouterPassword(String routerPassword) {

@Override
protected void configure() {
withFileSystemBind(getDirectoryBinding(), getInstanceDir(), BindMode.READ_WRITE);
withExposedPorts(instanceFileParser.getExposablePorts());
if (!getDirectoryBinding().isEmpty()) {
withFileSystemBind(getDirectoryBinding(), getInstanceDir(), BindMode.READ_WRITE);
}

if (useFixedPorts) {
for (Integer port : instanceFileParser.getExposablePorts()) {
addFixedExposedPort(port, port);
}
} else {
withExposedPorts(instanceFileParser.getExposablePorts());
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public class TarantoolContainer extends GenericContainer<TarantoolContainer>
private String directoryResourcePath = SCRIPT_RESOURCE_DIRECTORY;
private String scriptFileName = SCRIPT_FILENAME;
private String instanceDir = INSTANCE_DIR;
private boolean useFixedPorts = false;

private final TarantoolContainerClientHelper clientHelper;

Expand All @@ -60,6 +61,17 @@ public TarantoolContainer(Future<String> image) {
clientHelper = new TarantoolContainerClientHelper(this);
}

/**
* Use fixed ports binding.
* Defaults to false.
*
* @return HTTP API port
*/
public TarantoolContainer withUseFixedPorts(boolean useFixedPorts) {
this.useFixedPorts = useFixedPorts;
return this;
}

/**
* Specify the host for connecting to Tarantool with.
*
Expand Down Expand Up @@ -259,8 +271,16 @@ protected void configure() {
}
String sourceDirectoryPath = normalizePath(sourceDirectory.getPath());

withFileSystemBind(sourceDirectoryPath, getInstanceDir(), BindMode.READ_WRITE);
withExposedPorts(port);
//disable bind if directory is empty
if (!sourceDirectoryPath.isEmpty()) {
withFileSystemBind(sourceDirectoryPath, getInstanceDir(), BindMode.READ_WRITE);
}

if (useFixedPorts) {
addFixedExposedPort(port, port);
} else {
withExposedPorts(port);
}

withCommand("tarantool", normalizePath(
Paths.get(getInstanceDir(), getScriptFileName())));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.testcontainers.containers;

import java.util.Arrays;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;

/**
* @author Vladimir Rogach
*/
public class CartridgeContainerTestUtils {

private CartridgeContainerTestUtils() {
}

static public void executeProfileReplaceSmokeTest(TarantoolCartridgeContainer container) throws Exception {
container.executeCommand(
"return profile_replace(...)", Arrays.asList(1, "Ivanov Ivan Ivanovich", 33, 100500)).get();

List<?> result = container.executeCommand("return profile_get(...)", 1).get();
assertEquals(1, result.size());
assertEquals(33, ((List<?>) result.get(0)).get(3));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package org.testcontainers.containers;

import org.junit.jupiter.api.Test;
import org.slf4j.LoggerFactory;
import org.testcontainers.containers.output.Slf4jLogConsumer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.utility.MountableFile;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

/**
* @author Vladimir Rogach
*/
public class TarantoolCartridgeContainerTest {

@Test
public void test_ClusterContainer_StartsSuccessfully_ifFilesAreCopiedUnderRoot() throws Exception {
Map<String, String> buildArgs = new HashMap<String, String>() {
{
put("TARANTOOL_SERVER_USER", "root");
put("TARANTOOL_SERVER_UID", "0");
put("TARANTOOL_SERVER_GROUP", "root");
put("TARANTOOL_SERVER_GID", "0");
}
};
TarantoolCartridgeContainer container =
new TarantoolCartridgeContainer(
"Dockerfile",
"testcontainers-java-tarantool:test",
"cartridge/instances.yml",
"cartridge/topology.lua",
buildArgs)
.withCopyFileToContainer(MountableFile.forClasspathResource("cartridge"), "/app")
.withStartupTimeout(Duration.ofSeconds(300))
.withLogConsumer(new Slf4jLogConsumer(
LoggerFactory.getLogger(TarantoolCartridgeContainerTest.class)));

container.start();
CartridgeContainerTestUtils.executeProfileReplaceSmokeTest(container);
}

@Test
public void test_ClusterContainer_StartsSuccessfully_ifFixedPortsAreConfigured() throws Exception {
TarantoolCartridgeContainer container =
new TarantoolCartridgeContainer(
"Dockerfile",
"testcontainers-java-tarantool:test",
"cartridge/instances_fixedport.yml",
"cartridge/topology_fixedport.lua")
.withDirectoryBinding("cartridge")
.withStartupTimeout(Duration.ofSeconds(300))
.withUseFixedPorts(true)
.withAPIPort(18081)
.withRouterPort(13301)
.waitingFor(
Wait.forLogMessage(".*Listening HTTP on.*", 1)
)
.withLogConsumer(new Slf4jLogConsumer(
LoggerFactory.getLogger(TarantoolCartridgeContainerTest.class)));

container.start();
CartridgeContainerTestUtils.executeProfileReplaceSmokeTest(container);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@
import org.testcontainers.junit.jupiter.Testcontainers;

import java.time.Duration;
import java.util.Arrays;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;

/**
* @author Alexey Kuzin
Expand All @@ -32,11 +28,6 @@ public class TarantoolCartridgeStaticContainerTest {

@Test
public void testContainerWithParameters() throws Exception {
container.executeCommand(
"return profile_replace(...)", Arrays.asList(1, "Ivanov Ivan Ivanovich", 33, 100500)).get();

List<?> result = container.executeCommand("return profile_get(...)", 1).get();
assertEquals(1, result.size());
assertEquals(33, ((List<?>) result.get(0)).get(3));
CartridgeContainerTestUtils.executeProfileReplaceSmokeTest(container);
}
}
10 changes: 10 additions & 0 deletions src/test/resources/cartridge/instances.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,13 @@ testapp-stateboard:
workdir: ./tmp/db_dev/3310
listen: localhost:3310
password: passwd

testapp.router-fixedport:
workdir: ./tmp/db_dev/13301
advertise_uri: localhost:13301
http_port: 18081

testapp.storage-fixedport:
workdir: ./tmp/db_dev/13302
advertise_uri: localhost:13302
http_port: 18082
9 changes: 9 additions & 0 deletions src/test/resources/cartridge/instances_fixedport.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
testapp.router-fixedport:
workdir: ./tmp/db_dev/13301
advertise_uri: localhost:13301
http_port: 18081

testapp.storage-fixedport:
workdir: ./tmp/db_dev/13302
advertise_uri: localhost:13302
http_port: 18082
11 changes: 11 additions & 0 deletions src/test/resources/cartridge/topology_fixedport.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
cartridge = require('cartridge')
replicasets = {{
alias = 'router-fixedport',
roles = {'vshard-router', 'app.roles.custom', 'app.roles.api_router'},
join_servers = {{uri = 'localhost:13301'}}
}, {
alias = 'storage-fixedport',
roles = {'vshard-storage', 'app.roles.api_storage'},
join_servers = {{uri = 'localhost:13302'}}
}}
return cartridge.admin_edit_topology({replicasets = replicasets})