diff --git a/hadoop-yarn-project/hadoop-yarn/conf/container-executor.cfg b/hadoop-yarn-project/hadoop-yarn/conf/container-executor.cfg index d68cee8cc98..023654b7dba 100644 --- a/hadoop-yarn-project/hadoop-yarn/conf/container-executor.cfg +++ b/hadoop-yarn-project/hadoop-yarn/conf/container-executor.cfg @@ -2,3 +2,15 @@ yarn.nodemanager.linux-container-executor.group=#configured value of yarn.nodema banned.users=#comma separated list of users who can not run applications min.user.id=1000#Prevent other super-users allowed.system.users=##comma separated list of system users who CAN run applications +feature.tc.enabled=0 + +# The configs below deal with settings for Docker +#[docker] +# module.enabled=## enable/disable the module. set to "true" to enable, disabled by default +# docker.binary=/usr/bin/docker +# docker.allowed.capabilities=## comma seperated capabilities that can be granted, e.g CHOWN,DAC_OVERRIDE,FSETID,FOWNER,MKNOD,NET_RAW,SETGID,SETUID,SETFCAP,SETPCAP,NET_BIND_SERVICE,SYS_CHROOT,KILL,AUDIT_WRITE +# docker.allowed.devices=## comma seperated list of devices that can be mounted into a container +# docker.allowed.networks=## comma seperated networks that can be used. e.g bridge,host,none +# docker.allowed.ro-mounts=## comma seperated volumes that can be mounted as read-only +# docker.allowed.rw-mounts=## comma seperate volumes that can be mounted as read-write, add the yarn local and log dirs to this list to run Hadoop jobs +# docker.privileged-containers.enabled=0 diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/CMakeLists.txt b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/CMakeLists.txt index 3d5b506e05f..9d83bf339e8 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/CMakeLists.txt +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/CMakeLists.txt @@ -132,6 +132,7 @@ add_library(container main/native/container-executor/impl/modules/cgroups/cgroups-operations.c main/native/container-executor/impl/modules/common/module-configs.c main/native/container-executor/impl/modules/gpu/gpu-module.c + main/native/container-executor/impl/utils/docker-util.c ) add_executable(container-executor @@ -156,6 +157,7 @@ output_directory(test-container-executor target/usr/local/bin) # unit tests for container executor add_executable(cetest + main/native/container-executor/impl/get_executable.c main/native/container-executor/impl/util.c main/native/container-executor/test/test_configuration.cc main/native/container-executor/test/test_main.cc @@ -163,6 +165,7 @@ add_executable(cetest main/native/container-executor/test/utils/test-path-utils.cc main/native/container-executor/test/modules/cgroups/test-cgroups-module.cc main/native/container-executor/test/modules/gpu/test-gpu-module.cc - main/native/container-executor/test/test_util.cc) + main/native/container-executor/test/test_util.cc + main/native/container-executor/test/utils/test_docker_util.cc) target_link_libraries(cetest gtest container) output_directory(cetest test) diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/DockerLinuxContainerRuntime.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/DockerLinuxContainerRuntime.java index b8d9b0ad859..29c6fe99894 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/DockerLinuxContainerRuntime.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/DockerLinuxContainerRuntime.java @@ -559,8 +559,8 @@ public class DockerLinuxContainerRuntime implements LinuxContainerRuntime { runCommand.setCapabilities(capabilities); if(cgroupsRootDirectory != null) { - runCommand.addMountLocation(cgroupsRootDirectory, - cgroupsRootDirectory + ":ro", false); + runCommand.addReadOnlyMountLocation(cgroupsRootDirectory, + cgroupsRootDirectory, false); } List allDirs = new ArrayList<>(containerLocalDirs); @@ -584,7 +584,7 @@ public class DockerLinuxContainerRuntime implements LinuxContainerRuntime { } String src = validateMount(dir[0], localizedResources); String dst = dir[1]; - runCommand.addMountLocation(src, dst + ":ro", true); + runCommand.addReadOnlyMountLocation(src, dst, true); } } } @@ -626,7 +626,7 @@ public class DockerLinuxContainerRuntime implements LinuxContainerRuntime { launchOp, null, null, false, false); } catch (PrivilegedOperationException e) { LOG.warn("Launch container failed. Exception: ", e); - LOG.info("Docker command used: " + runCommand.getCommandWithArguments()); + LOG.info("Docker command used: " + runCommand); throw new ContainerExecutionException("Launch container failed", e .getExitCode(), e.getOutput(), e.getErrorOutput()); @@ -757,8 +757,7 @@ public class DockerLinuxContainerRuntime implements LinuxContainerRuntime { launchOp.appendArgs(tcCommandFile); } if (LOG.isDebugEnabled()) { - LOG.debug("Launching container with cmd: " + runCommand - .getCommandWithArguments()); + LOG.debug("Launching container with cmd: " + runCommand); } return launchOp; diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerClient.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerClient.java index 536a22d5e0c..77c53a86970 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerClient.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerClient.java @@ -23,7 +23,7 @@ package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.resources.ResourceHandlerException; +import org.apache.hadoop.util.StringUtils; import org.apache.hadoop.yarn.server.nodemanager.containermanager.runtime.ContainerExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,6 +34,8 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.Writer; +import java.util.List; +import java.util.Map; @InterfaceAudience.Private @InterfaceStability.Unstable @@ -68,10 +70,25 @@ public final class DockerClient { TMP_FILE_SUFFIX, new File(tmpDirPath)); - Writer writer = new OutputStreamWriter(new FileOutputStream(dockerCommandFile), - "UTF-8"); + Writer writer = new OutputStreamWriter( + new FileOutputStream(dockerCommandFile), "UTF-8"); PrintWriter printWriter = new PrintWriter(writer); - printWriter.print(cmd.getCommandWithArguments()); + printWriter.println("[docker-command-execution]"); + for (Map.Entry> entry : + cmd.getDockerCommandWithArguments().entrySet()) { + if (entry.getKey().contains("=")) { + throw new ContainerExecutionException( + "'=' found in entry for docker command file, key = " + entry + .getKey() + "; value = " + entry.getValue()); + } + if (entry.getValue().contains("\n")) { + throw new ContainerExecutionException( + "'\\n' found in entry for docker command file, key = " + entry + .getKey() + "; value = " + entry.getValue()); + } + printWriter.println(" " + entry.getKey() + "=" + StringUtils + .join(",", entry.getValue())); + } printWriter.close(); return dockerCommandFile.getAbsolutePath(); diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerCommand.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerCommand.java index 3b76a5cca40..7802209a8f3 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerCommand.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerCommand.java @@ -25,8 +25,10 @@ import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.util.StringUtils; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.TreeMap; @InterfaceAudience.Private @InterfaceStability.Unstable @@ -35,32 +37,55 @@ import java.util.List; * e.g 'run', 'load', 'inspect' etc., */ -public abstract class DockerCommand { +public abstract class DockerCommand { private final String command; - private final List commandWithArguments; + private final Map> commandArguments; protected DockerCommand(String command) { + String dockerCommandKey = "docker-command"; this.command = command; - this.commandWithArguments = new ArrayList<>(); - commandWithArguments.add(command); + this.commandArguments = new TreeMap<>(); + commandArguments.put(dockerCommandKey, new ArrayList<>()); + commandArguments.get(dockerCommandKey).add(command); } - /** Returns the docker sub-command string being used - * e.g 'run' + /** + * Returns the docker sub-command string being used + * e.g 'run'. */ public final String getCommandOption() { return this.command; } - /** Add command commandWithArguments - this method is only meant for use by - * sub-classes - * @param arguments to be added + /** + * Add command commandWithArguments - this method is only meant for use by + * sub-classes. + * + * @param key name of the key to be added + * @param value value of the key */ - protected final void addCommandArguments(String... arguments) { - this.commandWithArguments.addAll(Arrays.asList(arguments)); + protected final void addCommandArguments(String key, String value) { + List list = commandArguments.get(key); + if (list != null) { + list.add(value); + return; + } + list = new ArrayList<>(); + list.add(value); + this.commandArguments.put(key, list); } - public String getCommandWithArguments() { - return StringUtils.join(" ", commandWithArguments); + public Map> getDockerCommandWithArguments() { + return Collections.unmodifiableMap(commandArguments); } -} \ No newline at end of file + + @Override + public String toString() { + StringBuffer ret = new StringBuffer(this.command); + for (Map.Entry> entry : commandArguments.entrySet()) { + ret.append(" ").append(entry.getKey()); + ret.append("=").append(StringUtils.join(",", entry.getValue())); + } + return ret.toString(); + } +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerCommandExecutor.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerCommandExecutor.java index 5739912dd45..76b53af3fc6 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerCommandExecutor.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerCommandExecutor.java @@ -88,8 +88,7 @@ public final class DockerCommandExecutor { dockerOp.disableFailureLogging(); } if (LOG.isDebugEnabled()) { - LOG.debug("Running docker command: " - + dockerCommand.getCommandWithArguments()); + LOG.debug("Running docker command: " + dockerCommand); } try { String result = privilegedOperationExecutor diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerInspectCommand.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerInspectCommand.java index 812a35f43ff..d27f74d0f16 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerInspectCommand.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerInspectCommand.java @@ -26,16 +26,14 @@ package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime */ public class DockerInspectCommand extends DockerCommand { private static final String INSPECT_COMMAND = "inspect"; - private String containerName; public DockerInspectCommand(String containerName) { super(INSPECT_COMMAND); - this.containerName = containerName; + super.addCommandArguments("name", containerName); } public DockerInspectCommand getContainerStatus() { - super.addCommandArguments("--format='{{.State.Status}}'"); - super.addCommandArguments(containerName); + super.addCommandArguments("format", "{{.State.Status}}"); return this; } @@ -43,9 +41,8 @@ public class DockerInspectCommand extends DockerCommand { // Be sure to not use space in the argument, otherwise the // extract_values_delim method in container-executor binary // cannot parse the arguments correctly. - super.addCommandArguments("--format='{{range(.NetworkSettings.Networks)}}" - + "{{.IPAddress}},{{end}}{{.Config.Hostname}}'"); - super.addCommandArguments(containerName); + super.addCommandArguments("format", "{{range(.NetworkSettings.Networks)}}" + + "{{.IPAddress}},{{end}}{{.Config.Hostname}}"); return this; } -} \ No newline at end of file +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerLoadCommand.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerLoadCommand.java index e4d92e08bc1..fa2988c6461 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerLoadCommand.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerLoadCommand.java @@ -25,6 +25,6 @@ public class DockerLoadCommand extends DockerCommand { public DockerLoadCommand(String localImageFile) { super(LOAD_COMMAND); - super.addCommandArguments("--i=" + localImageFile); + super.addCommandArguments("image", localImageFile); } } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerPullCommand.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerPullCommand.java index 351e09ebc00..5e6108e8c3f 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerPullCommand.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerPullCommand.java @@ -25,7 +25,7 @@ public class DockerPullCommand extends DockerCommand { public DockerPullCommand(String imageName) { super(PULL_COMMAND); - super.addCommandArguments(imageName); + super.addCommandArguments("image", imageName); } } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerRmCommand.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerRmCommand.java index b1aea61659a..dcfe7779dbc 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerRmCommand.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerRmCommand.java @@ -25,6 +25,6 @@ public class DockerRmCommand extends DockerCommand { public DockerRmCommand(String containerName) { super(RM_COMMAND); - super.addCommandArguments(containerName); + super.addCommandArguments("name", containerName); } -} \ No newline at end of file +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerRunCommand.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerRunCommand.java index 1e1e6e8014e..c7bf827f545 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerRunCommand.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerRunCommand.java @@ -20,42 +20,39 @@ package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker; -import org.apache.hadoop.util.StringUtils; - import java.io.File; -import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Set; public class DockerRunCommand extends DockerCommand { private static final String RUN_COMMAND = "run"; - private final String image; - private List overrrideCommandWithArgs; /** The following are mandatory: */ public DockerRunCommand(String containerId, String user, String image) { super(RUN_COMMAND); - super.addCommandArguments("--name=" + containerId, "--user=" + user); - this.image = image; + super.addCommandArguments("name", containerId); + super.addCommandArguments("user", user); + super.addCommandArguments("image", image); } public DockerRunCommand removeContainerOnExit() { - super.addCommandArguments("--rm"); + super.addCommandArguments("rm", "true"); return this; } public DockerRunCommand detachOnRun() { - super.addCommandArguments("-d"); + super.addCommandArguments("detach", "true"); return this; } public DockerRunCommand setContainerWorkDir(String workdir) { - super.addCommandArguments("--workdir=" + workdir); + super.addCommandArguments("workdir", workdir); return this; } public DockerRunCommand setNetworkType(String type) { - super.addCommandArguments("--net=" + type); + super.addCommandArguments("net", type); return this; } @@ -65,79 +62,80 @@ public class DockerRunCommand extends DockerCommand { if (!sourceExists && !createSource) { return this; } - super.addCommandArguments("-v", sourcePath + ":" + destinationPath); + super.addCommandArguments("rw-mounts", sourcePath + ":" + destinationPath); + return this; + } + + public DockerRunCommand addReadOnlyMountLocation(String sourcePath, String + destinationPath, boolean createSource) { + boolean sourceExists = new File(sourcePath).exists(); + if (!sourceExists && !createSource) { + return this; + } + super.addCommandArguments("ro-mounts", sourcePath + ":" + destinationPath); return this; } public DockerRunCommand setCGroupParent(String parentPath) { - super.addCommandArguments("--cgroup-parent=" + parentPath); + super.addCommandArguments("cgroup-parent", parentPath); return this; } /* Run a privileged container. Use with extreme care */ public DockerRunCommand setPrivileged() { - super.addCommandArguments("--privileged"); + super.addCommandArguments("privileged", "true"); return this; } public DockerRunCommand setCapabilities(Set capabilties) { //first, drop all capabilities - super.addCommandArguments("--cap-drop=ALL"); + super.addCommandArguments("cap-drop", "ALL"); //now, add the capabilities supplied for (String capability : capabilties) { - super.addCommandArguments("--cap-add=" + capability); + super.addCommandArguments("cap-add", capability); } return this; } public DockerRunCommand setHostname(String hostname) { - super.addCommandArguments("--hostname=" + hostname); + super.addCommandArguments("hostname", hostname); return this; } public DockerRunCommand addDevice(String sourceDevice, String destinationDevice) { - super.addCommandArguments("--device=" + sourceDevice + ":" + + super.addCommandArguments("devices", sourceDevice + ":" + destinationDevice); return this; } public DockerRunCommand enableDetach() { - super.addCommandArguments("--detach=true"); + super.addCommandArguments("detach", "true"); return this; } public DockerRunCommand disableDetach() { - super.addCommandArguments("--detach=false"); + super.addCommandArguments("detach", "false"); return this; } public DockerRunCommand groupAdd(String[] groups) { - for(int i = 0; i < groups.length; i++) { - super.addCommandArguments("--group-add " + groups[i]); - } + super.addCommandArguments("group-add", String.join(",", groups)); return this; } public DockerRunCommand setOverrideCommandWithArgs( List overrideCommandWithArgs) { - this.overrrideCommandWithArgs = overrideCommandWithArgs; + for(String override: overrideCommandWithArgs) { + super.addCommandArguments("launch-command", override); + } return this; } @Override - public String getCommandWithArguments() { - List argList = new ArrayList<>(); - - argList.add(super.getCommandWithArguments()); - argList.add(image); - - if (overrrideCommandWithArgs != null) { - argList.addAll(overrrideCommandWithArgs); - } - - return StringUtils.join(" ", argList); + public Map> getDockerCommandWithArguments() { + return super.getDockerCommandWithArguments(); } } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerStopCommand.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerStopCommand.java index e9d6c43c0f0..ccfff827a0a 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerStopCommand.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerStopCommand.java @@ -29,11 +29,11 @@ public class DockerStopCommand extends DockerCommand { public DockerStopCommand(String containerName) { super(STOP_COMMAND); - super.addCommandArguments(containerName); + super.addCommandArguments("name", containerName); } public DockerStopCommand setGracePeriod(int value) { - super.addCommandArguments("--time=" + Integer.toString(value)); + super.addCommandArguments("time", Integer.toString(value)); return this; } -} \ No newline at end of file +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/configuration.c b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/configuration.c index 12dbc4c5e4a..f23cff0ef2e 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/configuration.c +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/configuration.c @@ -21,6 +21,7 @@ #include "configuration.h" #include "util.h" +#include "get_executable.h" #define __STDC_FORMAT_MACROS #include @@ -696,3 +697,19 @@ int get_kv_value(const char *input, char *out, size_t out_len) { return 0; } + +char *get_config_path(const char *argv0) { + char *executable_file = get_executable((char *) argv0); + if (!executable_file) { + fprintf(ERRORFILE, "realpath of executable: %s\n", + errno != 0 ? strerror(errno) : "unknown"); + return NULL; + } + + const char *orig_conf_file = HADOOP_CONF_DIR "/" CONF_FILENAME; + char *conf_file = resolve_config_path(orig_conf_file, executable_file); + if (conf_file == NULL) { + fprintf(ERRORFILE, "Configuration file %s not found.\n", orig_conf_file); + } + return conf_file; +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/configuration.h b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/configuration.h index 1ea5561bc7f..7fa684e09e5 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/configuration.h +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/configuration.h @@ -23,10 +23,21 @@ #define _WITH_GETLINE #endif -#include +#include "config.h" -/** Define a platform-independent constant instead of using PATH_MAX */ -#define EXECUTOR_PATH_MAX 4096 +#define CONF_FILENAME "container-executor.cfg" + +// When building as part of a Maven build this value gets defined by using +// container-executor.conf.dir property. See: +// hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/pom.xml +// for details. +// NOTE: if this ends up being a relative path it gets resolved relative to +// the location of the container-executor binary itself, not getwd(3) +#ifndef HADOOP_CONF_DIR +#error HADOOP_CONF_DIR must be defined +#endif + +#include // Configuration data structures. struct kv_pair { @@ -207,4 +218,6 @@ int get_kv_key(const char *input, char *out, size_t out_len); */ int get_kv_value(const char *input, char *out, size_t out_len); +char *get_config_path(const char* argv0); + #endif diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.c b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.c index e8bf564fd38..08d69a5d5ba 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.c +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.c @@ -18,7 +18,7 @@ #include "configuration.h" #include "container-executor.h" -#include "utils/string-utils.h" +#include "utils/docker-util.h" #include "util.h" #include "config.h" @@ -43,7 +43,6 @@ #include #include #include -#include #ifndef HAVE_FCHMODAT #include "compat/fchmodat.h" @@ -81,11 +80,6 @@ static const char* TC_READ_STATS_OPTS [] = { "-s", "-b", NULL}; //struct to store the user details struct passwd *user_detail = NULL; -//Docker container related constants. -static const char* DOCKER_CONTAINER_NAME_PREFIX = "container_"; -static const char* DOCKER_CLIENT_CONFIG_ARG = "--config="; -static const char* DOCKER_PULL_COMMAND = "pull"; - FILE* LOGFILE = NULL; FILE* ERRORFILE = NULL; @@ -465,8 +459,9 @@ int is_feature_enabled(const char* feature_key, int default_value, } int is_docker_support_enabled() { - return is_feature_enabled(DOCKER_SUPPORT_ENABLED_KEY, - DEFAULT_DOCKER_SUPPORT_ENABLED, &executor_cfg); + return is_feature_enabled(DOCKER_SUPPORT_ENABLED_KEY, + DEFAULT_DOCKER_SUPPORT_ENABLED, &executor_cfg) + || docker_module_enabled(&CFG); } int is_tc_support_enabled() { @@ -474,13 +469,6 @@ int is_tc_support_enabled() { DEFAULT_TC_SUPPORT_ENABLED, &executor_cfg); } -char* check_docker_binary(char *docker_binary) { - if (docker_binary == NULL) { - return "docker"; - } - return docker_binary; -} - /** * Utility function to concatenate argB to argA using the concat_pattern. */ @@ -1157,273 +1145,28 @@ int initialize_app(const char *user, const char *app_id, return -1; } -static char* escape_single_quote(const char *str) { - int p = 0; - int i = 0; - char replacement[] = "'\"'\"'"; - size_t replacement_length = strlen(replacement); - size_t ret_size = strlen(str) * replacement_length + 1; - char *ret = (char *) calloc(ret_size, sizeof(char)); - if(ret == NULL) { - exit(OUT_OF_MEMORY); +char *construct_docker_command(const char *command_file) { + int ret = 0; + size_t command_size = MIN(sysconf(_SC_ARG_MAX), 128*1024); + char *buffer = alloc_and_clear_memory(command_size, sizeof(char)); + ret = get_docker_command(command_file, &CFG, buffer, command_size); + if (ret != 0) { + fprintf(ERRORFILE, "Error constructing docker command, docker error code=%d, error message='%s'\n", ret, + get_docker_error_message(ret)); + fflush(ERRORFILE); + exit(DOCKER_RUN_FAILED); } - while(str[p] != '\0') { - if(str[p] == '\'') { - strncat(ret, replacement, ret_size - strlen(ret)); - i += replacement_length; - } - else { - ret[i] = str[p]; - ret[i + 1] = '\0'; - i++; - } - p++; - } - return ret; -} - -static void quote_and_append_arg(char **str, size_t *size, const char* param, const char *arg) { - char *tmp = escape_single_quote(arg); - strcat(*str, param); - strcat(*str, "'"); - if(strlen(*str) + strlen(tmp) > *size) { - *str = (char *) realloc(*str, strlen(*str) + strlen(tmp) + 1024); - if(*str == NULL) { - exit(OUT_OF_MEMORY); - } - *size = strlen(*str) + strlen(tmp) + 1024; - } - strcat(*str, tmp); - strcat(*str, "' "); - free(tmp); -} - -char** tokenize_docker_command(const char *input, int *split_counter) { - char *line = (char *)calloc(strlen(input) + 1, sizeof(char)); - char **linesplit = (char **) malloc(sizeof(char *)); - char *p = NULL; - *split_counter = 0; - strncpy(line, input, strlen(input)); - - p = strtok(line, " "); - while(p != NULL) { - linesplit[*split_counter] = p; - (*split_counter)++; - linesplit = realloc(linesplit, (sizeof(char *) * (*split_counter + 1))); - if(linesplit == NULL) { - fprintf(ERRORFILE, "Cannot allocate memory to parse docker command %s", - strerror(errno)); - fflush(ERRORFILE); - exit(OUT_OF_MEMORY); - } - p = strtok(NULL, " "); - } - linesplit[*split_counter] = NULL; - return linesplit; -} - -int execute_regex_match(const char *regex_str, const char *input) { - regex_t regex; - int regex_match; - if (0 != regcomp(®ex, regex_str, REG_EXTENDED|REG_NOSUB)) { - fprintf(LOGFILE, "Unable to compile regex."); - fflush(LOGFILE); - exit(ERROR_COMPILING_REGEX); - } - regex_match = regexec(®ex, input, (size_t) 0, NULL, 0); - regfree(®ex); - if(0 == regex_match) { - return 0; - } - return 1; -} - -int validate_docker_image_name(const char *image_name) { - char *regex_str = "^(([a-zA-Z0-9.-]+)(:[0-9]+)?/)?([a-z0-9_./-]+)(:[a-zA-Z0-9_.-]+)?$"; - return execute_regex_match(regex_str, image_name); -} - -char* sanitize_docker_command(const char *line) { - static struct option long_options[] = { - {"name", required_argument, 0, 'n' }, - {"user", required_argument, 0, 'u' }, - {"rm", no_argument, 0, 'r' }, - {"workdir", required_argument, 0, 'w' }, - {"net", required_argument, 0, 'e' }, - {"hostname", required_argument, 0, 'h' }, - {"cgroup-parent", required_argument, 0, 'g' }, - {"privileged", no_argument, 0, 'p' }, - {"cap-add", required_argument, 0, 'a' }, - {"cap-drop", required_argument, 0, 'o' }, - {"device", required_argument, 0, 'i' }, - {"detach", required_argument, 0, 't' }, - {"format", required_argument, 0, 'f' }, - {"group-add", required_argument, 0, 'x' }, - {0, 0, 0, 0} - }; - - int c = 0; - int option_index = 0; - char *output = NULL; - size_t output_size = 0; - char **linesplit; - int split_counter = 0; - int len = strlen(line); - - linesplit = tokenize_docker_command(line, &split_counter); - - output_size = len * 2; - output = (char *) calloc(output_size, sizeof(char)); - if(output == NULL) { - exit(OUT_OF_MEMORY); - } - - // Handle docker client config option. - if(0 == strncmp(linesplit[0], DOCKER_CLIENT_CONFIG_ARG, strlen(DOCKER_CLIENT_CONFIG_ARG))) { - strcat(output, linesplit[0]); - strcat(output, " "); - long index = 0; - while(index < split_counter) { - linesplit[index] = linesplit[index + 1]; - if (linesplit[index] == NULL) { - split_counter--; - break; - } - index++; - } - } - - // Handle docker pull and image name validation. - if (0 == strncmp(linesplit[0], DOCKER_PULL_COMMAND, strlen(DOCKER_PULL_COMMAND))) { - if (0 != validate_docker_image_name(linesplit[1])) { - fprintf(ERRORFILE, "Invalid Docker image name, exiting."); - fflush(ERRORFILE); - exit(DOCKER_IMAGE_INVALID); - } - strcat(output, linesplit[0]); - strcat(output, " "); - strcat(output, linesplit[1]); - return output; - } - - strcat(output, linesplit[0]); - strcat(output, " "); - optind = 1; - while((c=getopt_long(split_counter, linesplit, "dv:", long_options, &option_index)) != -1) { - switch(c) { - case 'n': - quote_and_append_arg(&output, &output_size, "--name=", optarg); - break; - case 'w': - quote_and_append_arg(&output, &output_size, "--workdir=", optarg); - break; - case 'u': - quote_and_append_arg(&output, &output_size, "--user=", optarg); - break; - case 'e': - quote_and_append_arg(&output, &output_size, "--net=", optarg); - break; - case 'h': - quote_and_append_arg(&output, &output_size, "--hostname=", optarg); - break; - case 'v': - quote_and_append_arg(&output, &output_size, "-v ", optarg); - break; - case 'a': - quote_and_append_arg(&output, &output_size, "--cap-add=", optarg); - break; - case 'o': - quote_and_append_arg(&output, &output_size, "--cap-drop=", optarg); - break; - case 'd': - strcat(output, "-d "); - break; - case 'r': - strcat(output, "--rm "); - break; - case 'g': - quote_and_append_arg(&output, &output_size, "--cgroup-parent=", optarg); - break; - case 'p': - strcat(output, "--privileged "); - break; - case 'i': - quote_and_append_arg(&output, &output_size, "--device=", optarg); - break; - case 't': - quote_and_append_arg(&output, &output_size, "--detach=", optarg); - break; - case 'f': - strcat(output, "--format="); - strcat(output, optarg); - strcat(output, " "); - break; - case 'x': - quote_and_append_arg(&output, &output_size, "--group-add ", optarg); - break; - default: - fprintf(LOGFILE, "Unknown option in docker command, character %d %c, optionindex = %d\n", c, c, optind); - fflush(LOGFILE); - return NULL; - break; - } - } - - if(optind < split_counter) { - while(optind < split_counter) { - if (0 == strncmp(linesplit[optind], DOCKER_CONTAINER_NAME_PREFIX, strlen(DOCKER_CONTAINER_NAME_PREFIX))) { - if (1 != validate_container_id(linesplit[optind])) { - fprintf(ERRORFILE, "Specified container_id=%s is invalid\n", linesplit[optind]); - fflush(ERRORFILE); - exit(DOCKER_CONTAINER_NAME_INVALID); - } - strcat(output, linesplit[optind++]); - } else { - quote_and_append_arg(&output, &output_size, "", linesplit[optind++]); - } - } - } - - return output; -} - -char* parse_docker_command_file(const char* command_file) { - size_t len = 0; - char *line = NULL; - ssize_t read; - FILE *stream; - stream = fopen(command_file, "r"); - if (stream == NULL) { - fprintf(ERRORFILE, "Cannot open file %s - %s", - command_file, strerror(errno)); - fflush(ERRORFILE); - exit(ERROR_OPENING_DOCKER_FILE); - } - if ((read = getline(&line, &len, stream)) == -1) { - fprintf(ERRORFILE, "Error reading command_file %s\n", command_file); - fflush(ERRORFILE); - exit(ERROR_READING_DOCKER_FILE); - } - fclose(stream); - - char* ret = sanitize_docker_command(line); - if(ret == NULL) { - exit(ERROR_SANITIZING_DOCKER_COMMAND); - } - fprintf(ERRORFILE, "Using command %s\n", ret); - fflush(ERRORFILE); - - return ret; + return buffer; } int run_docker(const char *command_file) { - char* docker_command = parse_docker_command_file(command_file); - char* docker_binary = get_section_value(DOCKER_BINARY_KEY, &executor_cfg); - docker_binary = check_docker_binary(docker_binary); + char* docker_command = construct_docker_command(command_file); + char* docker_binary = get_docker_binary(&CFG); size_t command_size = MIN(sysconf(_SC_ARG_MAX), 128*1024); - char* docker_command_with_binary = calloc(sizeof(char), command_size); + char* docker_command_with_binary = alloc_and_clear_memory(command_size, sizeof(char)); snprintf(docker_command_with_binary, command_size, "%s %s", docker_binary, docker_command); + fprintf(LOGFILE, "Invoking '%s'\n", docker_command_with_binary); char **args = split_delimiter(docker_command_with_binary, " "); int exit_code = -1; @@ -1437,8 +1180,9 @@ int run_docker(const char *command_file) { free(docker_command_with_binary); free(docker_command); exit_code = DOCKER_RUN_FAILED; + } else { + exit_code = 0; } - exit_code = 0; return exit_code; } @@ -1583,18 +1327,17 @@ int launch_docker_container_as_user(const char * user, const char *app_id, size_t command_size = MIN(sysconf(_SC_ARG_MAX), 128*1024); - docker_command_with_binary = calloc(sizeof(char), command_size); - docker_wait_command = calloc(sizeof(char), command_size); - docker_logs_command = calloc(sizeof(char), command_size); - docker_inspect_command = calloc(sizeof(char), command_size); - docker_rm_command = calloc(sizeof(char), command_size); + docker_command_with_binary = (char *) alloc_and_clear_memory(command_size, sizeof(char)); + docker_wait_command = (char *) alloc_and_clear_memory(command_size, sizeof(char)); + docker_logs_command = (char *) alloc_and_clear_memory(command_size, sizeof(char)); + docker_inspect_command = (char *) alloc_and_clear_memory(command_size, sizeof(char)); + docker_rm_command = (char *) alloc_and_clear_memory(command_size, sizeof(char)); gid_t user_gid = getegid(); uid_t prev_uid = geteuid(); - char *docker_command = parse_docker_command_file(command_file); - char *docker_binary = get_section_value(DOCKER_BINARY_KEY, &executor_cfg); - docker_binary = check_docker_binary(docker_binary); + char *docker_command = NULL; + char *docker_binary = NULL; fprintf(LOGFILE, "Creating script paths...\n"); exit_code = create_script_paths( @@ -1617,6 +1360,9 @@ int launch_docker_container_as_user(const char * user, const char *app_id, goto cleanup; } + docker_command = construct_docker_command(command_file); + docker_binary = get_docker_binary(&CFG); + fprintf(LOGFILE, "Getting exit code file...\n"); exit_code_file = get_exit_code_file(pid_file); if (NULL == exit_code_file) { diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.h b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.h index aa38abfee19..6d4220f843a 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.h +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.h @@ -59,16 +59,12 @@ enum operations { #define MIN_USERID_KEY "min.user.id" #define BANNED_USERS_KEY "banned.users" #define ALLOWED_SYSTEM_USERS_KEY "allowed.system.users" -#define DOCKER_BINARY_KEY "docker.binary" #define DOCKER_SUPPORT_ENABLED_KEY "feature.docker.enabled" #define TC_SUPPORT_ENABLED_KEY "feature.tc.enabled" #define TMP_DIR "tmp" extern struct passwd *user_detail; -// get the executable's filename -char* get_executable(char *argv0); - //function used to load the configurations present in the secure config void read_executor_config(const char* file_name); @@ -258,11 +254,6 @@ int is_docker_support_enabled(); */ int run_docker(const char *command_file); -/** - * Sanitize docker commands. Returns NULL if there was any failure. -*/ -char* sanitize_docker_command(const char *line); - /* * Compile the regex_str and determine if the input string matches. * Return 0 on match, 1 of non-match. diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/get_executable.c b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/get_executable.c index 55973a2db57..e1ec293cd47 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/get_executable.c +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/get_executable.c @@ -29,12 +29,9 @@ */ #include "config.h" -#include "configuration.h" -#include "container-executor.h" #include "util.h" #include -#include #include #include #include diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/get_executable.h b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/get_executable.h new file mode 100644 index 00000000000..ee6b375d52b --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/get_executable.h @@ -0,0 +1,29 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __YARN_POSIX_CONTAINER_EXECUTOR_GET_EXECUTABLE_H__ +#define __YARN_POSIX_CONTAINER_EXECUTOR_GET_EXECUTABLE_H__ + +/** + * Get the path to executable that is currently running + * @param argv0 the name of the executable + * @return the path to the currently running executable + */ +char* get_executable(char *argv0); + +#endif diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/main.c b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/main.c index a05dc787005..7b8f63f2008 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/main.c +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/main.c @@ -20,30 +20,15 @@ #include "configuration.h" #include "container-executor.h" #include "util.h" +#include "get_executable.h" #include "modules/gpu/gpu-module.h" #include "modules/cgroups/cgroups-operations.h" #include #include -#include #include -#include -#include #include #include -#include - -#define CONF_FILENAME "container-executor.cfg" - -// When building as part of a Maven build this value gets defined by using -// container-executor.conf.dir property. See: -// hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/pom.xml -// for details. -// NOTE: if this ends up being a relative path it gets resolved relative to -// the location of the container-executor binary itself, not getwd(3) -#ifndef HADOOP_CONF_DIR - #error HADOOP_CONF_DIR must be defined -#endif static void display_usage(FILE *stream) { fprintf(stream, @@ -145,25 +130,21 @@ of whether an explicit checksetup operation is requested. */ static void assert_valid_setup(char *argv0) { int ret; char *executable_file = get_executable(argv0); - if (!executable_file) { - fprintf(ERRORFILE,"realpath of executable: %s\n", - errno != 0 ? strerror(errno) : "unknown"); + if (!executable_file || executable_file[0] == 0) { + fprintf(ERRORFILE, "realpath of executable: %s\n", + errno != 0 ? strerror(errno) : "unknown"); flush_and_close_log_files(); - exit(-1); + exit(INVALID_CONFIG_FILE); } - char *orig_conf_file = HADOOP_CONF_DIR "/" CONF_FILENAME; - char *conf_file = resolve_config_path(orig_conf_file, executable_file); + char *conf_file = get_config_path(argv0); if (conf_file == NULL) { - free(executable_file); - fprintf(ERRORFILE, "Configuration file %s not found.\n", orig_conf_file); flush_and_close_log_files(); exit(INVALID_CONFIG_FILE); } if (check_configuration_permissions(conf_file) != 0) { - free(executable_file); flush_and_close_log_files(); exit(INVALID_CONFIG_FILE); } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/modules/common/module-configs.c b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/modules/common/module-configs.c index f0c6d16fbb2..4c76e515e91 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/modules/common/module-configs.c +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/modules/common/module-configs.c @@ -16,9 +16,8 @@ * limitations under the License. */ +#include "module-configs.h" #include "util.h" -#include "configuration.h" -#include "container-executor.h" #include "modules/common/constants.h" #include diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/modules/common/module-configs.h b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/modules/common/module-configs.h index d58c618d517..b8c57df2282 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/modules/common/module-configs.h +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/modules/common/module-configs.h @@ -23,6 +23,7 @@ #ifndef _MODULES_COMMON_MODULE_CONFIGS_H_ #define _MODULES_COMMON_MODULE_CONFIGS_H_ +#include "configuration.h" /** * check if module enabled given name of module. diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/util.c b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/util.c index 8e39ca85760..a9539cf0059 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/util.c +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/util.c @@ -17,10 +17,10 @@ */ #include "util.h" -#include #include #include #include +#include char** split_delimiter(char *value, const char *delim) { char **return_values = NULL; @@ -132,3 +132,61 @@ char* trim(const char* input) { ret[val_end - val_begin] = '\0'; return ret; } + +int execute_regex_match(const char *regex_str, const char *input) { + regex_t regex; + int regex_match; + if (0 != regcomp(®ex, regex_str, REG_EXTENDED|REG_NOSUB)) { + fprintf(LOGFILE, "Unable to compile regex."); + fflush(LOGFILE); + exit(ERROR_COMPILING_REGEX); + } + regex_match = regexec(®ex, input, (size_t) 0, NULL, 0); + regfree(®ex); + if(0 == regex_match) { + return 0; + } + return 1; +} + +char* escape_single_quote(const char *str) { + int p = 0; + int i = 0; + char replacement[] = "'\"'\"'"; + size_t replacement_length = strlen(replacement); + size_t ret_size = strlen(str) * replacement_length + 1; + char *ret = (char *) alloc_and_clear_memory(ret_size, sizeof(char)); + if(ret == NULL) { + exit(OUT_OF_MEMORY); + } + while(str[p] != '\0') { + if(str[p] == '\'') { + strncat(ret, replacement, ret_size - strlen(ret)); + i += replacement_length; + } + else { + ret[i] = str[p]; + i++; + } + p++; + } + ret[i] = '\0'; + return ret; +} + +void quote_and_append_arg(char **str, size_t *size, const char* param, const char *arg) { + char *tmp = escape_single_quote(arg); + int alloc_block = 1024; + strcat(*str, param); + strcat(*str, "'"); + if (strlen(*str) + strlen(tmp) > *size) { + *size = (strlen(*str) + strlen(tmp) + alloc_block) * sizeof(char); + *str = (char *) realloc(*str, *size); + if (*str == NULL) { + exit(OUT_OF_MEMORY); + } + } + strcat(*str, tmp); + strcat(*str, "' "); + free(tmp); +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/util.h b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/util.h index fa21def5339..8758d90acea 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/util.h +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/util.h @@ -19,7 +19,11 @@ #ifndef __YARN_POSIX_CONTAINER_EXECUTOR_UTIL_H__ #define __YARN_POSIX_CONTAINER_EXECUTOR_UTIL_H__ +/** Define a platform-independent constant instead of using PATH_MAX, set to 4K */ +#define EXECUTOR_PATH_MAX 4096 + #include +#include enum errorcodes { INVALID_ARGUMENT_NUMBER = 1, @@ -62,7 +66,7 @@ enum errorcodes { ERROR_CREATE_CONTAINER_DIRECTORIES_ARGUMENTS = 38, ERROR_SANITIZING_DOCKER_COMMAND = 39, DOCKER_IMAGE_INVALID = 40, - DOCKER_CONTAINER_NAME_INVALID = 41, + // DOCKER_CONTAINER_NAME_INVALID = 41, (NOT USED) ERROR_COMPILING_REGEX = 42 }; @@ -119,4 +123,44 @@ void free_values(char **values); */ char* trim(const char *input); +/** + * Run a regex to check if the provided input matches against it + * @param regex_str Regex to run + * @param input String to run the regex against + * @return 0 on match, non-zero on no match + */ +int execute_regex_match(const char *regex_str, const char *input); + +/** + * Helper function to escape single-quotes in a string. The assumption is that the string passed will be enclosed in + * single quotes and passed to bash for a command invocation. + * @param str The string in which to esacpe single quotes + * @return String with single quotes escaped, must be freed by the user. + */ +char* escape_single_quote(const char *str); + +/** + * Helper function to quote the argument to a parameter and then append it to the provided string. + * @param str Buffer to which the param='arg' string must be appended + * @param size Size of the buffer + * @param param Param name + * @param arg Argument to be quoted + */ +void quote_and_append_arg(char **str, size_t *size, const char* param, const char *arg); + +/** + * Helper function to allocate and clear a block of memory. It'll call exit if the allocation fails. + * @param num Num of elements to be allocated + * @param size Size of each element + * @return Pointer to the allocated memory, must be freed by the user + */ +inline void* alloc_and_clear_memory(size_t num, size_t size) { + void *ret = calloc(num, size); + if (ret == NULL) { + printf("Could not allocate memory, exiting\n"); + exit(OUT_OF_MEMORY); + } + return ret; +} + #endif diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/utils/docker-util.c b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/utils/docker-util.c new file mode 100644 index 00000000000..860320d907d --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/utils/docker-util.c @@ -0,0 +1,998 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include "../modules/common/module-configs.h" +#include "docker-util.h" +#include "string-utils.h" +#include "util.h" + +static int read_and_verify_command_file(const char *command_file, const char *docker_command, + struct configuration *command_config) { + int ret = 0; + ret = read_config(command_file, command_config); + if (ret != 0) { + return INVALID_COMMAND_FILE; + } + char *command = get_configuration_value("docker-command", DOCKER_COMMAND_FILE_SECTION, command_config); + if (command == NULL || (strcmp(command, docker_command) != 0)) { + ret = INCORRECT_COMMAND; + } + free(command); + return ret; +} + +static int add_to_buffer(char *buff, const size_t bufflen, const char *string) { + size_t current_len = strlen(buff); + size_t string_len = strlen(string); + if (current_len + string_len < bufflen - 1) { + strncpy(buff + current_len, string, string_len); + buff[current_len + string_len] = '\0'; + return 0; + } + return -1; +} + +static int add_param_to_command(const struct configuration *command_config, const char *key, const char *param, + const int with_argument, char *out, const size_t outlen) { + size_t tmp_buffer_size = 4096; + int ret = 0; + char *tmp_buffer = (char *) alloc_and_clear_memory(tmp_buffer_size, sizeof(char)); + char *value = get_configuration_value(key, DOCKER_COMMAND_FILE_SECTION, command_config); + if (value != NULL) { + if (with_argument) { + quote_and_append_arg(&tmp_buffer, &tmp_buffer_size, param, value); + ret = add_to_buffer(out, outlen, tmp_buffer); + } else if (strcmp(value, "true") == 0) { + ret = add_to_buffer(out, outlen, param); + } + free(value); + if (ret != 0) { + ret = BUFFER_TOO_SMALL; + } + } + free(tmp_buffer); + return ret; +} + +static int add_param_to_command_if_allowed(const struct configuration *command_config, + const struct configuration *executor_cfg, + const char *key, const char *allowed_key, const char *param, + const int multiple_values, const char prefix, + char *out, const size_t outlen) { + size_t tmp_buffer_size = 4096; + char *tmp_buffer = (char *) alloc_and_clear_memory(tmp_buffer_size, sizeof(char)); + char *tmp_ptr = NULL; + char **values = NULL; + char **permitted_values = get_configuration_values_delimiter(allowed_key, + CONTAINER_EXECUTOR_CFG_DOCKER_SECTION, executor_cfg, + ","); + int i = 0, j = 0, permitted = 0, ret = 0; + if (multiple_values) { + values = get_configuration_values_delimiter(key, DOCKER_COMMAND_FILE_SECTION, command_config, ","); + } else { + values = (char **) alloc_and_clear_memory(2, sizeof(char *)); + values[0] = get_configuration_value(key, DOCKER_COMMAND_FILE_SECTION, command_config); + values[1] = NULL; + if (values[0] == NULL) { + ret = 0; + goto free_and_exit; + } + } + + if (values != NULL) { + if (permitted_values != NULL) { + for (i = 0; values[i] != NULL; ++i) { + memset(tmp_buffer, 0, tmp_buffer_size); + permitted = 0; + if(prefix != 0) { + tmp_ptr = strchr(values[i], prefix); + if (tmp_ptr == NULL) { + fprintf(ERRORFILE, "Prefix char '%c' not found in '%s'\n", + prefix, values[i]); + ret = -1; + goto free_and_exit; + } + } + for (j = 0; permitted_values[j] != NULL; ++j) { + if (prefix == 0) { + ret = strcmp(values[i], permitted_values[j]); + } else { + ret = strncmp(values[i], permitted_values[j], tmp_ptr - values[i]); + } + if (ret == 0) { + permitted = 1; + break; + } + } + if (permitted == 1) { + quote_and_append_arg(&tmp_buffer, &tmp_buffer_size, param, values[i]); + ret = add_to_buffer(out, outlen, tmp_buffer); + if (ret != 0) { + fprintf(ERRORFILE, "Output buffer too small\n"); + ret = BUFFER_TOO_SMALL; + goto free_and_exit; + } + } else { + fprintf(ERRORFILE, "Invalid param '%s' requested\n", values[i]); + ret = -1; + goto free_and_exit; + } + } + } else { + fprintf(ERRORFILE, "Invalid param '%s' requested, " + "permitted values list is empty\n", values[0]); + ret = -1; + goto free_and_exit; + } + } + + free_and_exit: + free_values(values); + free_values(permitted_values); + free(tmp_buffer); + if (ret != 0) { + memset(out, 0, outlen); + } + return ret; +} + +static int add_docker_config_param(const struct configuration *command_config, char *out, const size_t outlen) { + return add_param_to_command(command_config, "docker-config", "--config=", 1, out, outlen); +} + +static int validate_container_name(const char *container_name) { + const char *CONTAINER_NAME_PREFIX = "container_"; + if (0 == strncmp(container_name, CONTAINER_NAME_PREFIX, strlen(CONTAINER_NAME_PREFIX))) { + if (1 == validate_container_id(container_name)) { + return 0; + } + } + fprintf(ERRORFILE, "Specified container_id=%s is invalid\n", container_name); + fflush(ERRORFILE); + return INVALID_DOCKER_CONTAINER_NAME; +} + +const char *get_docker_error_message(const int error_code) { + + switch (error_code) { + case INVALID_COMMAND_FILE: + return "Invalid command file passed"; + case INCORRECT_COMMAND: + return "Incorrect command"; + case BUFFER_TOO_SMALL: + return "Command buffer too small"; + case INVALID_DOCKER_CONTAINER_NAME: + return "Invalid docker container name"; + case INVALID_DOCKER_IMAGE_NAME: + return "Invalid docker image name"; + case INVALID_DOCKER_USER_NAME: + return "Invalid docker user name"; + case INVALID_DOCKER_INSPECT_FORMAT: + return "Invalid docker inspect format"; + case UNKNOWN_DOCKER_COMMAND: + return "Unknown docker command"; + case INVALID_DOCKER_NETWORK: + return "Invalid docker network"; + case INVALID_DOCKER_CAPABILITY: + return "Invalid docker capability"; + case PRIVILEGED_CONTAINERS_DISABLED: + return "Privileged containers are disabled"; + case INVALID_DOCKER_MOUNT: + return "Invalid docker mount"; + case INVALID_DOCKER_RO_MOUNT: + return "Invalid docker read-only mount"; + case INVALID_DOCKER_RW_MOUNT: + return "Invalid docker read-write mount"; + case MOUNT_ACCESS_ERROR: + return "Mount access error"; + case INVALID_DOCKER_DEVICE: + return "Invalid docker device"; + default: + return "Unknown error"; + } +} + +char *get_docker_binary(const struct configuration *conf) { + char *docker_binary = NULL; + docker_binary = get_configuration_value(DOCKER_BINARY_KEY, CONTAINER_EXECUTOR_CFG_DOCKER_SECTION, conf); + if (docker_binary == NULL) { + docker_binary = get_configuration_value(DOCKER_BINARY_KEY, "", conf); + if (docker_binary == NULL) { + docker_binary = strdup("/usr/bin/docker"); + } + } + return docker_binary; +} + +int docker_module_enabled(const struct configuration *conf) { + struct section *section = get_configuration_section(CONTAINER_EXECUTOR_CFG_DOCKER_SECTION, conf); + if (section != NULL) { + return module_enabled(section, CONTAINER_EXECUTOR_CFG_DOCKER_SECTION); + } + return 0; +} + +int get_docker_command(const char *command_file, const struct configuration *conf, char *out, const size_t outlen) { + int ret = 0; + struct configuration command_config = {0, NULL}; + ret = read_config(command_file, &command_config); + if (ret != 0) { + return INVALID_COMMAND_FILE; + } + + char *command = get_configuration_value("docker-command", DOCKER_COMMAND_FILE_SECTION, &command_config); + if (strcmp(DOCKER_INSPECT_COMMAND, command) == 0) { + return get_docker_inspect_command(command_file, conf, out, outlen); + } else if (strcmp(DOCKER_LOAD_COMMAND, command) == 0) { + return get_docker_load_command(command_file, conf, out, outlen); + } else if (strcmp(DOCKER_PULL_COMMAND, command) == 0) { + return get_docker_pull_command(command_file, conf, out, outlen); + } else if (strcmp(DOCKER_RM_COMMAND, command) == 0) { + return get_docker_rm_command(command_file, conf, out, outlen); + } else if (strcmp(DOCKER_RUN_COMMAND, command) == 0) { + return get_docker_run_command(command_file, conf, out, outlen); + } else if (strcmp(DOCKER_STOP_COMMAND, command) == 0) { + return get_docker_stop_command(command_file, conf, out, outlen); + } else { + return UNKNOWN_DOCKER_COMMAND; + } +} + +int get_docker_inspect_command(const char *command_file, const struct configuration *conf, char *out, + const size_t outlen) { + const char *valid_format_strings[] = { "{{.State.Status}}", + "{{range(.NetworkSettings.Networks)}}{{.IPAddress}},{{end}}{{.Config.Hostname}}" }; + int ret = 0, i = 0, valid_format = 0; + char *format = NULL, *container_name = NULL; + struct configuration command_config = {0, NULL}; + ret = read_and_verify_command_file(command_file, DOCKER_INSPECT_COMMAND, &command_config); + if (ret != 0) { + return ret; + } + + container_name = get_configuration_value("name", DOCKER_COMMAND_FILE_SECTION, &command_config); + if (container_name == NULL || validate_container_name(container_name) != 0) { + return INVALID_DOCKER_CONTAINER_NAME; + } + + format = get_configuration_value("format", DOCKER_COMMAND_FILE_SECTION, &command_config); + if (format == NULL) { + free(container_name); + return INVALID_DOCKER_INSPECT_FORMAT; + } + for (i = 0; i < 2; ++i) { + if (strcmp(format, valid_format_strings[i]) == 0) { + valid_format = 1; + break; + } + } + if (valid_format != 1) { + fprintf(ERRORFILE, "Invalid format option '%s' not permitted\n", format); + free(container_name); + free(format); + return INVALID_DOCKER_INSPECT_FORMAT; + } + + memset(out, 0, outlen); + + ret = add_docker_config_param(&command_config, out, outlen); + if (ret != 0) { + free(container_name); + free(format); + return BUFFER_TOO_SMALL; + } + + ret = add_to_buffer(out, outlen, DOCKER_INSPECT_COMMAND); + if (ret != 0) { + goto free_and_exit; + } + ret = add_to_buffer(out, outlen, " --format="); + if (ret != 0) { + goto free_and_exit; + } + ret = add_to_buffer(out, outlen, format); + if (ret != 0) { + goto free_and_exit; + } + ret = add_to_buffer(out, outlen, " "); + if (ret != 0) { + goto free_and_exit; + } + ret = add_to_buffer(out, outlen, container_name); + if (ret != 0) { + goto free_and_exit; + } + free(format); + free(container_name); + return 0; + + free_and_exit: + free(format); + free(container_name); + return BUFFER_TOO_SMALL; +} + +int get_docker_load_command(const char *command_file, const struct configuration *conf, char *out, const size_t outlen) { + int ret = 0; + char *image_name = NULL; + size_t tmp_buffer_size = 1024; + char *tmp_buffer = NULL; + struct configuration command_config = {0, NULL}; + ret = read_and_verify_command_file(command_file, DOCKER_LOAD_COMMAND, &command_config); + if (ret != 0) { + return ret; + } + + image_name = get_configuration_value("image", DOCKER_COMMAND_FILE_SECTION, &command_config); + if (image_name == NULL) { + return INVALID_DOCKER_IMAGE_NAME; + } + + memset(out, 0, outlen); + + ret = add_docker_config_param(&command_config, out, outlen); + if (ret != 0) { + free(image_name); + return BUFFER_TOO_SMALL; + } + + ret = add_to_buffer(out, outlen, DOCKER_LOAD_COMMAND); + if (ret == 0) { + tmp_buffer = (char *) alloc_and_clear_memory(tmp_buffer_size, sizeof(char)); + quote_and_append_arg(&tmp_buffer, &tmp_buffer_size, " --i=", image_name); + ret = add_to_buffer(out, outlen, tmp_buffer); + free(tmp_buffer); + free(image_name); + if (ret != 0) { + return BUFFER_TOO_SMALL; + } + return 0; + } + free(image_name); + return BUFFER_TOO_SMALL; +} + +static int validate_docker_image_name(const char *image_name) { + const char *regex_str = "^(([a-zA-Z0-9.-]+)(:[0-9]+)?/)?([a-z0-9_./-]+)(:[a-zA-Z0-9_.-]+)?$"; + return execute_regex_match(regex_str, image_name); +} + +int get_docker_pull_command(const char *command_file, const struct configuration *conf, char *out, const size_t outlen) { + int ret = 0; + char *image_name = NULL; + size_t tmp_buffer_size = 1024; + char *tmp_buffer = NULL; + struct configuration command_config = {0, NULL}; + ret = read_and_verify_command_file(command_file, DOCKER_PULL_COMMAND, &command_config); + if (ret != 0) { + return ret; + } + + image_name = get_configuration_value("image", DOCKER_COMMAND_FILE_SECTION, &command_config); + if (image_name == NULL || validate_docker_image_name(image_name) != 0) { + return INVALID_DOCKER_IMAGE_NAME; + } + + memset(out, 0, outlen); + + ret = add_docker_config_param(&command_config, out, outlen); + if (ret != 0) { + return BUFFER_TOO_SMALL; + } + + ret = add_to_buffer(out, outlen, DOCKER_PULL_COMMAND); + if (ret == 0) { + tmp_buffer = (char *) alloc_and_clear_memory(tmp_buffer_size, sizeof(char)); + quote_and_append_arg(&tmp_buffer, &tmp_buffer_size, " ", image_name); + ret = add_to_buffer(out, outlen, tmp_buffer); + free(tmp_buffer); + free(image_name); + if (ret != 0) { + return BUFFER_TOO_SMALL; + } + return 0; + } + free(image_name); + return BUFFER_TOO_SMALL; +} + +int get_docker_rm_command(const char *command_file, const struct configuration *conf, char *out, const size_t outlen) { + int ret = 0; + char *container_name = NULL; + struct configuration command_config = {0, NULL}; + ret = read_and_verify_command_file(command_file, DOCKER_RM_COMMAND, &command_config); + if (ret != 0) { + return ret; + } + + container_name = get_configuration_value("name", DOCKER_COMMAND_FILE_SECTION, &command_config); + if (container_name == NULL || validate_container_name(container_name) != 0) { + return INVALID_DOCKER_CONTAINER_NAME; + } + + memset(out, 0, outlen); + + ret = add_docker_config_param(&command_config, out, outlen); + if (ret != 0) { + return BUFFER_TOO_SMALL; + } + + ret = add_to_buffer(out, outlen, DOCKER_RM_COMMAND); + if (ret == 0) { + ret = add_to_buffer(out, outlen, " "); + if (ret == 0) { + ret = add_to_buffer(out, outlen, container_name); + } + free(container_name); + if (ret != 0) { + return BUFFER_TOO_SMALL; + } + return 0; + } + free(container_name); + return BUFFER_TOO_SMALL; +} + +int get_docker_stop_command(const char *command_file, const struct configuration *conf, + char *out, const size_t outlen) { + int ret = 0; + size_t len = 0, i = 0; + char *value = NULL; + char *container_name = NULL; + struct configuration command_config = {0, NULL}; + ret = read_and_verify_command_file(command_file, DOCKER_STOP_COMMAND, &command_config); + if (ret != 0) { + return ret; + } + + container_name = get_configuration_value("name", DOCKER_COMMAND_FILE_SECTION, &command_config); + if (container_name == NULL || validate_container_name(container_name) != 0) { + return INVALID_DOCKER_CONTAINER_NAME; + } + + memset(out, 0, outlen); + + ret = add_docker_config_param(&command_config, out, outlen); + if (ret != 0) { + return BUFFER_TOO_SMALL; + } + + ret = add_to_buffer(out, outlen, DOCKER_STOP_COMMAND); + if (ret == 0) { + value = get_configuration_value("time", DOCKER_COMMAND_FILE_SECTION, &command_config); + if (value != NULL) { + len = strlen(value); + for (i = 0; i < len; ++i) { + if (isdigit(value[i]) == 0) { + fprintf(ERRORFILE, "Value for time is not a number '%s'\n", value); + free(container_name); + memset(out, 0, outlen); + return INVALID_DOCKER_STOP_COMMAND; + } + } + ret = add_to_buffer(out, outlen, " --time="); + if (ret == 0) { + ret = add_to_buffer(out, outlen, value); + } + if (ret != 0) { + free(container_name); + return BUFFER_TOO_SMALL; + } + } + ret = add_to_buffer(out, outlen, " "); + if (ret == 0) { + ret = add_to_buffer(out, outlen, container_name); + } + free(container_name); + if (ret != 0) { + return BUFFER_TOO_SMALL; + } + return 0; + } + free(container_name); + return BUFFER_TOO_SMALL; +} + +static int detach_container(const struct configuration *command_config, char *out, const size_t outlen) { + return add_param_to_command(command_config, "detach", "-d ", 0, out, outlen); +} + +static int rm_container_on_exit(const struct configuration *command_config, char *out, const size_t outlen) { + return add_param_to_command(command_config, "rm", "--rm ", 0, out, outlen); +} + +static int set_container_workdir(const struct configuration *command_config, char *out, const size_t outlen) { + return add_param_to_command(command_config, "workdir", "--workdir=", 1, out, outlen); +} + +static int set_cgroup_parent(const struct configuration *command_config, char *out, const size_t outlen) { + return add_param_to_command(command_config, "cgroup-parent", "--cgroup-parent=", 1, out, outlen); +} + +static int set_hostname(const struct configuration *command_config, char *out, const size_t outlen) { + return add_param_to_command(command_config, "hostname", "--hostname=", 1, out, outlen); +} + +static int set_group_add(const struct configuration *command_config, char *out, const size_t outlen) { + int i = 0, ret = 0; + char **group_add = get_configuration_values_delimiter("group-add", DOCKER_COMMAND_FILE_SECTION, command_config, ","); + size_t tmp_buffer_size = 4096; + char *tmp_buffer = NULL; + + if (group_add != NULL) { + for (i = 0; group_add[i] != NULL; ++i) { + tmp_buffer = (char *) alloc_and_clear_memory(tmp_buffer_size, sizeof(char)); + quote_and_append_arg(&tmp_buffer, &tmp_buffer_size, "--group-add ", group_add[i]); + ret = add_to_buffer(out, outlen, tmp_buffer); + if (ret != 0) { + return BUFFER_TOO_SMALL; + } + } + } + return ret; +} + +static int set_network(const struct configuration *command_config, + const struct configuration *conf, char *out, + const size_t outlen) { + + int ret = 0; + ret = add_param_to_command_if_allowed(command_config, conf, "net", + "docker.allowed.networks", "--net=", + 0, 0, out, outlen); + if (ret != 0) { + fprintf(ERRORFILE, "Could not find requested network in allowed networks\n"); + ret = INVALID_DOCKER_NETWORK; + memset(out, 0, outlen); + } + + return ret; +} + +static int set_capabilities(const struct configuration *command_config, + const struct configuration *conf, char *out, + const size_t outlen) { + + int ret = 0; + + ret = add_to_buffer(out, outlen, "--cap-drop='ALL' "); + if (ret != 0) { + return BUFFER_TOO_SMALL; + } + ret = add_param_to_command_if_allowed(command_config, conf, "cap-add", + "docker.allowed.capabilities", + "--cap-add=", 1, 0, + out, outlen); + if (ret != 0) { + fprintf(ERRORFILE, "Invalid docker capability requested\n"); + ret = INVALID_DOCKER_CAPABILITY; + memset(out, 0, outlen); + } + + return ret; +} + +static int set_devices(const struct configuration *command_config, const struct configuration *conf, char *out, + const size_t outlen) { + int ret = 0; + ret = add_param_to_command_if_allowed(command_config, conf, "devices", "docker.allowed.devices", "--device=", 1, ':', + out, outlen); + if (ret != 0) { + fprintf(ERRORFILE, "Invalid docker device requested\n"); + ret = INVALID_DOCKER_DEVICE; + memset(out, 0, outlen); + } + + return ret; +} + +/** + * Helper function to help normalize mounts for checking if mounts are + * permitted. The function does the following - + * 1. Find the canonical path for mount using realpath + * 2. If the path is a directory, add a '/' at the end (if not present) + * 3. Return a copy of the canonicalised path(to be freed by the caller) + * @param mount path to be canonicalised + * @return pointer to canonicalised path, NULL on error + */ +static char* normalize_mount(const char* mount) { + int ret = 0; + struct stat buff; + char *ret_ptr = NULL, *real_mount = NULL; + if (mount == NULL) { + return NULL; + } + real_mount = realpath(mount, NULL); + if (real_mount == NULL) { + fprintf(ERRORFILE, "Could not determine real path of mount '%s'\n", mount); + free(real_mount); + return NULL; + } + ret = stat(real_mount, &buff); + if (ret == 0) { + if (S_ISDIR(buff.st_mode)) { + size_t len = strlen(real_mount); + if (len <= 0) { + return NULL; + } + if (real_mount[len - 1] != '/') { + ret_ptr = (char *) alloc_and_clear_memory(len + 2, sizeof(char)); + strncpy(ret_ptr, real_mount, len); + ret_ptr[len] = '/'; + ret_ptr[len + 1] = '\0'; + } else { + ret_ptr = strdup(real_mount); + } + } else { + ret_ptr = strdup(real_mount); + } + } else { + fprintf(ERRORFILE, "Could not stat path '%s'\n", real_mount); + ret_ptr = NULL; + } + free(real_mount); + return ret_ptr; +} + +static int normalize_mounts(char **mounts) { + int i = 0; + char *tmp = NULL; + if (mounts == NULL) { + return 0; + } + for (i = 0; mounts[i] != NULL; ++i) { + tmp = normalize_mount(mounts[i]); + if (tmp == NULL) { + return -1; + } + free(mounts[i]); + mounts[i] = tmp; + } + return 0; +} + +static int check_mount_permitted(const char **permitted_mounts, const char *requested) { + int i = 0, ret = 0; + size_t permitted_mount_len = 0; + char *normalized_path = normalize_mount(requested); + if (permitted_mounts == NULL) { + return 0; + } + if (normalized_path == NULL) { + return -1; + } + for (i = 0; permitted_mounts[i] != NULL; ++i) { + if (strcmp(normalized_path, permitted_mounts[i]) == 0) { + ret = 1; + break; + } + // directory check + permitted_mount_len = strlen(permitted_mounts[i]); + if (permitted_mount_len > 0 + && permitted_mounts[i][permitted_mount_len - 1] == '/') { + if (strncmp(normalized_path, permitted_mounts[i], permitted_mount_len) == 0) { + ret = 1; + break; + } + } + + } + free(normalized_path); + return ret; +} + +static char* get_mount_source(const char *mount) { + char *src_mount = NULL; + const char *tmp = NULL; + tmp = strchr(mount, ':'); + if (tmp == NULL) { + fprintf(ERRORFILE, "Invalid docker mount '%s'\n", mount); + return NULL; + } + src_mount = strndup(mount, tmp - mount); + return src_mount; +} + +static int add_mounts(const struct configuration *command_config, const struct configuration *conf, const char *key, + const int ro, char *out, const size_t outlen) { + size_t tmp_buffer_size = 1024; + const char *ro_suffix = ""; + const char *tmp_path_buffer[2] = {NULL, NULL}; + char *tmp_buffer = (char *) alloc_and_clear_memory(tmp_buffer_size, sizeof(char)); + char **permitted_ro_mounts = get_configuration_values_delimiter("docker.allowed.ro-mounts", + CONTAINER_EXECUTOR_CFG_DOCKER_SECTION, conf, ","); + char **permitted_rw_mounts = get_configuration_values_delimiter("docker.allowed.rw-mounts", + CONTAINER_EXECUTOR_CFG_DOCKER_SECTION, conf, ","); + char **values = get_configuration_values_delimiter(key, DOCKER_COMMAND_FILE_SECTION, command_config, ","); + char *tmp_buffer_2 = NULL, *mount_src = NULL; + const char *container_executor_cfg_path = normalize_mount(get_config_path("")); + int i = 0, permitted_rw = 0, permitted_ro = 0, ret = 0; + if (ro != 0) { + ro_suffix = ":ro"; + } + + if (values != NULL) { + ret = normalize_mounts(permitted_ro_mounts); + ret |= normalize_mounts(permitted_rw_mounts); + if (ret != 0) { + fprintf(ERRORFILE, "Unable to find permitted docker mounts on disk\n"); + ret = MOUNT_ACCESS_ERROR; + goto free_and_exit; + } + for (i = 0; values[i] != NULL; ++i) { + mount_src = get_mount_source(values[i]); + if (mount_src == NULL) { + fprintf(ERRORFILE, "Invalid docker mount '%s', realpath=%s\n", values[i], mount_src); + ret = INVALID_DOCKER_MOUNT; + goto free_and_exit; + } + permitted_rw = check_mount_permitted((const char **) permitted_rw_mounts, mount_src); + permitted_ro = check_mount_permitted((const char **) permitted_ro_mounts, mount_src); + if (permitted_ro == -1 || permitted_rw == -1) { + fprintf(ERRORFILE, "Invalid docker mount '%s', realpath=%s\n", values[i], mount_src); + ret = INVALID_DOCKER_MOUNT; + goto free_and_exit; + } + // rw mount + if (ro == 0) { + if (permitted_rw == 0) { + fprintf(ERRORFILE, "Invalid docker rw mount '%s', realpath=%s\n", values[i], mount_src); + ret = INVALID_DOCKER_RW_MOUNT; + goto free_and_exit; + } else { + // determine if the user can modify the container-executor.cfg file + tmp_path_buffer[0] = normalize_mount(mount_src); + // just re-use the function, flip the args to check if the container-executor path is in the requested + // mount point + ret = check_mount_permitted(tmp_path_buffer, container_executor_cfg_path); + free((void *) tmp_path_buffer[0]); + if (ret == 1) { + fprintf(ERRORFILE, "Attempting to mount a parent directory '%s' of container-executor.cfg as read-write\n", + values[i]); + ret = INVALID_DOCKER_RW_MOUNT; + goto free_and_exit; + } + } + } + //ro mount + if (ro != 0 && permitted_ro == 0 && permitted_rw == 0) { + fprintf(ERRORFILE, "Invalid docker ro mount '%s', realpath=%s\n", values[i], mount_src); + ret = INVALID_DOCKER_RO_MOUNT; + goto free_and_exit; + } + tmp_buffer_2 = (char *) alloc_and_clear_memory(strlen(values[i]) + strlen(ro_suffix) + 1, sizeof(char)); + strncpy(tmp_buffer_2, values[i], strlen(values[i])); + strncpy(tmp_buffer_2 + strlen(values[i]), ro_suffix, strlen(ro_suffix)); + quote_and_append_arg(&tmp_buffer, &tmp_buffer_size, "-v ", tmp_buffer_2); + ret = add_to_buffer(out, outlen, tmp_buffer); + free(tmp_buffer_2); + free(mount_src); + tmp_buffer_2 = NULL; + mount_src = NULL; + memset(tmp_buffer, 0, tmp_buffer_size); + if (ret != 0) { + ret = BUFFER_TOO_SMALL; + goto free_and_exit; + } + } + } + + free_and_exit: + free_values(permitted_ro_mounts); + free_values(permitted_rw_mounts); + free_values(values); + free(mount_src); + free((void *) container_executor_cfg_path); + free(tmp_buffer); + if (ret != 0) { + memset(out, 0, outlen); + } + return ret; +} + +static int add_ro_mounts(const struct configuration *command_config, const struct configuration *conf, char *out, + const size_t outlen) { + return add_mounts(command_config, conf, "ro-mounts", 1, out, outlen); +} + +static int add_rw_mounts(const struct configuration *command_config, const struct configuration *conf, char *out, + const size_t outlen) { + return add_mounts(command_config, conf, "rw-mounts", 0, out, outlen); +} + +static int set_privileged(const struct configuration *command_config, const struct configuration *conf, char *out, + const size_t outlen) { + size_t tmp_buffer_size = 1024; + char *tmp_buffer = (char *) alloc_and_clear_memory(tmp_buffer_size, sizeof(char)); + char *value = get_configuration_value("privileged", DOCKER_COMMAND_FILE_SECTION, command_config); + char *privileged_container_enabled + = get_configuration_value("docker.privileged-containers.enabled", CONTAINER_EXECUTOR_CFG_DOCKER_SECTION, conf); + int ret = 0; + + if (value != NULL && strcmp(value, "true") == 0) { + if (privileged_container_enabled != NULL) { + if (strcmp(privileged_container_enabled, "1") == 0) { + ret = add_to_buffer(out, outlen, "--privileged "); + if (ret != 0) { + ret = BUFFER_TOO_SMALL; + } + } else { + fprintf(ERRORFILE, "Privileged containers are disabled\n"); + ret = PRIVILEGED_CONTAINERS_DISABLED; + goto free_and_exit; + } + } else { + fprintf(ERRORFILE, "Privileged containers are disabled\n"); + ret = PRIVILEGED_CONTAINERS_DISABLED; + goto free_and_exit; + } + } + + free_and_exit: + free(tmp_buffer); + free(value); + free(privileged_container_enabled); + if (ret != 0) { + memset(out, 0, outlen); + } + return ret; +} + +int get_docker_run_command(const char *command_file, const struct configuration *conf, char *out, const size_t outlen) { + int ret = 0, i = 0; + char *container_name = NULL, *user = NULL, *image = NULL; + size_t tmp_buffer_size = 1024; + char *tmp_buffer = NULL; + char **launch_command = NULL; + struct configuration command_config = {0, NULL}; + ret = read_and_verify_command_file(command_file, DOCKER_RUN_COMMAND, &command_config); + if (ret != 0) { + return ret; + } + + container_name = get_configuration_value("name", DOCKER_COMMAND_FILE_SECTION, &command_config); + if (container_name == NULL || validate_container_name(container_name) != 0) { + return INVALID_DOCKER_CONTAINER_NAME; + } + user = get_configuration_value("user", DOCKER_COMMAND_FILE_SECTION, &command_config); + if (user == NULL) { + return INVALID_DOCKER_USER_NAME; + } + image = get_configuration_value("image", DOCKER_COMMAND_FILE_SECTION, &command_config); + if (image == NULL || validate_docker_image_name(image) != 0) { + return INVALID_DOCKER_IMAGE_NAME; + } + + ret = add_docker_config_param(&command_config, out, outlen); + if (ret != 0) { + return BUFFER_TOO_SMALL; + } + + ret = add_to_buffer(out, outlen, DOCKER_RUN_COMMAND); + if(ret != 0) { + return BUFFER_TOO_SMALL; + } + + + tmp_buffer = (char *) alloc_and_clear_memory(tmp_buffer_size, sizeof(char)); + + quote_and_append_arg(&tmp_buffer, &tmp_buffer_size, " --name=", container_name); + ret = add_to_buffer(out, outlen, tmp_buffer); + if (ret != 0) { + return BUFFER_TOO_SMALL; + } + memset(tmp_buffer, 0, tmp_buffer_size); + + quote_and_append_arg(&tmp_buffer, &tmp_buffer_size, "--user=", user); + ret = add_to_buffer(out, outlen, tmp_buffer); + if (ret != 0) { + return BUFFER_TOO_SMALL; + } + memset(tmp_buffer, 0, tmp_buffer_size); + + ret = detach_container(&command_config, out, outlen); + if (ret != 0) { + return ret; + } + + ret = rm_container_on_exit(&command_config, out, outlen); + if (ret != 0) { + return ret; + } + + ret = set_container_workdir(&command_config, out, outlen); + if (ret != 0) { + return ret; + } + + ret = set_network(&command_config, conf, out, outlen); + if (ret != 0) { + return ret; + } + + ret = add_ro_mounts(&command_config, conf, out, outlen); + if (ret != 0) { + return ret; + } + + ret = add_rw_mounts(&command_config, conf, out, outlen); + if (ret != 0) { + return ret; + } + + ret = set_cgroup_parent(&command_config, out, outlen); + if (ret != 0) { + return ret; + } + + ret = set_privileged(&command_config, conf, out, outlen); + if (ret != 0) { + return ret; + } + + ret = set_capabilities(&command_config, conf, out, outlen); + if (ret != 0) { + return ret; + } + + ret = set_hostname(&command_config, out, outlen); + if (ret != 0) { + return ret; + } + + ret = set_group_add(&command_config, out, outlen); + if (ret != 0) { + return ret; + } + + ret = set_devices(&command_config, conf, out, outlen); + if (ret != 0) { + return ret; + } + + quote_and_append_arg(&tmp_buffer, &tmp_buffer_size, "", image); + ret = add_to_buffer(out, outlen, tmp_buffer); + if (ret != 0) { + return BUFFER_TOO_SMALL; + } + + launch_command = get_configuration_values_delimiter("launch-command", DOCKER_COMMAND_FILE_SECTION, &command_config, + ","); + if (launch_command != NULL) { + for (i = 0; launch_command[i] != NULL; ++i) { + memset(tmp_buffer, 0, tmp_buffer_size); + quote_and_append_arg(&tmp_buffer, &tmp_buffer_size, "", launch_command[i]); + ret = add_to_buffer(out, outlen, tmp_buffer); + if (ret != 0) { + free_values(launch_command); + free(tmp_buffer); + return BUFFER_TOO_SMALL; + } + } + free_values(launch_command); + } + free(tmp_buffer); + return 0; +} + + + diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/utils/docker-util.h b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/utils/docker-util.h new file mode 100644 index 00000000000..37ec88077c5 --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/utils/docker-util.h @@ -0,0 +1,147 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __YARN_POSIX_CONTAINER_EXECUTOR_DOCKER_UTIL_H__ +#define __YARN_POSIX_CONTAINER_EXECUTOR_DOCKER_UTIL_H__ + +#include "configuration.h" + +#define CONTAINER_EXECUTOR_CFG_DOCKER_SECTION "docker" +#define DOCKER_BINARY_KEY "docker.binary" +#define DOCKER_COMMAND_FILE_SECTION "docker-command-execution" +#define DOCKER_INSPECT_COMMAND "inspect" +#define DOCKER_LOAD_COMMAND "load" +#define DOCKER_PULL_COMMAND "pull" +#define DOCKER_RM_COMMAND "rm" +#define DOCKER_RUN_COMMAND "run" +#define DOCKER_STOP_COMMAND "stop" + + +enum docker_error_codes { + INVALID_COMMAND_FILE = 1, + INCORRECT_COMMAND, + BUFFER_TOO_SMALL, + INVALID_DOCKER_CONTAINER_NAME, + INVALID_DOCKER_IMAGE_NAME, + INVALID_DOCKER_USER_NAME, + INVALID_DOCKER_INSPECT_FORMAT, + UNKNOWN_DOCKER_COMMAND, + INVALID_DOCKER_NETWORK, + INVALID_DOCKER_CAPABILITY, + PRIVILEGED_CONTAINERS_DISABLED, + INVALID_DOCKER_MOUNT, + INVALID_DOCKER_RO_MOUNT, + INVALID_DOCKER_RW_MOUNT, + MOUNT_ACCESS_ERROR, + INVALID_DOCKER_DEVICE, + INVALID_DOCKER_STOP_COMMAND +}; + +/** + * Get the full path for the docker binary. + * @param conf Configuration for the container-executor + * @return String containing the docker binary to be freed by the user. + */ +char *get_docker_binary(const struct configuration *conf); + +/** + * Get the Docker command line string. The function will inspect the params file to determine the command to be run. + * @param command_file File containing the params for the Docker command + * @param conf Configuration struct containing the container-executor.cfg details + * @param out Buffer to fill with the Docker command + * @param outlen Size of the output buffer + * @return Return code with 0 indicating success and non-zero codes indicating error + */ +int get_docker_command(const char* command_file, const struct configuration* conf, char *out, const size_t outlen); + +/** + * Get the Docker inspect command line string. The function will verify that the params file is meant for the + * inspect command. + * @param command_file File containing the params for the Docker inspect command + * @param conf Configuration struct containing the container-executor.cfg details + * @param out Buffer to fill with the inspect command + * @param outlen Size of the output buffer + * @return Return code with 0 indicating success and non-zero codes indicating error + */ +int get_docker_inspect_command(const char* command_file, const struct configuration* conf, char *out, const size_t outlen); + +/** + * Get the Docker load command line string. The function will verify that the params file is meant for the load command. + * @param command_file File containing the params for the Docker load command + * @param conf Configuration struct containing the container-executor.cfg details + * @param out Buffer to fill with the load command + * @param outlen Size of the output buffer + * @return Return code with 0 indicating success and non-zero codes indicating error + */ +int get_docker_load_command(const char* command_file, const struct configuration* conf, char *out, const size_t outlen); + +/** + * Get the Docker pull command line string. The function will verify that the params file is meant for the pull command. + * @param command_file File containing the params for the Docker pull command + * @param conf Configuration struct containing the container-executor.cfg details + * @param out Buffer to fill with the pull command + * @param outlen Size of the output buffer + * @return Return code with 0 indicating success and non-zero codes indicating error + */ +int get_docker_pull_command(const char* command_file, const struct configuration* conf, char *out, const size_t outlen); + +/** + * Get the Docker rm command line string. The function will verify that the params file is meant for the rm command. + * @param command_file File containing the params for the Docker rm command + * @param conf Configuration struct containing the container-executor.cfg details + * @param out Buffer to fill with the rm command + * @param outlen Size of the output buffer + * @return Return code with 0 indicating success and non-zero codes indicating error + */ +int get_docker_rm_command(const char* command_file, const struct configuration* conf, char *out, const size_t outlen); + +/** + * Get the Docker run command line string. The function will verify that the params file is meant for the run command. + * @param command_file File containing the params for the Docker run command + * @param conf Configuration struct containing the container-executor.cfg details + * @param out Buffer to fill with the run command + * @param outlen Size of the output buffer + * @return Return code with 0 indicating success and non-zero codes indicating error + */ +int get_docker_run_command(const char* command_file, const struct configuration* conf, char *out, const size_t outlen); + +/** + * Get the Docker stop command line string. The function will verify that the params file is meant for the stop command. + * @param command_file File containing the params for the Docker stop command + * @param conf Configuration struct containing the container-executor.cfg details + * @param out Buffer to fill with the stop command + * @param outlen Size of the output buffer + * @return Return code with 0 indicating success and non-zero codes indicating error + */ +int get_docker_stop_command(const char* command_file, const struct configuration* conf, char *out, const size_t outlen); + +/** + * Give an error message for the supplied error code + * @param error_code the error code + * @return const string containing the error message + */ +const char *get_docker_error_message(const int error_code); + +/** + * Determine if the docker module is enabled in the config. + * @param conf Configuration structure + * @return 1 if enabled, 0 otherwise + */ +int docker_module_enabled(const struct configuration *conf); + +#endif diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/utils/string-utils.c b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/utils/string-utils.c index d19c08493a4..40b2c2588c8 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/utils/string-utils.c +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/utils/string-utils.c @@ -21,7 +21,6 @@ #include #include #include -#include #include /* diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/resources/test/test-configurations/docker-container-executor.cfg b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/resources/test/test-configurations/docker-container-executor.cfg new file mode 100644 index 00000000000..339bec950cc --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/resources/test/test-configurations/docker-container-executor.cfg @@ -0,0 +1,13 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +[docker] +privieged-containers.enabled=0 diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/test-container-executor.c b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/test-container-executor.c index 64ee717afdc..609dd2cfcb1 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/test-container-executor.c +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/test-container-executor.c @@ -19,6 +19,7 @@ #include "container-executor.h" #include "utils/string-utils.h" #include "util.h" +#include "get_executable.h" #include "test/test-container-executor-common.h" #include @@ -1155,145 +1156,6 @@ void test_trim_function() { free(trimmed); } -void test_sanitize_docker_command() { - - char *input[] = { - "run --name=cname --user=nobody -d --workdir=/yarn/local/cdir --privileged --rm --device=/sys/fs/cgroup/device:/sys/fs/cgroup/device --detach=true --cgroup-parent=/sys/fs/cgroup/cpu/yarn/cid --net=host --hostname=test.host.name --cap-drop=ALL --cap-add=SYS_CHROOT --cap-add=MKNOD --cap-add=SETFCAP --cap-add=SETPCAP --cap-add=FSETID --cap-add=CHOWN --cap-add=AUDIT_WRITE --cap-add=SETGID --cap-add=NET_RAW --cap-add=FOWNER --cap-add=SETUID --cap-add=DAC_OVERRIDE --cap-add=KILL --cap-add=NET_BIND_SERVICE -v /sys/fs/cgroup:/sys/fs/cgroup:ro -v /yarn/local/cdir:/yarn/local/cdir -v /yarn/local/usercache/test/:/yarn/local/usercache/test/ ubuntu bash /yarn/local/usercache/test/appcache/aid/cid/launch_container.sh", - "run --name=$CID --user=nobody -d --workdir=/yarn/local/cdir --privileged --rm --device=/sys/fs/cgroup/device:/sys/fs/cgroup/device --detach=true --cgroup-parent=/sys/fs/cgroup/cpu/yarn/cid --net=host --hostname=test.host.name --cap-drop=ALL --cap-add=SYS_CHROOT --cap-add=MKNOD --cap-add=SETFCAP --cap-add=SETPCAP --cap-add=FSETID --cap-add=CHOWN --cap-add=AUDIT_WRITE --cap-add=SETGID --cap-add=NET_RAW --cap-add=FOWNER --cap-add=SETUID --cap-add=DAC_OVERRIDE --cap-add=KILL --cap-add=NET_BIND_SERVICE -v /sys/fs/cgroup:/sys/fs/cgroup:ro -v /yarn/local/cdir:/yarn/local/cdir -v /yarn/local/usercache/test/:/yarn/local/usercache/test/ ubuntu bash /yarn/local/usercache/test/appcache/aid/cid/launch_container.sh", - "run --name=cname --user=nobody -d --workdir=/yarn/local/cdir --privileged --rm --device=/sys/fs/cgroup/device:/sys/fs/cgroup/device --detach=true --cgroup-parent=/sys/fs/cgroup/cpu/yarn/cid --net=host --hostname=test.host.name --cap-drop=ALL --cap-add=SYS_CHROOT --cap-add=MKNOD --cap-add=SETFCAP --cap-add=SETPCAP --cap-add=FSETID --cap-add=CHOWN --cap-add=AUDIT_WRITE --cap-add=SETGID --cap-add=NET_RAW --cap-add=FOWNER --cap-add=SETUID --cap-add=DAC_OVERRIDE --cap-add=KILL --cap-add=NET_BIND_SERVICE -v /sys/fs/cgroup:/sys/fs/cgroup:ro -v /yarn/local/cdir:/yarn/local/cdir -v /yarn/local/usercache/test/:/yarn/local/usercache/test/ ubuntu || touch /tmp/file # bash /yarn/local/usercache/test/appcache/aid/cid/launch_container.sh", - "run --name=cname --user=nobody -d --workdir=/yarn/local/cdir --privileged --rm --device=/sys/fs/cgroup/device:/sys/fs/cgroup/device --detach=true --cgroup-parent=/sys/fs/cgroup/cpu/yarn/cid --net=host --hostname=test.host.name --cap-drop=ALL --cap-add=SYS_CHROOT --cap-add=MKNOD --cap-add=SETFCAP --cap-add=SETPCAP --cap-add=FSETID --cap-add=CHOWN --cap-add=AUDIT_WRITE --cap-add=SETGID --cap-add=NET_RAW --cap-add=FOWNER --cap-add=SETUID --cap-add=DAC_OVERRIDE --cap-add=KILL --cap-add=NET_BIND_SERVICE -v /sys/fs/cgroup:/sys/fs/cgroup:ro -v /yarn/local/cdir:/yarn/local/cdir -v /yarn/local/usercache/test/:/yarn/local/usercache/test/ ubuntu' || touch /tmp/file # bash /yarn/local/usercache/test/appcache/aid/cid/launch_container.sh", - "run ''''''''", - "inspect --format='{{range(.NetworkSettings.Networks)}}{{.IPAddress}},{{end}}{{.Config.Hostname}}' container_e111_1111111111111_1111_01_111111", - "rm container_e111_1111111111111_1111_01_111111", - "stop container_e111_1111111111111_1111_01_111111", - "pull ubuntu", - "pull registry.com/user/ubuntu", - "--config=/yarn/local/cdir/ pull registry.com/user/ubuntu" - }; - char *expected_output[] = { - "run --name='cname' --user='nobody' -d --workdir='/yarn/local/cdir' --privileged --rm --device='/sys/fs/cgroup/device:/sys/fs/cgroup/device' --detach='true' --cgroup-parent='/sys/fs/cgroup/cpu/yarn/cid' --net='host' --hostname='test.host.name' --cap-drop='ALL' --cap-add='SYS_CHROOT' --cap-add='MKNOD' --cap-add='SETFCAP' --cap-add='SETPCAP' --cap-add='FSETID' --cap-add='CHOWN' --cap-add='AUDIT_WRITE' --cap-add='SETGID' --cap-add='NET_RAW' --cap-add='FOWNER' --cap-add='SETUID' --cap-add='DAC_OVERRIDE' --cap-add='KILL' --cap-add='NET_BIND_SERVICE' -v '/sys/fs/cgroup:/sys/fs/cgroup:ro' -v '/yarn/local/cdir:/yarn/local/cdir' -v '/yarn/local/usercache/test/:/yarn/local/usercache/test/' 'ubuntu' 'bash' '/yarn/local/usercache/test/appcache/aid/cid/launch_container.sh' ", - "run --name='$CID' --user='nobody' -d --workdir='/yarn/local/cdir' --privileged --rm --device='/sys/fs/cgroup/device:/sys/fs/cgroup/device' --detach='true' --cgroup-parent='/sys/fs/cgroup/cpu/yarn/cid' --net='host' --hostname='test.host.name' --cap-drop='ALL' --cap-add='SYS_CHROOT' --cap-add='MKNOD' --cap-add='SETFCAP' --cap-add='SETPCAP' --cap-add='FSETID' --cap-add='CHOWN' --cap-add='AUDIT_WRITE' --cap-add='SETGID' --cap-add='NET_RAW' --cap-add='FOWNER' --cap-add='SETUID' --cap-add='DAC_OVERRIDE' --cap-add='KILL' --cap-add='NET_BIND_SERVICE' -v '/sys/fs/cgroup:/sys/fs/cgroup:ro' -v '/yarn/local/cdir:/yarn/local/cdir' -v '/yarn/local/usercache/test/:/yarn/local/usercache/test/' 'ubuntu' 'bash' '/yarn/local/usercache/test/appcache/aid/cid/launch_container.sh' ", - "run --name='cname' --user='nobody' -d --workdir='/yarn/local/cdir' --privileged --rm --device='/sys/fs/cgroup/device:/sys/fs/cgroup/device' --detach='true' --cgroup-parent='/sys/fs/cgroup/cpu/yarn/cid' --net='host' --hostname='test.host.name' --cap-drop='ALL' --cap-add='SYS_CHROOT' --cap-add='MKNOD' --cap-add='SETFCAP' --cap-add='SETPCAP' --cap-add='FSETID' --cap-add='CHOWN' --cap-add='AUDIT_WRITE' --cap-add='SETGID' --cap-add='NET_RAW' --cap-add='FOWNER' --cap-add='SETUID' --cap-add='DAC_OVERRIDE' --cap-add='KILL' --cap-add='NET_BIND_SERVICE' -v '/sys/fs/cgroup:/sys/fs/cgroup:ro' -v '/yarn/local/cdir:/yarn/local/cdir' -v '/yarn/local/usercache/test/:/yarn/local/usercache/test/' 'ubuntu' '||' 'touch' '/tmp/file' '#' 'bash' '/yarn/local/usercache/test/appcache/aid/cid/launch_container.sh' ", - "run --name='cname' --user='nobody' -d --workdir='/yarn/local/cdir' --privileged --rm --device='/sys/fs/cgroup/device:/sys/fs/cgroup/device' --detach='true' --cgroup-parent='/sys/fs/cgroup/cpu/yarn/cid' --net='host' --hostname='test.host.name' --cap-drop='ALL' --cap-add='SYS_CHROOT' --cap-add='MKNOD' --cap-add='SETFCAP' --cap-add='SETPCAP' --cap-add='FSETID' --cap-add='CHOWN' --cap-add='AUDIT_WRITE' --cap-add='SETGID' --cap-add='NET_RAW' --cap-add='FOWNER' --cap-add='SETUID' --cap-add='DAC_OVERRIDE' --cap-add='KILL' --cap-add='NET_BIND_SERVICE' -v '/sys/fs/cgroup:/sys/fs/cgroup:ro' -v '/yarn/local/cdir:/yarn/local/cdir' -v '/yarn/local/usercache/test/:/yarn/local/usercache/test/' 'ubuntu'\"'\"'' '||' 'touch' '/tmp/file' '#' 'bash' '/yarn/local/usercache/test/appcache/aid/cid/launch_container.sh' ", - "run ''\"'\"''\"'\"''\"'\"''\"'\"''\"'\"''\"'\"''\"'\"''\"'\"'' ", - "inspect --format='{{range(.NetworkSettings.Networks)}}{{.IPAddress}},{{end}}{{.Config.Hostname}}' container_e111_1111111111111_1111_01_111111", - "rm container_e111_1111111111111_1111_01_111111", - "stop container_e111_1111111111111_1111_01_111111", - "pull ubuntu", - "pull registry.com/user/ubuntu", - "--config=/yarn/local/cdir/ pull registry.com/user/ubuntu" - }; - - int input_size = sizeof(input) / sizeof(char *); - int i = 0; - for(i = 0; i < input_size; i++) { - char *command = (char *) calloc(strlen(input[i]) + 1 , sizeof(char)); - strncpy(command, input[i], strlen(input[i])); - char *op = sanitize_docker_command(command); - if(strncmp(expected_output[i], op, strlen(expected_output[i])) != 0) { - printf("FAIL: expected output %s does not match actual output '%s'\n", expected_output[i], op); - exit(1); - } - free(command); - } -} - -void test_validate_docker_image_name() { - - char *good_input[] = { - "ubuntu", - "ubuntu:latest", - "ubuntu:14.04", - "ubuntu:LATEST", - "registry.com:5000/user/ubuntu", - "registry.com:5000/user/ubuntu:latest", - "registry.com:5000/user/ubuntu:0.1.2.3", - "registry.com/user/ubuntu", - "registry.com/user/ubuntu:latest", - "registry.com/user/ubuntu:0.1.2.3", - "registry.com/user/ubuntu:test-image", - "registry.com/user/ubuntu:test_image", - "registry.com/ubuntu", - "user/ubuntu", - "user/ubuntu:0.1.2.3", - "user/ubuntu:latest", - "user/ubuntu:test_image", - "user/ubuntu.test:test_image", - "user/ubuntu-test:test-image", - "registry.com/ubuntu/ubuntu/ubuntu" - }; - - char *bad_input[] = { - "UBUNTU", - "registry.com|5000/user/ubuntu", - "registry.com | 5000/user/ubuntu", - "ubuntu' || touch /tmp/file #", - "ubuntu || touch /tmp/file #", - "''''''''", - "bad_host_name:5000/user/ubuntu", - "registry.com:foo/ubuntu/ubuntu/ubuntu", - "registry.com/ubuntu:foo/ubuntu/ubuntu" - }; - - int good_input_size = sizeof(good_input) / sizeof(char *); - int i = 0; - for(i = 0; i < good_input_size; i++) { - int op = validate_docker_image_name(good_input[i]); - if(0 != op) { - printf("\nFAIL: docker image name %s is invalid", good_input[i]); - exit(1); - } - } - - int bad_input_size = sizeof(bad_input) / sizeof(char *); - int j = 0; - for(j = 0; j < bad_input_size; j++) { - int op = validate_docker_image_name(bad_input[j]); - if(1 != op) { - printf("\nFAIL: docker image name %s is valid, expected invalid", bad_input[j]); - exit(1); - } - } -} - -void test_validate_container_id() { - char *good_input[] = { - "container_e134_1499953498516_50875_01_000007", - "container_1499953498516_50875_01_000007", - "container_e1_12312_11111_02_000001" - }; - - char *bad_input[] = { - "CONTAINER", - "container_e1_12312_11111_02_000001 | /tmp/file" - "container_e1_12312_11111_02_000001 || # /tmp/file", - "container_e1_12312_11111_02_000001 # /tmp/file", - "container_e1_12312_11111_02_000001' || touch /tmp/file #", - "ubuntu || touch /tmp/file #", - "''''''''" - }; - - int good_input_size = sizeof(good_input) / sizeof(char *); - int i = 0; - for(i = 0; i < good_input_size; i++) { - int op = validate_container_id(good_input[i]); - if(1 != op) { - printf("FAIL: docker container name %s is invalid\n", good_input[i]); - exit(1); - } - } - - int bad_input_size = sizeof(bad_input) / sizeof(char *); - int j = 0; - for(j = 0; j < bad_input_size; j++) { - int op = validate_container_id(bad_input[j]); - if(0 != op) { - printf("FAIL: docker container name %s is valid, expected invalid\n", bad_input[j]); - exit(1); - } - } -} - // This test is expected to be executed either by a regular // user or by root. If executed by a regular user it doesn't // test all the functions that would depend on changing the @@ -1388,15 +1250,6 @@ int main(int argc, char **argv) { printf("\nTesting is_feature_enabled()\n"); test_is_feature_enabled(); - printf("\nTesting sanitize docker commands()\n"); - test_sanitize_docker_command(); - - printf("\nTesting validate_docker_image_name()\n"); - test_validate_docker_image_name(); - - printf("\nTesting validate_container_id()\n"); - test_validate_container_id(); - test_check_user(0); #ifdef __APPLE__ diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/test_util.cc b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/test_util.cc index 2ec7b2a09c7..b96dea15779 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/test_util.cc +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/test_util.cc @@ -17,7 +17,7 @@ */ #include -#include +#include extern "C" { #include "util.h" @@ -135,4 +135,39 @@ namespace ContainerExecutor { ASSERT_STREQ("foo", trimmed); free(trimmed); } + + TEST_F(TestUtil, test_escape_single_quote) { + std::vector > input_output_vec; + input_output_vec.push_back(std::make_pair("'abcd'", "'\"'\"'abcd'\"'\"'")); + input_output_vec.push_back(std::make_pair("'", "'\"'\"'")); + + std::vector >::const_iterator itr; + for (itr = input_output_vec.begin(); itr != input_output_vec.end(); ++itr) { + char *ret = escape_single_quote(itr->first.c_str()); + ASSERT_STREQ(itr->second.c_str(), ret); + free(ret); + } + } + + TEST_F(TestUtil, test_quote_and_append_arg) { + + char *tmp = static_cast(malloc(4096)); + size_t tmp_size = 4096; + + memset(tmp, 0, tmp_size); + quote_and_append_arg(&tmp, &tmp_size, "param=", "argument1"); + ASSERT_STREQ("param='argument1' ", tmp); + + memset(tmp, 0, tmp_size); + quote_and_append_arg(&tmp, &tmp_size, "param=", "ab'cd"); + ASSERT_STREQ("param='ab'\"'\"'cd' ", tmp); + free(tmp); + + tmp = static_cast(malloc(4)); + tmp_size = 4; + memset(tmp, 0, tmp_size); + quote_and_append_arg(&tmp, &tmp_size, "param=", "argument1"); + ASSERT_STREQ("param='argument1' ", tmp); + ASSERT_EQ(1040, tmp_size); + } } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/utils/test-string-utils.cc b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/utils/test-string-utils.cc index 037816a1546..b259c6e00d4 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/utils/test-string-utils.cc +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/utils/test-string-utils.cc @@ -88,6 +88,39 @@ rc = get_numbers_split_by_comma(input, &numbers, &n_numbers); std::cout << "Testing input=" << input << "\n"; ASSERT_TRUE(0 != rc) << "Should failed\n"; -} + } -} // namespace ContainerExecutor \ No newline at end of file + TEST_F(TestStringUtils, test_validate_container_id) { + + const char *good_input[] = { + "container_e134_1499953498516_50875_01_000007", + "container_1499953498516_50875_01_000007", + "container_e1_12312_11111_02_000001" + }; + + const char *bad_input[] = { + "CONTAINER", + "container_e1_12312_11111_02_000001 | /tmp/file" + "container_e1_12312_11111_02_000001 || # /tmp/file", + "container_e1_12312_11111_02_000001 # /tmp/file", + "container_e1_12312_11111_02_000001' || touch /tmp/file #", + "ubuntu || touch /tmp/file #", + "''''''''" + }; + + int good_input_size = sizeof(good_input) / sizeof(char *); + int i = 0; + for (i = 0; i < good_input_size; i++) { + int op = validate_container_id(good_input[i]); + ASSERT_EQ(1, op); + } + + int bad_input_size = sizeof(bad_input) / sizeof(char *); + int j = 0; + for (j = 0; j < bad_input_size; j++) { + int op = validate_container_id(bad_input[j]); + ASSERT_EQ(0, op); + } + } + +} // namespace ContainerExecutor diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/utils/test_docker_util.cc b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/utils/test_docker_util.cc new file mode 100644 index 00000000000..c627ca84e4f --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/utils/test_docker_util.cc @@ -0,0 +1,1122 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "errno.h" + +extern "C" { +#include "utils/docker-util.c" +} + +namespace ContainerExecutor { + + class TestDockerUtil : public ::testing::Test { + protected: + virtual void SetUp() { + docker_command_file = "docker-command.cmd"; + container_executor_cfg_file = "container-executor.cfg"; + container_executor_cfg.size = 0; + container_executor_cfg.sections = NULL; + } + + virtual void TearDown() { + remove(docker_command_file.c_str()); + remove(container_executor_cfg_file.c_str()); + delete_ce_file(); + } + + struct configuration container_executor_cfg; + std::string docker_command_file; + std::string container_executor_cfg_file; + + + void write_file(const std::string fname, const std::string contents) { + std::ofstream command_file; + command_file.open(fname.c_str()); + command_file << contents; + command_file.close(); + } + + int create_ce_file() { + int ret = 0; + const char *fname = HADOOP_CONF_DIR "/" CONF_FILENAME; + if (strcmp("../etc/hadoop/container-executor.cfg", fname) == 0) { + ret = mkdir("../etc", 0755); + if (ret == 0 || errno == EEXIST) { + ret = mkdir("../etc/hadoop", 0755); + if (ret == 0 || errno == EEXIST) { + write_file("../etc/hadoop/container-executor.cfg", ""); + return 0; + } else { + std::cerr << "Could not create ../etc/hadoop, " << strerror(errno) << std::endl; + } + } else { + std::cerr << "Could not create ../etc, " << strerror(errno) << std::endl; + } + } + std::cerr << "Could not create " << fname << std::endl; + return 1; + } + + void delete_ce_file() { + const char *fname = HADOOP_CONF_DIR "/" CONF_FILENAME; + if (strcmp("../etc/hadoop/container-executor.cfg", fname) == 0) { + struct stat buffer; + if (stat(fname, &buffer) == 0) { + remove("../etc/hadoop/container-executor.cfg"); + rmdir("../etc/hadoop"); + rmdir("../etc"); + } + } + } + + void write_container_executor_cfg(const std::string contents) { + write_file(container_executor_cfg_file, contents); + } + + void write_command_file(const std::string contents) { + write_file(docker_command_file, contents); + } + + void run_docker_command_test(const std::vector > &file_cmd_vec, + const std::vector > &bad_file_cmd_vec, + int (*docker_func)(const char *, const struct configuration *, char *, const size_t)) { + char tmp[8192]; + std::vector >::const_iterator itr; + for (itr = file_cmd_vec.begin(); itr != file_cmd_vec.end(); ++itr) { + memset(tmp, 0, 8192); + write_command_file(itr->first); + int ret = (*docker_func)(docker_command_file.c_str(), &container_executor_cfg, tmp, 8192); + ASSERT_EQ(0, ret) << "error message: " << get_docker_error_message(ret) << " for input " << itr->first; + ASSERT_STREQ(itr->second.c_str(), tmp); + } + + std::vector >::const_iterator itr2; + for (itr2 = bad_file_cmd_vec.begin(); itr2 != bad_file_cmd_vec.end(); ++itr2) { + memset(tmp, 0, 8192); + write_command_file(itr2->first); + int ret = (*docker_func)(docker_command_file.c_str(), &container_executor_cfg, tmp, 8192); + ASSERT_EQ(itr2->second, ret) << " for " << itr2->first << std::endl; + ASSERT_EQ(0, strlen(tmp)); + } + int ret = (*docker_func)("unknown-file", &container_executor_cfg, tmp, 8192); + ASSERT_EQ(static_cast(INVALID_COMMAND_FILE), ret); + } + + void run_docker_run_helper_function(const std::vector > &file_cmd_vec, + int (*helper_func)(const struct configuration *, char *, const size_t)) { + std::vector >::const_iterator itr; + for(itr = file_cmd_vec.begin(); itr != file_cmd_vec.end(); ++itr) { + struct configuration cfg; + const int buff_len = 1024; + char buff[buff_len]; + memset(buff, 0, buff_len); + write_command_file(itr->first); + int ret = read_config(docker_command_file.c_str(), &cfg); + if(ret == 0) { + ret = (*helper_func)(&cfg, buff, buff_len); + ASSERT_EQ(0, ret); + ASSERT_STREQ(itr->second.c_str(), buff); + } + } + } + }; + + TEST_F(TestDockerUtil, test_docker_inspect) { + std::vector > file_cmd_vec; + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=inspect\n format={{.State.Status}}\n name=container_e1_12312_11111_02_000001", + "inspect --format={{.State.Status}} container_e1_12312_11111_02_000001")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=inspect\n" + " format={{range(.NetworkSettings.Networks)}}{{.IPAddress}},{{end}}{{.Config.Hostname}}\n" + " name=container_e1_12312_11111_02_000001", + "inspect --format={{range(.NetworkSettings.Networks)}}{{.IPAddress}},{{end}}{{.Config.Hostname}} container_e1_12312_11111_02_000001")); + + std::vector > bad_file_cmd_vec; + bad_file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n format='{{.State.Status}}'", + static_cast(INCORRECT_COMMAND))); + bad_file_cmd_vec.push_back( + std::make_pair("docker-command=inspect\n format='{{.State.Status}}'", + static_cast(INCORRECT_COMMAND))); + bad_file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=inspect\n format={{.State.Status}}\n name=", + static_cast(INVALID_DOCKER_CONTAINER_NAME))); + bad_file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=inspect\n format={{.State.Status}}", + static_cast(INVALID_DOCKER_CONTAINER_NAME))); + bad_file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=inspect\n format=\n name=container_e1_12312_11111_02_000001", + static_cast(INVALID_DOCKER_INSPECT_FORMAT))); + bad_file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=inspect\n name=container_e1_12312_11111_02_000001", + static_cast(INVALID_DOCKER_INSPECT_FORMAT))); + bad_file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=inspect\n format={{.IPAddress}}\n name=container_e1_12312_11111_02_000001", + static_cast(INVALID_DOCKER_INSPECT_FORMAT))); + + run_docker_command_test(file_cmd_vec, bad_file_cmd_vec, get_docker_inspect_command); + } + + TEST_F(TestDockerUtil, test_docker_load) { + std::vector > file_cmd_vec; + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=load\n image=image-id", + "load --i='image-id' ")); + + std::vector > bad_file_cmd_vec; + bad_file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n image=image-id", static_cast(INCORRECT_COMMAND))); + bad_file_cmd_vec.push_back(std::make_pair( + "docker-command=load\n image=image-id", static_cast(INCORRECT_COMMAND))); + bad_file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=load\n image=", static_cast(INVALID_DOCKER_IMAGE_NAME))); + bad_file_cmd_vec.push_back(std::make_pair("[docker-command-execution]\n docker-command=load", + static_cast(INVALID_DOCKER_IMAGE_NAME))); + + run_docker_command_test(file_cmd_vec, bad_file_cmd_vec, get_docker_load_command); + } + + TEST_F(TestDockerUtil, test_docker_validate_image_name) { + const char *good_input[] = { + "ubuntu", + "ubuntu:latest", + "ubuntu:14.04", + "ubuntu:LATEST", + "registry.com:5000/user/ubuntu", + "registry.com:5000/user/ubuntu:latest", + "registry.com:5000/user/ubuntu:0.1.2.3", + "registry.com/user/ubuntu", + "registry.com/user/ubuntu:latest", + "registry.com/user/ubuntu:0.1.2.3", + "registry.com/user/ubuntu:test-image", + "registry.com/user/ubuntu:test_image", + "registry.com/ubuntu", + "user/ubuntu", + "user/ubuntu:0.1.2.3", + "user/ubuntu:latest", + "user/ubuntu:test_image", + "user/ubuntu.test:test_image", + "user/ubuntu-test:test-image", + "registry.com/ubuntu/ubuntu/ubuntu" + }; + + const char *bad_input[] = { + "UBUNTU", + "registry.com|5000/user/ubuntu", + "registry.com | 5000/user/ubuntu", + "ubuntu' || touch /tmp/file #", + "ubuntu || touch /tmp/file #", + "''''''''", + "bad_host_name:5000/user/ubuntu", + "registry.com:foo/ubuntu/ubuntu/ubuntu", + "registry.com/ubuntu:foo/ubuntu/ubuntu" + }; + + int good_input_size = sizeof(good_input) / sizeof(char *); + int i = 0; + for (i = 0; i < good_input_size; i++) { + int op = validate_docker_image_name(good_input[i]); + ASSERT_EQ(0, op); + } + + int bad_input_size = sizeof(bad_input) / sizeof(char *); + int j = 0; + for (j = 0; j < bad_input_size; j++) { + int op = validate_docker_image_name(bad_input[j]); + ASSERT_EQ(1, op); + } + } + + TEST_F(TestDockerUtil, test_docker_pull) { + std::vector > file_cmd_vec; + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=pull\n image=image-id", + "pull 'image-id' ")); + + std::vector > bad_file_cmd_vec; + bad_file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n image=image-id", static_cast(INCORRECT_COMMAND))); + bad_file_cmd_vec.push_back(std::make_pair( + "docker-command=pull\n image=image-id", static_cast(INCORRECT_COMMAND))); + bad_file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=pull\n image=", static_cast(INVALID_DOCKER_IMAGE_NAME))); + bad_file_cmd_vec.push_back(std::make_pair("[docker-command-execution]\n docker-command=pull", + static_cast(INVALID_DOCKER_IMAGE_NAME))); + + run_docker_command_test(file_cmd_vec, bad_file_cmd_vec, get_docker_pull_command); + } + + TEST_F(TestDockerUtil, test_docker_rm) { + std::vector > file_cmd_vec; + file_cmd_vec.push_back( + std::make_pair( + "[docker-command-execution]\n docker-command=rm\n name=container_e1_12312_11111_02_000001", + "rm container_e1_12312_11111_02_000001")); + + std::vector > bad_file_cmd_vec; + bad_file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n name=container_e1_12312_11111_02_000001", + static_cast(INCORRECT_COMMAND))); + bad_file_cmd_vec.push_back(std::make_pair( + "docker-command=rm\n name=ctr-id", static_cast(INCORRECT_COMMAND))); + bad_file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=rm\n name=", static_cast(INVALID_DOCKER_CONTAINER_NAME))); + bad_file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=rm", static_cast(INVALID_DOCKER_CONTAINER_NAME))); + + run_docker_command_test(file_cmd_vec, bad_file_cmd_vec, get_docker_rm_command); + } + + TEST_F(TestDockerUtil, test_docker_stop) { + std::vector > file_cmd_vec; + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=stop\n name=container_e1_12312_11111_02_000001", + "stop container_e1_12312_11111_02_000001")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=stop\n name=container_e1_12312_11111_02_000001\ntime=25", + "stop --time=25 container_e1_12312_11111_02_000001")); + + std::vector > bad_file_cmd_vec; + bad_file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n name=container_e1_12312_11111_02_000001", + static_cast(INCORRECT_COMMAND))); + bad_file_cmd_vec.push_back(std::make_pair( + "docker-command=stop\n name=ctr-id", static_cast(INCORRECT_COMMAND))); + bad_file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=stop\n name=", static_cast(INVALID_DOCKER_CONTAINER_NAME))); + bad_file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=stop", static_cast(INVALID_DOCKER_CONTAINER_NAME))); + bad_file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=stop\n name=container_e1_12312_11111_02_000001\n time=abcd", + static_cast(INVALID_DOCKER_STOP_COMMAND))); + + + run_docker_command_test(file_cmd_vec, bad_file_cmd_vec, get_docker_stop_command); + } + + TEST_F(TestDockerUtil, test_detach_container) { + std::vector > file_cmd_vec; + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n detach=true", "-d ")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run", "")); + + run_docker_run_helper_function(file_cmd_vec, detach_container); + } + + TEST_F(TestDockerUtil, test_rm_container) { + std::vector > file_cmd_vec; + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n rm=true", "--rm ")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run", "")); + + run_docker_run_helper_function(file_cmd_vec, rm_container_on_exit); + } + + TEST_F(TestDockerUtil, test_set_container_workdir) { + std::vector > file_cmd_vec; + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n workdir=/tmp/test", "--workdir='/tmp/test' ")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run", "")); + + run_docker_run_helper_function(file_cmd_vec, set_container_workdir); + } + + TEST_F(TestDockerUtil, test_set_cgroup_parent) { + std::vector > file_cmd_vec; + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n cgroup-parent=/sys/fs/cgroup/yarn", + "--cgroup-parent='/sys/fs/cgroup/yarn' ")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run", "")); + + run_docker_run_helper_function(file_cmd_vec, set_cgroup_parent); + } + + TEST_F(TestDockerUtil, test_set_hostname) { + std::vector > file_cmd_vec; + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n hostname=ctr-id", "--hostname='ctr-id' ")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run", "")); + + run_docker_run_helper_function(file_cmd_vec, set_hostname); + } + + TEST_F(TestDockerUtil, test_set_group_add) { + std::vector > file_cmd_vec; + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n group-add=1000,1001", "--group-add '1000' --group-add '1001' ")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run", "")); + + run_docker_run_helper_function(file_cmd_vec, set_group_add); + } + + TEST_F(TestDockerUtil, test_set_network) { + struct configuration container_cfg; + const int buff_len = 1024; + char buff[buff_len]; + int ret = 0; + std::string container_executor_cfg_contents = "[docker]\n docker.allowed.networks=sdn1,bridge"; + std::vector > file_cmd_vec; + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n net=bridge", "--net='bridge' ")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n net=sdn1", "--net='sdn1' ")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run", "")); + write_container_executor_cfg(container_executor_cfg_contents); + ret = read_config(container_executor_cfg_file.c_str(), &container_cfg); + + std::vector >::const_iterator itr; + if (ret != 0) { + FAIL(); + } + for (itr = file_cmd_vec.begin(); itr != file_cmd_vec.end(); ++itr) { + struct configuration cmd_cfg; + memset(buff, 0, buff_len); + write_command_file(itr->first); + ret = read_config(docker_command_file.c_str(), &cmd_cfg); + if (ret != 0) { + FAIL(); + } + ret = set_network(&cmd_cfg, &container_cfg, buff, buff_len); + ASSERT_EQ(0, ret); + ASSERT_STREQ(itr->second.c_str(), buff); + } + struct configuration cmd_cfg_1; + write_command_file("[docker-command-execution]\n docker-command=run\n net=sdn2"); + ret = read_config(docker_command_file.c_str(), &cmd_cfg_1); + if (ret != 0) { + FAIL(); + } + strcpy(buff, "test string"); + ret = set_network(&cmd_cfg_1, &container_cfg, buff, buff_len); + ASSERT_EQ(INVALID_DOCKER_NETWORK, ret); + ASSERT_EQ(0, strlen(buff)); + + container_executor_cfg_contents = "[docker]\n"; + write_container_executor_cfg(container_executor_cfg_contents); + ret = read_config(container_executor_cfg_file.c_str(), &container_cfg); + if (ret != 0) { + FAIL(); + } + strcpy(buff, "test string"); + ret = set_network(&cmd_cfg_1, &container_cfg, buff, buff_len); + ASSERT_EQ(INVALID_DOCKER_NETWORK, ret); + ASSERT_EQ(0, strlen(buff)); + } + + TEST_F(TestDockerUtil, test_check_mount_permitted) { + const char *permitted_mounts[] = {"/usr/", "/bin/ls", NULL}; + std::vector > test_data; + test_data.push_back(std::make_pair("/usr", 1)); + test_data.push_back(std::make_pair("/usr/", 1)); + test_data.push_back(std::make_pair("/bin/ls", 1)); + test_data.push_back(std::make_pair("//bin/", 0)); + test_data.push_back(std::make_pair("/tmp/random-file", -1)); + + std::vector >::const_iterator itr; + for (itr = test_data.begin(); itr != test_data.end(); ++itr) { + int ret = check_mount_permitted(permitted_mounts, itr->first.c_str()); + ASSERT_EQ(itr->second, ret) << "for input " << itr->first; + } + } + + TEST_F(TestDockerUtil, test_normalize_mounts) { + const int entries = 4; + const char *permitted_mounts[] = {"/home", "/usr", "/bin/ls", NULL}; + const char *expected[] = {"/home/", "/usr/", "/bin/ls", NULL}; + char **ptr = static_cast(malloc(entries * sizeof(char *))); + for (int i = 0; i < entries; ++i) { + if (permitted_mounts[i] != NULL) { + ptr[i] = strdup(permitted_mounts[i]); + } else { + ptr[i] = NULL; + } + } + normalize_mounts(ptr); + for (int i = 0; i < entries; ++i) { + ASSERT_STREQ(expected[i], ptr[i]); + } + } + + TEST_F(TestDockerUtil, test_set_privileged) { + struct configuration container_cfg, cmd_cfg; + const int buff_len = 1024; + char buff[buff_len]; + int ret = 0; + std::string container_executor_cfg_contents[] = {"[docker]\n docker.privileged-containers.enabled=1", + "[docker]\n docker.privileged-containers.enabled=0", + "[docker]\n"}; + std::vector > file_cmd_vec; + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n privileged=true", "--privileged ")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n privileged=false", "")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run", "")); + write_container_executor_cfg(container_executor_cfg_contents[0]); + ret = read_config(container_executor_cfg_file.c_str(), &container_cfg); + + std::vector >::const_iterator itr; + if (ret != 0) { + FAIL(); + } + for (itr = file_cmd_vec.begin(); itr != file_cmd_vec.end(); ++itr) { + memset(buff, 0, buff_len); + write_command_file(itr->first); + ret = read_config(docker_command_file.c_str(), &cmd_cfg); + if (ret != 0) { + FAIL(); + } + ret = set_privileged(&cmd_cfg, &container_cfg, buff, buff_len); + ASSERT_EQ(0, ret); + ASSERT_STREQ(itr->second.c_str(), buff); + } + + // check default case and when it's turned off + for (int i = 1; i < 3; ++i) { + write_container_executor_cfg(container_executor_cfg_contents[i]); + ret = read_config(container_executor_cfg_file.c_str(), &container_cfg); + if (ret != 0) { + FAIL(); + } + file_cmd_vec.clear(); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n privileged=false", "")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run", "")); + for (itr = file_cmd_vec.begin(); itr != file_cmd_vec.end(); ++itr) { + memset(buff, 0, buff_len); + write_command_file(itr->first); + ret = read_config(docker_command_file.c_str(), &cmd_cfg); + if (ret != 0) { + FAIL(); + } + ret = set_privileged(&cmd_cfg, &container_cfg, buff, buff_len); + ASSERT_EQ(0, ret); + ASSERT_STREQ(itr->second.c_str(), buff); + } + write_command_file("[docker-command-execution]\n docker-command=run\n privileged=true"); + ret = read_config(docker_command_file.c_str(), &cmd_cfg); + if (ret != 0) { + FAIL(); + } + ret = set_privileged(&cmd_cfg, &container_cfg, buff, buff_len); + ASSERT_EQ(PRIVILEGED_CONTAINERS_DISABLED, ret); + ASSERT_EQ(0, strlen(buff)); + } + } + + TEST_F(TestDockerUtil, test_set_capabilities) { + struct configuration container_cfg, cmd_cfg; + const int buff_len = 1024; + char buff[buff_len]; + int ret = 0; + std::string container_executor_cfg_contents = "[docker]\n docker.allowed.capabilities=CHROOT,MKNOD"; + std::vector > file_cmd_vec; + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n cap-add=CHROOT,MKNOD", + "--cap-drop='ALL' --cap-add='CHROOT' --cap-add='MKNOD' ")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n cap-add=CHROOT", "--cap-drop='ALL' --cap-add='CHROOT' ")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run", "--cap-drop='ALL' ")); + write_container_executor_cfg(container_executor_cfg_contents); + ret = read_config(container_executor_cfg_file.c_str(), &container_cfg); + + std::vector >::const_iterator itr; + if (ret != 0) { + FAIL(); + } + for (itr = file_cmd_vec.begin(); itr != file_cmd_vec.end(); ++itr) { + memset(buff, 0, buff_len); + write_command_file(itr->first); + ret = read_config(docker_command_file.c_str(), &cmd_cfg); + if (ret != 0) { + FAIL(); + } + ret = set_capabilities(&cmd_cfg, &container_cfg, buff, buff_len); + ASSERT_EQ(0, ret); + ASSERT_STREQ(itr->second.c_str(), buff); + } + write_command_file("[docker-command-execution]\n docker-command=run\n cap-add=SETGID"); + ret = read_config(docker_command_file.c_str(), &cmd_cfg); + if (ret != 0) { + FAIL(); + } + strcpy(buff, "test string"); + ret = set_capabilities(&cmd_cfg, &container_cfg, buff, buff_len); + ASSERT_EQ(INVALID_DOCKER_CAPABILITY, ret); + ASSERT_EQ(0, strlen(buff)); + + container_executor_cfg_contents = "[docker]\n"; + write_container_executor_cfg(container_executor_cfg_contents); + ret = read_config(container_executor_cfg_file.c_str(), &container_cfg); + if (ret != 0) { + FAIL(); + } + strcpy(buff, "test string"); + ret = set_capabilities(&cmd_cfg, &container_cfg, buff, buff_len); + ASSERT_EQ(INVALID_DOCKER_CAPABILITY, ret); + ASSERT_EQ(0, strlen(buff)); + } + + TEST_F(TestDockerUtil, test_set_devices) { + struct configuration container_cfg, cmd_cfg; + const int buff_len = 1024; + char buff[buff_len]; + int ret = 0; + std::string container_executor_cfg_contents = "[docker]\n docker.allowed.devices=/dev/test-device,/dev/device2"; + std::vector > file_cmd_vec; + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n devices=/dev/test-device:/dev/test-device", + "--device='/dev/test-device:/dev/test-device' ")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n devices=/dev/device2:/dev/device2", + "--device='/dev/device2:/dev/device2' ")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n " + "devices=/dev/test-device:/dev/test-device,/dev/device2:/dev/device2", + "--device='/dev/test-device:/dev/test-device' --device='/dev/device2:/dev/device2' ")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n", "")); + write_container_executor_cfg(container_executor_cfg_contents); + ret = read_config(container_executor_cfg_file.c_str(), &container_cfg); + + std::vector >::const_iterator itr; + if (ret != 0) { + FAIL(); + } + for (itr = file_cmd_vec.begin(); itr != file_cmd_vec.end(); ++itr) { + memset(buff, 0, buff_len); + write_command_file(itr->first); + ret = read_config(docker_command_file.c_str(), &cmd_cfg); + if (ret != 0) { + FAIL(); + } + ret = set_devices(&cmd_cfg, &container_cfg, buff, buff_len); + ASSERT_EQ(0, ret); + ASSERT_STREQ(itr->second.c_str(), buff); + } + write_command_file("[docker-command-execution]\n docker-command=run\n devices=/dev/device3:/dev/device3"); + ret = read_config(docker_command_file.c_str(), &cmd_cfg); + if (ret != 0) { + FAIL(); + } + strcpy(buff, "test string"); + ret = set_devices(&cmd_cfg, &container_cfg, buff, buff_len); + ASSERT_EQ(INVALID_DOCKER_DEVICE, ret); + ASSERT_EQ(0, strlen(buff)); + + write_command_file("[docker-command-execution]\n docker-command=run\n devices=/dev/device1"); + ret = read_config(docker_command_file.c_str(), &cmd_cfg); + if (ret != 0) { + FAIL(); + } + strcpy(buff, "test string"); + ret = set_devices(&cmd_cfg, &container_cfg, buff, buff_len); + ASSERT_EQ(INVALID_DOCKER_DEVICE, ret); + ASSERT_EQ(0, strlen(buff)); + + container_executor_cfg_contents = "[docker]\n"; + write_container_executor_cfg(container_executor_cfg_contents); + ret = read_config(container_executor_cfg_file.c_str(), &container_cfg); + if (ret != 0) { + FAIL(); + } + strcpy(buff, "test string"); + ret = set_devices(&cmd_cfg, &container_cfg, buff, buff_len); + ASSERT_EQ(INVALID_DOCKER_DEVICE, ret); + ASSERT_EQ(0, strlen(buff)); + } + + + TEST_F(TestDockerUtil, test_add_rw_mounts) { + struct configuration container_cfg, cmd_cfg; + const int buff_len = 1024; + char buff[buff_len]; + int ret = 0; + std::string container_executor_cfg_contents = "[docker]\n docker.allowed.rw-mounts=/usr,/var,/bin/ls,..\n " + "docker.allowed.ro-mounts=/bin/cat"; + std::vector > file_cmd_vec; + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n rw-mounts=/var:/var", "-v '/var:/var' ")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n rw-mounts=/var/:/var/", "-v '/var/:/var/' ")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n rw-mounts=/usr:/usr", "-v '/usr:/usr' ")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n rw-mounts=/usr/:/usr", "-v '/usr/:/usr' ")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n rw-mounts=/bin/ls:/bin/ls", "-v '/bin/ls:/bin/ls' ")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n rw-mounts=/usr/bin:/mydisk1,/var/log/:/mydisk2", + "-v '/usr/bin:/mydisk1' -v '/var/log/:/mydisk2' ")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n", "")); + write_container_executor_cfg(container_executor_cfg_contents); + ret = read_config(container_executor_cfg_file.c_str(), &container_cfg); + if (ret != 0) { + FAIL(); + } + + ret = create_ce_file(); + if (ret != 0) { + std::cerr << "Could not create ce file, skipping test" << std::endl; + return; + } + + std::vector >::const_iterator itr; + + for (itr = file_cmd_vec.begin(); itr != file_cmd_vec.end(); ++itr) { + memset(buff, 0, buff_len); + write_command_file(itr->first); + ret = read_config(docker_command_file.c_str(), &cmd_cfg); + if (ret != 0) { + FAIL(); + } + ret = add_rw_mounts(&cmd_cfg, &container_cfg, buff, buff_len); + ASSERT_EQ(0, ret); + ASSERT_STREQ(itr->second.c_str(), buff); + } + + std::vector > bad_file_cmds_vec; + bad_file_cmds_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n rw-mounts=/home:/home", + static_cast(INVALID_DOCKER_RW_MOUNT))); + bad_file_cmds_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n rw-mounts=/bin/cat:/bin/cat", + static_cast(INVALID_DOCKER_RW_MOUNT))); + bad_file_cmds_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n rw-mounts=/blah:/blah", + static_cast(INVALID_DOCKER_MOUNT))); + + std::vector >::const_iterator itr2; + + for (itr2 = bad_file_cmds_vec.begin(); itr2 != bad_file_cmds_vec.end(); ++itr2) { + memset(buff, 0, buff_len); + write_command_file(itr2->first); + ret = read_config(docker_command_file.c_str(), &cmd_cfg); + if (ret != 0) { + FAIL(); + } + strcpy(buff, "test string"); + ret = add_rw_mounts(&cmd_cfg, &container_cfg, buff, buff_len); + ASSERT_EQ(itr2->second, ret); + ASSERT_STREQ("", buff); + } + + // verify that you can't mount any directory in the container-executor.cfg path + char *ce_path = realpath("../etc/hadoop/container-executor.cfg", NULL); + while (strlen(ce_path) != 0) { + std::string cmd_file_contents = "[docker-command-execution]\n docker-command=run\n rw-mounts="; + cmd_file_contents.append(ce_path).append(":").append("/etc/hadoop"); + memset(buff, 0, buff_len); + write_command_file(cmd_file_contents); + ret = read_config(docker_command_file.c_str(), &cmd_cfg); + if (ret != 0) { + FAIL(); + } + strcpy(buff, "test string"); + ret = add_rw_mounts(&cmd_cfg, &container_cfg, buff, buff_len); + ASSERT_EQ(INVALID_DOCKER_RW_MOUNT, ret) << " for input " << cmd_file_contents; + ASSERT_STREQ("", buff); + char *tmp = strrchr(ce_path, '/'); + if (tmp != NULL) { + *tmp = '\0'; + } + } + free(ce_path); + + container_executor_cfg_contents = "[docker]\n"; + write_container_executor_cfg(container_executor_cfg_contents); + ret = read_config(container_executor_cfg_file.c_str(), &container_cfg); + if (ret != 0) { + FAIL(); + } + strcpy(buff, "test string"); + ret = add_rw_mounts(&cmd_cfg, &container_cfg, buff, buff_len); + ASSERT_EQ(INVALID_DOCKER_RW_MOUNT, ret); + ASSERT_EQ(0, strlen(buff)); + } + + TEST_F(TestDockerUtil, test_add_ro_mounts) { + struct configuration container_cfg, cmd_cfg; + const int buff_len = 1024; + char buff[buff_len]; + int ret = 0; + std::string container_executor_cfg_contents = "[docker]\n docker.allowed.rw-mounts=/usr,/var,/bin/ls\n " + "docker.allowed.ro-mounts=/bin/cat,/bin/ln"; + std::vector > file_cmd_vec; + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n ro-mounts=/var:/var", "-v '/var:/var:ro' ")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n ro-mounts=/var/:/var/", "-v '/var/:/var/:ro' ")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n ro-mounts=/usr:/usr", "-v '/usr:/usr:ro' ")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n ro-mounts=/usr/:/usr", "-v '/usr/:/usr:ro' ")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n ro-mounts=/bin/ls:/bin/ls", "-v '/bin/ls:/bin/ls:ro' ")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n ro-mounts=/bin/ln:/bin/ln", "-v '/bin/ln:/bin/ln:ro' ")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n ro-mounts=/bin/cat:/bin/cat", + "-v '/bin/cat:/bin/cat:ro' ")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n ro-mounts=/usr/bin:/mydisk1,/bin/cat:/bin/cat", + "-v '/usr/bin:/mydisk1:ro' -v '/bin/cat:/bin/cat:ro' ")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n", "")); + write_container_executor_cfg(container_executor_cfg_contents); + ret = read_config(container_executor_cfg_file.c_str(), &container_cfg); + if (ret != 0) { + FAIL(); + } + + ret = create_ce_file(); + if (ret != 0) { + std::cerr << "Could not create ce file, skipping test" << std::endl; + return; + } + + std::vector >::const_iterator itr; + + for (itr = file_cmd_vec.begin(); itr != file_cmd_vec.end(); ++itr) { + memset(buff, 0, buff_len); + write_command_file(itr->first); + ret = read_config(docker_command_file.c_str(), &cmd_cfg); + if (ret != 0) { + FAIL(); + } + ret = add_ro_mounts(&cmd_cfg, &container_cfg, buff, buff_len); + ASSERT_EQ(0, ret); + ASSERT_STREQ(itr->second.c_str(), buff); + } + + std::vector > bad_file_cmds_vec; + bad_file_cmds_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n ro-mounts=/home:/home", + static_cast(INVALID_DOCKER_RO_MOUNT))); + bad_file_cmds_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n ro-mounts=/blah:/blah", + static_cast(INVALID_DOCKER_MOUNT))); + + std::vector >::const_iterator itr2; + + for (itr2 = bad_file_cmds_vec.begin(); itr2 != bad_file_cmds_vec.end(); ++itr2) { + memset(buff, 0, buff_len); + write_command_file(itr2->first); + ret = read_config(docker_command_file.c_str(), &cmd_cfg); + if (ret != 0) { + FAIL(); + } + strcpy(buff, "test string"); + ret = add_ro_mounts(&cmd_cfg, &container_cfg, buff, buff_len); + ASSERT_EQ(itr2->second, ret); + ASSERT_STREQ("", buff); + } + + container_executor_cfg_contents = "[docker]\n"; + write_container_executor_cfg(container_executor_cfg_contents); + ret = read_config(container_executor_cfg_file.c_str(), &container_cfg); + if (ret != 0) { + FAIL(); + } + write_command_file("[docker-command-execution]\n docker-command=run\n ro-mounts=/home:/home"); + strcpy(buff, "test string"); + ret = add_ro_mounts(&cmd_cfg, &container_cfg, buff, buff_len); + ASSERT_EQ(INVALID_DOCKER_RO_MOUNT, ret); + ASSERT_EQ(0, strlen(buff)); + } + + TEST_F(TestDockerUtil, test_docker_run_privileged) { + + std::string container_executor_contents = "[docker]\n docker.allowed.ro-mounts=/var,/etc,/bin/ls\n" + " docker.allowed.rw-mounts=/tmp\n docker.allowed.networks=bridge\n " + " docker.privileged-containers.enabled=1\n docker.allowed.capabilities=CHOWN,SETUID\n" + " docker.allowed.devices=/dev/test"; + write_file(container_executor_cfg_file, container_executor_contents); + int ret = read_config(container_executor_cfg_file.c_str(), &container_executor_cfg); + if (ret != 0) { + FAIL(); + } + ret = create_ce_file(); + if (ret != 0) { + std::cerr << "Could not create ce file, skipping test" << std::endl; + return; + } + + std::vector > file_cmd_vec; + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n name=container_e1_12312_11111_02_000001\n image=docker-image\n user=test", + "run --name='container_e1_12312_11111_02_000001' --user='test' --cap-drop='ALL' 'docker-image' ")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n name=container_e1_12312_11111_02_000001\n image=docker-image\n user=test\n" + " launch-command=bash,test_script.sh,arg1,arg2", + "run --name='container_e1_12312_11111_02_000001' --user='test' --cap-drop='ALL' 'docker-image' 'bash' 'test_script.sh' 'arg1' 'arg2' ")); + + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n" + " docker-command=run\n name=container_e1_12312_11111_02_000001\n image=docker-image\n user=test\n hostname=host-id\n" + " ro-mounts=/var/log:/var/log,/var/lib:/lib,/bin/ls:/bin/ls\n rw-mounts=/tmp:/tmp\n" + " network=bridge\n devices=/dev/test:/dev/test\n" + " cap-add=CHOWN,SETUID\n cgroup-parent=ctr-cgroup\n detach=true\n rm=true\n" + " launch-command=bash,test_script.sh,arg1,arg2", + "run --name='container_e1_12312_11111_02_000001' --user='test' -d --rm -v '/var/log:/var/log:ro' -v '/var/lib:/lib:ro'" + " -v '/bin/ls:/bin/ls:ro' -v '/tmp:/tmp' --cgroup-parent='ctr-cgroup' --cap-drop='ALL' --cap-add='CHOWN'" + " --cap-add='SETUID' --hostname='host-id' --device='/dev/test:/dev/test' 'docker-image' 'bash' " + "'test_script.sh' 'arg1' 'arg2' ")); + + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n" + " docker-command=run\n name=container_e1_12312_11111_02_000001\n image=docker-image\n user=test\n hostname=host-id\n" + " ro-mounts=/var/log:/var/log,/var/lib:/lib,/bin/ls:/bin/ls\n rw-mounts=/tmp:/tmp\n" + " network=bridge\n devices=/dev/test:/dev/test\n net=bridge\n" + " cap-add=CHOWN,SETUID\n cgroup-parent=ctr-cgroup\n detach=true\n rm=true\n" + " launch-command=bash,test_script.sh,arg1,arg2", + "run --name='container_e1_12312_11111_02_000001' --user='test' -d --rm --net='bridge' -v '/var/log:/var/log:ro' -v '/var/lib:/lib:ro'" + " -v '/bin/ls:/bin/ls:ro' -v '/tmp:/tmp' --cgroup-parent='ctr-cgroup' --cap-drop='ALL' --cap-add='CHOWN' " + "--cap-add='SETUID' --hostname='host-id' --device='/dev/test:/dev/test' 'docker-image' 'bash'" + " 'test_script.sh' 'arg1' 'arg2' ")); + + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n" + " docker-command=run\n name=container_e1_12312_11111_02_000001\n image=docker-image\n user=test\n hostname=host-id\n" + " ro-mounts=/var/log:/var/log,/var/lib:/lib,/bin/ls:/bin/ls\n rw-mounts=/tmp:/tmp\n" + " network=bridge\n devices=/dev/test:/dev/test\n net=bridge\n privileged=true\n" + " cap-add=CHOWN,SETUID\n cgroup-parent=ctr-cgroup\n detach=true\n rm=true\n" + " launch-command=bash,test_script.sh,arg1,arg2", + "run --name='container_e1_12312_11111_02_000001' --user='test' -d --rm --net='bridge' -v '/var/log:/var/log:ro' -v '/var/lib:/lib:ro'" + " -v '/bin/ls:/bin/ls:ro' -v '/tmp:/tmp' --cgroup-parent='ctr-cgroup' --privileged --cap-drop='ALL' " + "--cap-add='CHOWN' --cap-add='SETUID' --hostname='host-id' --device='/dev/test:/dev/test' 'docker-image' " + "'bash' 'test_script.sh' 'arg1' 'arg2' ")); + + + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n" + " docker-command=run\n name=container_e1_12312_11111_02_000001\n image=docker-image\n user=test\n hostname=host-id\n" + " ro-mounts=/var/log:/var/log,/var/lib:/lib,/bin/ls:/bin/ls\n rw-mounts=/tmp:/tmp\n" + " network=bridge\n devices=/dev/test:/dev/test\n net=bridge\n privileged=true\n" + " cap-add=CHOWN,SETUID\n cgroup-parent=ctr-cgroup\n detach=true\n rm=true\n group-add=1000,1001\n" + " launch-command=bash,test_script.sh,arg1,arg2", + "run --name='container_e1_12312_11111_02_000001' --user='test' -d --rm --net='bridge' -v '/var/log:/var/log:ro' -v '/var/lib:/lib:ro'" + " -v '/bin/ls:/bin/ls:ro' -v '/tmp:/tmp' --cgroup-parent='ctr-cgroup' --privileged --cap-drop='ALL' " + "--cap-add='CHOWN' --cap-add='SETUID' --hostname='host-id' --group-add '1000' --group-add '1001' " + "--device='/dev/test:/dev/test' 'docker-image' 'bash' 'test_script.sh' 'arg1' 'arg2' ")); + + + std::vector > bad_file_cmd_vec; + + bad_file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n image=docker-image\n user=test", + static_cast(INVALID_DOCKER_CONTAINER_NAME))); + bad_file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n name=container_e1_12312_11111_02_000001\n user=test\n", + static_cast(INVALID_DOCKER_IMAGE_NAME))); + bad_file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n name=container_e1_12312_11111_02_000001\n image=docker-image\n", + static_cast(INVALID_DOCKER_USER_NAME))); + + // invalid rw mount + bad_file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n" + " docker-command=run\n name=container_e1_12312_11111_02_000001\n image=docker-image\n user=test\n hostname=host-id\n" + " ro-mounts=/var/lib:/lib,/bin/ls:/bin/ls\n rw-mounts=/var/log:/var/log\n" + " network=bridge\n devices=/dev/test:/dev/test\n" + " cap-add=CHOWN,SETUID\n cgroup-parent=ctr-cgroup\n detach=true\n rm=true\n" + " launch-command=bash,test_script.sh,arg1,arg2", + static_cast(INVALID_DOCKER_RW_MOUNT))); + + // invalid ro mount + bad_file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n" + " docker-command=run\n name=container_e1_12312_11111_02_000001\n image=docker-image\n user=test\n hostname=host-id\n" + " ro-mounts=/bin:/bin,/bin/ls:/bin/ls\n rw-mounts=/tmp:/tmp\n" + " network=bridge\n devices=/dev/test:/dev/test\n" + " cap-add=CHOWN,SETUID\n cgroup-parent=ctr-cgroup\n detach=true\n rm=true\n" + " launch-command=bash,test_script.sh,arg1,arg2", + static_cast(INVALID_DOCKER_RO_MOUNT))); + + // invalid capability + bad_file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n" + " docker-command=run\n name=container_e1_12312_11111_02_000001\n image=docker-image\n user=test\n hostname=host-id\n" + " ro-mounts=/bin/ls:/bin/ls\n rw-mounts=/tmp:/tmp\n" + " network=bridge\n devices=/dev/test:/dev/test\n" + " cap-add=CHOWN,SETUID,SETGID\n cgroup-parent=ctr-cgroup\n detach=true\n rm=true\n" + " launch-command=bash,test_script.sh,arg1,arg2", + static_cast(INVALID_DOCKER_CAPABILITY))); + + // invalid device + bad_file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n" + " docker-command=run\n name=container_e1_12312_11111_02_000001\n image=docker-image\n user=test\n hostname=host-id\n" + " ro-mounts=/var/log:/var/log,/var/lib:/lib,/bin/ls:/bin/ls\n rw-mounts=/tmp:/tmp\n" + " network=bridge\n devices=/dev/dev1:/dev/dev1\n privileged=true\n" + " cap-add=CHOWN,SETUID\n cgroup-parent=ctr-cgroup\n detach=true\n rm=true\n" + " launch-command=bash,test_script.sh,arg1,arg2", + static_cast(INVALID_DOCKER_DEVICE))); + + // invalid network + bad_file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n" + " docker-command=run\n name=container_e1_12312_11111_02_000001\n image=docker-image\n user=test\n hostname=host-id\n" + " ro-mounts=/var/log:/var/log,/var/lib:/lib,/bin/ls:/bin/ls\n rw-mounts=/tmp:/tmp\n" + " network=bridge\n devices=/dev/test:/dev/test\n privileged=true\n net=host\n" + " cap-add=CHOWN,SETUID\n cgroup-parent=ctr-cgroup\n detach=true\n rm=true\n" + " launch-command=bash,test_script.sh,arg1,arg2", + static_cast(INVALID_DOCKER_NETWORK))); + + run_docker_command_test(file_cmd_vec, bad_file_cmd_vec, get_docker_run_command); + } + + TEST_F(TestDockerUtil, test_docker_run_no_privileged) { + + std::string container_executor_contents[] = {"[docker]\n docker.allowed.ro-mounts=/var,/etc,/bin/ls\n" + " docker.allowed.rw-mounts=/tmp\n docker.allowed.networks=bridge\n " + " docker.allowed.capabilities=CHOWN,SETUID\n" + " docker.allowed.devices=/dev/test", + "[docker]\n docker.allowed.ro-mounts=/var,/etc,/bin/ls\n" + " docker.allowed.rw-mounts=/tmp\n docker.allowed.networks=bridge\n " + " docker.allowed.capabilities=CHOWN,SETUID\n" + " privileged=0\n" + " docker.allowed.devices=/dev/test"}; + for (int i = 0; i < 2; ++i) { + write_file(container_executor_cfg_file, container_executor_contents[i]); + int ret = read_config(container_executor_cfg_file.c_str(), &container_executor_cfg); + if (ret != 0) { + FAIL(); + } + ret = create_ce_file(); + if (ret != 0) { + std::cerr << "Could not create ce file, skipping test" << std::endl; + return; + } + + std::vector > file_cmd_vec; + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n name=container_e1_12312_11111_02_000001\n image=docker-image\n user=test", + "run --name='container_e1_12312_11111_02_000001' --user='test' --cap-drop='ALL' 'docker-image' ")); + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n name=container_e1_12312_11111_02_000001\n image=docker-image\n" + " user=test\n launch-command=bash,test_script.sh,arg1,arg2", + "run --name='container_e1_12312_11111_02_000001' --user='test' --cap-drop='ALL' 'docker-image' 'bash' 'test_script.sh' 'arg1' 'arg2' ")); + + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n" + " docker-command=run\n name=container_e1_12312_11111_02_000001\n image=docker-image\n user=test\n hostname=host-id\n" + " ro-mounts=/var/log:/var/log,/var/lib:/lib,/bin/ls:/bin/ls\n rw-mounts=/tmp:/tmp\n" + " network=bridge\n devices=/dev/test:/dev/test\n" + " cap-add=CHOWN,SETUID\n cgroup-parent=ctr-cgroup\n detach=true\n rm=true\n" + " launch-command=bash,test_script.sh,arg1,arg2", + "run --name='container_e1_12312_11111_02_000001' --user='test' -d --rm -v '/var/log:/var/log:ro' -v '/var/lib:/lib:ro'" + " -v '/bin/ls:/bin/ls:ro' -v '/tmp:/tmp' --cgroup-parent='ctr-cgroup' --cap-drop='ALL' --cap-add='CHOWN'" + " --cap-add='SETUID' --hostname='host-id' --device='/dev/test:/dev/test' 'docker-image' 'bash' " + "'test_script.sh' 'arg1' 'arg2' ")); + + file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n" + " docker-command=run\n name=container_e1_12312_11111_02_000001\n image=docker-image\n user=test\n hostname=host-id\n" + " ro-mounts=/var/log:/var/log,/var/lib:/lib,/bin/ls:/bin/ls\n rw-mounts=/tmp:/tmp\n" + " network=bridge\n devices=/dev/test:/dev/test\n net=bridge\n" + " cap-add=CHOWN,SETUID\n cgroup-parent=ctr-cgroup\n detach=true\n rm=true\n" + " launch-command=bash,test_script.sh,arg1,arg2", + "run --name='container_e1_12312_11111_02_000001' --user='test' -d --rm --net='bridge' -v '/var/log:/var/log:ro' -v '/var/lib:/lib:ro'" + " -v '/bin/ls:/bin/ls:ro' -v '/tmp:/tmp' --cgroup-parent='ctr-cgroup' --cap-drop='ALL' --cap-add='CHOWN' " + "--cap-add='SETUID' --hostname='host-id' --device='/dev/test:/dev/test' 'docker-image' 'bash'" + " 'test_script.sh' 'arg1' 'arg2' ")); + + std::vector > bad_file_cmd_vec; + bad_file_cmd_vec.push_back(std::make_pair( + "[docker-command-execution]\n" + " docker-command=run\n name=container_e1_12312_11111_02_000001\n image=docker-image\n user=test\n hostname=host-id\n" + " ro-mounts=/var/log:/var/log,/var/lib:/lib,/bin/ls:/bin/ls\n rw-mounts=/tmp:/tmp\n" + " network=bridge\n devices=/dev/test:/dev/test\n net=bridge\n privileged=true\n" + " cap-add=CHOWN,SETUID\n cgroup-parent=ctr-cgroup\n detach=true\n rm=true\n" + " launch-command=bash,test_script.sh,arg1,arg2", + static_cast(PRIVILEGED_CONTAINERS_DISABLED))); + + run_docker_command_test(file_cmd_vec, bad_file_cmd_vec, get_docker_run_command); + } + } + + TEST_F(TestDockerUtil, test_docker_config_param) { + std::vector > input_output_map; + input_output_map.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=inspect\n docker-config=/my-config\n" + " format={{.State.Status}}\n name=container_e1_12312_11111_02_000001", + "--config='/my-config' inspect --format={{.State.Status}} container_e1_12312_11111_02_000001")); + input_output_map.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=load\n docker-config=/my-config\n image=image-id", + "--config='/my-config' load --i='image-id' ")); + input_output_map.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=pull\n docker-config=/my-config\n image=image-id", + "--config='/my-config' pull 'image-id' ")); + input_output_map.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=rm\n docker-config=/my-config\n name=container_e1_12312_11111_02_000001", + "--config='/my-config' rm container_e1_12312_11111_02_000001")); + input_output_map.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=stop\n docker-config=/my-config\n name=container_e1_12312_11111_02_000001", + "--config='/my-config' stop container_e1_12312_11111_02_000001")); + input_output_map.push_back(std::make_pair( + "[docker-command-execution]\n docker-command=run\n docker-config=/my-config\n name=container_e1_12312_11111_02_000001\n" + " image=docker-image\n user=test", + "--config='/my-config' run --name='container_e1_12312_11111_02_000001' --user='test' --cap-drop='ALL' 'docker-image' ")); + + std::vector >::const_iterator itr; + char buffer[4096]; + struct configuration cfg = {0, NULL}; + for (itr = input_output_map.begin(); itr != input_output_map.end(); ++itr) { + memset(buffer, 0, 4096); + write_command_file(itr->first); + int ret = get_docker_command(docker_command_file.c_str(), &cfg, buffer, 4096); + ASSERT_EQ(0, ret) << "for input " << itr->first; + ASSERT_STREQ(itr->second.c_str(), buffer); + } + } + + TEST_F(TestDockerUtil, test_docker_module_enabled) { + + std::vector > input_out_vec; + input_out_vec.push_back(std::make_pair("[docker]\n module.enabled=true", 1)); + input_out_vec.push_back(std::make_pair("[docker]\n module.enabled=false", 0)); + input_out_vec.push_back(std::make_pair("[docker]\n module.enabled=1", 0)); + input_out_vec.push_back(std::make_pair("[docker]\n", 0)); + + for (size_t i = 0; i < input_out_vec.size(); ++i) { + write_file(container_executor_cfg_file, input_out_vec[i].first); + int ret = read_config(container_executor_cfg_file.c_str(), &container_executor_cfg); + if (ret != 0) { + FAIL(); + } + ret = docker_module_enabled(&container_executor_cfg); + ASSERT_EQ(input_out_vec[i].second, ret) << " incorrect output for " + << input_out_vec[i].first; + } + } + +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/TestDockerContainerRuntime.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/TestDockerContainerRuntime.java index e5487906423..fbfee545f5d 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/TestDockerContainerRuntime.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/TestDockerContainerRuntime.java @@ -295,32 +295,37 @@ public class TestDockerContainerRuntime { List args = op.getArguments(); String dockerCommandFile = args.get(11); - //This is the expected docker invocation for this case - StringBuffer expectedCommandTemplate = new StringBuffer("run --name=%1$s ") - .append("--user=%2$s -d ") - .append("--workdir=%3$s ") - .append("--net=host ") - .append("--hostname=" + defaultHostname + " ") - .append(getExpectedTestCapabilitiesArgumentString()) - .append(getExpectedCGroupsMountString()) - .append("-v %4$s:%4$s ") - .append("-v %5$s:%5$s ") - .append("-v %6$s:%6$s ") - .append("-v %7$s:%7$s ") - .append("-v %8$s:%8$s ").append("%9$s ") - .append("bash %10$s/launch_container.sh"); - - String expectedCommand = String - .format(expectedCommandTemplate.toString(), containerId, runAsUser, - containerWorkDir, containerLocalDirs.get(0), filecacheDirs.get(0), - containerWorkDir, containerLogDirs.get(0), userLocalDirs.get(0), - image, containerWorkDir); - List dockerCommands = Files.readAllLines(Paths.get (dockerCommandFile), Charset.forName("UTF-8")); - Assert.assertEquals(1, dockerCommands.size()); - Assert.assertEquals(expectedCommand, dockerCommands.get(0)); + int expected = 13; + int counter = 0; + Assert.assertEquals(expected, dockerCommands.size()); + Assert.assertEquals("[docker-command-execution]", + dockerCommands.get(counter++)); + Assert.assertEquals(" cap-add=SYS_CHROOT,NET_BIND_SERVICE", + dockerCommands.get(counter++)); + Assert.assertEquals(" cap-drop=ALL", dockerCommands.get(counter++)); + Assert.assertEquals(" detach=true", dockerCommands.get(counter++)); + Assert.assertEquals(" docker-command=run", dockerCommands.get(counter++)); + Assert.assertEquals(" hostname=ctr-id", dockerCommands.get(counter++)); + Assert + .assertEquals(" image=busybox:latest", dockerCommands.get(counter++)); + Assert.assertEquals( + " launch-command=bash,/test_container_work_dir/launch_container.sh", + dockerCommands.get(counter++)); + Assert.assertEquals(" name=container_id", dockerCommands.get(counter++)); + Assert.assertEquals(" net=host", dockerCommands.get(counter++)); + Assert.assertEquals( + " rw-mounts=/test_container_local_dir:/test_container_local_dir," + + "/test_filecache_dir:/test_filecache_dir," + + "/test_container_work_dir:/test_container_work_dir," + + "/test_container_log_dir:/test_container_log_dir," + + "/test_user_local_dir:/test_user_local_dir", + dockerCommands.get(counter++)); + Assert.assertEquals(" user=run_as_user", dockerCommands.get(counter++)); + Assert.assertEquals(" workdir=/test_container_work_dir", + dockerCommands.get(counter++)); } @Test @@ -347,10 +352,13 @@ public class TestDockerContainerRuntime { String uid = ""; String gid = ""; + String[] groups = {}; Shell.ShellCommandExecutor shexec1 = new Shell.ShellCommandExecutor( new String[]{"id", "-u", runAsUser}); Shell.ShellCommandExecutor shexec2 = new Shell.ShellCommandExecutor( new String[]{"id", "-g", runAsUser}); + Shell.ShellCommandExecutor shexec3 = new Shell.ShellCommandExecutor( + new String[]{"id", "-G", runAsUser}); try { shexec1.execute(); // get rid of newline at the end @@ -365,37 +373,48 @@ public class TestDockerContainerRuntime { } catch (Exception e) { LOG.info("Could not run id -g command: " + e); } + try { + shexec3.execute(); + groups = shexec3.getOutput().replace("\n", " ").split(" "); + } catch (Exception e) { + LOG.info("Could not run id -G command: " + e); + } uidGidPair = uid + ":" + gid; - //This is the expected docker invocation for this case - StringBuffer expectedCommandTemplate = new StringBuffer("run --name=%1$s ") - .append("--user=%2$s -d ") - .append("--workdir=%3$s ") - .append("--net=host ") - .append("--hostname=" + defaultHostname + " ") - .append(getExpectedTestCapabilitiesArgumentString()) - .append(getExpectedCGroupsMountString()) - .append("-v %4$s:%4$s ") - .append("-v %5$s:%5$s ") - .append("-v %6$s:%6$s ") - .append("-v %7$s:%7$s ") - .append("-v %8$s:%8$s ") - .append("(--group-add \\d+ )*") - .append("%9$s ") - .append("bash %10$s/launch_container.sh"); - - String expectedCommand = String - .format(expectedCommandTemplate.toString(), containerId, uidGidPair, - containerWorkDir, containerLocalDirs.get(0), filecacheDirs.get(0), - containerWorkDir, containerLogDirs.get(0), userLocalDirs.get(0), - image, containerWorkDir); - List dockerCommands = Files.readAllLines( Paths.get(dockerCommandFile), Charset.forName("UTF-8")); - Assert.assertEquals(1, dockerCommands.size()); - //Assert.assertEquals(expectedCommand, dockerCommands.get(0)); - Assert.assertTrue(dockerCommands.get(0).matches(expectedCommand)); + Assert.assertEquals(14, dockerCommands.size()); + int counter = 0; + Assert.assertEquals("[docker-command-execution]", + dockerCommands.get(counter++)); + Assert.assertEquals(" cap-add=SYS_CHROOT,NET_BIND_SERVICE", + dockerCommands.get(counter++)); + Assert.assertEquals(" cap-drop=ALL", dockerCommands.get(counter++)); + Assert.assertEquals(" detach=true", dockerCommands.get(counter++)); + Assert.assertEquals(" docker-command=run", dockerCommands.get(counter++)); + Assert.assertEquals(" group-add=" + String.join(",", groups), + dockerCommands.get(counter++)); + Assert.assertEquals(" hostname=ctr-id", + dockerCommands.get(counter++)); + Assert + .assertEquals(" image=busybox:latest", dockerCommands.get(counter++)); + Assert.assertEquals( + " launch-command=bash,/test_container_work_dir/launch_container.sh", + dockerCommands.get(counter++)); + Assert.assertEquals(" name=container_id", dockerCommands.get(counter++)); + Assert + .assertEquals(" net=host", dockerCommands.get(counter++)); + Assert.assertEquals( + " rw-mounts=/test_container_local_dir:/test_container_local_dir," + + "/test_filecache_dir:/test_filecache_dir," + + "/test_container_work_dir:/test_container_work_dir," + + "/test_container_log_dir:/test_container_log_dir," + + "/test_user_local_dir:/test_user_local_dir", + dockerCommands.get(counter++)); + Assert.assertEquals(" user=" + uidGidPair, dockerCommands.get(counter++)); + Assert.assertEquals(" workdir=/test_container_work_dir", + dockerCommands.get(counter++)); } @Test @@ -481,29 +500,38 @@ public class TestDockerContainerRuntime { String dockerCommandFile = args.get(11); //This is the expected docker invocation for this case - StringBuffer expectedCommandTemplate = - new StringBuffer("run --name=%1$s ").append("--user=%2$s -d ") - .append("--workdir=%3$s ") - .append("--net=" + allowedNetwork + " ") - .append("--hostname=" + expectedHostname + " ") - .append(getExpectedTestCapabilitiesArgumentString()) - .append(getExpectedCGroupsMountString()) - .append("-v %4$s:%4$s ").append("-v %5$s:%5$s ") - .append("-v %6$s:%6$s ").append("-v %7$s:%7$s ") - .append("-v %8$s:%8$s ").append("%9$s ") - .append("bash %10$s/launch_container.sh"); - - String expectedCommand = String - .format(expectedCommandTemplate.toString(), containerId, runAsUser, - containerWorkDir, containerLocalDirs.get(0), filecacheDirs.get(0), - containerWorkDir, containerLogDirs.get(0), userLocalDirs.get(0), - image, containerWorkDir); - List dockerCommands = Files .readAllLines(Paths.get(dockerCommandFile), Charset.forName("UTF-8")); - - Assert.assertEquals(1, dockerCommands.size()); - Assert.assertEquals(expectedCommand, dockerCommands.get(0)); + int expected = 13; + int counter = 0; + Assert.assertEquals(expected, dockerCommands.size()); + Assert.assertEquals("[docker-command-execution]", + dockerCommands.get(counter++)); + Assert.assertEquals(" cap-add=SYS_CHROOT,NET_BIND_SERVICE", + dockerCommands.get(counter++)); + Assert.assertEquals(" cap-drop=ALL", dockerCommands.get(counter++)); + Assert.assertEquals(" detach=true", dockerCommands.get(counter++)); + Assert.assertEquals(" docker-command=run", dockerCommands.get(counter++)); + Assert.assertEquals(" hostname=test.hostname", + dockerCommands.get(counter++)); + Assert + .assertEquals(" image=busybox:latest", dockerCommands.get(counter++)); + Assert.assertEquals( + " launch-command=bash,/test_container_work_dir/launch_container.sh", + dockerCommands.get(counter++)); + Assert.assertEquals(" name=container_id", dockerCommands.get(counter++)); + Assert + .assertEquals(" net=" + allowedNetwork, dockerCommands.get(counter++)); + Assert.assertEquals( + " rw-mounts=/test_container_local_dir:/test_container_local_dir," + + "/test_filecache_dir:/test_filecache_dir," + + "/test_container_work_dir:/test_container_work_dir," + + "/test_container_log_dir:/test_container_log_dir," + + "/test_user_local_dir:/test_user_local_dir", + dockerCommands.get(counter++)); + Assert.assertEquals(" user=run_as_user", dockerCommands.get(counter++)); + Assert.assertEquals(" workdir=/test_container_work_dir", + dockerCommands.get(counter++)); } @Test @@ -537,30 +565,37 @@ public class TestDockerContainerRuntime { //This is the expected docker invocation for this case. customNetwork1 // ("sdn1") is the expected network to be used in this case - StringBuffer expectedCommandTemplate = - new StringBuffer("run --name=%1$s ").append("--user=%2$s -d ") - .append("--workdir=%3$s ") - .append("--net=" + customNetwork1 + " ") - .append("--hostname=" + defaultHostname + " ") - .append(getExpectedTestCapabilitiesArgumentString()) - .append(getExpectedCGroupsMountString()) - .append("-v %4$s:%4$s ").append("-v %5$s:%5$s ") - .append("-v %6$s:%6$s ").append("-v %7$s:%7$s ") - .append("-v %8$s:%8$s ").append("%9$s ") - .append("bash %10$s/launch_container.sh"); - - String expectedCommand = String - .format(expectedCommandTemplate.toString(), containerId, runAsUser, - containerWorkDir, containerLocalDirs.get(0), filecacheDirs.get(0), - containerWorkDir, containerLogDirs.get(0), userLocalDirs.get(0), - image, containerWorkDir); - List dockerCommands = Files .readAllLines(Paths.get(dockerCommandFile), Charset.forName("UTF-8")); - Assert.assertEquals(1, dockerCommands.size()); - Assert.assertEquals(expectedCommand, dockerCommands.get(0)); - + int expected = 13; + int counter = 0; + Assert.assertEquals(expected, dockerCommands.size()); + Assert.assertEquals("[docker-command-execution]", + dockerCommands.get(counter++)); + Assert.assertEquals(" cap-add=SYS_CHROOT,NET_BIND_SERVICE", + dockerCommands.get(counter++)); + Assert.assertEquals(" cap-drop=ALL", dockerCommands.get(counter++)); + Assert.assertEquals(" detach=true", dockerCommands.get(counter++)); + Assert.assertEquals(" docker-command=run", dockerCommands.get(counter++)); + Assert.assertEquals(" hostname=ctr-id", dockerCommands.get(counter++)); + Assert + .assertEquals(" image=busybox:latest", dockerCommands.get(counter++)); + Assert.assertEquals( + " launch-command=bash,/test_container_work_dir/launch_container.sh", + dockerCommands.get(counter++)); + Assert.assertEquals(" name=container_id", dockerCommands.get(counter++)); + Assert.assertEquals(" net=sdn1", dockerCommands.get(counter++)); + Assert.assertEquals( + " rw-mounts=/test_container_local_dir:/test_container_local_dir," + + "/test_filecache_dir:/test_filecache_dir," + + "/test_container_work_dir:/test_container_work_dir," + + "/test_container_log_dir:/test_container_log_dir," + + "/test_user_local_dir:/test_user_local_dir", + dockerCommands.get(counter++)); + Assert.assertEquals(" user=run_as_user", dockerCommands.get(counter++)); + Assert.assertEquals(" workdir=/test_container_work_dir", + dockerCommands.get(counter++)); //now set an explicit (non-default) allowedNetwork and ensure that it is // used. @@ -575,28 +610,37 @@ public class TestDockerContainerRuntime { //This is the expected docker invocation for this case. customNetwork2 // ("sdn2") is the expected network to be used in this case - expectedCommandTemplate = - new StringBuffer("run --name=%1$s ").append("--user=%2$s -d ") - .append("--workdir=%3$s ") - .append("--net=" + customNetwork2 + " ") - .append("--hostname=" + defaultHostname + " ") - .append(getExpectedTestCapabilitiesArgumentString()) - .append(getExpectedCGroupsMountString()) - .append("-v %4$s:%4$s ").append("-v %5$s:%5$s ") - .append("-v %6$s:%6$s ").append("-v %7$s:%7$s ") - .append("-v %8$s:%8$s ").append("%9$s ") - .append("bash %10$s/launch_container.sh"); - - expectedCommand = String - .format(expectedCommandTemplate.toString(), containerId, runAsUser, - containerWorkDir, containerLocalDirs.get(0), filecacheDirs.get(0), - containerWorkDir, containerLogDirs.get(0), userLocalDirs.get(0), - image, containerWorkDir); dockerCommands = Files .readAllLines(Paths.get(dockerCommandFile), Charset.forName("UTF-8")); + counter = 0; + Assert.assertEquals(expected, dockerCommands.size()); + Assert.assertEquals("[docker-command-execution]", + dockerCommands.get(counter++)); + Assert.assertEquals(" cap-add=SYS_CHROOT,NET_BIND_SERVICE", + dockerCommands.get(counter++)); + Assert.assertEquals(" cap-drop=ALL", dockerCommands.get(counter++)); + Assert.assertEquals(" detach=true", dockerCommands.get(counter++)); + Assert.assertEquals(" docker-command=run", dockerCommands.get(counter++)); + Assert.assertEquals(" hostname=ctr-id", dockerCommands.get(counter++)); + Assert + .assertEquals(" image=busybox:latest", dockerCommands.get(counter++)); + Assert.assertEquals( + " launch-command=bash,/test_container_work_dir/launch_container.sh", + dockerCommands.get(counter++)); + + Assert.assertEquals(" name=container_id", dockerCommands.get(counter++)); + Assert.assertEquals(" net=sdn2", dockerCommands.get(counter++)); + Assert.assertEquals( + " rw-mounts=/test_container_local_dir:/test_container_local_dir," + + "/test_filecache_dir:/test_filecache_dir," + + "/test_container_work_dir:/test_container_work_dir," + + "/test_container_log_dir:/test_container_log_dir," + + "/test_user_local_dir:/test_user_local_dir", + dockerCommands.get(counter++)); + Assert.assertEquals(" user=run_as_user", dockerCommands.get(counter++)); + Assert.assertEquals(" workdir=/test_container_work_dir", + dockerCommands.get(counter++)); - Assert.assertEquals(1, dockerCommands.size()); - Assert.assertEquals(expectedCommand, dockerCommands.get(0)); //disallowed network should trigger a launch failure @@ -630,7 +674,8 @@ public class TestDockerContainerRuntime { List dockerCommands = Files.readAllLines(Paths.get (dockerCommandFile), Charset.forName("UTF-8")); - Assert.assertEquals(1, dockerCommands.size()); + int expected = 13; + Assert.assertEquals(expected, dockerCommands.size()); String command = dockerCommands.get(0); @@ -738,13 +783,35 @@ public class TestDockerContainerRuntime { List dockerCommands = Files.readAllLines(Paths.get (dockerCommandFile), Charset.forName("UTF-8")); - Assert.assertEquals(1, dockerCommands.size()); - - String command = dockerCommands.get(0); - - //submitting user is whitelisted. ensure --privileged is in the invocation - Assert.assertTrue("Did not find expected '--privileged' in docker run args " - + ": " + command, command.contains("--privileged")); + int expected = 14; + int counter = 0; + Assert.assertEquals(expected, dockerCommands.size()); + Assert.assertEquals("[docker-command-execution]", + dockerCommands.get(counter++)); + Assert.assertEquals(" cap-add=SYS_CHROOT,NET_BIND_SERVICE", + dockerCommands.get(counter++)); + Assert.assertEquals(" cap-drop=ALL", dockerCommands.get(counter++)); + Assert.assertEquals(" detach=true", dockerCommands.get(counter++)); + Assert.assertEquals(" docker-command=run", dockerCommands.get(counter++)); + Assert.assertEquals(" hostname=ctr-id", dockerCommands.get(counter++)); + Assert + .assertEquals(" image=busybox:latest", dockerCommands.get(counter++)); + Assert.assertEquals( + " launch-command=bash,/test_container_work_dir/launch_container.sh", + dockerCommands.get(counter++)); + Assert.assertEquals(" name=container_id", dockerCommands.get(counter++)); + Assert.assertEquals(" net=host", dockerCommands.get(counter++)); + Assert.assertEquals(" privileged=true", dockerCommands.get(counter++)); + Assert.assertEquals( + " rw-mounts=/test_container_local_dir:/test_container_local_dir," + + "/test_filecache_dir:/test_filecache_dir," + + "/test_container_work_dir:/test_container_work_dir," + + "/test_container_log_dir:/test_container_log_dir," + + "/test_user_local_dir:/test_user_local_dir", + dockerCommands.get(counter++)); + Assert.assertEquals(" user=run_as_user", dockerCommands.get(counter++)); + Assert.assertEquals(" workdir=/test_container_work_dir", + dockerCommands.get(counter++)); } @Test @@ -833,15 +900,33 @@ public class TestDockerContainerRuntime { List dockerCommands = Files.readAllLines(Paths.get (dockerCommandFile), Charset.forName("UTF-8")); - Assert.assertEquals(1, dockerCommands.size()); - - String command = dockerCommands.get(0); - - Assert.assertTrue("Did not find expected " + - "/test_local_dir/test_resource_file:test_mount mount in docker " + - "run args : " + command, - command.contains(" -v /test_local_dir/test_resource_file:test_mount" + - ":ro ")); + Assert.assertEquals(14, dockerCommands.size()); + Assert.assertEquals("[docker-command-execution]", dockerCommands.get(0)); + Assert.assertEquals(" cap-add=SYS_CHROOT,NET_BIND_SERVICE", + dockerCommands.get(1)); + Assert.assertEquals(" cap-drop=ALL", dockerCommands.get(2)); + Assert.assertEquals(" detach=true", dockerCommands.get(3)); + Assert.assertEquals(" docker-command=run", dockerCommands.get(4)); + Assert.assertEquals(" hostname=ctr-id", dockerCommands.get(5)); + Assert.assertEquals(" image=busybox:latest", dockerCommands.get(6)); + Assert.assertEquals( + " launch-command=bash,/test_container_work_dir/launch_container.sh", + dockerCommands.get(7)); + Assert.assertEquals(" name=container_id", dockerCommands.get(8)); + Assert.assertEquals(" net=host", dockerCommands.get(9)); + Assert.assertEquals( + " ro-mounts=/test_local_dir/test_resource_file:test_mount", + dockerCommands.get(10)); + Assert.assertEquals( + " rw-mounts=/test_container_local_dir:/test_container_local_dir," + + "/test_filecache_dir:/test_filecache_dir," + + "/test_container_work_dir:/test_container_work_dir," + + "/test_container_log_dir:/test_container_log_dir," + + "/test_user_local_dir:/test_user_local_dir", + dockerCommands.get(11)); + Assert.assertEquals(" user=run_as_user", dockerCommands.get(12)); + Assert.assertEquals(" workdir=/test_container_work_dir", + dockerCommands.get(13)); } @Test @@ -885,20 +970,35 @@ public class TestDockerContainerRuntime { List dockerCommands = Files.readAllLines(Paths.get (dockerCommandFile), Charset.forName("UTF-8")); - Assert.assertEquals(1, dockerCommands.size()); + Assert.assertEquals(14, dockerCommands.size()); + Assert.assertEquals("[docker-command-execution]", dockerCommands.get(0)); + Assert.assertEquals(" cap-add=SYS_CHROOT,NET_BIND_SERVICE", + dockerCommands.get(1)); + Assert.assertEquals(" cap-drop=ALL", dockerCommands.get(2)); + Assert.assertEquals(" detach=true", dockerCommands.get(3)); + Assert.assertEquals(" docker-command=run", dockerCommands.get(4)); + Assert.assertEquals(" hostname=ctr-id", dockerCommands.get(5)); + Assert.assertEquals(" image=busybox:latest", dockerCommands.get(6)); + Assert.assertEquals( + " launch-command=bash,/test_container_work_dir/launch_container.sh", + dockerCommands.get(7)); + Assert.assertEquals(" name=container_id", dockerCommands.get(8)); + Assert.assertEquals(" net=host", dockerCommands.get(9)); + Assert.assertEquals( + " ro-mounts=/test_local_dir/test_resource_file:test_mount1," + + "/test_local_dir/test_resource_file:test_mount2", + dockerCommands.get(10)); + Assert.assertEquals( + " rw-mounts=/test_container_local_dir:/test_container_local_dir," + + "/test_filecache_dir:/test_filecache_dir," + + "/test_container_work_dir:/test_container_work_dir," + + "/test_container_log_dir:/test_container_log_dir," + + "/test_user_local_dir:/test_user_local_dir", + dockerCommands.get(11)); + Assert.assertEquals(" user=run_as_user", dockerCommands.get(12)); + Assert.assertEquals(" workdir=/test_container_work_dir", + dockerCommands.get(13)); - String command = dockerCommands.get(0); - - Assert.assertTrue("Did not find expected " + - "/test_local_dir/test_resource_file:test_mount1 mount in docker " + - "run args : " + command, - command.contains(" -v /test_local_dir/test_resource_file:test_mount1" + - ":ro ")); - Assert.assertTrue("Did not find expected " + - "/test_local_dir/test_resource_file:test_mount2 mount in docker " + - "run args : " + command, - command.contains(" -v /test_local_dir/test_resource_file:test_mount2" + - ":ro ")); } @Test @@ -930,8 +1030,10 @@ public class TestDockerContainerRuntime { IOException { List dockerCommands = getDockerCommandsForSignal( ContainerExecutor.Signal.TERM); - Assert.assertEquals(1, dockerCommands.size()); - Assert.assertEquals("stop container_id", dockerCommands.get(0)); + Assert.assertEquals(3, dockerCommands.size()); + Assert.assertEquals("[docker-command-execution]", dockerCommands.get(0)); + Assert.assertEquals(" docker-command=stop", dockerCommands.get(1)); + Assert.assertEquals(" name=container_id", dockerCommands.get(2)); } @Test @@ -940,8 +1042,10 @@ public class TestDockerContainerRuntime { IOException { List dockerCommands = getDockerCommandsForSignal( ContainerExecutor.Signal.KILL); - Assert.assertEquals(1, dockerCommands.size()); - Assert.assertEquals("stop container_id", dockerCommands.get(0)); + Assert.assertEquals(3, dockerCommands.size()); + Assert.assertEquals("[docker-command-execution]", dockerCommands.get(0)); + Assert.assertEquals(" docker-command=stop", dockerCommands.get(1)); + Assert.assertEquals(" name=container_id", dockerCommands.get(2)); } @Test @@ -950,8 +1054,10 @@ public class TestDockerContainerRuntime { IOException { List dockerCommands = getDockerCommandsForSignal( ContainerExecutor.Signal.QUIT); - Assert.assertEquals(1, dockerCommands.size()); - Assert.assertEquals("stop container_id", dockerCommands.get(0)); + Assert.assertEquals(3, dockerCommands.size()); + Assert.assertEquals("[docker-command-execution]", dockerCommands.get(0)); + Assert.assertEquals(" docker-command=stop", dockerCommands.get(1)); + Assert.assertEquals(" name=container_id", dockerCommands.get(2)); } private List getDockerCommandsForSignal( diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/TestDockerCommandExecutor.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/TestDockerCommandExecutor.java index 60fce406605..05b44b8a93c 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/TestDockerCommandExecutor.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/TestDockerCommandExecutor.java @@ -114,8 +114,10 @@ public class TestDockerCommandExecutor { assertEquals(1, ops.size()); assertEquals(PrivilegedOperation.OperationType.RUN_DOCKER_CMD.name(), ops.get(0).getOperationType().name()); - assertEquals(1, dockerCommands.size()); - assertEquals("rm " + MOCK_CONTAINER_ID, dockerCommands.get(0)); + assertEquals(3, dockerCommands.size()); + assertEquals("[docker-command-execution]", dockerCommands.get(0)); + assertEquals(" docker-command=rm", dockerCommands.get(1)); + assertEquals(" name=" + MOCK_CONTAINER_ID, dockerCommands.get(2)); } @Test @@ -130,8 +132,10 @@ public class TestDockerCommandExecutor { assertEquals(1, ops.size()); assertEquals(PrivilegedOperation.OperationType.RUN_DOCKER_CMD.name(), ops.get(0).getOperationType().name()); - assertEquals(1, dockerCommands.size()); - assertEquals("stop " + MOCK_CONTAINER_ID, dockerCommands.get(0)); + assertEquals(3, dockerCommands.size()); + assertEquals("[docker-command-execution]", dockerCommands.get(0)); + assertEquals(" docker-command=stop", dockerCommands.get(1)); + assertEquals(" name=" + MOCK_CONTAINER_ID, dockerCommands.get(2)); } @Test @@ -147,9 +151,12 @@ public class TestDockerCommandExecutor { assertEquals(1, ops.size()); assertEquals(PrivilegedOperation.OperationType.RUN_DOCKER_CMD.name(), ops.get(0).getOperationType().name()); - assertEquals(1, dockerCommands.size()); - assertEquals("inspect --format='{{.State.Status}}' " + MOCK_CONTAINER_ID, - dockerCommands.get(0)); + assertEquals(4, dockerCommands.size()); + assertEquals("[docker-command-execution]", dockerCommands.get(0)); + assertEquals(" docker-command=inspect", dockerCommands.get(1)); + assertEquals(" format={{.State.Status}}", dockerCommands.get(2)); + assertEquals(" name=" + MOCK_CONTAINER_ID, dockerCommands.get(3)); + } @Test @@ -165,8 +172,10 @@ public class TestDockerCommandExecutor { assertEquals(1, ops.size()); assertEquals(PrivilegedOperation.OperationType.RUN_DOCKER_CMD.name(), ops.get(0).getOperationType().name()); - assertEquals(1, dockerCommands.size()); - assertEquals("pull " + MOCK_IMAGE_NAME, dockerCommands.get(0)); + assertEquals(3, dockerCommands.size()); + assertEquals("[docker-command-execution]", dockerCommands.get(0)); + assertEquals(" docker-command=pull", dockerCommands.get(1)); + assertEquals(" image=" + MOCK_IMAGE_NAME, dockerCommands.get(2)); } @Test @@ -182,8 +191,12 @@ public class TestDockerCommandExecutor { assertEquals(1, ops.size()); assertEquals(PrivilegedOperation.OperationType.RUN_DOCKER_CMD.name(), ops.get(0).getOperationType().name()); - assertEquals(1, dockerCommands.size()); - assertEquals("load --i=" + MOCK_LOCAL_IMAGE_NAME, dockerCommands.get(0)); + assertEquals(3, dockerCommands.size()); + assertEquals("[docker-command-execution]", dockerCommands.get(0)); + assertEquals(" docker-command=load", dockerCommands.get(1)); + assertEquals(" image=" + MOCK_LOCAL_IMAGE_NAME, dockerCommands.get(2)); + + } @Test diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/TestDockerInspectCommand.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/TestDockerInspectCommand.java index 619f202d05b..4092e6c45b5 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/TestDockerInspectCommand.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/TestDockerInspectCommand.java @@ -18,6 +18,8 @@ package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker; import static org.junit.Assert.assertEquals; + +import org.apache.hadoop.util.StringUtils; import org.junit.Before; import org.junit.Test; @@ -44,16 +46,29 @@ public class TestDockerInspectCommand { @Test public void testGetContainerStatus() throws Exception { dockerInspectCommand.getContainerStatus(); - assertEquals("inspect --format='{{.State.Status}}' foo", - dockerInspectCommand.getCommandWithArguments()); + assertEquals("inspect", StringUtils.join(",", + dockerInspectCommand.getDockerCommandWithArguments() + .get("docker-command"))); + assertEquals("{{.State.Status}}", StringUtils.join(",", + dockerInspectCommand.getDockerCommandWithArguments().get("format"))); + assertEquals("foo", StringUtils.join(",", + dockerInspectCommand.getDockerCommandWithArguments().get("name"))); + assertEquals(3, + dockerInspectCommand.getDockerCommandWithArguments().size()); } @Test public void testGetIpAndHost() throws Exception { dockerInspectCommand.getIpAndHost(); - assertEquals( - "inspect --format='{{range(.NetworkSettings.Networks)}}{{.IPAddress}}" - + ",{{end}}{{.Config.Hostname}}' foo", - dockerInspectCommand.getCommandWithArguments()); + assertEquals("inspect", StringUtils.join(",", + dockerInspectCommand.getDockerCommandWithArguments() + .get("docker-command"))); + assertEquals("{{range(.NetworkSettings.Networks)}}" + + "{{.IPAddress}},{{end}}{{.Config.Hostname}}", StringUtils.join(",", + dockerInspectCommand.getDockerCommandWithArguments().get("format"))); + assertEquals("foo", StringUtils.join(",", + dockerInspectCommand.getDockerCommandWithArguments().get("name"))); + assertEquals(3, + dockerInspectCommand.getDockerCommandWithArguments().size()); } -} \ No newline at end of file +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/TestDockerLoadCommand.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/TestDockerLoadCommand.java index 85fa0f8f733..e5bff26c260 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/TestDockerLoadCommand.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/TestDockerLoadCommand.java @@ -16,6 +16,7 @@ */ package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker; +import org.apache.hadoop.util.StringUtils; import org.junit.Before; import org.junit.Test; @@ -42,7 +43,11 @@ public class TestDockerLoadCommand { @Test public void testGetCommandWithArguments() { - assertEquals("load --i=foo", - dockerLoadCommand.getCommandWithArguments()); + assertEquals("load", StringUtils.join(",", + dockerLoadCommand.getDockerCommandWithArguments() + .get("docker-command"))); + assertEquals("foo", StringUtils.join(",", + dockerLoadCommand.getDockerCommandWithArguments().get("image"))); + assertEquals(2, dockerLoadCommand.getDockerCommandWithArguments().size()); } } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/TestDockerPullCommand.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/TestDockerPullCommand.java index 89157ff751a..ccf7000ab75 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/TestDockerPullCommand.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/TestDockerPullCommand.java @@ -16,6 +16,7 @@ */ package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker; +import org.apache.hadoop.util.StringUtils; import org.junit.Before; import org.junit.Test; @@ -42,7 +43,12 @@ public class TestDockerPullCommand { @Test public void testGetCommandWithArguments() { - assertEquals("pull foo", dockerPullCommand.getCommandWithArguments()); + assertEquals("pull", StringUtils.join(",", + dockerPullCommand.getDockerCommandWithArguments() + .get("docker-command"))); + assertEquals("foo", StringUtils.join(",", + dockerPullCommand.getDockerCommandWithArguments().get("image"))); + assertEquals(2, dockerPullCommand.getDockerCommandWithArguments().size()); } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/TestDockerRmCommand.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/TestDockerRmCommand.java index d1b99043aec..a8d4bddae03 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/TestDockerRmCommand.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/TestDockerRmCommand.java @@ -17,6 +17,8 @@ package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker; import static org.junit.Assert.assertEquals; + +import org.apache.hadoop.util.StringUtils; import org.junit.Before; import org.junit.Test; @@ -42,7 +44,11 @@ public class TestDockerRmCommand { @Test public void testGetCommandWithArguments() { - assertEquals("rm foo", dockerRmCommand.getCommandWithArguments()); + assertEquals("rm", StringUtils.join(",", + dockerRmCommand.getDockerCommandWithArguments().get("docker-command"))); + assertEquals("foo", StringUtils.join(",", + dockerRmCommand.getDockerCommandWithArguments().get("name"))); + assertEquals(2, dockerRmCommand.getDockerCommandWithArguments().size()); } } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/TestDockerRunCommand.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/TestDockerRunCommand.java index 85bccd2d51d..e51d7ecc7c5 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/TestDockerRunCommand.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/TestDockerRunCommand.java @@ -16,6 +16,7 @@ */ package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker; +import org.apache.hadoop.util.StringUtils; import org.junit.Before; import org.junit.Test; @@ -56,8 +57,24 @@ public class TestDockerRunCommand { commands.add("launch_command"); dockerRunCommand.setOverrideCommandWithArgs(commands); dockerRunCommand.removeContainerOnExit(); - assertEquals("run --name=foo --user=user_id --device=source:dest --rm " - + "image_name launch_command", - dockerRunCommand.getCommandWithArguments()); + + assertEquals("run", StringUtils.join(",", + dockerRunCommand.getDockerCommandWithArguments() + .get("docker-command"))); + assertEquals("foo", StringUtils.join(",", + dockerRunCommand.getDockerCommandWithArguments().get("name"))); + assertEquals("user_id", StringUtils.join(",", + dockerRunCommand.getDockerCommandWithArguments().get("user"))); + assertEquals("image_name", StringUtils.join(",", + dockerRunCommand.getDockerCommandWithArguments().get("image"))); + + assertEquals("source:dest", StringUtils.join(",", + dockerRunCommand.getDockerCommandWithArguments().get("devices"))); + assertEquals("true", StringUtils + .join(",", dockerRunCommand.getDockerCommandWithArguments().get("rm"))); + assertEquals("launch_command", StringUtils.join(",", + dockerRunCommand.getDockerCommandWithArguments() + .get("launch-command"))); + assertEquals(7, dockerRunCommand.getDockerCommandWithArguments().size()); } -} \ No newline at end of file +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/TestDockerStopCommand.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/TestDockerStopCommand.java index c9743f3d069..efbde776e64 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/TestDockerStopCommand.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/TestDockerStopCommand.java @@ -21,6 +21,8 @@ package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker; import static org.junit.Assert.assertEquals; + +import org.apache.hadoop.util.StringUtils; import org.junit.Before; import org.junit.Test; @@ -48,8 +50,13 @@ public class TestDockerStopCommand { @Test public void testSetGracePeriod() throws Exception { dockerStopCommand.setGracePeriod(GRACE_PERIOD); - assertEquals("stop foo --time=10", - dockerStopCommand.getCommandWithArguments()); - + assertEquals("stop", StringUtils.join(",", + dockerStopCommand.getDockerCommandWithArguments() + .get("docker-command"))); + assertEquals("foo", StringUtils.join(",", + dockerStopCommand.getDockerCommandWithArguments().get("name"))); + assertEquals("10", StringUtils.join(",", + dockerStopCommand.getDockerCommandWithArguments().get("time"))); + assertEquals(3, dockerStopCommand.getDockerCommandWithArguments().size()); } -} \ No newline at end of file +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/DockerContainers.md b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/DockerContainers.md index 23f41348370..36c391ad085 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/DockerContainers.md +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/DockerContainers.md @@ -167,7 +167,24 @@ The following properties are required to enable Docker support: |Configuration Name | Description | |:---- |:---- | | `yarn.nodemanager.linux-container-executor.group` | The Unix group of the NodeManager. It should match the yarn.nodemanager.linux-container-executor.group in the yarn-site.xml file. | -| `feature.docker.enabled` | Must be 0 or 1. 0 means launching Docker containers is disabled. 1 means launching Docker containers is allowed. | + +The container-executor.cfg must contain a section to determine the capabilities that containers +are allowed. It contains the following properties: + +|Configuration Name | Description | +|:---- |:---- | +| `module.enabled` | Must be "true" or "false" to enable or disable launching Docker containers respectively. Default value is 0. | +| `docker.binary` | The binary used to launch Docker containers. /usr/bin/docker by default. | +| `docker.allowed.capabilities` | Comma separated capabilities that containers are allowed to add. By default no capabilities are allowed to be added. | +| `docker.allowed.devices` | Comma separated devices that containers are allowed to mount. By default no devices are allowed to be added. | +| `docker.allowed.networks` | Comma separated networks that containers are allowed to use. If no network is specified when launching the container, the default Docker network will be used. | +| `docker.allowed.ro-mounts` | Comma separated directories that containers are allowed to mount in read-only mode. By default, no directories are allowed to mounted. | +| `docker.allowed.rw-mounts` | Comma separated directories that containers are allowed to mount in read-write mode. By default, no directories are allowed to mounted. | +| `docker.privileged-containers.enabled` | Set to 1 or 0 to enable or disable launching privileged containers. Default value is 0. | + +Please note that if you wish to run Docker containers that require access to the YARN local directories, you must add them to the docker.allowed.rw-mounts list. + +In addition, containers are not permitted to mount any parent of the container-executor.cfg directory in read-write mode. The following properties are optional: @@ -176,9 +193,21 @@ The following properties are optional: | `min.user.id` | The minimum UID that is allowed to launch applications. The default is no minimum | | `banned.users` | A comma-separated list of usernames who should not be allowed to launch applications. The default setting is: yarn, mapred, hdfs, and bin. | | `allowed.system.users` | A comma-separated list of usernames who should be allowed to launch applications even if their UIDs are below the configured minimum. If a user appears in allowed.system.users and banned.users, the user will be considered banned. | -| `docker.binary` | The path to the Docker binary. The default is "docker". | | `feature.tc.enabled` | Must be 0 or 1. 0 means traffic control commands are disabled. 1 means traffic control commands are allowed. | +Part of a container-executor.cfg which allows Docker containers to be launched is below: + +``` +yarn.nodemanager.linux-container-executor.group=yarn +[docker] + module.enabled=true + docker.allowed.capabilities=SYS_CHROOT,MKNOD,SETFCAP,SETPCAP,FSETID,CHOWN,AUDIT_WRITE,SETGID,NET_RAW,FOWNER,SETUID,DAC_OVERRIDE,KILL,NET_BIND_SERVICE + docker.allowed.networks=bridge,host,none + docker.allowed.ro-mounts=/sys/fs/cgroup + docker.allowed.rw-mounts=/var/hadoop/yarn/local-dir,/var/hadoop/yarn/log-dir + +``` + Docker Image Requirements -------------------------