YARN-6623. Add support to turn off launching privileged containers in the container-executor. (Varun Vasudev via wangda)
Change-Id: I76aec68cbfb42d239279d7245a03290d691e87a4
This commit is contained in:
parent
c114da5e64
commit
d3b1c63195
|
@ -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
|
banned.users=#comma separated list of users who can not run applications
|
||||||
min.user.id=1000#Prevent other super-users
|
min.user.id=1000#Prevent other super-users
|
||||||
allowed.system.users=##comma separated list of system users who CAN run applications
|
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
|
||||||
|
|
|
@ -132,6 +132,7 @@ add_library(container
|
||||||
main/native/container-executor/impl/modules/cgroups/cgroups-operations.c
|
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/common/module-configs.c
|
||||||
main/native/container-executor/impl/modules/gpu/gpu-module.c
|
main/native/container-executor/impl/modules/gpu/gpu-module.c
|
||||||
|
main/native/container-executor/impl/utils/docker-util.c
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(container-executor
|
add_executable(container-executor
|
||||||
|
@ -156,6 +157,7 @@ output_directory(test-container-executor target/usr/local/bin)
|
||||||
|
|
||||||
# unit tests for container executor
|
# unit tests for container executor
|
||||||
add_executable(cetest
|
add_executable(cetest
|
||||||
|
main/native/container-executor/impl/get_executable.c
|
||||||
main/native/container-executor/impl/util.c
|
main/native/container-executor/impl/util.c
|
||||||
main/native/container-executor/test/test_configuration.cc
|
main/native/container-executor/test/test_configuration.cc
|
||||||
main/native/container-executor/test/test_main.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/utils/test-path-utils.cc
|
||||||
main/native/container-executor/test/modules/cgroups/test-cgroups-module.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/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)
|
target_link_libraries(cetest gtest container)
|
||||||
output_directory(cetest test)
|
output_directory(cetest test)
|
||||||
|
|
|
@ -559,8 +559,8 @@ public class DockerLinuxContainerRuntime implements LinuxContainerRuntime {
|
||||||
runCommand.setCapabilities(capabilities);
|
runCommand.setCapabilities(capabilities);
|
||||||
|
|
||||||
if(cgroupsRootDirectory != null) {
|
if(cgroupsRootDirectory != null) {
|
||||||
runCommand.addMountLocation(cgroupsRootDirectory,
|
runCommand.addReadOnlyMountLocation(cgroupsRootDirectory,
|
||||||
cgroupsRootDirectory + ":ro", false);
|
cgroupsRootDirectory, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> allDirs = new ArrayList<>(containerLocalDirs);
|
List<String> allDirs = new ArrayList<>(containerLocalDirs);
|
||||||
|
@ -584,7 +584,7 @@ public class DockerLinuxContainerRuntime implements LinuxContainerRuntime {
|
||||||
}
|
}
|
||||||
String src = validateMount(dir[0], localizedResources);
|
String src = validateMount(dir[0], localizedResources);
|
||||||
String dst = dir[1];
|
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);
|
launchOp, null, null, false, false);
|
||||||
} catch (PrivilegedOperationException e) {
|
} catch (PrivilegedOperationException e) {
|
||||||
LOG.warn("Launch container failed. Exception: ", 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
|
throw new ContainerExecutionException("Launch container failed", e
|
||||||
.getExitCode(), e.getOutput(), e.getErrorOutput());
|
.getExitCode(), e.getOutput(), e.getErrorOutput());
|
||||||
|
@ -757,8 +757,7 @@ public class DockerLinuxContainerRuntime implements LinuxContainerRuntime {
|
||||||
launchOp.appendArgs(tcCommandFile);
|
launchOp.appendArgs(tcCommandFile);
|
||||||
}
|
}
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug("Launching container with cmd: " + runCommand
|
LOG.debug("Launching container with cmd: " + runCommand);
|
||||||
.getCommandWithArguments());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return launchOp;
|
return launchOp;
|
||||||
|
|
|
@ -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.InterfaceAudience;
|
||||||
import org.apache.hadoop.classification.InterfaceStability;
|
import org.apache.hadoop.classification.InterfaceStability;
|
||||||
import org.apache.hadoop.conf.Configuration;
|
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.apache.hadoop.yarn.server.nodemanager.containermanager.runtime.ContainerExecutionException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -34,6 +34,8 @@ import java.io.IOException;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
@InterfaceAudience.Private
|
@InterfaceAudience.Private
|
||||||
@InterfaceStability.Unstable
|
@InterfaceStability.Unstable
|
||||||
|
@ -68,10 +70,25 @@ public final class DockerClient {
|
||||||
TMP_FILE_SUFFIX, new
|
TMP_FILE_SUFFIX, new
|
||||||
File(tmpDirPath));
|
File(tmpDirPath));
|
||||||
|
|
||||||
Writer writer = new OutputStreamWriter(new FileOutputStream(dockerCommandFile),
|
Writer writer = new OutputStreamWriter(
|
||||||
"UTF-8");
|
new FileOutputStream(dockerCommandFile), "UTF-8");
|
||||||
PrintWriter printWriter = new PrintWriter(writer);
|
PrintWriter printWriter = new PrintWriter(writer);
|
||||||
printWriter.print(cmd.getCommandWithArguments());
|
printWriter.println("[docker-command-execution]");
|
||||||
|
for (Map.Entry<String, List<String>> 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();
|
printWriter.close();
|
||||||
|
|
||||||
return dockerCommandFile.getAbsolutePath();
|
return dockerCommandFile.getAbsolutePath();
|
||||||
|
|
|
@ -25,8 +25,10 @@ import org.apache.hadoop.classification.InterfaceStability;
|
||||||
import org.apache.hadoop.util.StringUtils;
|
import org.apache.hadoop.util.StringUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
@InterfaceAudience.Private
|
@InterfaceAudience.Private
|
||||||
@InterfaceStability.Unstable
|
@InterfaceStability.Unstable
|
||||||
|
@ -37,30 +39,53 @@ import java.util.List;
|
||||||
|
|
||||||
public abstract class DockerCommand {
|
public abstract class DockerCommand {
|
||||||
private final String command;
|
private final String command;
|
||||||
private final List<String> commandWithArguments;
|
private final Map<String, List<String>> commandArguments;
|
||||||
|
|
||||||
protected DockerCommand(String command) {
|
protected DockerCommand(String command) {
|
||||||
|
String dockerCommandKey = "docker-command";
|
||||||
this.command = command;
|
this.command = command;
|
||||||
this.commandWithArguments = new ArrayList<>();
|
this.commandArguments = new TreeMap<>();
|
||||||
commandWithArguments.add(command);
|
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() {
|
public final String getCommandOption() {
|
||||||
return this.command;
|
return this.command;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Add command commandWithArguments - this method is only meant for use by
|
/**
|
||||||
* sub-classes
|
* Add command commandWithArguments - this method is only meant for use by
|
||||||
* @param arguments to be added
|
* sub-classes.
|
||||||
|
*
|
||||||
|
* @param key name of the key to be added
|
||||||
|
* @param value value of the key
|
||||||
*/
|
*/
|
||||||
protected final void addCommandArguments(String... arguments) {
|
protected final void addCommandArguments(String key, String value) {
|
||||||
this.commandWithArguments.addAll(Arrays.asList(arguments));
|
List<String> 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() {
|
public Map<String, List<String>> getDockerCommandWithArguments() {
|
||||||
return StringUtils.join(" ", commandWithArguments);
|
return Collections.unmodifiableMap(commandArguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuffer ret = new StringBuffer(this.command);
|
||||||
|
for (Map.Entry<String, List<String>> entry : commandArguments.entrySet()) {
|
||||||
|
ret.append(" ").append(entry.getKey());
|
||||||
|
ret.append("=").append(StringUtils.join(",", entry.getValue()));
|
||||||
|
}
|
||||||
|
return ret.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -88,8 +88,7 @@ public final class DockerCommandExecutor {
|
||||||
dockerOp.disableFailureLogging();
|
dockerOp.disableFailureLogging();
|
||||||
}
|
}
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug("Running docker command: "
|
LOG.debug("Running docker command: " + dockerCommand);
|
||||||
+ dockerCommand.getCommandWithArguments());
|
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
String result = privilegedOperationExecutor
|
String result = privilegedOperationExecutor
|
||||||
|
|
|
@ -26,16 +26,14 @@ package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime
|
||||||
*/
|
*/
|
||||||
public class DockerInspectCommand extends DockerCommand {
|
public class DockerInspectCommand extends DockerCommand {
|
||||||
private static final String INSPECT_COMMAND = "inspect";
|
private static final String INSPECT_COMMAND = "inspect";
|
||||||
private String containerName;
|
|
||||||
|
|
||||||
public DockerInspectCommand(String containerName) {
|
public DockerInspectCommand(String containerName) {
|
||||||
super(INSPECT_COMMAND);
|
super(INSPECT_COMMAND);
|
||||||
this.containerName = containerName;
|
super.addCommandArguments("name", containerName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DockerInspectCommand getContainerStatus() {
|
public DockerInspectCommand getContainerStatus() {
|
||||||
super.addCommandArguments("--format='{{.State.Status}}'");
|
super.addCommandArguments("format", "{{.State.Status}}");
|
||||||
super.addCommandArguments(containerName);
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,9 +41,8 @@ public class DockerInspectCommand extends DockerCommand {
|
||||||
// Be sure to not use space in the argument, otherwise the
|
// Be sure to not use space in the argument, otherwise the
|
||||||
// extract_values_delim method in container-executor binary
|
// extract_values_delim method in container-executor binary
|
||||||
// cannot parse the arguments correctly.
|
// cannot parse the arguments correctly.
|
||||||
super.addCommandArguments("--format='{{range(.NetworkSettings.Networks)}}"
|
super.addCommandArguments("format", "{{range(.NetworkSettings.Networks)}}"
|
||||||
+ "{{.IPAddress}},{{end}}{{.Config.Hostname}}'");
|
+ "{{.IPAddress}},{{end}}{{.Config.Hostname}}");
|
||||||
super.addCommandArguments(containerName);
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -25,6 +25,6 @@ public class DockerLoadCommand extends DockerCommand {
|
||||||
|
|
||||||
public DockerLoadCommand(String localImageFile) {
|
public DockerLoadCommand(String localImageFile) {
|
||||||
super(LOAD_COMMAND);
|
super(LOAD_COMMAND);
|
||||||
super.addCommandArguments("--i=" + localImageFile);
|
super.addCommandArguments("image", localImageFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ public class DockerPullCommand extends DockerCommand {
|
||||||
|
|
||||||
public DockerPullCommand(String imageName) {
|
public DockerPullCommand(String imageName) {
|
||||||
super(PULL_COMMAND);
|
super(PULL_COMMAND);
|
||||||
super.addCommandArguments(imageName);
|
super.addCommandArguments("image", imageName);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,6 @@ public class DockerRmCommand extends DockerCommand {
|
||||||
|
|
||||||
public DockerRmCommand(String containerName) {
|
public DockerRmCommand(String containerName) {
|
||||||
super(RM_COMMAND);
|
super(RM_COMMAND);
|
||||||
super.addCommandArguments(containerName);
|
super.addCommandArguments("name", containerName);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -20,42 +20,39 @@
|
||||||
|
|
||||||
package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker;
|
package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker;
|
||||||
|
|
||||||
import org.apache.hadoop.util.StringUtils;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class DockerRunCommand extends DockerCommand {
|
public class DockerRunCommand extends DockerCommand {
|
||||||
private static final String RUN_COMMAND = "run";
|
private static final String RUN_COMMAND = "run";
|
||||||
private final String image;
|
|
||||||
private List<String> overrrideCommandWithArgs;
|
|
||||||
|
|
||||||
/** The following are mandatory: */
|
/** The following are mandatory: */
|
||||||
public DockerRunCommand(String containerId, String user, String image) {
|
public DockerRunCommand(String containerId, String user, String image) {
|
||||||
super(RUN_COMMAND);
|
super(RUN_COMMAND);
|
||||||
super.addCommandArguments("--name=" + containerId, "--user=" + user);
|
super.addCommandArguments("name", containerId);
|
||||||
this.image = image;
|
super.addCommandArguments("user", user);
|
||||||
|
super.addCommandArguments("image", image);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DockerRunCommand removeContainerOnExit() {
|
public DockerRunCommand removeContainerOnExit() {
|
||||||
super.addCommandArguments("--rm");
|
super.addCommandArguments("rm", "true");
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DockerRunCommand detachOnRun() {
|
public DockerRunCommand detachOnRun() {
|
||||||
super.addCommandArguments("-d");
|
super.addCommandArguments("detach", "true");
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DockerRunCommand setContainerWorkDir(String workdir) {
|
public DockerRunCommand setContainerWorkDir(String workdir) {
|
||||||
super.addCommandArguments("--workdir=" + workdir);
|
super.addCommandArguments("workdir", workdir);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DockerRunCommand setNetworkType(String type) {
|
public DockerRunCommand setNetworkType(String type) {
|
||||||
super.addCommandArguments("--net=" + type);
|
super.addCommandArguments("net", type);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,79 +62,80 @@ public class DockerRunCommand extends DockerCommand {
|
||||||
if (!sourceExists && !createSource) {
|
if (!sourceExists && !createSource) {
|
||||||
return this;
|
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;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DockerRunCommand setCGroupParent(String parentPath) {
|
public DockerRunCommand setCGroupParent(String parentPath) {
|
||||||
super.addCommandArguments("--cgroup-parent=" + parentPath);
|
super.addCommandArguments("cgroup-parent", parentPath);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Run a privileged container. Use with extreme care */
|
/* Run a privileged container. Use with extreme care */
|
||||||
public DockerRunCommand setPrivileged() {
|
public DockerRunCommand setPrivileged() {
|
||||||
super.addCommandArguments("--privileged");
|
super.addCommandArguments("privileged", "true");
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DockerRunCommand setCapabilities(Set<String> capabilties) {
|
public DockerRunCommand setCapabilities(Set<String> capabilties) {
|
||||||
//first, drop all capabilities
|
//first, drop all capabilities
|
||||||
super.addCommandArguments("--cap-drop=ALL");
|
super.addCommandArguments("cap-drop", "ALL");
|
||||||
|
|
||||||
//now, add the capabilities supplied
|
//now, add the capabilities supplied
|
||||||
for (String capability : capabilties) {
|
for (String capability : capabilties) {
|
||||||
super.addCommandArguments("--cap-add=" + capability);
|
super.addCommandArguments("cap-add", capability);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DockerRunCommand setHostname(String hostname) {
|
public DockerRunCommand setHostname(String hostname) {
|
||||||
super.addCommandArguments("--hostname=" + hostname);
|
super.addCommandArguments("hostname", hostname);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DockerRunCommand addDevice(String sourceDevice, String
|
public DockerRunCommand addDevice(String sourceDevice, String
|
||||||
destinationDevice) {
|
destinationDevice) {
|
||||||
super.addCommandArguments("--device=" + sourceDevice + ":" +
|
super.addCommandArguments("devices", sourceDevice + ":" +
|
||||||
destinationDevice);
|
destinationDevice);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DockerRunCommand enableDetach() {
|
public DockerRunCommand enableDetach() {
|
||||||
super.addCommandArguments("--detach=true");
|
super.addCommandArguments("detach", "true");
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DockerRunCommand disableDetach() {
|
public DockerRunCommand disableDetach() {
|
||||||
super.addCommandArguments("--detach=false");
|
super.addCommandArguments("detach", "false");
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DockerRunCommand groupAdd(String[] groups) {
|
public DockerRunCommand groupAdd(String[] groups) {
|
||||||
for(int i = 0; i < groups.length; i++) {
|
super.addCommandArguments("group-add", String.join(",", groups));
|
||||||
super.addCommandArguments("--group-add " + groups[i]);
|
|
||||||
}
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DockerRunCommand setOverrideCommandWithArgs(
|
public DockerRunCommand setOverrideCommandWithArgs(
|
||||||
List<String> overrideCommandWithArgs) {
|
List<String> overrideCommandWithArgs) {
|
||||||
this.overrrideCommandWithArgs = overrideCommandWithArgs;
|
for(String override: overrideCommandWithArgs) {
|
||||||
|
super.addCommandArguments("launch-command", override);
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getCommandWithArguments() {
|
public Map<String, List<String>> getDockerCommandWithArguments() {
|
||||||
List<String> argList = new ArrayList<>();
|
return super.getDockerCommandWithArguments();
|
||||||
|
|
||||||
argList.add(super.getCommandWithArguments());
|
|
||||||
argList.add(image);
|
|
||||||
|
|
||||||
if (overrrideCommandWithArgs != null) {
|
|
||||||
argList.addAll(overrrideCommandWithArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
return StringUtils.join(" ", argList);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,11 +29,11 @@ public class DockerStopCommand extends DockerCommand {
|
||||||
|
|
||||||
public DockerStopCommand(String containerName) {
|
public DockerStopCommand(String containerName) {
|
||||||
super(STOP_COMMAND);
|
super(STOP_COMMAND);
|
||||||
super.addCommandArguments(containerName);
|
super.addCommandArguments("name", containerName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DockerStopCommand setGracePeriod(int value) {
|
public DockerStopCommand setGracePeriod(int value) {
|
||||||
super.addCommandArguments("--time=" + Integer.toString(value));
|
super.addCommandArguments("time", Integer.toString(value));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "get_executable.h"
|
||||||
|
|
||||||
#define __STDC_FORMAT_MACROS
|
#define __STDC_FORMAT_MACROS
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
@ -696,3 +697,19 @@ int get_kv_value(const char *input, char *out, size_t out_len) {
|
||||||
|
|
||||||
return 0;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -23,10 +23,21 @@
|
||||||
#define _WITH_GETLINE
|
#define _WITH_GETLINE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <stddef.h>
|
#include "config.h"
|
||||||
|
|
||||||
/** Define a platform-independent constant instead of using PATH_MAX */
|
#define CONF_FILENAME "container-executor.cfg"
|
||||||
#define EXECUTOR_PATH_MAX 4096
|
|
||||||
|
// 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 <stddef.h>
|
||||||
|
|
||||||
// Configuration data structures.
|
// Configuration data structures.
|
||||||
struct kv_pair {
|
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);
|
int get_kv_value(const char *input, char *out, size_t out_len);
|
||||||
|
|
||||||
|
char *get_config_path(const char* argv0);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "container-executor.h"
|
#include "container-executor.h"
|
||||||
#include "utils/string-utils.h"
|
#include "utils/docker-util.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
@ -43,7 +43,6 @@
|
||||||
#include <sys/mount.h>
|
#include <sys/mount.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <regex.h>
|
|
||||||
|
|
||||||
#ifndef HAVE_FCHMODAT
|
#ifndef HAVE_FCHMODAT
|
||||||
#include "compat/fchmodat.h"
|
#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 to store the user details
|
||||||
struct passwd *user_detail = NULL;
|
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* LOGFILE = NULL;
|
||||||
FILE* ERRORFILE = NULL;
|
FILE* ERRORFILE = NULL;
|
||||||
|
|
||||||
|
@ -466,7 +460,8 @@ int is_feature_enabled(const char* feature_key, int default_value,
|
||||||
|
|
||||||
int is_docker_support_enabled() {
|
int is_docker_support_enabled() {
|
||||||
return is_feature_enabled(DOCKER_SUPPORT_ENABLED_KEY,
|
return is_feature_enabled(DOCKER_SUPPORT_ENABLED_KEY,
|
||||||
DEFAULT_DOCKER_SUPPORT_ENABLED, &executor_cfg);
|
DEFAULT_DOCKER_SUPPORT_ENABLED, &executor_cfg)
|
||||||
|
|| docker_module_enabled(&CFG);
|
||||||
}
|
}
|
||||||
|
|
||||||
int is_tc_support_enabled() {
|
int is_tc_support_enabled() {
|
||||||
|
@ -474,13 +469,6 @@ int is_tc_support_enabled() {
|
||||||
DEFAULT_TC_SUPPORT_ENABLED, &executor_cfg);
|
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.
|
* 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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char* escape_single_quote(const char *str) {
|
char *construct_docker_command(const char *command_file) {
|
||||||
int p = 0;
|
int ret = 0;
|
||||||
int i = 0;
|
size_t command_size = MIN(sysconf(_SC_ARG_MAX), 128*1024);
|
||||||
char replacement[] = "'\"'\"'";
|
char *buffer = alloc_and_clear_memory(command_size, sizeof(char));
|
||||||
size_t replacement_length = strlen(replacement);
|
ret = get_docker_command(command_file, &CFG, buffer, command_size);
|
||||||
size_t ret_size = strlen(str) * replacement_length + 1;
|
if (ret != 0) {
|
||||||
char *ret = (char *) calloc(ret_size, sizeof(char));
|
fprintf(ERRORFILE, "Error constructing docker command, docker error code=%d, error message='%s'\n", ret,
|
||||||
if(ret == NULL) {
|
get_docker_error_message(ret));
|
||||||
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];
|
|
||||||
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);
|
fflush(ERRORFILE);
|
||||||
exit(OUT_OF_MEMORY);
|
exit(DOCKER_RUN_FAILED);
|
||||||
}
|
}
|
||||||
p = strtok(NULL, " ");
|
return buffer;
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int run_docker(const char *command_file) {
|
int run_docker(const char *command_file) {
|
||||||
char* docker_command = parse_docker_command_file(command_file);
|
char* docker_command = construct_docker_command(command_file);
|
||||||
char* docker_binary = get_section_value(DOCKER_BINARY_KEY, &executor_cfg);
|
char* docker_binary = get_docker_binary(&CFG);
|
||||||
docker_binary = check_docker_binary(docker_binary);
|
|
||||||
size_t command_size = MIN(sysconf(_SC_ARG_MAX), 128*1024);
|
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);
|
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, " ");
|
char **args = split_delimiter(docker_command_with_binary, " ");
|
||||||
|
|
||||||
int exit_code = -1;
|
int exit_code = -1;
|
||||||
|
@ -1437,8 +1180,9 @@ int run_docker(const char *command_file) {
|
||||||
free(docker_command_with_binary);
|
free(docker_command_with_binary);
|
||||||
free(docker_command);
|
free(docker_command);
|
||||||
exit_code = DOCKER_RUN_FAILED;
|
exit_code = DOCKER_RUN_FAILED;
|
||||||
}
|
} else {
|
||||||
exit_code = 0;
|
exit_code = 0;
|
||||||
|
}
|
||||||
return exit_code;
|
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);
|
size_t command_size = MIN(sysconf(_SC_ARG_MAX), 128*1024);
|
||||||
|
|
||||||
docker_command_with_binary = calloc(sizeof(char), command_size);
|
docker_command_with_binary = (char *) alloc_and_clear_memory(command_size, sizeof(char));
|
||||||
docker_wait_command = calloc(sizeof(char), command_size);
|
docker_wait_command = (char *) alloc_and_clear_memory(command_size, sizeof(char));
|
||||||
docker_logs_command = calloc(sizeof(char), command_size);
|
docker_logs_command = (char *) alloc_and_clear_memory(command_size, sizeof(char));
|
||||||
docker_inspect_command = calloc(sizeof(char), command_size);
|
docker_inspect_command = (char *) alloc_and_clear_memory(command_size, sizeof(char));
|
||||||
docker_rm_command = calloc(sizeof(char), command_size);
|
docker_rm_command = (char *) alloc_and_clear_memory(command_size, sizeof(char));
|
||||||
|
|
||||||
gid_t user_gid = getegid();
|
gid_t user_gid = getegid();
|
||||||
uid_t prev_uid = geteuid();
|
uid_t prev_uid = geteuid();
|
||||||
|
|
||||||
char *docker_command = parse_docker_command_file(command_file);
|
char *docker_command = NULL;
|
||||||
char *docker_binary = get_section_value(DOCKER_BINARY_KEY, &executor_cfg);
|
char *docker_binary = NULL;
|
||||||
docker_binary = check_docker_binary(docker_binary);
|
|
||||||
|
|
||||||
fprintf(LOGFILE, "Creating script paths...\n");
|
fprintf(LOGFILE, "Creating script paths...\n");
|
||||||
exit_code = create_script_paths(
|
exit_code = create_script_paths(
|
||||||
|
@ -1617,6 +1360,9 @@ int launch_docker_container_as_user(const char * user, const char *app_id,
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
docker_command = construct_docker_command(command_file);
|
||||||
|
docker_binary = get_docker_binary(&CFG);
|
||||||
|
|
||||||
fprintf(LOGFILE, "Getting exit code file...\n");
|
fprintf(LOGFILE, "Getting exit code file...\n");
|
||||||
exit_code_file = get_exit_code_file(pid_file);
|
exit_code_file = get_exit_code_file(pid_file);
|
||||||
if (NULL == exit_code_file) {
|
if (NULL == exit_code_file) {
|
||||||
|
|
|
@ -59,16 +59,12 @@ enum operations {
|
||||||
#define MIN_USERID_KEY "min.user.id"
|
#define MIN_USERID_KEY "min.user.id"
|
||||||
#define BANNED_USERS_KEY "banned.users"
|
#define BANNED_USERS_KEY "banned.users"
|
||||||
#define ALLOWED_SYSTEM_USERS_KEY "allowed.system.users"
|
#define ALLOWED_SYSTEM_USERS_KEY "allowed.system.users"
|
||||||
#define DOCKER_BINARY_KEY "docker.binary"
|
|
||||||
#define DOCKER_SUPPORT_ENABLED_KEY "feature.docker.enabled"
|
#define DOCKER_SUPPORT_ENABLED_KEY "feature.docker.enabled"
|
||||||
#define TC_SUPPORT_ENABLED_KEY "feature.tc.enabled"
|
#define TC_SUPPORT_ENABLED_KEY "feature.tc.enabled"
|
||||||
#define TMP_DIR "tmp"
|
#define TMP_DIR "tmp"
|
||||||
|
|
||||||
extern struct passwd *user_detail;
|
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
|
//function used to load the configurations present in the secure config
|
||||||
void read_executor_config(const char* file_name);
|
void read_executor_config(const char* file_name);
|
||||||
|
|
||||||
|
@ -258,11 +254,6 @@ int is_docker_support_enabled();
|
||||||
*/
|
*/
|
||||||
int run_docker(const char *command_file);
|
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.
|
* Compile the regex_str and determine if the input string matches.
|
||||||
* Return 0 on match, 1 of non-match.
|
* Return 0 on match, 1 of non-match.
|
||||||
|
|
|
@ -29,12 +29,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "configuration.h"
|
|
||||||
#include "container-executor.h"
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.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
|
|
@ -20,30 +20,15 @@
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "container-executor.h"
|
#include "container-executor.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "get_executable.h"
|
||||||
#include "modules/gpu/gpu-module.h"
|
#include "modules/gpu/gpu-module.h"
|
||||||
#include "modules/cgroups/cgroups-operations.h"
|
#include "modules/cgroups/cgroups-operations.h"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <grp.h>
|
#include <grp.h>
|
||||||
#include <limits.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <signal.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#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) {
|
static void display_usage(FILE *stream) {
|
||||||
fprintf(stream,
|
fprintf(stream,
|
||||||
|
@ -145,25 +130,21 @@ of whether an explicit checksetup operation is requested. */
|
||||||
static void assert_valid_setup(char *argv0) {
|
static void assert_valid_setup(char *argv0) {
|
||||||
int ret;
|
int ret;
|
||||||
char *executable_file = get_executable(argv0);
|
char *executable_file = get_executable(argv0);
|
||||||
if (!executable_file) {
|
if (!executable_file || executable_file[0] == 0) {
|
||||||
fprintf(ERRORFILE, "realpath of executable: %s\n",
|
fprintf(ERRORFILE, "realpath of executable: %s\n",
|
||||||
errno != 0 ? strerror(errno) : "unknown");
|
errno != 0 ? strerror(errno) : "unknown");
|
||||||
flush_and_close_log_files();
|
flush_and_close_log_files();
|
||||||
exit(-1);
|
exit(INVALID_CONFIG_FILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *orig_conf_file = HADOOP_CONF_DIR "/" CONF_FILENAME;
|
char *conf_file = get_config_path(argv0);
|
||||||
char *conf_file = resolve_config_path(orig_conf_file, executable_file);
|
|
||||||
|
|
||||||
if (conf_file == NULL) {
|
if (conf_file == NULL) {
|
||||||
free(executable_file);
|
|
||||||
fprintf(ERRORFILE, "Configuration file %s not found.\n", orig_conf_file);
|
|
||||||
flush_and_close_log_files();
|
flush_and_close_log_files();
|
||||||
exit(INVALID_CONFIG_FILE);
|
exit(INVALID_CONFIG_FILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (check_configuration_permissions(conf_file) != 0) {
|
if (check_configuration_permissions(conf_file) != 0) {
|
||||||
free(executable_file);
|
|
||||||
flush_and_close_log_files();
|
flush_and_close_log_files();
|
||||||
exit(INVALID_CONFIG_FILE);
|
exit(INVALID_CONFIG_FILE);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,8 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "module-configs.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "configuration.h"
|
|
||||||
#include "container-executor.h"
|
|
||||||
#include "modules/common/constants.h"
|
#include "modules/common/constants.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#ifndef _MODULES_COMMON_MODULE_CONFIGS_H_
|
#ifndef _MODULES_COMMON_MODULE_CONFIGS_H_
|
||||||
#define _MODULES_COMMON_MODULE_CONFIGS_H_
|
#define _MODULES_COMMON_MODULE_CONFIGS_H_
|
||||||
|
|
||||||
|
#include "configuration.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* check if module enabled given name of module.
|
* check if module enabled given name of module.
|
||||||
|
|
|
@ -17,10 +17,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <regex.h>
|
||||||
|
|
||||||
char** split_delimiter(char *value, const char *delim) {
|
char** split_delimiter(char *value, const char *delim) {
|
||||||
char **return_values = NULL;
|
char **return_values = NULL;
|
||||||
|
@ -132,3 +132,61 @@ char* trim(const char* input) {
|
||||||
ret[val_end - val_begin] = '\0';
|
ret[val_end - val_begin] = '\0';
|
||||||
return ret;
|
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);
|
||||||
|
}
|
||||||
|
|
|
@ -19,7 +19,11 @@
|
||||||
#ifndef __YARN_POSIX_CONTAINER_EXECUTOR_UTIL_H__
|
#ifndef __YARN_POSIX_CONTAINER_EXECUTOR_UTIL_H__
|
||||||
#define __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 <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
enum errorcodes {
|
enum errorcodes {
|
||||||
INVALID_ARGUMENT_NUMBER = 1,
|
INVALID_ARGUMENT_NUMBER = 1,
|
||||||
|
@ -62,7 +66,7 @@ enum errorcodes {
|
||||||
ERROR_CREATE_CONTAINER_DIRECTORIES_ARGUMENTS = 38,
|
ERROR_CREATE_CONTAINER_DIRECTORIES_ARGUMENTS = 38,
|
||||||
ERROR_SANITIZING_DOCKER_COMMAND = 39,
|
ERROR_SANITIZING_DOCKER_COMMAND = 39,
|
||||||
DOCKER_IMAGE_INVALID = 40,
|
DOCKER_IMAGE_INVALID = 40,
|
||||||
DOCKER_CONTAINER_NAME_INVALID = 41,
|
// DOCKER_CONTAINER_NAME_INVALID = 41, (NOT USED)
|
||||||
ERROR_COMPILING_REGEX = 42
|
ERROR_COMPILING_REGEX = 42
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -119,4 +123,44 @@ void free_values(char **values);
|
||||||
*/
|
*/
|
||||||
char* trim(const char *input);
|
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
|
#endif
|
||||||
|
|
|
@ -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 <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -21,7 +21,6 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -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
|
|
@ -19,6 +19,7 @@
|
||||||
#include "container-executor.h"
|
#include "container-executor.h"
|
||||||
#include "utils/string-utils.h"
|
#include "utils/string-utils.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "get_executable.h"
|
||||||
#include "test/test-container-executor-common.h"
|
#include "test/test-container-executor-common.h"
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
@ -1155,145 +1156,6 @@ void test_trim_function() {
|
||||||
free(trimmed);
|
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
|
// This test is expected to be executed either by a regular
|
||||||
// user or by root. If executed by a regular user it doesn't
|
// user or by root. If executed by a regular user it doesn't
|
||||||
// test all the functions that would depend on changing the
|
// 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");
|
printf("\nTesting is_feature_enabled()\n");
|
||||||
test_is_feature_enabled();
|
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);
|
test_check_user(0);
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <sstream>
|
#include <vector>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
@ -135,4 +135,39 @@ namespace ContainerExecutor {
|
||||||
ASSERT_STREQ("foo", trimmed);
|
ASSERT_STREQ("foo", trimmed);
|
||||||
free(trimmed);
|
free(trimmed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(TestUtil, test_escape_single_quote) {
|
||||||
|
std::vector<std::pair<std::string, std::string> > input_output_vec;
|
||||||
|
input_output_vec.push_back(std::make_pair<std::string, std::string>("'abcd'", "'\"'\"'abcd'\"'\"'"));
|
||||||
|
input_output_vec.push_back(std::make_pair<std::string, std::string>("'", "'\"'\"'"));
|
||||||
|
|
||||||
|
std::vector<std::pair<std::string, std::string> >::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<char *>(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<char *>(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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,4 +90,37 @@
|
||||||
ASSERT_TRUE(0 != rc) << "Should failed\n";
|
ASSERT_TRUE(0 != rc) << "Should failed\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
} // namespace ContainerExecutor
|
File diff suppressed because it is too large
Load Diff
|
@ -295,32 +295,37 @@ public class TestDockerContainerRuntime {
|
||||||
List<String> args = op.getArguments();
|
List<String> args = op.getArguments();
|
||||||
String dockerCommandFile = args.get(11);
|
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<String> dockerCommands = Files.readAllLines(Paths.get
|
List<String> dockerCommands = Files.readAllLines(Paths.get
|
||||||
(dockerCommandFile), Charset.forName("UTF-8"));
|
(dockerCommandFile), Charset.forName("UTF-8"));
|
||||||
|
|
||||||
Assert.assertEquals(1, dockerCommands.size());
|
int expected = 13;
|
||||||
Assert.assertEquals(expectedCommand, dockerCommands.get(0));
|
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
|
@Test
|
||||||
|
@ -347,10 +352,13 @@ public class TestDockerContainerRuntime {
|
||||||
|
|
||||||
String uid = "";
|
String uid = "";
|
||||||
String gid = "";
|
String gid = "";
|
||||||
|
String[] groups = {};
|
||||||
Shell.ShellCommandExecutor shexec1 = new Shell.ShellCommandExecutor(
|
Shell.ShellCommandExecutor shexec1 = new Shell.ShellCommandExecutor(
|
||||||
new String[]{"id", "-u", runAsUser});
|
new String[]{"id", "-u", runAsUser});
|
||||||
Shell.ShellCommandExecutor shexec2 = new Shell.ShellCommandExecutor(
|
Shell.ShellCommandExecutor shexec2 = new Shell.ShellCommandExecutor(
|
||||||
new String[]{"id", "-g", runAsUser});
|
new String[]{"id", "-g", runAsUser});
|
||||||
|
Shell.ShellCommandExecutor shexec3 = new Shell.ShellCommandExecutor(
|
||||||
|
new String[]{"id", "-G", runAsUser});
|
||||||
try {
|
try {
|
||||||
shexec1.execute();
|
shexec1.execute();
|
||||||
// get rid of newline at the end
|
// get rid of newline at the end
|
||||||
|
@ -365,37 +373,48 @@ public class TestDockerContainerRuntime {
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.info("Could not run id -g command: " + 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;
|
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<String> dockerCommands = Files.readAllLines(
|
List<String> dockerCommands = Files.readAllLines(
|
||||||
Paths.get(dockerCommandFile), Charset.forName("UTF-8"));
|
Paths.get(dockerCommandFile), Charset.forName("UTF-8"));
|
||||||
|
|
||||||
Assert.assertEquals(1, dockerCommands.size());
|
Assert.assertEquals(14, dockerCommands.size());
|
||||||
//Assert.assertEquals(expectedCommand, dockerCommands.get(0));
|
int counter = 0;
|
||||||
Assert.assertTrue(dockerCommands.get(0).matches(expectedCommand));
|
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
|
@Test
|
||||||
|
@ -481,29 +500,38 @@ public class TestDockerContainerRuntime {
|
||||||
String dockerCommandFile = args.get(11);
|
String dockerCommandFile = args.get(11);
|
||||||
|
|
||||||
//This is the expected docker invocation for this case
|
//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<String> dockerCommands = Files
|
List<String> dockerCommands = Files
|
||||||
.readAllLines(Paths.get(dockerCommandFile), Charset.forName("UTF-8"));
|
.readAllLines(Paths.get(dockerCommandFile), Charset.forName("UTF-8"));
|
||||||
|
int expected = 13;
|
||||||
Assert.assertEquals(1, dockerCommands.size());
|
int counter = 0;
|
||||||
Assert.assertEquals(expectedCommand, dockerCommands.get(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
|
@Test
|
||||||
|
@ -537,30 +565,37 @@ public class TestDockerContainerRuntime {
|
||||||
|
|
||||||
//This is the expected docker invocation for this case. customNetwork1
|
//This is the expected docker invocation for this case. customNetwork1
|
||||||
// ("sdn1") is the expected network to be used in this case
|
// ("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<String> dockerCommands = Files
|
List<String> dockerCommands = Files
|
||||||
.readAllLines(Paths.get(dockerCommandFile), Charset.forName("UTF-8"));
|
.readAllLines(Paths.get(dockerCommandFile), Charset.forName("UTF-8"));
|
||||||
|
|
||||||
Assert.assertEquals(1, dockerCommands.size());
|
int expected = 13;
|
||||||
Assert.assertEquals(expectedCommand, dockerCommands.get(0));
|
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
|
//now set an explicit (non-default) allowedNetwork and ensure that it is
|
||||||
// used.
|
// used.
|
||||||
|
@ -575,28 +610,37 @@ public class TestDockerContainerRuntime {
|
||||||
|
|
||||||
//This is the expected docker invocation for this case. customNetwork2
|
//This is the expected docker invocation for this case. customNetwork2
|
||||||
// ("sdn2") is the expected network to be used in this case
|
// ("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
|
dockerCommands = Files
|
||||||
.readAllLines(Paths.get(dockerCommandFile), Charset.forName("UTF-8"));
|
.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
|
//disallowed network should trigger a launch failure
|
||||||
|
|
||||||
|
@ -630,7 +674,8 @@ public class TestDockerContainerRuntime {
|
||||||
List<String> dockerCommands = Files.readAllLines(Paths.get
|
List<String> dockerCommands = Files.readAllLines(Paths.get
|
||||||
(dockerCommandFile), Charset.forName("UTF-8"));
|
(dockerCommandFile), Charset.forName("UTF-8"));
|
||||||
|
|
||||||
Assert.assertEquals(1, dockerCommands.size());
|
int expected = 13;
|
||||||
|
Assert.assertEquals(expected, dockerCommands.size());
|
||||||
|
|
||||||
String command = dockerCommands.get(0);
|
String command = dockerCommands.get(0);
|
||||||
|
|
||||||
|
@ -738,13 +783,35 @@ public class TestDockerContainerRuntime {
|
||||||
List<String> dockerCommands = Files.readAllLines(Paths.get
|
List<String> dockerCommands = Files.readAllLines(Paths.get
|
||||||
(dockerCommandFile), Charset.forName("UTF-8"));
|
(dockerCommandFile), Charset.forName("UTF-8"));
|
||||||
|
|
||||||
Assert.assertEquals(1, dockerCommands.size());
|
int expected = 14;
|
||||||
|
int counter = 0;
|
||||||
String command = dockerCommands.get(0);
|
Assert.assertEquals(expected, dockerCommands.size());
|
||||||
|
Assert.assertEquals("[docker-command-execution]",
|
||||||
//submitting user is whitelisted. ensure --privileged is in the invocation
|
dockerCommands.get(counter++));
|
||||||
Assert.assertTrue("Did not find expected '--privileged' in docker run args "
|
Assert.assertEquals(" cap-add=SYS_CHROOT,NET_BIND_SERVICE",
|
||||||
+ ": " + command, command.contains("--privileged"));
|
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
|
@Test
|
||||||
|
@ -833,15 +900,33 @@ public class TestDockerContainerRuntime {
|
||||||
List<String> dockerCommands = Files.readAllLines(Paths.get
|
List<String> dockerCommands = Files.readAllLines(Paths.get
|
||||||
(dockerCommandFile), Charset.forName("UTF-8"));
|
(dockerCommandFile), Charset.forName("UTF-8"));
|
||||||
|
|
||||||
Assert.assertEquals(1, dockerCommands.size());
|
Assert.assertEquals(14, dockerCommands.size());
|
||||||
|
Assert.assertEquals("[docker-command-execution]", dockerCommands.get(0));
|
||||||
String command = dockerCommands.get(0);
|
Assert.assertEquals(" cap-add=SYS_CHROOT,NET_BIND_SERVICE",
|
||||||
|
dockerCommands.get(1));
|
||||||
Assert.assertTrue("Did not find expected " +
|
Assert.assertEquals(" cap-drop=ALL", dockerCommands.get(2));
|
||||||
"/test_local_dir/test_resource_file:test_mount mount in docker " +
|
Assert.assertEquals(" detach=true", dockerCommands.get(3));
|
||||||
"run args : " + command,
|
Assert.assertEquals(" docker-command=run", dockerCommands.get(4));
|
||||||
command.contains(" -v /test_local_dir/test_resource_file:test_mount" +
|
Assert.assertEquals(" hostname=ctr-id", dockerCommands.get(5));
|
||||||
":ro "));
|
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
|
@Test
|
||||||
|
@ -885,20 +970,35 @@ public class TestDockerContainerRuntime {
|
||||||
List<String> dockerCommands = Files.readAllLines(Paths.get
|
List<String> dockerCommands = Files.readAllLines(Paths.get
|
||||||
(dockerCommandFile), Charset.forName("UTF-8"));
|
(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
|
@Test
|
||||||
|
@ -930,8 +1030,10 @@ public class TestDockerContainerRuntime {
|
||||||
IOException {
|
IOException {
|
||||||
List<String> dockerCommands = getDockerCommandsForSignal(
|
List<String> dockerCommands = getDockerCommandsForSignal(
|
||||||
ContainerExecutor.Signal.TERM);
|
ContainerExecutor.Signal.TERM);
|
||||||
Assert.assertEquals(1, dockerCommands.size());
|
Assert.assertEquals(3, dockerCommands.size());
|
||||||
Assert.assertEquals("stop container_id", dockerCommands.get(0));
|
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
|
@Test
|
||||||
|
@ -940,8 +1042,10 @@ public class TestDockerContainerRuntime {
|
||||||
IOException {
|
IOException {
|
||||||
List<String> dockerCommands = getDockerCommandsForSignal(
|
List<String> dockerCommands = getDockerCommandsForSignal(
|
||||||
ContainerExecutor.Signal.KILL);
|
ContainerExecutor.Signal.KILL);
|
||||||
Assert.assertEquals(1, dockerCommands.size());
|
Assert.assertEquals(3, dockerCommands.size());
|
||||||
Assert.assertEquals("stop container_id", dockerCommands.get(0));
|
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
|
@Test
|
||||||
|
@ -950,8 +1054,10 @@ public class TestDockerContainerRuntime {
|
||||||
IOException {
|
IOException {
|
||||||
List<String> dockerCommands = getDockerCommandsForSignal(
|
List<String> dockerCommands = getDockerCommandsForSignal(
|
||||||
ContainerExecutor.Signal.QUIT);
|
ContainerExecutor.Signal.QUIT);
|
||||||
Assert.assertEquals(1, dockerCommands.size());
|
Assert.assertEquals(3, dockerCommands.size());
|
||||||
Assert.assertEquals("stop container_id", dockerCommands.get(0));
|
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<String> getDockerCommandsForSignal(
|
private List<String> getDockerCommandsForSignal(
|
||||||
|
|
|
@ -114,8 +114,10 @@ public class TestDockerCommandExecutor {
|
||||||
assertEquals(1, ops.size());
|
assertEquals(1, ops.size());
|
||||||
assertEquals(PrivilegedOperation.OperationType.RUN_DOCKER_CMD.name(),
|
assertEquals(PrivilegedOperation.OperationType.RUN_DOCKER_CMD.name(),
|
||||||
ops.get(0).getOperationType().name());
|
ops.get(0).getOperationType().name());
|
||||||
assertEquals(1, dockerCommands.size());
|
assertEquals(3, dockerCommands.size());
|
||||||
assertEquals("rm " + MOCK_CONTAINER_ID, dockerCommands.get(0));
|
assertEquals("[docker-command-execution]", dockerCommands.get(0));
|
||||||
|
assertEquals(" docker-command=rm", dockerCommands.get(1));
|
||||||
|
assertEquals(" name=" + MOCK_CONTAINER_ID, dockerCommands.get(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -130,8 +132,10 @@ public class TestDockerCommandExecutor {
|
||||||
assertEquals(1, ops.size());
|
assertEquals(1, ops.size());
|
||||||
assertEquals(PrivilegedOperation.OperationType.RUN_DOCKER_CMD.name(),
|
assertEquals(PrivilegedOperation.OperationType.RUN_DOCKER_CMD.name(),
|
||||||
ops.get(0).getOperationType().name());
|
ops.get(0).getOperationType().name());
|
||||||
assertEquals(1, dockerCommands.size());
|
assertEquals(3, dockerCommands.size());
|
||||||
assertEquals("stop " + MOCK_CONTAINER_ID, dockerCommands.get(0));
|
assertEquals("[docker-command-execution]", dockerCommands.get(0));
|
||||||
|
assertEquals(" docker-command=stop", dockerCommands.get(1));
|
||||||
|
assertEquals(" name=" + MOCK_CONTAINER_ID, dockerCommands.get(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -147,9 +151,12 @@ public class TestDockerCommandExecutor {
|
||||||
assertEquals(1, ops.size());
|
assertEquals(1, ops.size());
|
||||||
assertEquals(PrivilegedOperation.OperationType.RUN_DOCKER_CMD.name(),
|
assertEquals(PrivilegedOperation.OperationType.RUN_DOCKER_CMD.name(),
|
||||||
ops.get(0).getOperationType().name());
|
ops.get(0).getOperationType().name());
|
||||||
assertEquals(1, dockerCommands.size());
|
assertEquals(4, dockerCommands.size());
|
||||||
assertEquals("inspect --format='{{.State.Status}}' " + MOCK_CONTAINER_ID,
|
assertEquals("[docker-command-execution]", dockerCommands.get(0));
|
||||||
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
|
@Test
|
||||||
|
@ -165,8 +172,10 @@ public class TestDockerCommandExecutor {
|
||||||
assertEquals(1, ops.size());
|
assertEquals(1, ops.size());
|
||||||
assertEquals(PrivilegedOperation.OperationType.RUN_DOCKER_CMD.name(),
|
assertEquals(PrivilegedOperation.OperationType.RUN_DOCKER_CMD.name(),
|
||||||
ops.get(0).getOperationType().name());
|
ops.get(0).getOperationType().name());
|
||||||
assertEquals(1, dockerCommands.size());
|
assertEquals(3, dockerCommands.size());
|
||||||
assertEquals("pull " + MOCK_IMAGE_NAME, dockerCommands.get(0));
|
assertEquals("[docker-command-execution]", dockerCommands.get(0));
|
||||||
|
assertEquals(" docker-command=pull", dockerCommands.get(1));
|
||||||
|
assertEquals(" image=" + MOCK_IMAGE_NAME, dockerCommands.get(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -182,8 +191,12 @@ public class TestDockerCommandExecutor {
|
||||||
assertEquals(1, ops.size());
|
assertEquals(1, ops.size());
|
||||||
assertEquals(PrivilegedOperation.OperationType.RUN_DOCKER_CMD.name(),
|
assertEquals(PrivilegedOperation.OperationType.RUN_DOCKER_CMD.name(),
|
||||||
ops.get(0).getOperationType().name());
|
ops.get(0).getOperationType().name());
|
||||||
assertEquals(1, dockerCommands.size());
|
assertEquals(3, dockerCommands.size());
|
||||||
assertEquals("load --i=" + MOCK_LOCAL_IMAGE_NAME, dockerCommands.get(0));
|
assertEquals("[docker-command-execution]", dockerCommands.get(0));
|
||||||
|
assertEquals(" docker-command=load", dockerCommands.get(1));
|
||||||
|
assertEquals(" image=" + MOCK_LOCAL_IMAGE_NAME, dockerCommands.get(2));
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker;
|
package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import org.apache.hadoop.util.StringUtils;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -44,16 +46,29 @@ public class TestDockerInspectCommand {
|
||||||
@Test
|
@Test
|
||||||
public void testGetContainerStatus() throws Exception {
|
public void testGetContainerStatus() throws Exception {
|
||||||
dockerInspectCommand.getContainerStatus();
|
dockerInspectCommand.getContainerStatus();
|
||||||
assertEquals("inspect --format='{{.State.Status}}' foo",
|
assertEquals("inspect", StringUtils.join(",",
|
||||||
dockerInspectCommand.getCommandWithArguments());
|
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
|
@Test
|
||||||
public void testGetIpAndHost() throws Exception {
|
public void testGetIpAndHost() throws Exception {
|
||||||
dockerInspectCommand.getIpAndHost();
|
dockerInspectCommand.getIpAndHost();
|
||||||
assertEquals(
|
assertEquals("inspect", StringUtils.join(",",
|
||||||
"inspect --format='{{range(.NetworkSettings.Networks)}}{{.IPAddress}}"
|
dockerInspectCommand.getDockerCommandWithArguments()
|
||||||
+ ",{{end}}{{.Config.Hostname}}' foo",
|
.get("docker-command")));
|
||||||
dockerInspectCommand.getCommandWithArguments());
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -16,6 +16,7 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker;
|
package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker;
|
||||||
|
|
||||||
|
import org.apache.hadoop.util.StringUtils;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -42,7 +43,11 @@ public class TestDockerLoadCommand {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetCommandWithArguments() {
|
public void testGetCommandWithArguments() {
|
||||||
assertEquals("load --i=foo",
|
assertEquals("load", StringUtils.join(",",
|
||||||
dockerLoadCommand.getCommandWithArguments());
|
dockerLoadCommand.getDockerCommandWithArguments()
|
||||||
|
.get("docker-command")));
|
||||||
|
assertEquals("foo", StringUtils.join(",",
|
||||||
|
dockerLoadCommand.getDockerCommandWithArguments().get("image")));
|
||||||
|
assertEquals(2, dockerLoadCommand.getDockerCommandWithArguments().size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker;
|
package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker;
|
||||||
|
|
||||||
|
import org.apache.hadoop.util.StringUtils;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -42,7 +43,12 @@ public class TestDockerPullCommand {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetCommandWithArguments() {
|
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker;
|
package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import org.apache.hadoop.util.StringUtils;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -42,7 +44,11 @@ public class TestDockerRmCommand {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetCommandWithArguments() {
|
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker;
|
package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker;
|
||||||
|
|
||||||
|
import org.apache.hadoop.util.StringUtils;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -56,8 +57,24 @@ public class TestDockerRunCommand {
|
||||||
commands.add("launch_command");
|
commands.add("launch_command");
|
||||||
dockerRunCommand.setOverrideCommandWithArgs(commands);
|
dockerRunCommand.setOverrideCommandWithArgs(commands);
|
||||||
dockerRunCommand.removeContainerOnExit();
|
dockerRunCommand.removeContainerOnExit();
|
||||||
assertEquals("run --name=foo --user=user_id --device=source:dest --rm "
|
|
||||||
+ "image_name launch_command",
|
assertEquals("run", StringUtils.join(",",
|
||||||
dockerRunCommand.getCommandWithArguments());
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -21,6 +21,8 @@
|
||||||
package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker;
|
package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import org.apache.hadoop.util.StringUtils;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -48,8 +50,13 @@ public class TestDockerStopCommand {
|
||||||
@Test
|
@Test
|
||||||
public void testSetGracePeriod() throws Exception {
|
public void testSetGracePeriod() throws Exception {
|
||||||
dockerStopCommand.setGracePeriod(GRACE_PERIOD);
|
dockerStopCommand.setGracePeriod(GRACE_PERIOD);
|
||||||
assertEquals("stop foo --time=10",
|
assertEquals("stop", StringUtils.join(",",
|
||||||
dockerStopCommand.getCommandWithArguments());
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -167,7 +167,24 @@ The following properties are required to enable Docker support:
|
||||||
|Configuration Name | Description |
|
|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. |
|
| `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:
|
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 |
|
| `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. |
|
| `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. |
|
| `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. |
|
| `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
|
Docker Image Requirements
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue