diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java index 8d34f4e..4aca943 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java @@ -1120,6 +1120,44 @@ private static void addDeprecatedKeys() { public static final String NM_DEFAULT_DOCKER_CONTAINER_EXECUTOR_EXEC_NAME = "/usr/bin/docker"; + /** Prefix for runtime configuration constants. */ + public static final String LINUX_CONTAINER_RUNTIME_PREFIX = NM_PREFIX + + "runtime.linux."; + public static final String DOCKER_CONTAINER_RUNTIME_PREFIX = + LINUX_CONTAINER_RUNTIME_PREFIX + "docker."; + + /** Capabilities allowed (and added by default) for docker containers. **/ + public static final String NM_DOCKER_CONTAINER_CAPABILITIES = + DOCKER_CONTAINER_RUNTIME_PREFIX + "capabilities"; + + /** These are the default capabilities added by docker. We'll use the same + * set here. While these may not be case-sensitive from a docker + * perspective, it is best to keep these uppercase. + */ + public static final String[] DEFAULT_NM_DOCKER_CONTAINER_CAPABILITIES = { + "CHOWN", + "DAC_OVERRIDE", + "FSETID", + "FOWNER", + "MKNOD", + "NET_RAW", + "SETGID", + "SETUID", + "SETFCAP", + "SETPCAP", + "NET_BIND_SERVICE", + "SYS_CHROOT", + "KILL", + "AUDIT_WRITE" }; + + /** Allow privileged containers. Use with extreme care. */ + public static final String NM_DOCKER_ALLOW_PRIVILEGED_CONTAINERS = + DOCKER_CONTAINER_RUNTIME_PREFIX + "allow-privileged-containers"; + + /** Privileged containers are disabled by default. */ + public static final boolean DEFAULT_NM_DOCKER_ALLOW_PRIVILEGED_CONTAINERS = + false; + /** The path to the Linux container executor.*/ public static final String NM_LINUX_CONTAINER_EXECUTOR_PATH = NM_PREFIX + "linux-container-executor.path"; diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/resources/yarn-default.xml hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/resources/yarn-default.xml index e3d2c69..2ebad31 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/resources/yarn-default.xml +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/resources/yarn-default.xml @@ -1420,6 +1420,23 @@ + This configuration setting determines the capabilities + assigned to docker containers when they are launched. While these may not + be case-sensitive from a docker perspective, it is best to keep these + uppercase. + yarn.nodemanager.runtime.linux.docker.capabilities + CHOWN,DAC_OVERRIDE,FSETID,FOWNER,MKNOD,NET_RAW,SETGID,SETUID,SETFCAP,SETPCAP,NET_BIND_SERVICE,SYS_CHROOT,KILL,AUDIT_WRITE + + + + This configuration setting determines if + privileged docker containers are allowed on this cluster. + Use with extreme care. + yarn.nodemanager.runtime.linux.docker.allow-privileged-containers + false + + + This flag determines whether memory limit will be set for the Windows Job Object of the containers launched by the default container executor. yarn.nodemanager.windows-container.memory-limit.enabled diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/DockerLinuxContainerRuntime.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/DockerLinuxContainerRuntime.java index 2430a78..9974cc2 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/DockerLinuxContainerRuntime.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/DockerLinuxContainerRuntime.java @@ -26,7 +26,10 @@ import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; +import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.util.StringUtils; +import org.apache.hadoop.yarn.conf.YarnConfiguration; +import org.apache.hadoop.yarn.security.AdminACLsManager; import org.apache.hadoop.yarn.server.nodemanager.containermanager.container.Container; import org.apache.hadoop.yarn.server.nodemanager.containermanager.launcher.ContainerLaunch; import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.PrivilegedOperation; @@ -43,8 +46,11 @@ import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.*; @@ -63,11 +69,14 @@ @InterfaceAudience.Private public static final String ENV_DOCKER_CONTAINER_RUN_OVERRIDE_DISABLE = "YARN_CONTAINER_RUNTIME_DOCKER_RUN_OVERRIDE_DISABLE"; - + @InterfaceAudience.Private + public static final String ENV_DOCKER_CONTAINER_RUN_PRIVILEGED_CONTAINER = + "YARN_CONTAINER_RUNTIME_DOCKER_RUN_PRIVILEGED_CONTAINER"; private Configuration conf; private DockerClient dockerClient; private PrivilegedOperationExecutor privilegedOperationExecutor; + private AdminACLsManager adminACLsManager; public static boolean isDockerContainerRequested( Map env) { @@ -90,6 +99,7 @@ public void initialize(Configuration conf) throws ContainerExecutionException { this.conf = conf; dockerClient = new DockerClient(conf); + adminACLsManager = new AdminACLsManager(conf); } @Override @@ -131,6 +141,70 @@ public void addCGroupParentIfRequired(String resourcesOptions, } } + private boolean allowPrivilegedContainerExecution(Container container) + throws ContainerExecutionException { + //For a privileged container to be run all of the following three conditions + // must be satisfied: + //1) Submitting user must request for a privileged container + //2) Privileged containers must be enabled on the cluster + //3) Submitting user must be an admin. + + Map environment = container.getLaunchContext() + .getEnvironment(); + String runPrivilegedContainerEnvVar = environment + .get(ENV_DOCKER_CONTAINER_RUN_PRIVILEGED_CONTAINER); + + if (runPrivilegedContainerEnvVar == null) { + return false; + } + + if (!runPrivilegedContainerEnvVar.equalsIgnoreCase("true")) { + LOG.warn("NOT running a privileged container. Value of " + + ENV_DOCKER_CONTAINER_RUN_PRIVILEGED_CONTAINER + + "is invalid: " + runPrivilegedContainerEnvVar); + return false; + } + + if (LOG.isInfoEnabled()) { + LOG.info("Privileged container requested for : " + container + .getContainerId().toString()); + } + + //Ok, so we have been asked to run a privileged container. Security + // checks need to be run. Each violation is an error. + + //check if privileged containers are enabled. + boolean privilegedContainersEnabledOnCluster = conf.getBoolean( + YarnConfiguration.NM_DOCKER_ALLOW_PRIVILEGED_CONTAINERS, + YarnConfiguration.DEFAULT_NM_DOCKER_ALLOW_PRIVILEGED_CONTAINERS); + + if (!privilegedContainersEnabledOnCluster) { + String errorMsg = "Privileged container being requested but privileged " + + "containers are not enabled on this cluster"; + LOG.error(errorMsg); + throw new ContainerExecutionException(errorMsg); + } + + //check if submitting user is an admin. + String submittingUser = container.getUser(); + UserGroupInformation submitterUgi = UserGroupInformation + .createRemoteUser(submittingUser); + + if (!adminACLsManager.isAdmin(submitterUgi)) { + String errorMsg = "Cannot launch privileged container. Submitting user (" + + submittingUser + ") fails admin ACL check."; + LOG.error(errorMsg); + throw new ContainerExecutionException(errorMsg); + } + + if (LOG.isInfoEnabled()) { + LOG.info("All checks pass. Launching privileged container for : " + + container.getContainerId().toString()); + } + + return true; + } + @Override public void launchContainer(ContainerRuntimeContext ctx) @@ -154,12 +228,17 @@ public void launchContainer(ContainerRuntimeContext ctx) List localDirs = ctx.getExecutionAttribute(LOCAL_DIRS); @SuppressWarnings("unchecked") List logDirs = ctx.getExecutionAttribute(LOG_DIRS); + Set capabilities = new HashSet<>(Arrays.asList(conf.getStrings( + YarnConfiguration.NM_DOCKER_CONTAINER_CAPABILITIES, + YarnConfiguration.DEFAULT_NM_DOCKER_CONTAINER_CAPABILITIES))); + @SuppressWarnings("unchecked") DockerRunCommand runCommand = new DockerRunCommand(containerIdStr, runAsUser, imageName) .detachOnRun() .setContainerWorkDir(containerWorkDir.toString()) .setNetworkType("host") + .setCapabilities(capabilities) .addMountLocation("/etc/passwd", "/etc/password:ro"); List allDirs = new ArrayList<>(localDirs); @@ -169,6 +248,10 @@ public void launchContainer(ContainerRuntimeContext ctx) runCommand.addMountLocation(dir, dir); } + if (allowPrivilegedContainerExecution(container)) { + runCommand.setPrivileged(); + } + String resourcesOpts = ctx.getExecutionAttribute(RESOURCES_OPTIONS); /** Disabling docker's cgroup parent support for the time being. Docker diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerRunCommand.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerRunCommand.java index f9a890e..7c49ef9 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerRunCommand.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/docker/DockerRunCommand.java @@ -24,6 +24,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Set; public class DockerRunCommand extends DockerCommand { private static final String RUN_COMMAND = "run"; @@ -68,6 +69,23 @@ public DockerRunCommand setCGroupParent(String parentPath) { return this; } + /* Run a privileged container. Use with extreme care */ + public DockerRunCommand setPrivileged() { + super.addCommandArguments("--privileged"); + return this; + } + + public DockerRunCommand setCapabilities(Set capabilties) { + //first, drop all capabilities + super.addCommandArguments("--cap-drop=ALL"); + + //now, add the capabilities supplied + for (String capability : capabilties) { + super.addCommandArguments("--cap-add=" + capability); + } + + return this; + } public DockerRunCommand addDevice(String sourceDevice, String destinationDevice) { super.addCommandArguments("--device=" + sourceDevice + ":" + diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/TestDockerContainerRuntime.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/TestDockerContainerRuntime.java index 31ed496..a968137 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/TestDockerContainerRuntime.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/TestDockerContainerRuntime.java @@ -20,10 +20,13 @@ package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.yarn.api.records.ContainerId; import org.apache.hadoop.yarn.api.records.ContainerLaunchContext; +import org.apache.hadoop.yarn.conf.YarnConfiguration; import org.apache.hadoop.yarn.server.nodemanager.LocalDirsHandlerService; import org.apache.hadoop.yarn.server.nodemanager.containermanager.container.Container; import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.PrivilegedOperation; @@ -44,15 +47,20 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.*; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.*; public class TestDockerContainerRuntime { + private static final Log LOG = LogFactory + .getLog(TestDockerContainerRuntime.class); private Configuration conf; PrivilegedOperationExecutor mockExecutor; String containerId; @@ -72,6 +80,9 @@ List localDirs; List logDirs; String resourcesOptions; + ContainerRuntimeContext.Builder builder; + String submittingUser = "test"; + String adminUser = "admin"; @Before public void setup() { @@ -96,6 +107,7 @@ public void setup() { when(cId.toString()).thenReturn(containerId); when(container.getLaunchContext()).thenReturn(context); when(context.getEnvironment()).thenReturn(env); + when(container.getUser()).thenReturn(submittingUser); runAsUser = "run_as_user"; user = "user"; @@ -111,6 +123,22 @@ public void setup() { localDirs.add("/test_local_dir"); logDirs.add("/test_log_dir"); + + builder = new ContainerRuntimeContext + .Builder(container); + + builder.setExecutionAttribute(RUN_AS_USER, runAsUser) + .setExecutionAttribute(USER, user) + .setExecutionAttribute(APPID, appId) + .setExecutionAttribute(CONTAINER_ID_STR, containerIdStr) + .setExecutionAttribute(CONTAINER_WORK_DIR, containerWorkDir) + .setExecutionAttribute(NM_PRIVATE_CONTAINER_SCRIPT_PATH, + nmPrivateContainerScriptPath) + .setExecutionAttribute(NM_PRIVATE_TOKENS_PATH, nmPrivateTokensPath) + .setExecutionAttribute(PID_FILE_PATH, pidFilePath) + .setExecutionAttribute(LOCAL_DIRS, localDirs) + .setExecutionAttribute(LOG_DIRS, logDirs) + .setExecutionAttribute(RESOURCES_OPTIONS, resourcesOptions); } @Test @@ -129,33 +157,9 @@ public void testSelectDockerContainerType() { .isDockerContainerRequested(envOtherType)); } - @Test @SuppressWarnings("unchecked") - public void testDockerContainerLaunch() - throws ContainerExecutionException, PrivilegedOperationException, - IOException { - DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime( - mockExecutor); - runtime.initialize(conf); - - ContainerRuntimeContext.Builder builder = new ContainerRuntimeContext - .Builder(container); - - builder.setExecutionAttribute(RUN_AS_USER, runAsUser) - .setExecutionAttribute(USER, user) - .setExecutionAttribute(APPID, appId) - .setExecutionAttribute(CONTAINER_ID_STR, containerIdStr) - .setExecutionAttribute(CONTAINER_WORK_DIR, containerWorkDir) - .setExecutionAttribute(NM_PRIVATE_CONTAINER_SCRIPT_PATH, - nmPrivateContainerScriptPath) - .setExecutionAttribute(NM_PRIVATE_TOKENS_PATH, nmPrivateTokensPath) - .setExecutionAttribute(PID_FILE_PATH, pidFilePath) - .setExecutionAttribute(LOCAL_DIRS, localDirs) - .setExecutionAttribute(LOG_DIRS, logDirs) - .setExecutionAttribute(RESOURCES_OPTIONS, resourcesOptions); - - runtime.launchContainer(builder.build()); - + private PrivilegedOperation capturePrivilegedOperationAndVerifyArgs() + throws PrivilegedOperationException { ArgumentCaptor opCaptor = ArgumentCaptor.forClass( PrivilegedOperation.class); @@ -186,20 +190,51 @@ public void testDockerContainerLaunch() Assert.assertEquals(containerId, args.get(4)); Assert.assertEquals(containerWorkDir.toString(), args.get(5)); Assert.assertEquals(nmPrivateContainerScriptPath.toUri() - .toString(), args.get(6)); + .toString(), args.get(6)); Assert.assertEquals(nmPrivateTokensPath.toUri().getPath(), args.get(7)); Assert.assertEquals(pidFilePath.toString(), args.get(8)); Assert.assertEquals(localDirs.get(0), args.get(9)); Assert.assertEquals(logDirs.get(0), args.get(10)); Assert.assertEquals(resourcesOptions, args.get(12)); + return op; + } + + @Test + public void testDockerContainerLaunch() + throws ContainerExecutionException, PrivilegedOperationException, + IOException { + DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime( + mockExecutor); + runtime.initialize(conf); + + String[] testCapabilities = {"NET_BIND_SERVICE", "SYS_CHROOT"}; + + conf.setStrings(YarnConfiguration.NM_DOCKER_CONTAINER_CAPABILITIES, + testCapabilities); + runtime.launchContainer(builder.build()); + + PrivilegedOperation op = capturePrivilegedOperationAndVerifyArgs(); + List args = op.getArguments(); String dockerCommandFile = args.get(11); + /* Ordering of capabilities depends on HashSet ordering. */ + Set capabilitySet = new HashSet<>(Arrays.asList(testCapabilities)); + StringBuilder expectedCapabilitiesString = new StringBuilder( + "--cap-drop=ALL "); + + for(String capability : capabilitySet) { + expectedCapabilitiesString.append("--cap-add=").append(capability) + .append(" "); + } + //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 -v /etc/passwd:/etc/password:ro ") + .append("--net=host ") + .append(expectedCapabilitiesString) + .append("-v /etc/passwd:/etc/password:ro ") .append("-v %4$s:%4$s ") .append("-v %5$s:%5$s ") .append("-v %6$s:%6$s ") @@ -216,4 +251,146 @@ public void testDockerContainerLaunch() Assert.assertEquals(1, dockerCommands.size()); Assert.assertEquals(expectedCommand, dockerCommands.get(0)); } + + @Test + public void testLaunchPrivilegedContainersInvalidEnvVar() + throws ContainerExecutionException, PrivilegedOperationException, + IOException{ + DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime( + mockExecutor); + runtime.initialize(conf); + + env.put("YARN_CONTAINER_RUNTIME_DOCKER_RUN_PRIVILEGED_CONTAINER", + "invalid-value"); + runtime.launchContainer(builder.build()); + + PrivilegedOperation op = capturePrivilegedOperationAndVerifyArgs(); + List args = op.getArguments(); + String dockerCommandFile = args.get(11); + + List dockerCommands = Files.readAllLines(Paths.get + (dockerCommandFile), Charset.forName("UTF-8")); + + Assert.assertEquals(1, dockerCommands.size()); + + String command = dockerCommands.get(0); + + //ensure --privileged isn't in the invocation + Assert.assertTrue("Unexpected --privileged in docker run args : " + command, + !command.contains("--privileged")); + } + + @Test + public void testLaunchPrivilegedContainersWithDisabledSetting() + throws ContainerExecutionException, PrivilegedOperationException, + IOException{ + DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime( + mockExecutor); + runtime.initialize(conf); + + env.put("YARN_CONTAINER_RUNTIME_DOCKER_RUN_PRIVILEGED_CONTAINER", + "true"); + + try { + runtime.launchContainer(builder.build()); + Assert.fail("Expected a privileged launch container failure."); + } catch (ContainerExecutionException e) { + LOG.info("Caught expected exception : " + e); + } + } + + @Test + public void testLaunchPrivilegedContainersWithEnabledSettingAndDefaultACL() + throws ContainerExecutionException, PrivilegedOperationException, + IOException{ + //Enable privileged containers. + conf.setBoolean(YarnConfiguration.NM_DOCKER_ALLOW_PRIVILEGED_CONTAINERS, + true); + + DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime( + mockExecutor); + runtime.initialize(conf); + + env.put("YARN_CONTAINER_RUNTIME_DOCKER_RUN_PRIVILEGED_CONTAINER", + "true"); + + runtime.launchContainer(builder.build()); + PrivilegedOperation op = capturePrivilegedOperationAndVerifyArgs(); + List args = op.getArguments(); + String dockerCommandFile = args.get(11); + + List dockerCommands = Files.readAllLines(Paths.get + (dockerCommandFile), Charset.forName("UTF-8")); + + Assert.assertEquals(1, dockerCommands.size()); + + String command = dockerCommands.get(0); + + //by default yarn.admin.acl allows everyone to be an admin. + //ensure --privileged is in the invocation + Assert.assertTrue("Did not find expected '--privileged' in docker run args " + + ": " + command, command.contains("--privileged")); + } + + + @Test + public void testLaunchPrivilegedContainersWithEnabledSettingAndNonAdminUser() + throws ContainerExecutionException, PrivilegedOperationException, + IOException{ + //Enable privileged containers. + conf.setBoolean(YarnConfiguration.NM_DOCKER_ALLOW_PRIVILEGED_CONTAINERS, + true); + //Add user to admin user list. + conf.set(YarnConfiguration.YARN_ADMIN_ACL, adminUser); + + DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime( + mockExecutor); + runtime.initialize(conf); + + env.put("YARN_CONTAINER_RUNTIME_DOCKER_RUN_PRIVILEGED_CONTAINER", + "true"); + + try { + runtime.launchContainer(builder.build()); + Assert.fail("Expected a privileged launch container failure."); + } catch (ContainerExecutionException e) { + LOG.info("Caught expected exception : " + e); + } + } + + @Test + public void testLaunchPrivilegedContainersWithEnabledSettingAndAdminUser() + throws ContainerExecutionException, PrivilegedOperationException, + IOException{ + //Enable privileged containers. + conf.setBoolean(YarnConfiguration.NM_DOCKER_ALLOW_PRIVILEGED_CONTAINERS, + true); + //Add user to admin user list. + conf.set(YarnConfiguration.YARN_ADMIN_ACL, submittingUser); + + DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime( + mockExecutor); + runtime.initialize(conf); + + env.put("YARN_CONTAINER_RUNTIME_DOCKER_RUN_PRIVILEGED_CONTAINER", + "true"); + + runtime.launchContainer(builder.build()); + PrivilegedOperation op = capturePrivilegedOperationAndVerifyArgs(); + List args = op.getArguments(); + String dockerCommandFile = args.get(11); + + List dockerCommands = Files.readAllLines(Paths.get + (dockerCommandFile), Charset.forName("UTF-8")); + + Assert.assertEquals(1, dockerCommands.size()); + + String command = dockerCommands.get(0); + + //by default yarn.admin.acl allows everyone to be an admin. + //ensure --privileged is in the invocation + Assert.assertTrue("Did not find expected '--privileged' in docker run args " + + ": " + command, command.contains("--privileged")); + } + }