/******************************************************************************* * Copyright (c) 2014, 2016 Red Hat. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Red Hat - Initial Contribution *******************************************************************************/ package org.eclipse.linuxtools.internal.docker.core; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import org.eclipse.linuxtools.docker.core.IDockerContainerConfig; import com.spotify.docker.client.messages.ContainerConfig; import com.spotify.docker.client.messages.HostConfig; public class DockerContainerConfig implements IDockerContainerConfig { private final String hostname; private final String domainname; private final String user; private final Long memory; private final Long memorySwap; private final Long cpuShares; private final String cpuset; private final boolean attachStdin; private final boolean attachStdout; private final boolean attachStderr; private final List<String> portSpecs; private final Set<String> exposedPorts; private final boolean tty; private final boolean openStdin; private final boolean stdinOnce; private final List<String> env; private String rawcmd; private final List<String> cmd; private final String image; private final Set<String> volumes; private final String workingDir; private final List<String> entrypoint; private final boolean networkDisabled; private final List<String> onBuild; private final Map<String, String> labels; public DockerContainerConfig(final ContainerConfig containerConfig) { this.hostname = containerConfig != null ? containerConfig.hostname() : null; this.domainname = containerConfig != null ? containerConfig.domainname() : null; this.user = containerConfig != null ? containerConfig.user() : null; final HostConfig hc = containerConfig != null ? containerConfig.hostConfig() : null; this.memory = hc != null ? hc.memory() : null; this.memorySwap = hc != null ? hc.memorySwap() : null; this.cpuShares = hc != null ? hc.cpuShares() : null; this.cpuset = hc != null ? hc.cpusetCpus() : null; this.attachStdin = containerConfig != null && containerConfig.attachStdin() != null ? containerConfig.attachStdin() : false; this.attachStdout = containerConfig != null && containerConfig.attachStdout() != null ? containerConfig.attachStdout() : false; this.attachStderr = containerConfig != null && containerConfig.attachStderr() != null ? containerConfig.attachStderr() : false; this.portSpecs = containerConfig != null ? containerConfig.portSpecs() : null; this.exposedPorts = containerConfig != null ? containerConfig.exposedPorts() : null; this.tty = containerConfig != null && containerConfig.tty() != null ? containerConfig.tty() : false; this.openStdin = containerConfig != null && containerConfig.openStdin() != null ? containerConfig.openStdin() : false; this.stdinOnce = containerConfig != null && containerConfig.stdinOnce() != null ? containerConfig.stdinOnce() : false; this.env = containerConfig != null ? containerConfig.env() : null; this.cmd = containerConfig != null ? containerConfig.cmd() : null; this.image = containerConfig != null ? containerConfig.image() : null; /* * This is a bug in spotify/docker-client 6.1.1. * com.spotify.docker.client.ContainerConfig.volumes() tries * volumes.keySet() which might be null. For now we need to guard * against this. */ Set<String> res = null; try { res = containerConfig != null ? containerConfig.volumes() : null; } catch (NullPointerException e) { } this.volumes = res; this.workingDir = containerConfig != null ? containerConfig.workingDir() : null; this.entrypoint = containerConfig != null ? containerConfig.entrypoint() : null; this.networkDisabled = containerConfig != null && containerConfig.networkDisabled() != null ? containerConfig.networkDisabled() : false; this.onBuild = containerConfig != null ? containerConfig.onBuild() : null; this.labels = containerConfig != null ? containerConfig.labels() : null; } private DockerContainerConfig(final Builder builder) { this.hostname = builder.hostname; this.domainname = builder.domainname; this.user = builder.user; this.memory = builder.memory; this.memorySwap = builder.memorySwap; this.cpuShares = builder.cpuShares; this.cpuset = builder.cpuset; this.attachStdin = builder.attachStdin != null ? builder.attachStdin : false; this.attachStdout = builder.attachStdout != null ? builder.attachStdout : false; this.attachStderr = builder.attachStderr != null ? builder.attachStderr : false; this.portSpecs = builder.portSpecs; this.exposedPorts = builder.exposedPorts; this.tty = builder.tty != null ? builder.tty : false; this.openStdin = builder.openStdin != null ? builder.openStdin : false; this.stdinOnce = builder.stdinOnce != null ? builder.stdinOnce : false; this.env = builder.env; this.rawcmd = builder.rawcmd; this.cmd = builder.cmd; this.image = builder.image; this.volumes = builder.volumes; this.workingDir = builder.workingDir; this.entrypoint = builder.entrypoint; this.networkDisabled = builder.networkDisabled != null ? builder.networkDisabled : false; this.onBuild = builder.onBuild; this.labels = builder.labels; } @Override public String hostname() { return hostname; } @Override public String domainname() { return domainname; } @Override public String user() { return user; } /** * @see DockerHostConfig#memory() */ @Deprecated @Override public Long memory() { return memory; } @Deprecated @Override public Long memorySwap() { return memorySwap; } /** * @see DockerHostConfig#cpuShares() */ @Deprecated @Override public Long cpuShares() { return cpuShares; } @Deprecated @Override public String cpuset() { return cpuset; } @Override public boolean attachStdin() { return attachStdin; } @Override public boolean attachStdout() { return attachStdout; } @Override public boolean attachStderr() { return attachStderr; } @Override public List<String> portSpecs() { if (portSpecs == null) { return Collections.emptyList(); } return portSpecs; } @Override public Set<String> exposedPorts() { if (exposedPorts == null) { return Collections.emptySet(); } return exposedPorts; } @Override public boolean tty() { return tty; } @Override public boolean openStdin() { return openStdin; } @Override public boolean stdinOnce() { return stdinOnce; } @Override public List<String> env() { if (env == null) { return Collections.emptyList(); } return env; } @Override public List<String> cmd() { if (cmd == null) { return Collections.emptyList(); } return cmd; } public String rawcmd() { return rawcmd; } @Override public String image() { return image; } @Override public Set<String> volumes() { if (volumes == null) { return Collections.emptySet(); } return volumes; } @Override public String workingDir() { return workingDir; } @Override public List<String> entrypoint() { if (entrypoint == null) { return Collections.emptyList(); } return entrypoint; } @Override public boolean networkDisabled() { return networkDisabled; } @Override public List<String> onBuild() { if (onBuild == null) { return Collections.emptyList(); } return onBuild; } // @Override @Override public Map<String, String> labels() { if (this.labels == null) { return Collections.emptyMap(); } return this.labels; } public static class Builder { private String hostname; private String domainname; private String user; private Long memory; private Long memorySwap; private Long cpuShares; private String cpuset; private Boolean attachStdin; private Boolean attachStdout; private Boolean attachStderr; private List<String> portSpecs; private Set<String> exposedPorts; private Boolean tty; private Boolean openStdin; private Boolean stdinOnce; private List<String> env; private String rawcmd; private List<String> cmd; private String image; private Set<String> volumes; private String workingDir; private List<String> entrypoint; private Boolean networkDisabled; private List<String> onBuild; private Map<String, String> labels; public Builder hostname(final String hostname) { this.hostname = hostname; return this; } public String hostname() { return hostname; } public Builder domainname(final String domainname) { this.domainname = domainname; return this; } public String domainname() { return domainname; } public Builder user(final String user) { this.user = user; return this; } public String user() { return user; } public Builder memory(final Long memory) { this.memory = memory; return this; } public Long memory() { return memory; } public Builder memorySwap(final Long memorySwap) { this.memorySwap = memorySwap; return this; } public Long memorySwap() { return memorySwap; } public Builder cpuShares(final Long cpuShares) { this.cpuShares = cpuShares; return this; } public Long cpuShares() { return cpuShares; } public Builder cpuset(final String cpuset) { this.cpuset = cpuset; return this; } public String cpuset() { return cpuset; } public Builder attachStdin(final Boolean attachStdin) { this.attachStdin = attachStdin; return this; } public Boolean attachStdin() { return attachStdin; } public Builder attachStdout(final Boolean attachStdout) { this.attachStdout = attachStdout; return this; } public Boolean attachStdout() { return attachStdout; } public Builder attachStderr(final Boolean attachStderr) { this.attachStderr = attachStderr; return this; } public Boolean attachStderr() { return attachStderr; } public Builder portSpecs(final List<String> portSpecs) { this.portSpecs = new ArrayList<>(portSpecs); return this; } public Builder portSpecs(final String... portSpecs) { this.portSpecs = Arrays.asList(portSpecs); return this; } public List<String> portSpecs() { return portSpecs; } public Builder exposedPorts(final Set<String> exposedPorts) { this.exposedPorts = new TreeSet<>(exposedPorts); return this; } public Builder exposedPorts(final String... exposedPorts) { this.exposedPorts = new TreeSet<>(Arrays.asList(exposedPorts)); return this; } public Set<String> exposedPorts() { return exposedPorts; } public Builder tty(final Boolean tty) { this.tty = tty; return this; } public Boolean tty() { return tty; } public Builder openStdin(final Boolean openStdin) { this.openStdin = openStdin; return this; } public Boolean openStdin() { return openStdin; } public Builder stdinOnce(final Boolean stdinOnce) { this.stdinOnce = stdinOnce; return this; } public Boolean stdinOnce() { return stdinOnce; } public Builder env(final List<String> env) { this.env = new ArrayList<>(env); return this; } public Builder env(final String... env) { this.env = Arrays.asList(env); return this; } public List<String> env() { return env; } public Builder cmd(final List<String> cmd) { this.cmd = cmd; return this; } public Builder cmd(final String... cmd) { return cmd(Arrays.asList(cmd)); } public Builder cmd(final String cmd) { this.rawcmd = cmd; this.cmd = getCmdList(cmd); return this; } public List<String> cmd() { return cmd; } public Builder image(final String image) { this.image = image; return this; } public String image() { return image; } public Builder volumes(final Set<String> volumes) { this.volumes = new TreeSet<>(volumes); return this; } public Builder volumes(final String... volumes) { this.volumes = new TreeSet<>(Arrays.asList(volumes)); return this; } public Set<String> volumes() { return volumes; } public Builder workingDir(final String workingDir) { this.workingDir = workingDir; return this; } public String workingDir() { return workingDir; } public Builder entryPoint(final List<String> entrypoint) { if (entrypoint != null && !entrypoint.isEmpty()) { this.entrypoint = new ArrayList<>(entrypoint); } return this; } public Builder entryPoint(final String... entrypoint) { return entryPoint(Arrays.asList(entrypoint)); } public Builder entryPoint(final String entrypoint) { if (entrypoint != null && !entrypoint.isEmpty()) { return entryPoint(entrypoint.split(" ")); } else { this.entrypoint = null; } return this; } public List<String> entryPoint() { return entrypoint; } public Builder networkDisabled(final Boolean networkDisabled) { this.networkDisabled = networkDisabled; return this; } public Boolean networkDisabled() { return networkDisabled; } public Builder onBuild(final List<String> onBuild) { this.onBuild = new ArrayList<>(onBuild); return this; } public Builder onBuild(final String... onBuild) { this.onBuild = Arrays.asList(onBuild); return this; } public List<String> onBuild() { return onBuild; } public Builder labels(final Map<String, String> labels) { this.labels = labels; return this; } public DockerContainerConfig build() { return new DockerContainerConfig(this); } /** * Create a proper command list after handling quotation. * * @param command * the command as a single {@link String} * @return the command splitted in a list of ars or <code>null</code> if * the input <code>command</code> was <code>null</code>. */ private List<String> getCmdList(final String command) { if (command == null) { return null; } final List<String> list = new ArrayList<>(); int length = command.length(); boolean insideQuote1 = false; // single-quote boolean insideQuote2 = false; // double-quote boolean escaped = false; StringBuffer buffer = new StringBuffer(); // Parse the string and break it up into chunks that are // separated by white-space or are quoted. Ignore characters // that have been escaped, including the escape character. for (int i = 0; i < length; ++i) { char c = command.charAt(i); if (escaped) { buffer.append(c); escaped = false; } switch (c) { case '\'': if (!insideQuote2) insideQuote1 = insideQuote1 ^ true; else buffer.append(c); break; case '\"': if (!insideQuote1) insideQuote2 = insideQuote2 ^ true; else buffer.append(c); break; case '\\': escaped = true; break; case ' ': case '\t': case '\r': case '\n': if (insideQuote1 || insideQuote2) buffer.append(c); else { String item = buffer.toString(); buffer.setLength(0); if (item.length() > 0) list.add(item); } break; default: buffer.append(c); break; } } // add last item of string that will be in the buffer String item = buffer.toString(); if (item.length() > 0) list.add(item); return list; } } }