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

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

package au.net.causal.maven.plugins.boxdb.db;

import au.net.causal.maven.plugins.boxdb.DependencyUtils;
import au.net.causal.maven.plugins.boxdb.ImageCheckerUtils;
import au.net.causal.maven.plugins.boxdb.db.ImageComponent.ImageStatus;
import au.net.causal.maven.plugins.boxdb.vagrant.BoxDefinition;
import au.net.causal.maven.plugins.boxdb.vagrant.BoxUpdateStatus;
import au.net.causal.maven.plugins.boxdb.vagrant.BoxUpdateStatus.State;
import au.net.causal.maven.plugins.boxdb.vagrant.Vagrant;
import au.net.causal.maven.plugins.boxdb.vagrant.Vagrant.BoxAddOptions;
import au.net.causal.maven.plugins.boxdb.vagrant.Vagrant.BoxListOptions;
import au.net.causal.maven.plugins.boxdb.vagrant.Vagrant.BoxOutdatedOptions;
import au.net.causal.maven.plugins.boxdb.vagrant.VagrantException;
import com.google.common.collect.ImmutableList;
import org.apache.maven.shared.utils.io.FileUtils;
import org.eclipse.aether.resolution.DependencyResolutionException;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeoutException;

public abstract class VagrantDatabase implements BoxDatabase
{
    private final BoxConfiguration boxConfiguration;
    private final ProjectConfiguration projectConfiguration;
    private final BoxContext context;

    private final Vagrant vagrant;
    private final Path vagrantDirectory;
    private final Path vagrantMainFile;

    public VagrantDatabase(BoxConfiguration boxConfiguration, ProjectConfiguration projectConfiguration, BoxContext context,
                           Vagrant vagrant, Path vagrantDirectory, Path vagrantMainFile)
    {
        Objects.requireNonNull(boxConfiguration, "boxConfiguration == null");
        Objects.requireNonNull(projectConfiguration, "projectConfiguration == null");
        Objects.requireNonNull(context, "context == null");
        Objects.requireNonNull(vagrant, "vagrant == null");
        Objects.requireNonNull(vagrantDirectory, "vagrantDirectory == null");
        Objects.requireNonNull(vagrantMainFile, "vagrantMainFile == null");

        this.boxConfiguration = boxConfiguration;
        this.projectConfiguration = projectConfiguration;
        this.context = context;
        this.vagrant = vagrant;
        this.vagrantDirectory = vagrantDirectory;
        this.vagrantMainFile = vagrantMainFile;
    }

    protected Vagrant getVagrant()
    {
        return vagrant;
    }

    protected BoxConfiguration getBoxConfiguration()
    {
        return boxConfiguration;
    }

    protected ProjectConfiguration getProjectConfiguration()
    {
        return projectConfiguration;
    }

    @Override
    public void createAndStart() throws BoxDatabaseException
    {
        start();
    }

    protected Vagrant.BoxStatus getVagrantStatus()
    throws BoxDatabaseException
    {
        getContext().getLog().debug("Getting status for " + vagrantDirectory);
        Vagrant.StatusOptions options = new Vagrant.StatusOptions(vagrantDirectory, containerName());
        configureInstanceOptions(options);
        try
        {
            return vagrant.status(options);
        }
        catch (VagrantException e)
        {
            throw new BoxDatabaseException("Failed to get Vagrant status: " + e.getMessage(), e);
        }
    }

    @Override
    public boolean exists() throws BoxDatabaseException
    {
        return getVagrantStatus() != Vagrant.BoxStatus.NOT_CREATED;
    }

    @Override
    public boolean isRunning() throws BoxDatabaseException
    {
        return getVagrantStatus() == Vagrant.BoxStatus.RUNNING;
    }

    @Override
    public String getName()
    {
        return containerName();
    }

    @Override
    public void start() throws BoxDatabaseException
    {
        Vagrant.UpOptions options = new Vagrant.UpOptions(vagrantDirectory, containerName());
        configureInstanceOptions(options);
        try
        {
            vagrant.up(options);
        }
        catch (VagrantException e)
        {
            throw new BoxDatabaseException("Failed to start Vagrant: " + e.getMessage(), e);
        }
    }

    @Override
    public void stop() throws BoxDatabaseException
    {
        Vagrant.HaltOptions options = new Vagrant.HaltOptions(vagrantDirectory, containerName());
        configureInstanceOptions(options);
        try
        {
            vagrant.halt(options);
        }
        catch (VagrantException e)
        {
            throw new BoxDatabaseException("Failed to stop Vagrant: " + e.getMessage(), e);
        }
    }

    protected void configureInstanceOptions(Vagrant.InstanceOptions options)
    {
        configureBaseOptions(options);
        options.setBaseDirectory(getVagrantDirectory());
        options.setBoxName(containerName());
    }

    protected void configureBaseOptions(Vagrant.BaseOptions options)
    {
        options.env("CONTAINERNAME", containerName());
    }

    @Override
    public void delete() throws BoxDatabaseException
    {
        Vagrant.DestroyOptions options = new Vagrant.DestroyOptions(vagrantDirectory, containerName());
        configureInstanceOptions(options);
        try
        {
            vagrant.destroy(options);
        }
        catch (VagrantException e)
        {
            throw new BoxDatabaseException("Failed to perform Vagrant destroy: " + e.getMessage(), e);
        }

        //Also delete the Vagrant directory
        if (Files.exists(vagrantDirectory))
        {
            getContext().getLog().info("Deleting Vagrant directory " + vagrantDirectory.toString());
            try
            {
                FileUtils.deleteDirectory(vagrantDirectory.toFile());
            }
            catch (IOException e)
            {
                throw new BoxDatabaseException("Failed to delete container directory " + vagrantDirectory.toString() + ": " + e, e);
            }
        }
    }

    @Override
    public void deleteImage() throws BoxDatabaseException
    {
        Vagrant.BoxRemoveOptions options = new Vagrant.BoxRemoveOptions(vagrantBoxDefinition());
        configureBaseOptions(options);
        options.setForce(true);
        try
        {
            vagrant.boxRemove(options);
        }
        catch (VagrantException e)
        {
            throw new BoxDatabaseException("Failed to remove Vagrant box: " + e.getMessage(), e);
        }
    }

    @Override
    public void waitUntilStarted(Duration maxTimeToWait)
    throws TimeoutException, BoxDatabaseException
    {
        DatabaseUtils.waitUntilTcpPortResponding(maxTimeToWait, this, getContext());
    }

    /**
     * @return the name of the container.  Defaults to the name given in the configuration.
     */
    protected String containerName()
    {
        return boxConfiguration.getContainerName();
    }

    protected Path getVagrantDirectory()
    {
        return vagrantDirectory;
    }

    protected abstract BoxDefinition vagrantBoxDefinition();

    protected BoxContext getContext()
    {
        return context;
    }

    @Override
    public void prepareImage()
    throws BoxDatabaseException
    {
        //JDBC drivers
        try
        {
            DependencyUtils.resolveDependencies(jdbcDriverInfo().getDependencies(),
                                                getContext().getRepositorySystem(),
                                                getContext().getRepositorySystemSession(),
                                                getContext().getRemoteRepositories());
        }
        catch (DependencyResolutionException e)
        {
            throw new BoxDatabaseException("Failed to download dependencies for database: " + e.getMessage(), e);
        }

        //Check whether we already have local image before trying to fetch
        //Using --force would work but would also download the full image every time which we want to avoid
        BoxListOptions boxListOptions = new BoxListOptions();
        List boxes;
        try
        {
            boxes = getVagrant().boxList(boxListOptions);
        }
        catch (VagrantException e)
        {
            throw new BoxDatabaseException("Error getting box list: " + e.getMessage(), e);
        }

        if (boxes.contains(vagrantBoxDefinition()))
            getContext().getLog().info("Vagrant box is already downloaded.");
        else
        {
            //The version of the box is hardcoded, so never should have to update it
            //Fetch it if needed though using box add - this won't download it again if it is already downloaded
            BoxAddOptions boxAddOptions = new BoxAddOptions(vagrantBoxDefinition());
            try
            {
                getVagrant().boxAdd(boxAddOptions);
            }
            catch (VagrantException e)
            {
                throw new BoxDatabaseException("Error downloading Vagrant box: " + e.getMessage(), e);
            }
        }
   }

    /**
     * Checks the update status of a Vagrant image with a hardcoded version.  Will never detect an available update since
     * the version is hardcoded, but will detect when a remote image goes away.
     *
     * @return Vagrant image component update state.
     *
     * @throws BoxDatabaseException if an error occurs.
     */
    protected ImageComponent checkVagrantImage()
    throws BoxDatabaseException
    {
        String componentType = "Vagrant image";
        String componentName = vagrantBoxDefinition().getName() + ":" + vagrantBoxDefinition().getVersion();

        BoxOutdatedOptions outdatedOptions = new BoxOutdatedOptions();
        try
        {
            //Despite having a hardcoded version number, checking for updates will make Vagrant check
            //if the box exists at all remotely
            //This will allow us to detect if the image disappears one day, provided we have a local copy
            //If we don't have a local copy, prepare() will fail when trying to download it so all our bases
            //are recovered in regards to detecting a box that has gone missing
            List boxUpdateStatuses = getVagrant().boxOutdated(outdatedOptions);
            Optional boxStatus =
                    boxUpdateStatuses.stream().filter(status -> status.getName().equals(vagrantBoxDefinition().getName()) &&
                            status.getProvider().equals(vagrantBoxDefinition().getProvider()))
                                     .findFirst();
            if (boxStatus.isPresent())
            {
                //These two states mean the box exists remotely
                //Don't really care if there's an update version-wise since we hard-code our version
                //But that means the box exists remotely
                if (boxStatus.get().getState() == State.UP_TO_DATE || boxStatus.get().getState() == State.OUTDATED)
                    return new ImageComponent(componentType, componentName, ImageStatus.DOWNLOADED);
                else //Either NO_VERSION_INFORMATION or ERROR
                    return new ImageComponent(componentType, componentName, ImageStatus.LOCAL_ONLY, boxStatus.get().getDetail());
            }
            else
            {
                //We don't have a local image yet
                //Since Vagrant doesn't have the ability to check remote without downloading locally first, we'll skip
                //that, assume we can download it and let the prepare() method attempt and succeed/fail
                //The whole point of getting downloadable status is to check for the situation where we have
                //a local copy but the remote is gone so this should be covered
                return new ImageComponent(componentType, componentName, ImageStatus.NOT_DOWNLOADED);
            }
        }
        catch (VagrantException e)
        {
            throw new BoxDatabaseException("Error reading box update status: " + e.getMessage(), e);
        }
    }

    @Override
    public Collection checkImage()
    throws BoxDatabaseException
    {
        ImageComponent jdbcDriverComponent = ImageCheckerUtils.checkImageUsingMavenDependencies("JDBC driver",
                                                                                                getContext(),
                                                                                                jdbcDriverInfo().getDependencies());
        ImageComponent dockerDatabaseComponent = checkVagrantImage();

        return ImmutableList.of(jdbcDriverComponent, dockerDatabaseComponent);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy