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

dev.galasa.docker.internal.DockerVolumeImpl Maven / Gradle / Ivy

The newest version!
/*
 * Copyright contributors to the Galasa project
 *
 * SPDX-License-Identifier: EPL-2.0
 */
package dev.galasa.docker.internal;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;

import dev.galasa.artifact.TestBundleResourceException;
import dev.galasa.docker.DockerManagerException;
import dev.galasa.docker.IDockerVolume;

/**
 * A implementation of the docker volumes that will be brought up on the engine
 * 
 *   
 */
public class DockerVolumeImpl implements IDockerVolume {
    private DockerManagerImpl dockerManager;

    private final String volumeName;
    private final String mountPath;
    private final DockerEngineImpl engine;
    private final boolean readOnly;
    private final String tag;

    private static final Log logger = LogFactory.getLog(DockerVolumeImpl.class);

    /**
     * Constructor that determines the nature of the volume (readOnly or not), and
     * provisions or ensures the volumes exists.
     * 
     * @param volumeName
     * @param mountPath
     * @param engine
     * @throws DockerManagerException
     */
    public DockerVolumeImpl(DockerManagerImpl dockerManager, String volumeName, String tag, String mountPath,
            DockerEngineImpl engine, boolean readOnly, boolean provision) throws DockerManagerException {
        this.dockerManager = dockerManager;
        this.volumeName = volumeName;
        this.mountPath = mountPath;
        this.engine = engine;
        this.readOnly = readOnly;
        this.tag = tag;

        if (provision) {
            logger.info("Generating volume");
            engine.createVolume(volumeName);
        }

        if (!doesVolumeExist()) {
            logger.error("No volume found with name: " + this.volumeName);
            throw new DockerManagerException("Could not find volume with name: " + this.volumeName);

        }
        logger.info("Volume found.");
    }

    /**
     * Returns volume name
     * 
     * @return volumeName
     */
    @Override
    public String getVolumeName() {
        return this.volumeName;
    }

    /**
     * Returns mount path
     * 
     * @return mountPath
     */
    @Override
    public String getMountPath() {
        return this.mountPath;
    }

    /**
     * Returns readonly state
     * 
     * @return readOnly
     */
    @Override
    public boolean readOnly() {
        return this.readOnly;
    }

    /**
     * Return tag of hosted engine.
     */
    @Override
    public String getEngineTag() {
        return engine.getEngineTag();
    }

    @Override
    public String getVolumeTag() {
        return this.tag;
    }

    /**
     * Checks the docker engine for a specific named volume
     * 
     * @return boolean exists
     */
    private boolean doesVolumeExist() throws DockerManagerException {
        if (engine.getVolume(this.volumeName) != null) {
            return true;
        }
        return false;
    }

    /**
     * Discard a volume. This should only be called for galasa created volumes.
     * 
     * @throws DockerManagerException
     */
    public void discard() throws DockerManagerException {

        engine.deleteVolume(this.volumeName);
    }

    /**
     * Load a file from an InputStream into the volume. This can be called before the volume is used or 
     * mounted to any containers.
     * 
     * @param fileName
     * @param data
     * @throws DockerManagerException
     */
    @Override
    public void LoadFile(String fileName, InputStream data) throws DockerManagerException {
        DockerImageBuilderImpl builder = new DockerImageBuilderImpl(engine);
        
        Map subs = new HashMap<>();
        
        subs.put("BUSYBOX", this.engine.getBusybox());
        subs.put("FILENAME", fileName);
        subs.put("MOUNTPATH", this.mountPath);
        
        // Create a busy box image to load the volume
        InputStream dockerfile = createDockerfile("VolumeBusyboxDockerfile", subs);
        Map resources = new HashMap<>();
        resources.put(fileName, data);
        builder.buildImage("galasa-volume-loader", dockerfile, resources);

        //  Run the busybox, then remove it
        JsonObject json = engine.createContainer(this.volumeName + "_LOADER", generateMetadata("galasa-volume-loader"));
        if (json == null) {
        	throw new DockerManagerException("Create container did not return JSON object.");
        }
        JsonElement oId = json.get("Id");
        if (oId == null) {
        	throw new DockerManagerException("Id property missing from create container JSON :-\n" + json.toString());
        }
        String containerId = oId.getAsString();

        String status = "";
        engine.startContainer(containerId);
        
        while (!"exited".equals(status)) {
            json = engine.getContainer(containerId);
            json = json.get("State").getAsJsonObject();
            status = json.get("Status").getAsString();
        }
        
        engine.deleteContainer(containerId);
    }

    /**
     * Pass a fileName and String to load into a docker volume. This can be called before the volume is used or 
     * mounted to any containers.
     * 
     * @param fileName
     * @param data
     * @throws DockerManagerException
     */
    @Override
    public void LoadFileAsString(String fileName, String data) throws DockerManagerException {
        LoadFile(fileName, new ByteArrayInputStream(data.getBytes()));
    }

    /**
     * A private method used to create a dockerfile for the busybox image used to load files into the volume.
     * 
     * @param path
     * @param fileName
     * @return
     * @throws DockerManagerException
     */
    private InputStream createDockerfile(String dockerfile, Map subs) throws DockerManagerException {
        try {
            String dockerfileTemplate = this.dockerManager.getArtifactManager()
            .getBundleResources(this.getClass())
            .retrieveSkeletonFileAsString(dockerfile, subs);

            return new ByteArrayInputStream(dockerfileTemplate.getBytes());
        } catch (IOException | TestBundleResourceException e) {
            throw new DockerManagerException("Failed to generate the Dockerfile for loading volumes", e);
        }
    }

    /**
     * Generates the metadata used by container that will load a docker volume
     * @param imageName
     * @return
     */
    private JsonObject generateMetadata(String imageName) {
        JsonObject metadata = new JsonObject();
        metadata.addProperty("Image", imageName);

        JsonObject hostConfig = new JsonObject();
        metadata.add("HostConfig", hostConfig);
        
        JsonObject labels = new JsonObject();
        labels.addProperty("GALASA", "GALASA");
        metadata.add("Labels", labels);
        
        // Volumes
        JsonArray mounts = new JsonArray();

        JsonObject mount = new JsonObject();

        mount.addProperty("Target", this.mountPath);
        mount.addProperty("Source", this.volumeName);
        mount.addProperty("Type", "volume");
        mount.addProperty("ReadOnly", this.readOnly);

        mounts.add(mount);
        
        if (mounts.size() > 0 ) {
            hostConfig.add("Mounts", mounts);
            metadata.remove("HostConfig");
            metadata.add("HostConfig", hostConfig);
        }
        return metadata;
    }
    
    
    public void runCommand(String command) throws DockerManagerException {
        DockerImageBuilderImpl builder = new DockerImageBuilderImpl(engine);
        
        Map subs = new HashMap<>();
        subs.put("BUSYBOX", engine.getBusybox());
        subs.put("COMMAND", command);
        logger.info("Command: " + command);

        // Create a busy box image to load the volume
        InputStream dockerfile = createDockerfile("CommandBusyboxDockerfile", subs);

        builder.buildImage("galasa-volume-loader", dockerfile);

        //  Run the busybox, then remove it
        JsonObject json = engine.createContainer(this.volumeName + "_LOADER", generateMetadata("galasa-volume-loader"));
        String containerId = json.get("Id").getAsString();

        String status = "";
        engine.startContainer(containerId);
        
        while (!"exited".equals(status)) {
            json = engine.getContainer(containerId);
            json = json.get("State").getAsJsonObject();
            status = json.get("Status").getAsString();
        }
        
        engine.deleteContainer(containerId);
    }

	@Override
	public void fileChown(String userGroup, String filename) throws DockerManagerException {
		runCommand("\"chown\",\"" + userGroup + "\",\"" + this.mountPath + "/"+ filename +"\"");
	}

	@Override
	public void fileChmod(String permissions, String filename) throws DockerManagerException {
		runCommand("\"chmod\",\"" + permissions + "\",\"" + this.mountPath + "/"+ filename +"\"");
		
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy