Skip to content

WIP Fixes and refactoring before release #45

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

Closed
wants to merge 5 commits into from
Closed
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
5 changes: 0 additions & 5 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,6 @@
<version>1.2.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.docker-java</groupId>
<artifactId>docker-java</artifactId>
<version>3.2.13</version>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@

import com.github.dockerjava.api.command.InspectContainerResponse;
import io.tarantool.driver.exceptions.TarantoolConnectionException;
import org.testcontainers.images.builder.ImageFromDockerfile;

import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeoutException;

import static org.testcontainers.containers.PathUtils.normalizePath;
Expand Down Expand Up @@ -88,16 +87,13 @@ public class TarantoolCartridgeContainer extends GenericContainer<TarantoolCartr
private static final String CARTRIDGE_USERNAME = "admin";
private static final String CARTRIDGE_PASSWORD = "testapp-cluster-cookie";
private static final String DOCKERFILE = "Dockerfile";
private static final String BUILD_IMAGE_NAME = "tarantool_cartridge_app";
private static final int API_PORT = 8081;
private static final String VSHARD_BOOTSTRAP_COMMAND = "return require('cartridge').admin_bootstrap_vshard()";
private static final String SCRIPT_RESOURCE_DIRECTORY = "";
private static final String INSTANCE_DIR = "/app";

private static final String ENV_TARANTOOL_VERSION = "TARANTOOL_VERSION";
private static final String ENV_TARANTOOL_SERVER_USER = "TARANTOOL_SERVER_USER";
private static final String ENV_TARANTOOL_SERVER_UID = "TARANTOOL_SERVER_UID";
private static final String ENV_TARANTOOL_SERVER_GROUP = "TARANTOOL_SERVER_GROUP";
private static final String ENV_TARANTOOL_SERVER_GID = "TARANTOOL_SERVER_GID";
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";
Expand Down Expand Up @@ -137,7 +133,7 @@ public TarantoolCartridgeContainer(String instancesFile, String topologyConfigur
*/
public TarantoolCartridgeContainer(String instancesFile, String topologyConfigurationFile,
Map<String, String> buildArgs) {
this(DOCKERFILE, "", instancesFile, topologyConfigurationFile, buildArgs);
this(DOCKERFILE, BUILD_IMAGE_NAME, instancesFile, topologyConfigurationFile, buildArgs);
}

/**
Expand All @@ -148,7 +144,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(dockerFile, "", instancesFile, topologyConfigurationFile);
this(dockerFile, BUILD_IMAGE_NAME, instancesFile, topologyConfigurationFile);
}

/**
Expand Down Expand Up @@ -181,13 +177,13 @@ public TarantoolCartridgeContainer(String dockerFile, String buildImageName,
*/
public TarantoolCartridgeContainer(String dockerFile, String buildImageName, String instancesFile,
String topologyConfigurationFile, final Map<String, String> buildArgs) {
this(withArguments(buildImage(dockerFile, buildImageName), instancesFile, buildArgs),
instancesFile, topologyConfigurationFile);
this(buildImage(dockerFile, buildImageName, buildArgs), instancesFile, topologyConfigurationFile);
}


private TarantoolCartridgeContainer(Future<String> image, String instancesFile, String topologyConfigurationFile) {
super(image);
private TarantoolCartridgeContainer(TarantoolImageParams tarantoolImageParams, String instancesFile, String topologyConfigurationFile) {
super(TarantoolContainerImageHelper.getImage(tarantoolImageParams));

if (instancesFile == null || instancesFile.isEmpty()) {
throw new IllegalArgumentException("Instance file name must not be null or empty");
}
Expand All @@ -197,46 +193,44 @@ private TarantoolCartridgeContainer(Future<String> image, String instancesFile,
String fileType = topologyConfigurationFile.substring(topologyConfigurationFile.lastIndexOf('.') + 1);
if (fileType.equals("lua")) {
this.topologyConfigurationFile = topologyConfigurationFile;
}else{
this.replicasetsFileName = topologyConfigurationFile.substring(topologyConfigurationFile.lastIndexOf('/')+1);
} else {
this.replicasetsFileName = topologyConfigurationFile.substring(topologyConfigurationFile.lastIndexOf('/') + 1);
}
this.instanceFileParser = new CartridgeConfigParser(instancesFile);
this.clientHelper = new TarantoolContainerClientHelper(this);
}

private static Future<String> withArguments(ImageFromDockerfile image, String instancesFile,
final Map<String, String> buildArgs) {
if (!buildArgs.isEmpty()) {
image.withBuildArgs(buildArgs);
}

private static Map<String, String> mergeBuildArgsWithEnv(final Map<String, String> buildArgs) {
Map<String, String> args = new HashMap<>(buildArgs);
for (String envVariable : Arrays.asList(
ENV_TARANTOOL_VERSION,
ENV_TARANTOOL_SERVER_USER,
ENV_TARANTOOL_SERVER_UID,
ENV_TARANTOOL_SERVER_GROUP,
ENV_TARANTOOL_SERVER_GID,
ENV_TARANTOOL_WORKDIR,
ENV_TARANTOOL_RUNDIR,
ENV_TARANTOOL_DATADIR,
ENV_TARANTOOL_INSTANCES_FILE
)) {
String variableValue = System.getenv(envVariable);
if (variableValue != null) {
image.withBuildArg(envVariable, variableValue);
// env values do not override build args from code
if (variableValue != null && !args.containsKey(envVariable)) {
args.put(envVariable, variableValue);
}
}
return image;
return args;
}

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

private static TarantoolImageParams buildImage(String dockerFile, String buildImageName, Map<String, String> buildArgs) {
Map<String, String> args = mergeBuildArgsWithEnv(buildArgs);
return new TarantoolImageParams(getImageTag(buildImageName), dockerFile, args);
}


/**
* Get the router host
*
Expand Down Expand Up @@ -481,7 +475,7 @@ private boolean setupTopology() {

try {
Container.ExecResult lsResult = this.execInContainer("cartridge", "replicasets", "--run-dir=" + runDirPath,
"--file=" + this.replicasetsFileName, "setup", "--bootstrap-vshard");
"--file=" + this.replicasetsFileName, "setup", "--bootstrap-vshard");
String stdout = lsResult.getStdout();
int exitCode = lsResult.getExitCode();
if (exitCode != 0) {
Expand Down Expand Up @@ -512,20 +506,6 @@ private boolean setupTopology() {
return true;
}

private void retryingSetupTopology() {
if (!setupTopology()) {
try {
logger().info("Retrying setup topology in 10 seconds");
Thread.sleep(10000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (!setupTopology()) {
throw new RuntimeException("Failed to change the app topology after retry");
}
}
}

private void bootstrapVshard() {
try {
executeCommand(VSHARD_BOOTSTRAP_COMMAND).get();
Expand All @@ -539,14 +519,43 @@ private void bootstrapVshard() {
protected void containerIsStarted(InspectContainerResponse containerInfo, boolean reused) {
super.containerIsStarted(containerInfo, reused);

retryingSetupTopology();
setupTopology();
bootstrapVshard();
waitUntilCartridgeIsHealthy(60);

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

private void waitUntilCartridgeIsHealthy(int secondsToWait) {
int secondsPassed = 0;
boolean healthy = isCartridgeHealthy();
while (!healthy && secondsPassed < secondsToWait) {
healthy = isCartridgeHealthy();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
break;
}
}
if (!healthy) {
throw new RuntimeException("Failed to change the app topology after retry");
}
}

private boolean isCartridgeHealthy() {
String healthyCmd = " local cartridge = package.loaded['cartridge']" +
" return assert(cartridge) and assert(cartridge.is_healthy())";
try {
List<?> result = executeCommand(healthyCmd).get();
return (Boolean) result.get(0);
} catch (Exception e) {
logger().warn("Error while waiting for cartridge healthy state: " + e.getMessage());
return false;
}
}

@Override
public CompletableFuture<List<?>> executeScript(String scriptResourcePath) throws Exception {
return clientHelper.executeScript(scriptResourcePath);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@
import com.github.dockerjava.api.command.BuildImageCmd;
import com.github.dockerjava.api.command.BuildImageResultCallback;
import com.github.dockerjava.api.model.Image;
import com.github.dockerjava.core.DockerClientBuilder;
import org.apache.commons.lang3.StringUtils;
import org.testcontainers.DockerClientFactory;

import java.io.File;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
* Class for working with docker directly
Expand All @@ -21,21 +23,19 @@
*/
class TarantoolContainerImageHelper {

private static final DockerClient dockerClient = DockerClientBuilder.getInstance().build();

private TarantoolContainerImageHelper() {
}

/**
* Checks image for existing by name and build if it not exist
* Checks image for existing by name and build if it not exists
*
* @param imageParams parameters for building tarantool image
* @return image name
*/
static String getImage(TarantoolImageParams imageParams) {
final String tag = imageParams.getTag();

if (StringUtils.isEmpty(tag)) {
if (tag.isEmpty()) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is tag really never null?

throw new IllegalArgumentException("Image tag is null or empty!");
}

Expand All @@ -52,7 +52,18 @@ static String getImage(TarantoolImageParams imageParams) {
* @param imageParams parameters for building tarantool image
*/
private static void buildImage(TarantoolImageParams imageParams) {
final BuildImageCmd buildImageCmd = dockerClient.buildImageCmd(imageParams.getDockerfile());
final File dockerfile;
try {
dockerfile = new File(
Objects.requireNonNull(TarantoolContainerImageHelper.class.getClassLoader().getResource(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A message can be added here, otherwise it will be an unreadable stacktrace.

imageParams.getDockerfile())).toURI()
);
} catch (URISyntaxException e) {
throw new RuntimeException("Failed to build docker image from resource '"
+ imageParams.getDockerfile() + "'", e);
}

final BuildImageCmd buildImageCmd = getDockerClient().buildImageCmd(dockerfile);

final Map<String, String> buildArgs = imageParams.getBuildArgs();
for (Map.Entry<String, String> entry : buildArgs.entrySet()) {
Expand All @@ -71,11 +82,15 @@ private static void buildImage(TarantoolImageParams imageParams) {
* @return true if image exist and false if not
*/
private static boolean hasImage(String tag) {
final List<Image> images = dockerClient.listImagesCmd().exec();
final List<Image> images = getDockerClient().listImagesCmd().exec();
return images.stream()
.map(Image::getRepoTags)
.map(Arrays::asList)
.flatMap(Collection::stream)
.anyMatch(repoTag -> repoTag.equals(tag));
}

private static DockerClient getDockerClient() {
return DockerClientFactory.instance().client();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,36 @@
public class TarantoolImageParams {

private final String tag;
private final File dockerfile;
private final String dockerfile;
private final Map<String, String> buildArgs;

/**
* Custom constructor for tarantool image parameters
*
* @param tag docker image tag
* @param tag docker image tag. For example: "tarantool-enterprise-bundle:latest"
* @param dockerfile dockerfile for building custom tarantool image
*/
public TarantoolImageParams(String tag, File dockerfile) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather leave both variants (String and File). Both are useful

public TarantoolImageParams(String tag, String dockerfile) {
this(tag, dockerfile, Collections.emptyMap());
}

/**
* Custom constructor for tarantool image parameters
*
* @param tag docker image tag
* @param tag docker image tag. For example: "tarantool-enterprise-bundle:latest"
* @param dockerfile dockerfile for building custom tarantool image
* @param buildArgs args for building docker image
*/
public TarantoolImageParams(String tag, File dockerfile, Map<String, String> buildArgs) {
public TarantoolImageParams(String tag, String dockerfile, Map<String, String> buildArgs) {
this.tag = tag;
this.dockerfile = dockerfile;
this.buildArgs = buildArgs;
}

/**
* Getter for sdk version
* Getter for docker image tag
*
* @return sdk version
* @return docker image tag
*/
public String getTag() {
return tag;
Expand All @@ -52,7 +52,7 @@ public String getTag() {
*
* @return dockerfile
*/
public File getDockerfile() {
public String getDockerfile() {
return dockerfile;
}

Expand Down
15 changes: 3 additions & 12 deletions src/main/resources/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
FROM centos:7 AS tarantool-base
FROM centos:7
ARG TARANTOOL_VERSION=2.8
ARG TARANTOOL_SERVER_USER="tarantool"
ARG TARANTOOL_SERVER_UID=1000
ARG TARANTOOL_SERVER_GROUP="tarantool"
ARG TARANTOOL_SERVER_GID=1000
ARG TARANTOOL_WORKDIR="/app"
ARG TARANTOOL_RUNDIR="/tmp/run"
ARG TARANTOOL_DATADIR="/tmp/data"
Expand All @@ -15,12 +11,7 @@ ENV TARANTOOL_INSTANCES_FILE=$TARANTOOL_INSTANCES_FILE
RUN curl -L https://tarantool.io/installer.sh | VER=$TARANTOOL_VERSION /bin/bash -s -- --repo-only && \
yum -y install cmake make gcc gcc-c++ git unzip tarantool tarantool-devel cartridge-cli && \
yum clean all
RUN groupadd -g $TARANTOOL_SERVER_GID $TARANTOOL_SERVER_GROUP && \
useradd -u $TARANTOOL_SERVER_UID -g $TARANTOOL_SERVER_GID -m -s /bin/bash $TARANTOOL_SERVER_USER \
|| true
USER $TARANTOOL_SERVER_USER:$TARANTOOL_SERVER_GROUP
RUN cartridge version

FROM tarantool-base AS cartridge-base
WORKDIR $TARANTOOL_WORKDIR
CMD cartridge build && cartridge start --run-dir=$TARANTOOL_RUNDIR --data-dir=$TARANTOOL_DATADIR --cfg=$TARANTOOL_INSTANCES_FILE
CMD cartridge build && \
cartridge start --run-dir=$TARANTOOL_RUNDIR --data-dir=$TARANTOOL_DATADIR --cfg=$TARANTOOL_INSTANCES_FILE
Loading