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

io.fabric8.maven.docker.assembly.DockerFileBuilder Maven / Gradle / Ivy

The newest version!
package io.fabric8.maven.docker.assembly;

import java.io.File;
import java.io.IOException;
import java.util.*;

import com.google.common.base.Joiner;
import io.fabric8.maven.docker.config.Arguments;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.StringUtils;

/**
 * Create a dockerfile
 *
 * @author roland
 * @since 17.04.14
 */
public class DockerFileBuilder {

    private static final Joiner JOIN_ON_COMMA = Joiner.on("\",\"");

    // Base image to use as from
    private String baseImage;

    // Maintainer of this image
    private String maintainer;

    // Workdir
    private String workdir = null;

    // Basedir to be export
    private String basedir = "/maven";

    private Arguments entryPoint;
    private Arguments cmd;

    private Boolean exportBasedir = null;

    // User under which the files should be added
    private String user;

    // List of files to add. Source and destination follow except that destination
    // in interpreted as a relative path to the exportDir
    // See also http://docs.docker.io/reference/builder/#add
    private List addEntries = new ArrayList<>();

    // list of ports to expose and environments to use
    private List ports = new ArrayList<>();

    // list of RUN Commands to run along with image build see issue #191 on github
    private List runCmds = new ArrayList<>();

    // environment
    private Map envEntries = new HashMap<>();

    // image labels
    private Map labels = new HashMap<>();

    // exposed volumes
    private List volumes = new ArrayList<>();

    // whether the Dockerfile should be optimised. i.e. compressing run statements into a single statement
    private boolean shouldOptimise = false;

    /**
     * Create a DockerFile in the given directory
     * @param  destDir directory where to store the dockerfile
     * @return the full path to the docker file
     * @throws IOException if writing fails
     */
    public File write(File destDir) throws IOException {
        File target = new File(destDir,"Dockerfile");
        FileUtils.fileWrite(target, content());
        return target;
    }

    /**
     * Create a Dockerfile following the format described in the
     * Docker reference manual
     *
     * @return the dockerfile create
     * @throws IllegalArgumentException if no src/dest entries have been added
     */
    public String content() throws IllegalArgumentException {

        StringBuilder b = new StringBuilder();

        DockerFileKeyword.FROM.addTo(b, baseImage != null ? baseImage : DockerAssemblyManager.DEFAULT_DATA_BASE_IMAGE);
        if (maintainer != null) {
            DockerFileKeyword.MAINTAINER.addTo(b, maintainer);
        }

        addOptimisation();
        addEnv(b);
        addLabels(b);
        addPorts(b);

        addEntries(b);
        addWorkdir(b);
        addRun(b);
        addVolumes(b);

        addCmd(b);
        addEntryPoint(b);

        return b.toString();
    }

    private void addWorkdir(StringBuilder b) {
        if (workdir != null) {
            DockerFileKeyword.WORKDIR.addTo(b, workdir);
        }
    }

    private void addEntryPoint(StringBuilder b){
        if (entryPoint != null) {
            buildArguments(b, DockerFileKeyword.ENTRYPOINT, entryPoint);
        }
    }

    private void addCmd(StringBuilder b){
        if (cmd != null) {
            buildArguments(b, DockerFileKeyword.CMD, cmd);
        }
    }

    private static void buildArguments(StringBuilder b, DockerFileKeyword key, Arguments arguments) {
        String arg;
        if (arguments.getShell() != null) {
            arg = arguments.getShell();
        } else {
            arg = "[\""  + JOIN_ON_COMMA.join(arguments.getExec()) + "\"]";
        }
        key.addTo(b, arg);
    }

    private void addEntries(StringBuilder b) {
        if (user != null) {
            String tmpDir = createTempDir();
            copyAddEntries(b,tmpDir);

            String[] userParts = StringUtils.split(user, ":");
            String userArg = userParts.length > 1 ? userParts[0] + ":" + userParts[1] : userParts[0];
            String chmod = "chown -R " + userArg + " " + tmpDir + " && cp -rp " + tmpDir + "/* / && rm -rf " + tmpDir;
            if (userParts.length > 2) {
                DockerFileKeyword.USER.addTo(b, "root");
                DockerFileKeyword.RUN.addTo(b, chmod);
                DockerFileKeyword.USER.addTo(b, userParts[2]);
            } else {
                DockerFileKeyword.RUN.addTo(b, chmod);
            }
        } else {
            copyAddEntries(b, "");
        }
    }

    private String createTempDir() {
         return "/tmp/" + UUID.randomUUID().toString();
    }

    private void copyAddEntries(StringBuilder b, String topLevelDir) {
        for (AddEntry entry : addEntries) {
            String dest = topLevelDir + (basedir.equals("/") ? "" : basedir) + "/" + entry.destination;
            DockerFileKeyword.COPY.addTo(b, entry.source, dest);
        }
    }

    private void addEnv(StringBuilder b) {
        addMap(b, DockerFileKeyword.ENV, envEntries);
    }

    private void addLabels(StringBuilder b) {
        addMap(b, DockerFileKeyword.LABEL, labels);
    }

    private void addMap(StringBuilder b,DockerFileKeyword keyword, Map map) {
        if (map != null && map.size() > 0) {
            String entries[] = new String[map.size()];
            int i = 0;
            for (Map.Entry entry : map.entrySet()) {
                entries[i++] = quote(entry.getKey()) + "=" + quote(entry.getValue());
            }
            keyword.addTo(b, entries);
        }
    }

    private String quote(String value) {
        return StringUtils.quoteAndEscape(value,'"');
    }

    private void addPorts(StringBuilder b) {
        if (ports.size() > 0) {
            String[] portsS = new String[ports.size()];
            int i = 0;
            for (Integer port : ports) {
                portsS[i++] = port.toString();
            }
            DockerFileKeyword.EXPOSE.addTo(b, portsS);
        }
    }

    public void addOptimisation() {
        if (runCmds != null && !runCmds.isEmpty() && shouldOptimise) {
            String optimisedRunCmd = StringUtils.join(runCmds.iterator(), " && ");
            runCmds.clear();
            runCmds.add(optimisedRunCmd);
        }
    }

	private void addRun(StringBuilder b) {
		for (String run : runCmds) {
            DockerFileKeyword.RUN.addTo(b, run);
		}
	}

    private void addVolumes(StringBuilder b) {
        if (exportBasedir != null ? exportBasedir : baseImage == null) {
            addVolume(b, basedir);
        }

        for (String volume : volumes) {
            addVolume(b, volume);
        }
    }

    private void addVolume(StringBuilder buffer, String volume) {
        while (volume.endsWith("/")) {
            volume = volume.substring(0, volume.length() - 1);
        }
        // don't export '/'
        if (volume.length() > 0) {
            DockerFileKeyword.VOLUME.addTo(buffer, "[\"" + volume + "\"]");
        }
    }

    // ==========================================================================
    // Builder stuff ....
    public DockerFileBuilder() {}

    public DockerFileBuilder baseImage(String baseImage) {
        if (baseImage != null) {
            this.baseImage = baseImage;
        }
        return this;
    }

    public DockerFileBuilder maintainer(String maintainer) {
        this.maintainer = maintainer;
        return this;
    }

    public DockerFileBuilder workdir(String workdir) {
        this.workdir = workdir;
        return this;
    }

    public DockerFileBuilder basedir(String dir) {
        if (dir != null) {
            if (!dir.startsWith("/")) {
                throw new IllegalArgumentException("'basedir' must be an absolute path starting with / (and not " +
                                                   "'" + basedir + "')");
            }
            basedir = dir;
        }
        return this;
    }

    public DockerFileBuilder cmd(Arguments cmd) {
        this.cmd = cmd;
        return this;
    }

    public DockerFileBuilder entryPoint(Arguments entryPoint) {
        this.entryPoint = entryPoint;
        return this;
    }

    public DockerFileBuilder user(String user) {
        this.user = user;
        return this;
    }

    public DockerFileBuilder add(String source, String destination) {
        this.addEntries.add(new AddEntry(source, destination));
        return this;
    }

    public DockerFileBuilder expose(List ports) {
        if (ports != null) {
            for (String port : ports) {
                if (port != null) {
                    try {
                        this.ports.add(Integer.parseInt(port));
                    } catch (NumberFormatException exp) {
                        throw new IllegalArgumentException("Non numeric port " + port + " specified in port mapping",exp);
                    }
                }
            }
        }
        return this;
    }

    /**
     * Adds the RUN Commands within the build image section
     * @param runCmds
     * @return
     */
    public DockerFileBuilder run(List runCmds) {
        if (runCmds != null) {
            for (String cmd : runCmds) {
                if (!StringUtils.isEmpty(cmd)) {
                    this.runCmds.add(cmd);
                }
            }
        }
        return this;
    }

    public DockerFileBuilder exportBasedir(Boolean exportBasedir) {
        this.exportBasedir = exportBasedir;
        return this;
    }

    public DockerFileBuilder env(Map values) {
        if (values != null) {
            this.envEntries.putAll(values);
            validateMap(envEntries);
        }
        return this;
    }

    public DockerFileBuilder labels(Map values) {
        if (values != null) {
            this.labels.putAll(values);
            validateMap(labels);
        }
        return this;
    }

    public DockerFileBuilder volumes(List volumes) {
        if (volumes != null) {
           this.volumes.addAll(volumes);
        }
        return this;
    }

    public DockerFileBuilder optimise() {
        this.shouldOptimise = true;
        return this;
    }

    private void validateMap(Map env) {
        for (Map.Entry entry : env.entrySet()) {
            if (entry.getValue() == null || entry.getValue().length() == 0) {
                throw new IllegalArgumentException("Environment variable '" +
                                                   entry.getKey() + "' must not be null or empty if building an image");
            }
        }
    }

    // All entries required, destination is relative to exportDir
    private static final class AddEntry {
        private String source,destination;

        private AddEntry(String src, String dest) {
            source = src;

            // Strip leading slashes
            destination = dest;

            // squeeze slashes
            while (destination.startsWith("/")) {
                destination = destination.substring(1);
            }
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy