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

au.net.causal.maven.plugins.boxdb.db.BaseMySqlDatabase Maven / Gradle / Ivy

There is a newer version: 3.3
Show newest version
package au.net.causal.maven.plugins.boxdb.db;

import au.net.causal.maven.plugins.boxdb.JdbcSqlRunner;
import io.fabric8.maven.docker.access.DockerAccess;
import io.fabric8.maven.docker.access.DockerAccessException;
import io.fabric8.maven.docker.access.PortMapping;
import io.fabric8.maven.docker.config.ImageConfiguration;
import io.fabric8.maven.docker.config.LogConfiguration;
import io.fabric8.maven.docker.config.RunImageConfiguration;
import io.fabric8.maven.docker.config.RunVolumeConfiguration;
import io.fabric8.maven.docker.log.LogDispatcher;
import io.fabric8.maven.docker.log.LogOutputSpec;
import io.fabric8.maven.docker.model.Container;
import io.fabric8.maven.docker.service.RunService;
import io.fabric8.maven.docker.util.PomLabel;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.sql.Connection;
import java.sql.SQLException;
import java.time.Duration;
import java.util.Arrays;
import java.util.Collections;
import java.util.Properties;

/**
 * Common superclass for MySQL and MariaDB databases, since they have many things in common.
 */
public abstract class BaseMySqlDatabase extends DockerDatabase
{
    public BaseMySqlDatabase(BoxConfiguration boxConfiguration, ProjectConfiguration projectConfiguration, BoxContext context)
    {
        super(boxConfiguration, projectConfiguration, context);
    }

    @Override
    protected int containerDatabasePort()
    {
        return 3306;
    }

    @Override
    protected void configureRunImage(RunImageConfiguration.Builder builder)
    {
        super.configureRunImage(builder);
        builder.env(Collections.singletonMap("MYSQL_ROOT_PASSWORD", getBoxConfiguration().getAdminPassword()));
    }

    private void executeClient(RunVolumeConfiguration volumes, String clientArgs, DatabaseTarget targetDatabase, Duration timeout)
    throws IOException, SQLException, BoxDatabaseException
    {
        if (targetDatabase == DatabaseTarget.USER)
            clientArgs = clientArgs + " " + getBoxConfiguration().getDatabaseName();

        RunService runService = getContext().getDockerServiceHub().getRunService();
        DockerAccess docker = getContext().getDockerServiceHub().getDockerAccess();

        Properties projectProperties = getProjectConfiguration().getProjectProperties();
        PomLabel pomLabel = getProjectConfiguration().getPomLabel();
        PortMapping mappedPorts = new PortMapping(Collections.emptyList(), projectProperties);

        int maxExecutionTimeSeconds = Math.toIntExact(timeout.getSeconds());
        LogConfiguration logConfig = new LogConfiguration.Builder()
                .enabled(true)
                .prefix("mysql")
                .build();
        RunImageConfiguration clientRunConfig = new RunImageConfiguration.Builder()
                .links(Collections.singletonList(containerName() + ":mysql"))
                .namingStrategy("alias")
                .cmd("unused") //not used but must be non-null - is replaced later
                .volumes(volumes)
                .log(logConfig)
                .build();
        clientRunConfig.getCmd().setExec(Arrays.asList("sh", "-c", "exec mysql -h\"$MYSQL_PORT_3306_TCP_ADDR\" -P\"$MYSQL_PORT_3306_TCP_PORT\" -u" + targetDatabase.user(getBoxConfiguration()) + " -p" + targetDatabase.password(getBoxConfiguration()) + " " + clientArgs));

        clientRunConfig.getCmd().setShell(null);
        ImageConfiguration clientConfig = new ImageConfiguration.Builder()
                .runConfig(clientRunConfig)
                .name(dockerImageName())
                .alias(containerName() + "-client")
                .build();

        LogDispatcher dispatcher = new LogDispatcher(getContext().getDockerServiceHub().getDockerAccess());

        String containerId = runService.createAndStartContainer(clientConfig, mappedPorts, pomLabel, projectProperties);
        Container container = null;
        try
        {
            getContext().getLog().debug("Client running in container " + containerId);

            dispatcher.trackContainerLog(containerId, new LogOutputSpec.Builder()
                    .logStdout(true)
                    .prefix("mysql")
                    .containerId(containerId)
                    .build());
        }
        finally
        {
            try
            {
                container = waitForContainerToFinish(containerId, maxExecutionTimeSeconds);
            }
            catch (DockerAccessException e)
            {
                getContext().getLog().warn("Error shutting down client container", e);
            }

            try
            {
                boolean removeVolumes = true;
                docker.removeContainer(containerId, removeVolumes);
            }
            catch (DockerAccessException e)
            {
                getContext().getLog().warn("Error removing client container", e);
            }
        }

        int exitCode = readExitCodeFromContainer(container);
        if (exitCode != 0)
            throw new SQLException("Client exit code: " + exitCode);

        getContext().getLog().debug("All done");
    }

    @Override
    protected void executeScriptFile(Path scriptFile, DatabaseTarget targetDatabase, Duration timeout)
    throws IOException, SQLException, BoxDatabaseException
    {
        if (!Files.exists(scriptFile))
            throw new NoSuchFileException(scriptFile.toString());

        RunVolumeConfiguration volumes = new RunVolumeConfiguration.Builder()
                .bind(Arrays.asList(scriptFile.getParent().toAbsolutePath().toString() + ":/data/scripts:ro"))
                .build();

        executeClient(volumes, "< /data/scripts/" + scriptFile.getFileName().toString(), targetDatabase, timeout);
    }

    @Override
    public void executeSql(String sql, DatabaseTarget targetDatabase, Duration timeout)
    throws IOException, SQLException, BoxDatabaseException
    {
        executeClient(new RunVolumeConfiguration.Builder().build(), "-e\"" + ScriptUtils.shellEscape(sql) + "\"", targetDatabase, timeout);
    }

    @Override
    public void executeJdbcScript(Reader scriptReader, DatabaseTarget targetDatabase)
    throws IOException, SQLException, BoxDatabaseException
    {
        try (Connection con = createJdbcConnection(targetDatabase);
             JdbcSqlRunner sqlRunner = new JdbcSqlRunner(con, getContext().getLog()))
        {
            sqlRunner.executeSql(new BufferedReader(scriptReader));
        }
    }

    @Override
    public void backup(Path backupFile, BackupFileTypeHint backupFileTypeHint)
    throws BoxDatabaseException, IOException, SQLException
    {
        Path backupDirectory = backupFile.getParent();

        if (!Files.exists(backupDirectory))
            Files.createDirectories(backupDirectory);

        //Mount backup file as volume to /data/backup/dump
        RunVolumeConfiguration volumes = new RunVolumeConfiguration.Builder()
                .bind(Arrays.asList(backupDirectory.toAbsolutePath().toString() + ":/data/backup"))
                .build();

        RunService runService = getContext().getDockerServiceHub().getRunService();
        DockerAccess docker = getContext().getDockerServiceHub().getDockerAccess();

        Properties projectProperties = getProjectConfiguration().getProjectProperties();
        PomLabel pomLabel = getProjectConfiguration().getPomLabel();
        PortMapping mappedPorts = new PortMapping(Collections.emptyList(), projectProperties);

        int maxExecutionTimeSeconds = Math.toIntExact(getProjectConfiguration().getBackupTimeout().getSeconds());
        LogConfiguration logConfig = new LogConfiguration.Builder()
                .enabled(true)
                .prefix("mysqldump")
                .build();
        RunImageConfiguration clientRunConfig = new RunImageConfiguration.Builder()
                .links(Collections.singletonList(containerName() + ":mysql"))
                .namingStrategy("alias")
                .cmd("unused") //not used but must be non-null - is replaced later
                .volumes(volumes)
                .log(logConfig)
                .build();
        clientRunConfig.getCmd().setExec(Arrays.asList("sh", "-c", "exec mysqldump -h\"$MYSQL_PORT_3306_TCP_ADDR\" -P\"$MYSQL_PORT_3306_TCP_PORT\" -u" + getBoxConfiguration().getDatabaseUser() + " -p" + getBoxConfiguration().getDatabasePassword() + " -r/data/backup/" + backupFile.getFileName().toString() + " " + getBoxConfiguration().getDatabaseName()));

        clientRunConfig.getCmd().setShell(null);
        ImageConfiguration clientConfig = new ImageConfiguration.Builder()
                .runConfig(clientRunConfig)
                .name(dockerImageName())
                .alias(containerName() + "-dump")
                .build();

        LogDispatcher dispatcher = new LogDispatcher(getContext().getDockerServiceHub().getDockerAccess());

        String containerId = runService.createAndStartContainer(clientConfig, mappedPorts, pomLabel, projectProperties);
        Container container = null;
        try
        {
            getContext().getLog().debug("Mysqldump running in container " + containerId);

            dispatcher.trackContainerLog(containerId, new LogOutputSpec.Builder()
                    .logStdout(true)
                    .prefix("mysqldump")
                    .containerId(containerId)
                    .build());
        }
        finally
        {
            try
            {
                container = waitForContainerToFinish(containerId, maxExecutionTimeSeconds);
            }
            catch (DockerAccessException e)
            {
                getContext().getLog().warn("Error shutting down mysqldump container", e);
            }

            try
            {
                boolean removeVolumes = true;
                docker.removeContainer(containerId, removeVolumes);
            }
            catch (DockerAccessException e)
            {
                getContext().getLog().warn("Error removing mysqldump container", e);
            }
        }

        int exitCode = readExitCodeFromContainer(container);
        if (exitCode != 0)
            throw new SQLException("Mysqldump exit code: " + exitCode);

        getContext().getLog().debug("All done");
    }

    @Override
    public void restore(Path backupFile)
    throws BoxDatabaseException, IOException, SQLException
    {
        executeScriptFile(backupFile, DatabaseTarget.USER, getProjectConfiguration().getBackupTimeout());
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy