Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 3BE33200AC6 for ; Fri, 6 May 2016 17:47:54 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 3A959160A0C; Fri, 6 May 2016 15:47:54 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 3D2831608F8 for ; Fri, 6 May 2016 17:47:53 +0200 (CEST) Received: (qmail 30689 invoked by uid 500); 6 May 2016 15:47:52 -0000 Mailing-List: contact common-commits-help@hadoop.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Delivered-To: mailing list common-commits@hadoop.apache.org Received: (qmail 30671 invoked by uid 99); 6 May 2016 15:47:52 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 06 May 2016 15:47:52 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 1C7FBDFFAB; Fri, 6 May 2016 15:47:52 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: aw@apache.org To: common-commits@hadoop.apache.org Date: Fri, 06 May 2016 15:47:54 -0000 Message-Id: In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [03/28] hadoop git commit: YARN-4595. Add support for configurable read-only mounts when launching Docker containers. Contributed by Billie Rinaldi. archived-at: Fri, 06 May 2016 15:47:54 -0000 YARN-4595. Add support for configurable read-only mounts when launching Docker containers. Contributed by Billie Rinaldi. Project: http://git-wip-us.apache.org/repos/asf/hadoop/repo Commit: http://git-wip-us.apache.org/repos/asf/hadoop/commit/72b04771 Tree: http://git-wip-us.apache.org/repos/asf/hadoop/tree/72b04771 Diff: http://git-wip-us.apache.org/repos/asf/hadoop/diff/72b04771 Branch: refs/heads/HADOOP-12930 Commit: 72b047715c1ae89dff3dfe76c1af91dfe255ed70 Parents: 9d3fcdf Author: Varun Vasudev Authored: Thu May 5 13:01:54 2016 +0530 Committer: Varun Vasudev Committed: Thu May 5 13:01:54 2016 +0530 ---------------------------------------------------------------------- .../runtime/DockerLinuxContainerRuntime.java | 47 ++++++++ .../runtime/TestDockerContainerRuntime.java | 115 +++++++++++++++++++ 2 files changed, 162 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/hadoop/blob/72b04771/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 ---------------------------------------------------------------------- diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/DockerLinuxContainerRuntime.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/DockerLinuxContainerRuntime.java index 681cae2..43d8b4e 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/DockerLinuxContainerRuntime.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/DockerLinuxContainerRuntime.java @@ -45,11 +45,14 @@ import org.apache.hadoop.yarn.server.nodemanager.containermanager.runtime.Contai import org.apache.hadoop.yarn.server.nodemanager.containermanager.runtime.ContainerRuntimeContext; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.*; @@ -72,6 +75,9 @@ public class DockerLinuxContainerRuntime implements LinuxContainerRuntime { @InterfaceAudience.Private public static final String ENV_DOCKER_CONTAINER_RUN_PRIVILEGED_CONTAINER = "YARN_CONTAINER_RUNTIME_DOCKER_RUN_PRIVILEGED_CONTAINER"; + @InterfaceAudience.Private + public static final String ENV_DOCKER_CONTAINER_LOCAL_RESOURCE_MOUNTS = + "YARN_CONTAINER_RUNTIME_DOCKER_LOCAL_RESOURCE_MOUNTS"; private Configuration conf; private DockerClient dockerClient; @@ -225,6 +231,27 @@ public class DockerLinuxContainerRuntime implements LinuxContainerRuntime { return true; } + @VisibleForTesting + protected String validateMount(String mount, + Map> localizedResources) + throws ContainerExecutionException { + for (Entry> resource : localizedResources.entrySet()) { + if (resource.getValue().contains(mount)) { + java.nio.file.Path path = Paths.get(resource.getKey().toString()); + if (!path.isAbsolute()) { + throw new ContainerExecutionException("Mount must be absolute: " + + mount); + } + if (Files.isSymbolicLink(path)) { + throw new ContainerExecutionException("Mount cannot be a symlink: " + + mount); + } + return path.toString(); + } + } + throw new ContainerExecutionException("Mount must be a localized " + + "resource: " + mount); + } @Override public void launchContainer(ContainerRuntimeContext ctx) @@ -254,6 +281,9 @@ public class DockerLinuxContainerRuntime implements LinuxContainerRuntime { @SuppressWarnings("unchecked") List containerLogDirs = ctx.getExecutionAttribute( CONTAINER_LOG_DIRS); + @SuppressWarnings("unchecked") + Map> localizedResources = ctx.getExecutionAttribute( + LOCALIZED_RESOURCES); Set capabilities = new HashSet<>(Arrays.asList(conf.getStrings( YarnConfiguration.NM_DOCKER_CONTAINER_CAPABILITIES, YarnConfiguration.DEFAULT_NM_DOCKER_CONTAINER_CAPABILITIES))); @@ -274,6 +304,23 @@ public class DockerLinuxContainerRuntime implements LinuxContainerRuntime { runCommand.addMountLocation(dir, dir); } + if (environment.containsKey(ENV_DOCKER_CONTAINER_LOCAL_RESOURCE_MOUNTS)) { + String mounts = environment.get( + ENV_DOCKER_CONTAINER_LOCAL_RESOURCE_MOUNTS); + if (!mounts.isEmpty()) { + for (String mount : StringUtils.split(mounts)) { + String[] dir = StringUtils.split(mount, ':'); + if (dir.length != 2) { + throw new ContainerExecutionException("Invalid mount : " + + mount); + } + String src = validateMount(dir[0], localizedResources); + String dst = dir[1]; + runCommand.addMountLocation(src, dst + ":ro"); + } + } + } + if (allowPrivilegedContainerExecution(container)) { runCommand.setPrivileged(); } http://git-wip-us.apache.org/repos/asf/hadoop/blob/72b04771/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 ---------------------------------------------------------------------- diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/TestDockerContainerRuntime.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/TestDockerContainerRuntime.java index d1bdabe..538b03f 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/TestDockerContainerRuntime.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/runtime/TestDockerContainerRuntime.java @@ -49,6 +49,7 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -83,6 +84,7 @@ public class TestDockerContainerRuntime { List logDirs; List containerLocalDirs; List containerLogDirs; + Map> localizedResources; String resourcesOptions; ContainerRuntimeContext.Builder builder; String submittingUser = "anakin"; @@ -127,11 +129,14 @@ public class TestDockerContainerRuntime { resourcesOptions = "cgroups=none"; containerLocalDirs = new ArrayList<>(); containerLogDirs = new ArrayList<>(); + localizedResources = new HashMap<>(); localDirs.add("/test_local_dir"); logDirs.add("/test_log_dir"); containerLocalDirs.add("/test_container_local_dir"); containerLogDirs.add("/test_container_log_dir"); + localizedResources.put(new Path("/test_local_dir/test_resource_file"), + Collections.singletonList("test_dir/test_resource_file")); builder = new ContainerRuntimeContext .Builder(container); @@ -149,6 +154,7 @@ public class TestDockerContainerRuntime { .setExecutionAttribute(LOG_DIRS, logDirs) .setExecutionAttribute(CONTAINER_LOCAL_DIRS, containerLocalDirs) .setExecutionAttribute(CONTAINER_LOG_DIRS, containerLogDirs) + .setExecutionAttribute(LOCALIZED_RESOURCES, localizedResources) .setExecutionAttribute(RESOURCES_OPTIONS, resourcesOptions); } @@ -445,4 +451,113 @@ public class TestDockerContainerRuntime { //no --cgroup-parent should be added in either case Mockito.verifyZeroInteractions(command); } + + @Test + public void testMountSourceOnly() + throws ContainerExecutionException, PrivilegedOperationException, + IOException{ + DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime( + mockExecutor, mockCGroupsHandler); + runtime.initialize(conf); + + env.put( + DockerLinuxContainerRuntime.ENV_DOCKER_CONTAINER_LOCAL_RESOURCE_MOUNTS, + "source"); + + try { + runtime.launchContainer(builder.build()); + Assert.fail("Expected a launch container failure due to invalid mount."); + } catch (ContainerExecutionException e) { + LOG.info("Caught expected exception : " + e); + } + } + + @Test + public void testMountSourceTarget() + throws ContainerExecutionException, PrivilegedOperationException, + IOException{ + DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime( + mockExecutor, mockCGroupsHandler); + runtime.initialize(conf); + + env.put( + DockerLinuxContainerRuntime.ENV_DOCKER_CONTAINER_LOCAL_RESOURCE_MOUNTS, + "test_dir/test_resource_file:test_mount"); + + 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); + + Assert.assertTrue("Did not find expected " + + "/test_local_dir/test_resource_file:test_mount mount in docker " + + "run args : " + command, + command.contains(" -v /test_local_dir/test_resource_file:test_mount" + + ":ro ")); + } + + @Test + public void testMountInvalid() + throws ContainerExecutionException, PrivilegedOperationException, + IOException{ + DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime( + mockExecutor, mockCGroupsHandler); + runtime.initialize(conf); + + env.put( + DockerLinuxContainerRuntime.ENV_DOCKER_CONTAINER_LOCAL_RESOURCE_MOUNTS, + "source:target:other"); + + try { + runtime.launchContainer(builder.build()); + Assert.fail("Expected a launch container failure due to invalid mount."); + } catch (ContainerExecutionException e) { + LOG.info("Caught expected exception : " + e); + } + } + + @Test + public void testMountMultiple() + throws ContainerExecutionException, PrivilegedOperationException, + IOException{ + DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime( + mockExecutor, mockCGroupsHandler); + runtime.initialize(conf); + + env.put( + DockerLinuxContainerRuntime.ENV_DOCKER_CONTAINER_LOCAL_RESOURCE_MOUNTS, + "test_dir/test_resource_file:test_mount1," + + "test_dir/test_resource_file:test_mount2"); + + 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); + + 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 ")); + } + } --------------------------------------------------------------------- To unsubscribe, e-mail: common-commits-unsubscribe@hadoop.apache.org For additional commands, e-mail: common-commits-help@hadoop.apache.org