diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/LinuxContainerExecutor.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/LinuxContainerExecutor.java index b4ecfba..f19ecfe 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/LinuxContainerExecutor.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/LinuxContainerExecutor.java @@ -18,6 +18,7 @@ package org.apache.hadoop.yarn.server.nodemanager; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Optional; import java.io.File; @@ -220,7 +221,7 @@ public void startLocalizer(Path nmPrivateContainerTokensPath, if (javaLibPath != null) { command.add("-Djava.library.path=" + javaLibPath); } - ContainerLocalizer.buildMainArgs(command, user, appId, locId, nmAddr, localDirs); + buildMainArgs(command, user, appId, locId, nmAddr, localDirs); String[] commandArray = command.toArray(new String[command.size()]); ShellCommandExecutor shExec = new ShellCommandExecutor(commandArray); if (LOG.isDebugEnabled()) { @@ -241,6 +242,13 @@ public void startLocalizer(Path nmPrivateContainerTokensPath, } } + @VisibleForTesting + public void buildMainArgs(List command, String user, String appId, + String locId, InetSocketAddress nmAddr, List localDirs) { + ContainerLocalizer.buildMainArgs(command, user, appId, locId, nmAddr, + localDirs); + } + @Override public int launchContainer(Container container, Path nmPrivateCotainerScriptPath, Path nmPrivateTokensPath, diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.c hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.c index f582d85..b60561b 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.c +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.c @@ -475,16 +475,7 @@ int mkdirs(const char* path, mode_t perm) { while (NULL != (p = strchr(p, '/'))) { *p = '\0'; - if (stat(npath, &sb) != 0) { - if (mkdir(npath, perm) != 0) { - fprintf(LOGFILE, "Can't create directory %s in %s - %s\n", npath, - path, strerror(errno)); - free(npath); - return -1; - } - } else if (!S_ISDIR (sb.st_mode)) { - fprintf(LOGFILE, "Path %s is file not dir\n", npath); - free(npath); + if (create_validate_dirs(npath, perm, path, 0) == -1) { return -1; } *p++ = '/'; /* restore slash */ @@ -493,17 +484,73 @@ int mkdirs(const char* path, mode_t perm) { } /* Create the final directory component. */ - if (mkdir(npath, perm) != 0) { - fprintf(LOGFILE, "Can't create directory %s - %s\n", npath, - strerror(errno)); - free(npath); + if (create_validate_dirs(npath, perm, path, 1) == -1) { return -1; } free(npath); return 0; } - +/* + * Create the parent directory if they do not exist. Or check the permission if + * the race condition happens. + * Give 0 or 1 to represent whether this is the final component. If it is, we + * need to check the permission. + */ +int create_validate_dirs(char* npath, mode_t perm, char* path, int finalComponent) { + struct stat sb; + if (stat(npath, &sb) != 0) { + if (mkdir(npath, perm) != 0) { + if (errno != EEXIST) { + fprintf(LOGFILE, "Can't create directory %s - %s\n", npath, + strerror(errno)); + free(npath); + return -1; + } + // The directory npath should exist. + if (stat(npath, &sb) == 0) { + // Check whether it is a directory + if (!S_ISDIR (sb.st_mode)) { + fprintf(LOGFILE, "Path %s is file not dir\n", npath); + free(npath); + return -1; + } else if (check_permission(sb.st_mode, perm) == -1) { + fprintf(LOGFILE, "Path %s does not have desired permission.\n", npath); + free(npath); + return -1; + } + } else { + fprintf(LOGFILE, "Can't create directory %s in %s - %s\n", npath, + path, strerror(errno)); + free(npath); + return -1; + } + } + } else { + if (!S_ISDIR (sb.st_mode)) { + fprintf(LOGFILE, "Path %s is file not dir\n", npath); + free(npath); + return -1; + } else if (finalComponent == 1 && check_permission(sb.st_mode, perm) == -1) { + fprintf(LOGFILE, "Path %s does not have desired permission.\n", npath); + free(npath); + return -1; + } + } + return 0; +} +/* + * Check whether the access permission of existing file + * is the same as desired permissions + */ +int check_permission(mode_t filePerm, mode_t desired) { + int filePermInt = filePerm & (S_IRWXU | S_IRWXG | S_IRWXO); + int desiredInt = desired & (S_IRWXU | S_IRWXG | S_IRWXO); + if (filePermInt == desiredInt) { + return 0; + } + return -1; +} /** * Function to prepare the container directories. * It creates the container work and log directories. diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/MockContainerLocalizer.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/MockContainerLocalizer.java new file mode 100644 index 0000000..0ff8bf6 --- /dev/null +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/MockContainerLocalizer.java @@ -0,0 +1,41 @@ +/** +* 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.server.nodemanager; + +import java.net.InetSocketAddress; +import java.util.List; + +public class MockContainerLocalizer { + public static void buildMainArgs(List command, + String user, String appId, String locId, + InetSocketAddress nmAddr, List localDirs) { + command.add(MockContainerLocalizer.class.getName()); + command.add(user); + command.add(appId); + command.add(locId); + command.add(nmAddr.getHostName()); + command.add(Integer.toString(nmAddr.getPort())); + for(String dir : localDirs) { + command.add(dir); + } + } + + public static void main(String[] argv) throws Throwable { + //DO Nothing + } +} diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestLinuxContainerExecutor.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestLinuxContainerExecutor.java index c02212e..1d5b2fb 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestLinuxContainerExecutor.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestLinuxContainerExecutor.java @@ -18,6 +18,8 @@ package org.apache.hadoop.yarn.server.nodemanager; +import static org.apache.hadoop.fs.CreateFlag.CREATE; +import static org.apache.hadoop.fs.CreateFlag.OVERWRITE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -29,8 +31,11 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; +import java.net.InetSocketAddress; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Set; import org.junit.Assert; @@ -52,6 +57,8 @@ import org.apache.hadoop.yarn.conf.YarnConfiguration; import org.apache.hadoop.yarn.server.nodemanager.ContainerExecutor.Signal; import org.apache.hadoop.yarn.server.nodemanager.containermanager.container.Container; +import org.apache.hadoop.yarn.server.nodemanager.containermanager.localizer.ContainerLocalizer; +import org.apache.hadoop.yarn.server.nodemanager.containermanager.localizer.ResourceLocalizationService; import org.apache.hadoop.yarn.server.nodemanager.util.LCEResourcesHandler; import org.junit.After; import org.junit.Before; @@ -108,10 +115,12 @@ private LinuxContainerExecutor exec = null; private String appSubmitter = null; private LocalDirsHandlerService dirsHandler; + private Configuration conf; + private FileContext files; @Before public void setup() throws Exception { - FileContext files = FileContext.getLocalFSFileContext(); + files = FileContext.getLocalFSFileContext(); Path workSpacePath = new Path(workSpace.getAbsolutePath()); files.mkdir(workSpacePath, null, true); FileUtil.chmod(workSpace.getAbsolutePath(), "777"); @@ -123,7 +132,11 @@ public void setup() throws Exception { new FsPermission("777"), false); String exec_path = System.getProperty("container-executor.path"); if(exec_path != null && !exec_path.isEmpty()) { - Configuration conf = new Configuration(false); + conf = new Configuration(false); + conf.setClass("fs.AbstractFileSystem.file.impl", + org.apache.hadoop.fs.local.LocalFs.class, + org.apache.hadoop.fs.AbstractFileSystem.class); + conf.set(YarnConfiguration.NM_NONSECURE_MODE_LOCAL_USER_KEY, "xuan"); LOG.info("Setting "+YarnConfiguration.NM_LINUX_CONTAINER_EXECUTOR_PATH +"="+exec_path); conf.set(YarnConfiguration.NM_LINUX_CONTAINER_EXECUTOR_PATH, exec_path); @@ -212,6 +225,59 @@ private int runAndBlock(ContainerId cId, String ... cmd) throws IOException { dirsHandler.getLogDirs()); } + @Test + public void testContainerLocalizer() throws Exception { + if (!shouldRun()) { + return; + } + List localDirs = dirsHandler.getLocalDirs(); + List logDirs = dirsHandler.getLogDirs(); + for (String localDir : localDirs) { + Path userDir = + new Path(localDir, ContainerLocalizer.USERCACHE); + files.mkdir(userDir, new FsPermission("777"), false); + // $local/filecache + Path fileDir = + new Path(localDir, ContainerLocalizer.FILECACHE); + files.mkdir(fileDir, new FsPermission("777"), false); + } + String locId = "container_01_01"; + Path nmPrivateContainerTokensPath = + dirsHandler.getLocalPathForWrite( + ResourceLocalizationService.NM_PRIVATE_DIR + Path.SEPARATOR + + String.format(ContainerLocalizer.TOKEN_FILE_NAME_FMT, + locId)); + files.create(nmPrivateContainerTokensPath, EnumSet.of(CREATE, OVERWRITE)); + Configuration config = new YarnConfiguration(conf); + InetSocketAddress nmAddr = config.getSocketAddr( + YarnConfiguration.NM_BIND_HOST, + YarnConfiguration.NM_LOCALIZER_ADDRESS, + YarnConfiguration.DEFAULT_NM_LOCALIZER_ADDRESS, + YarnConfiguration.DEFAULT_NM_LOCALIZER_PORT); + String appId = "application_01_01"; + exec = new LinuxContainerExecutor() { + @Override + public void buildMainArgs(List command, String user, String appId, + String locId, InetSocketAddress nmAddr, List localDirs) { + MockContainerLocalizer.buildMainArgs(command, user, appId, locId, nmAddr, + localDirs); + } + }; + exec.setConf(conf); + + exec.startLocalizer(nmPrivateContainerTokensPath, nmAddr, appSubmitter, + appId, locId, localDirs, logDirs); + + String locId2 = "container_01_02"; + Path nmPrivateContainerTokensPath2 = + dirsHandler + .getLocalPathForWrite(ResourceLocalizationService.NM_PRIVATE_DIR + + Path.SEPARATOR + + String.format(ContainerLocalizer.TOKEN_FILE_NAME_FMT, locId2)); + files.create(nmPrivateContainerTokensPath2, EnumSet.of(CREATE, OVERWRITE)); + exec.startLocalizer(nmPrivateContainerTokensPath2, nmAddr, appSubmitter, + appId, locId2, localDirs, logDirs); + } @Test public void testContainerLaunch() throws IOException {