diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/containerlaunch/AbstractLauncher.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/containerlaunch/AbstractLauncher.java index dc51b250723..da5a8d6dbbd 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/containerlaunch/AbstractLauncher.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/containerlaunch/AbstractLauncher.java @@ -46,6 +46,8 @@ private static final Logger log = LoggerFactory.getLogger(AbstractLauncher.class); public static final String CLASSPATH = "CLASSPATH"; + public static final String ENV_DOCKER_CONTAINER_MOUNTS = + "YARN_CONTAINER_RUNTIME_DOCKER_MOUNTS"; /** * Env vars; set up at final launch stage */ @@ -153,17 +155,23 @@ public ContainerLaunchContext completeContainerLaunch() throws IOException { env.put("YARN_CONTAINER_RUNTIME_DOCKER_RUN_PRIVILEGED_CONTAINER", "true"); } - StringBuilder sb = new StringBuilder(); - for (Entry mount : mountPaths.entrySet()) { - if (sb.length() > 0) { - sb.append(","); + if (!mountPaths.isEmpty()) { + StringBuilder sb = new StringBuilder(); + if (env.get(ENV_DOCKER_CONTAINER_MOUNTS) != null) { + // user specified mounts in the spec + sb.append(env.get(ENV_DOCKER_CONTAINER_MOUNTS)); } - sb.append(mount.getKey()); - sb.append(":"); - sb.append(mount.getValue()); + for (Entry mount : mountPaths.entrySet()) { + if (sb.length() > 0) { + sb.append(","); + } + sb.append(mount.getKey()).append(":"); + sb.append(mount.getValue()).append(":ro"); + } + env.put(ENV_DOCKER_CONTAINER_MOUNTS, sb.toString()); } - env.put("YARN_CONTAINER_RUNTIME_DOCKER_LOCAL_RESOURCE_MOUNTS", sb.toString()); - log.info("yarn docker env var has been set {}", containerLaunchContext.getEnvironment().toString()); + log.info("yarn docker env var has been set {}", + containerLaunchContext.getEnvironment().toString()); } return containerLaunchContext; diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/test/java/org/apache/hadoop/yarn/service/containerlaunch/TestAbstractLauncher.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/test/java/org/apache/hadoop/yarn/service/containerlaunch/TestAbstractLauncher.java new file mode 100644 index 00000000000..f4f1a50e439 --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/test/java/org/apache/hadoop/yarn/service/containerlaunch/TestAbstractLauncher.java @@ -0,0 +1,54 @@ +/* + * 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. + */ + +package org.apache.hadoop.yarn.service.containerlaunch; + +import org.apache.hadoop.yarn.service.ServiceContext; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; + +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link AbstractLauncher}. + */ +public class TestAbstractLauncher { + + private AbstractLauncher launcher; + + @Before + public void setup() { + launcher = new AbstractLauncher(mock(ServiceContext.class)); + } + + @Test + public void testDockerContainerMounts() throws IOException { + launcher.yarnDockerMode = true; + launcher.envVars.put(AbstractLauncher.ENV_DOCKER_CONTAINER_MOUNTS, + "s1:t1:ro"); + launcher.mountPaths.put("s2", "t2"); + launcher.completeContainerLaunch(); + String dockerContainerMounts = launcher.containerLaunchContext + .getEnvironment().get(AbstractLauncher.ENV_DOCKER_CONTAINER_MOUNTS); + + Assert.assertEquals("s1:t1:ro,s2:t2:ro", dockerContainerMounts); + } +} 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 a14b0853093..e871f1dfa56 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 @@ -153,14 +153,6 @@ * setting it to false. * *
  • - * {@code YARN_CONTAINER_RUNTIME_DOCKER_LOCAL_RESOURCE_MOUNTS} adds - * additional volume mounts to the Docker container. The value of the - * environment variable should be a comma-separated list of mounts. - * All such mounts must be given as {@code source:dest}, where the - * source is an absolute path that is not a symlink and that points to a - * localized resource. - *
  • - *
  • * {@code YARN_CONTAINER_RUNTIME_DOCKER_MOUNTS} allows users to specify + additional volume mounts for the Docker container. The value of the * environment variable should be a comma-separated list of mounts. @@ -227,9 +219,6 @@ public static final String ENV_DOCKER_CONTAINER_RUN_ENABLE_USER_REMAPPING = "YARN_CONTAINER_RUNTIME_DOCKER_RUN_ENABLE_USER_REMAPPING"; @InterfaceAudience.Private - public static final String ENV_DOCKER_CONTAINER_LOCAL_RESOURCE_MOUNTS = - "YARN_CONTAINER_RUNTIME_DOCKER_LOCAL_RESOURCE_MOUNTS"; - @InterfaceAudience.Private public static final String ENV_DOCKER_CONTAINER_MOUNTS = "YARN_CONTAINER_RUNTIME_DOCKER_MOUNTS"; @InterfaceAudience.Private @@ -680,8 +669,7 @@ private boolean allowPrivilegedContainerExecution(Container container) return true; } - @VisibleForTesting - protected String validateMount(String mount, + private String findAbsoluteMount(String mount, Map> localizedResources) throws ContainerExecutionException { for (Entry> resource : localizedResources.entrySet()) { @@ -817,23 +805,6 @@ public void launchContainer(ContainerRuntimeContext ctx) runCommand.addAllReadOnlyMountLocations(filecacheDirs); runCommand.addAllReadOnlyMountLocations(userFilecacheDirs); - 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.addReadOnlyMountLocation(src, dst, true); - } - } - } - if (environment.containsKey(ENV_DOCKER_CONTAINER_MOUNTS)) { Matcher parsedMounts = USER_MOUNT_PATTERN.matcher( environment.get(ENV_DOCKER_CONTAINER_MOUNTS)); @@ -845,6 +816,10 @@ public void launchContainer(ContainerRuntimeContext ctx) parsedMounts.reset(); while (parsedMounts.find()) { String src = parsedMounts.group(1); + java.nio.file.Path srcPath = java.nio.file.Paths.get(src); + if (!srcPath.isAbsolute()) { + src = findAbsoluteMount(src, localizedResources); + } String dst = parsedMounts.group(2); String mode = parsedMounts.group(3); if (!mode.equals("ro") && !mode.equals("rw")) { 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 6ad35b2a43b..af69e2226a2 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 @@ -26,7 +26,6 @@ import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.DataOutputBuffer; import org.apache.hadoop.registry.client.api.RegistryConstants; -import org.apache.hadoop.registry.client.binding.RegistryPathUtils; import org.apache.hadoop.security.Credentials; import org.apache.hadoop.util.Shell; import org.apache.hadoop.util.StringUtils; @@ -1098,7 +1097,7 @@ public void testMountSourceOnly() throws ContainerExecutionException { runtime.initialize(conf, nmContext); env.put( - DockerLinuxContainerRuntime.ENV_DOCKER_CONTAINER_LOCAL_RESOURCE_MOUNTS, + DockerLinuxContainerRuntime.ENV_DOCKER_CONTAINER_MOUNTS, "source"); try { @@ -1118,8 +1117,8 @@ public void testMountSourceTarget() runtime.initialize(conf, nmContext); env.put( - DockerLinuxContainerRuntime.ENV_DOCKER_CONTAINER_LOCAL_RESOURCE_MOUNTS, - "test_dir/test_resource_file:test_mount"); + DockerLinuxContainerRuntime.ENV_DOCKER_CONTAINER_MOUNTS, + "test_dir/test_resource_file:test_mount:ro"); runtime.launchContainer(builder.build()); PrivilegedOperation op = capturePrivilegedOperationAndVerifyArgs(); @@ -1164,24 +1163,6 @@ public void testMountSourceTarget() dockerCommands.get(counter)); } - @Test - public void testMountInvalid() throws ContainerExecutionException { - DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime( - mockExecutor, mockCGroupsHandler); - runtime.initialize(conf, nmContext); - - 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, @@ -1191,9 +1172,9 @@ public void testMountMultiple() runtime.initialize(conf, nmContext); env.put( - DockerLinuxContainerRuntime.ENV_DOCKER_CONTAINER_LOCAL_RESOURCE_MOUNTS, - "test_dir/test_resource_file:test_mount1," + - "test_dir/test_resource_file:test_mount2"); + DockerLinuxContainerRuntime.ENV_DOCKER_CONTAINER_MOUNTS, + "test_dir/test_resource_file:test_mount1:ro," + + "test_dir/test_resource_file:test_mount2:ro"); runtime.launchContainer(builder.build()); PrivilegedOperation op = capturePrivilegedOperationAndVerifyArgs();