All Downloads are FREE. Search and download functionalities are using the official Maven repository.

no.mnemonic.commons.junit.docker.MariaDBDockerResource Maven / Gradle / Ivy

package no.mnemonic.commons.junit.docker;

import com.spotify.docker.client.DockerClient;
import com.spotify.docker.client.messages.HostConfig;
import no.mnemonic.commons.utilities.ObjectUtils;
import no.mnemonic.commons.utilities.StringUtils;

import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;

import static com.spotify.docker.client.DockerClient.ExecCreateParam.*;
import static no.mnemonic.commons.utilities.collections.MapUtils.Pair.T;
import static no.mnemonic.commons.utilities.collections.MapUtils.map;

/**
 * MariaDBDockerResource is a JUnit resource which can be used to write integration tests against a MariaDB
 * server executed inside an isolated Docker container. It extends the basic {@link DockerResource} and makes sure that
 * the container initialization waits until MariaDB is available.
 * 

* Initialize MariaDBDockerResource in the following way as a {@link org.junit.ClassRule}: *

 * {@code @ClassRule
 *  public static MariaDBDockerResource elastic = MariaDBDockerResource.builder()
 *     .setImageName("mariadb:10.0")
 *     .build(); }
 * 
* This resource will by default expose port 3306. * See {@link DockerResource.Builder} and {@link MariaDBDockerResource.Builder} for more information on the * configuration properties. */ public class MariaDBDockerResource extends DockerResource { private static final String SUCCESS_MESSAGE = "mysqld is alive\n"; private final Path setupScript; private final Path truncateScript; private MariaDBDockerResource(String imageName, Set applicationPorts, String exposedPortsRange, int reachabilityTimeout, Supplier dockerClientResolver, Map environmentVariables, String setupScript, String truncateScript) { super(imageName, applicationPorts, exposedPortsRange, reachabilityTimeout, dockerClientResolver, environmentVariables); // Both parameters are optional. this.setupScript = !StringUtils.isBlank(setupScript) ? checkFileExists(setupScript) : null; this.truncateScript = !StringUtils.isBlank(truncateScript) ? checkFileExists(truncateScript) : null; } @Override protected boolean isContainerReachable() { try { String[] cmd = {"mysqladmin", "-uroot", "-proot", "ping"}; String id = getDockerClient().execCreate(getContainerID(), cmd, attachStdout(), attachStderr(), attachStdin()).id(); String output = getDockerClient().execStart(id).readFully(); return SUCCESS_MESSAGE.equals(output); } catch (Exception e) { throw new IllegalStateException("Unable to reach container", e); } } @Override protected void prepareContainer() { copyFilesToContainer(); ObjectUtils.ifNotNullDo(setupScript, this::executeSqlScript); } @Override protected HostConfig additionalHostConfig(HostConfig config) { return config.toBuilder() // Write data to tmpfs. .tmpfs(map(T("/var/lib/mysql", ""))) // Deactivate swap. .memorySwappiness(0) .build(); } /** * Truncate data stored inside database by executing the truncate SQL script. * * @throws IllegalStateException If SQL script could not be executed */ public void truncate() { ObjectUtils.ifNotNullDo(truncateScript, this::executeSqlScript); } private Path checkFileExists(String fileName) { URL fileUrl = ClassLoader.getSystemResource(fileName); if (fileUrl == null || !Files.isReadable(Paths.get(fileUrl.getPath()))) { throw new IllegalArgumentException(String.format("Cannot read '%s'!", fileName)); } return Paths.get(fileUrl.getPath()); } private void copyFilesToContainer() { try { // Copy start up script and truncate script to the container's /tmp/ folder. // Need to specify the parent folder where the file resides. This will copy all files in that folder. if (setupScript != null) { getDockerClient().copyToContainer(setupScript.getParent(), getContainerID(), "/tmp/"); } if (truncateScript != null) { getDockerClient().copyToContainer(truncateScript.getParent(), getContainerID(), "/tmp/"); } } catch (Exception ex) { throw new IllegalStateException("Could not copy files to container.", ex); } } private void executeSqlScript(Path script) { String output; try { // Workaround for https://github.com/spotify/docker-client/issues/513: also attach stdin. String id = getDockerClient().execCreate(getContainerID(), new String[]{"mysql", "-uroot", "-proot", "-e", "source /tmp/" + script.getFileName()}, attachStdout(), attachStderr(), attachStdin()).id(); output = getDockerClient().execStart(id).readFully(); } catch (Exception ex) { throw new IllegalStateException(String.format("Could not execute SQL script %s.", script.getFileName()), ex); } if (!StringUtils.isBlank(output)) { throw new IllegalStateException(String.format("Evaluation of SQL script %s failed.\n%s", script.getFileName(), output)); } } public static Builder builder() { return new Builder(); } /** * Builder to create {@link MariaDBDockerResource} which extends {@link DockerResource.Builder} */ public static class Builder extends DockerResource.Builder { private String setupScript; private String truncateScript; @Override public MariaDBDockerResource build() { //add 3306 by default addApplicationPort(3306); addEnvironmentVariable("MYSQL_ROOT_PASSWORD", "root"); addEnvironmentVariable("MYSQL_ROOT_HOST", "%"); return new MariaDBDockerResource(imageName, applicationPorts, exposedPortsRange, reachabilityTimeout, dockerClientResolver, environmentVariables, setupScript, truncateScript); } /** * Script to run on initial setup to prepare the database * @param setupScript script path and filename * @return this builder */ public Builder setSetupScript(String setupScript) { this.setupScript = setupScript; return this; } /** * Script to run on truncate(). Use to reset the database between tests. * @param truncateScript script path and filename * @return thils builder */ public Builder setTruncateScript(String truncateScript) { this.truncateScript = truncateScript; return this; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy