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 0a11948..9550c05 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 @@ -856,6 +856,12 @@ public static final long DEFAULT_NM_LINUX_CONTAINER_CGROUPS_DELETE_TIMEOUT = 1000; + /** + /* The Windows group that the windows-secure-container-executor should run as. + */ + public static final String NM_WINDOWS_SECURE_CONTAINER_GROUP = + NM_PREFIX + "windows-secure-container-executor.group"; + /** T-file compression types used to compress aggregated logs.*/ public static final String NM_LOG_AGG_COMPRESSION_TYPE = NM_PREFIX + "log-aggregation.compression-type"; diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/ContainerExecutor.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/ContainerExecutor.java index ee72fbc..b76b17d 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/ContainerExecutor.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/ContainerExecutor.java @@ -77,6 +77,20 @@ public Configuration getConf() { public abstract void init() throws IOException; /** + * On Windows the ContainerLaunch creates a temporary empty jar to workaround the CLASSPATH length + * In a secure cluster this jar must be localized so that the container has access to it + * This function localizes on-demand the jar. + * + * @param classPathJar + * @param owner + * @throws IOException + */ + public void localizeClasspathJar(Path classPathJar, String owner) throws IOException { + // For the default container this is a no-op + // The WindowsSecureContainerExecutor overrides this + } + + /** * Prepare the environment for containers in this application to execute. * For $x in local.dirs * create $x/$user/$appId @@ -195,8 +209,8 @@ protected Path getPidFilePath(ContainerId containerId) { * and associate the given groupId in a process group. On * non-Windows, groupId is ignored. */ - protected static String[] getRunCommand(String command, String groupId, - Configuration conf) { + protected String[] getRunCommand(String command, String groupId, + String userName, Path pidFile, Configuration conf) { boolean containerSchedPriorityIsSet = false; int containerSchedPriorityAdjustment = YarnConfiguration.DEFAULT_NM_CONTAINER_EXECUTOR_SCHED_PRIORITY; @@ -327,5 +341,4 @@ public void run() { } } } - } diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/DefaultContainerExecutor.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/DefaultContainerExecutor.java index 9e2e111..54866bb 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/DefaultContainerExecutor.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/DefaultContainerExecutor.java @@ -60,7 +60,7 @@ private static final int WIN_MAX_PATH = 260; - private final FileContext lfs; + protected final FileContext lfs; public DefaultContainerExecutor() { try { @@ -74,6 +74,14 @@ public DefaultContainerExecutor() { this.lfs = lfs; } + protected void copyFile(Path src, Path dst, String owner) throws IOException { + lfs.util().copy(src, dst); + } + + protected void setScriptExecutable(Path script, String owner) throws IOException { + lfs.setPermission(script, ContainerExecutor.TASK_LAUNCH_SCRIPT_PERMISSION); + } + @Override public void init() throws IOException { // nothing to do or verify here @@ -92,14 +100,14 @@ public synchronized void startLocalizer(Path nmPrivateContainerTokensPath, createUserLocalDirs(localDirs, user); createUserCacheDirs(localDirs, user); createAppDirs(localDirs, user, appId); - createAppLogDirs(appId, logDirs); + createAppLogDirs(appId, logDirs, user); // TODO: Why pick first app dir. The same in LCE why not random? Path appStorageDir = getFirstApplicationDir(localDirs, user, appId); String tokenFn = String.format(ContainerLocalizer.TOKEN_FILE_NAME_FMT, locId); Path tokenDst = new Path(appStorageDir, tokenFn); - lfs.util().copy(nmPrivateContainerTokensPath, tokenDst); + copyFile(nmPrivateContainerTokensPath, tokenDst, user); LOG.info("Copying from " + nmPrivateContainerTokensPath + " to " + tokenDst); lfs.setWorkingDirectory(appStorageDir); LOG.info("CWD set to " + appStorageDir + " = " + lfs.getWorkingDirectory()); @@ -128,30 +136,29 @@ public int launchContainer(Container container, Path appCacheDir = new Path(userdir, ContainerLocalizer.APPCACHE); Path appDir = new Path(appCacheDir, appIdStr); Path containerDir = new Path(appDir, containerIdStr); - createDir(containerDir, dirPerm, true); + createDir(containerDir, dirPerm, true, userName); } // Create the container log-dirs on all disks - createContainerLogDirs(appIdStr, containerIdStr, logDirs); + createContainerLogDirs(appIdStr, containerIdStr, logDirs, userName); Path tmpDir = new Path(containerWorkDir, YarnConfiguration.DEFAULT_CONTAINER_TEMP_DIR); - createDir(tmpDir, dirPerm, false); + createDir(tmpDir, dirPerm, false, userName); // copy launch script to work dir Path launchDst = new Path(containerWorkDir, ContainerLaunch.CONTAINER_SCRIPT); - lfs.util().copy(nmPrivateContainerScriptPath, launchDst); + copyFile(nmPrivateContainerScriptPath, launchDst, userName); // copy container tokens to work dir Path tokenDst = new Path(containerWorkDir, ContainerLaunch.FINAL_CONTAINER_TOKENS_FILE); - lfs.util().copy(nmPrivateTokensPath, tokenDst); + copyFile(nmPrivateTokensPath, tokenDst, userName); // Create new local launch wrapper script - LocalWrapperScriptBuilder sb = Shell.WINDOWS ? - new WindowsLocalWrapperScriptBuilder(containerIdStr, containerWorkDir) : - new UnixLocalWrapperScriptBuilder(containerWorkDir); + LocalWrapperScriptBuilder sb = getLocalWrapperScriptBuilder( + containerIdStr, containerWorkDir); // Fail fast if attempting to launch the wrapper script would fail due to // Windows path length limitation. @@ -177,14 +184,12 @@ public int launchContainer(Container container, // fork script ShellCommandExecutor shExec = null; try { - lfs.setPermission(launchDst, - ContainerExecutor.TASK_LAUNCH_SCRIPT_PERMISSION); - lfs.setPermission(sb.getWrapperScriptPath(), - ContainerExecutor.TASK_LAUNCH_SCRIPT_PERMISSION); + setScriptExecutable(launchDst, userName); + setScriptExecutable(sb.getWrapperScriptPath(), userName); // Setup command to run String[] command = getRunCommand(sb.getWrapperScriptPath().toString(), - containerIdStr, this.getConf()); + containerIdStr, userName, pidFile, this.getConf()); LOG.info("launchContainer: " + Arrays.toString(command)); shExec = new ShellCommandExecutor( @@ -229,7 +234,14 @@ public int launchContainer(Container container, return 0; } - private abstract class LocalWrapperScriptBuilder { + protected LocalWrapperScriptBuilder getLocalWrapperScriptBuilder( + String containerIdStr, Path containerWorkDir) { + return Shell.WINDOWS ? + new WindowsLocalWrapperScriptBuilder(containerIdStr, containerWorkDir) : + new UnixLocalWrapperScriptBuilder(containerWorkDir); + } + + protected abstract class LocalWrapperScriptBuilder { private final Path wrapperScriptPath; @@ -398,7 +410,7 @@ public void deleteAsUser(String user, Path subDir, Path... baseDirs) * $logdir/$user/$appId */ static final short LOGDIR_PERM = (short)0710; - private Path getFirstApplicationDir(List localDirs, String user, + protected Path getFirstApplicationDir(List localDirs, String user, String appId) { return getApplicationDir(new Path(localDirs.get(0)), user, appId); } @@ -421,8 +433,8 @@ private Path getFileCacheDir(Path base, String user) { ContainerLocalizer.FILECACHE); } - private void createDir(Path dirPath, FsPermission perms, - boolean createParent) throws IOException { + protected void createDir(Path dirPath, FsPermission perms, + boolean createParent, String user) throws IOException { lfs.mkdir(dirPath, perms, createParent); if (!perms.equals(perms.applyUMask(lfs.getUMask()))) { lfs.setPermission(dirPath, perms); @@ -442,7 +454,7 @@ void createUserLocalDirs(List localDirs, String user) for (String localDir : localDirs) { // create $local.dir/usercache/$user and its immediate parent try { - createDir(getUserCacheDir(new Path(localDir), user), userperms, true); + createDir(getUserCacheDir(new Path(localDir), user), userperms, true, user); } catch (IOException e) { LOG.warn("Unable to create the user directory : " + localDir, e); continue; @@ -478,7 +490,7 @@ void createUserCacheDirs(List localDirs, String user) Path localDirPath = new Path(localDir); final Path appDir = getAppcacheDir(localDirPath, user); try { - createDir(appDir, appCachePerms, true); + createDir(appDir, appCachePerms, true, user); appcacheDirStatus = true; } catch (IOException e) { LOG.warn("Unable to create app cache directory : " + appDir, e); @@ -486,7 +498,7 @@ void createUserCacheDirs(List localDirs, String user) // create $local.dir/usercache/$user/filecache final Path distDir = getFileCacheDir(localDirPath, user); try { - createDir(distDir, fileperms, true); + createDir(distDir, fileperms, true, user); distributedCacheDirStatus = true; } catch (IOException e) { LOG.warn("Unable to create file cache directory : " + distDir, e); @@ -519,7 +531,7 @@ void createAppDirs(List localDirs, String user, String appId) Path fullAppDir = getApplicationDir(new Path(localDir), user, appId); // create $local.dir/usercache/$user/appcache/$appId try { - createDir(fullAppDir, appperms, true); + createDir(fullAppDir, appperms, true, user); initAppDirStatus = true; } catch (IOException e) { LOG.warn("Unable to create app directory " + fullAppDir.toString(), e); @@ -535,7 +547,7 @@ void createAppDirs(List localDirs, String user, String appId) /** * Create application log directories on all disks. */ - void createAppLogDirs(String appId, List logDirs) + void createAppLogDirs(String appId, List logDirs, String user) throws IOException { boolean appLogDirStatus = false; @@ -544,7 +556,7 @@ void createAppLogDirs(String appId, List logDirs) // create $log.dir/$appid Path appLogDir = new Path(rootLogDir, appId); try { - createDir(appLogDir, appLogDirPerms, true); + createDir(appLogDir, appLogDirPerms, true, user); } catch (IOException e) { LOG.warn("Unable to create the app-log directory : " + appLogDir, e); continue; @@ -561,7 +573,7 @@ void createAppLogDirs(String appId, List logDirs) * Create application log directories on all disks. */ void createContainerLogDirs(String appId, String containerId, - List logDirs) throws IOException { + List logDirs, String user) throws IOException { boolean containerLogDirStatus = false; FsPermission containerLogDirPerms = new FsPermission(LOGDIR_PERM); @@ -570,7 +582,7 @@ void createContainerLogDirs(String appId, String containerId, Path appLogDir = new Path(rootLogDir, appId); Path containerLogDir = new Path(appLogDir, containerId); try { - createDir(containerLogDir, containerLogDirPerms, true); + createDir(containerLogDir, containerLogDirPerms, true, user); } catch (IOException e) { LOG.warn("Unable to create the container-log directory : " + appLogDir, e); diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/WindowsSecureContainerExecutor.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/WindowsSecureContainerExecutor.java new file mode 100644 index 0000000..a3a3b92 --- /dev/null +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/WindowsSecureContainerExecutor.java @@ -0,0 +1,183 @@ +/** + * 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.io.File; +import java.io.IOException; +import java.io.PrintStream; +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.FileUtil; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.fs.permission.FsPermission; +import org.apache.hadoop.util.Shell; +import org.apache.hadoop.util.Shell.ShellCommandExecutor; +import org.apache.hadoop.yarn.conf.YarnConfiguration; +import org.apache.hadoop.yarn.server.nodemanager.DefaultContainerExecutor.LocalWrapperScriptBuilder; +import org.apache.hadoop.yarn.server.nodemanager.containermanager.localizer.ContainerLocalizer; + +/** + * Windows secure container executor. Uses winutils task createAsUser. + * + */ +public class WindowsSecureContainerExecutor extends DefaultContainerExecutor { + + private static final Log LOG = LogFactory + .getLog(WindowsSecureContainerExecutor.class); + + private class WindowsSecureWrapperScriptBuilder + extends LocalWrapperScriptBuilder { + + public WindowsSecureWrapperScriptBuilder(Path containerWorkDir) { + super(containerWorkDir); + } + + @Override + protected void writeLocalWrapperScript(Path launchDst, Path pidFile, PrintStream pout) { + pout.format("@call \"%s\"", launchDst); + } + } + + private String namenodeGroup; + + protected static Integer appLocalizationCounter = 0; + + @Override + public void setConf(Configuration conf) { + super.setConf(conf); + namenodeGroup = conf.get(YarnConfiguration.NM_WINDOWS_SECURE_CONTAINER_GROUP); + } + + @Override + protected String[] getRunCommand(String command, String groupId, + String userName, Path pidFile, Configuration conf) { + return new String[] { Shell.WINUTILS, "task", "createAsUser", groupId, userName, + pidFile.toString(), "cmd /c " + command }; + } + + @Override + protected LocalWrapperScriptBuilder getLocalWrapperScriptBuilder( + String containerIdStr, Path containerWorkDir) { + return new WindowsSecureWrapperScriptBuilder(containerWorkDir); + } + + @Override + protected void copyFile(Path src, Path dst, String owner) throws IOException { + super.copyFile(src, dst, owner); + lfs.setOwner(dst, owner, namenodeGroup); + } + + @Override + protected void createDir(Path dirPath, FsPermission perms, + boolean createParent, String owner) throws IOException { + super.createDir(dirPath, perms, createParent, owner); + lfs.setOwner(dirPath, owner, namenodeGroup); + } + + @Override + protected void setScriptExecutable(Path script, String owner) throws IOException { + super.setScriptExecutable(script, null); + lfs.setOwner(script, owner, namenodeGroup); + } + + @Override + public void localizeClasspathJar(Path classpathJar, String owner) throws IOException { + lfs.setOwner(classpathJar, owner, namenodeGroup); + } + + @Override + public void startLocalizer(Path nmPrivateContainerTokens, + InetSocketAddress nmAddr, String user, String appId, String locId, + List localDirs, List logDirs) throws IOException, + InterruptedException { + + createUserLocalDirs(localDirs, user); + createUserCacheDirs(localDirs, user); + createAppDirs(localDirs, user, appId); + createAppLogDirs(appId, logDirs, user); + + // TODO: Why pick first app dir. The same in LCE why not random? + Path appStorageDir = getFirstApplicationDir(localDirs, user, appId); + + String tokenFn = String.format(ContainerLocalizer.TOKEN_FILE_NAME_FMT, locId); + Path tokenDst = new Path(appStorageDir, tokenFn); + LOG.info("Copying from " + nmPrivateContainerTokens + " to " + tokenDst); + copyFile(nmPrivateContainerTokens, tokenDst, user); + + List command ; + String[] commandArray; + ShellCommandExecutor shExec; + + File cwdApp = new File(appStorageDir.toString()); + LOG.info(String.format("cwdApp: %s", cwdApp)); + + command = new ArrayList(); + + command.add(Shell.WINUTILS); + command.add("task"); + command.add("createAsUser"); + command.add("START_LOCALIZER_" + appId + appLocalizationCounter.toString()); + command.add(user); + command.add("nul:"); // PID file + + ++appLocalizationCounter; + + //use same jvm as parent + File jvm = new File(new File(System.getProperty("java.home"), "bin"), "java.exe"); + command.add(jvm.toString()); + + + // Build a temp classpath jar. See ContainerLaunch.sanitizeEnv(). + // Passing CLASSPATH explicitly is *way* too long for command line. + String classPath = System.getProperty("java.class.path"); + Map env = new HashMap(System.getenv()); + String classPathJar = FileUtil.createJarWithClassPath(classPath, appStorageDir, env); + localizeClasspathJar(new Path(classPathJar), user); + command.add("-classpath"); + command.add(classPathJar); + + String javaLibPath = System.getProperty("java.library.path"); + if (javaLibPath != null) { + command.add("-Djava.library.path=" + javaLibPath); + } + + command.add(ContainerLocalizer.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); + } + commandArray = command.toArray(new String[command.size()]); + + shExec = new ShellCommandExecutor( + commandArray, cwdApp); + + shExec.execute(); + } +} + diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/launcher/ContainerLaunch.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/launcher/ContainerLaunch.java index e252e35..174e932 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/launcher/ContainerLaunch.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/launcher/ContainerLaunch.java @@ -745,6 +745,8 @@ public void sanitizeEnv(Map environment, Path pwd, String classPathJar = FileUtil.createJarWithClassPath( newClassPath.toString(), pwd, mergedEnv); + // In a secure cluster the classpath jar must be localized to grant access + this.exec.localizeClasspathJar(new Path(classPathJar), container.getUser()); environment.put(Environment.CLASSPATH.name(), classPathJar); } } diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestContainerExecutor.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestContainerExecutor.java index c04ec29..fd3634b 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestContainerExecutor.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestContainerExecutor.java @@ -28,10 +28,12 @@ public class TestContainerExecutor { + private ContainerExecutor containerExecutor = new DefaultContainerExecutor(); + @Test (timeout = 5000) public void testRunCommandNoPriority() throws Exception { Configuration conf = new Configuration(); - String[] command = ContainerExecutor.getRunCommand("echo", "group1", conf); + String[] command = containerExecutor.getRunCommand("echo", "group1", "user", null, conf); assertTrue("first command should be the run command for the platform", command[0].equals(Shell.WINUTILS) || command[0].equals("bash")); } @@ -40,7 +42,7 @@ public void testRunCommandNoPriority() throws Exception { public void testRunCommandwithPriority() throws Exception { Configuration conf = new Configuration(); conf.setInt(YarnConfiguration.NM_CONTAINER_EXECUTOR_SCHED_PRIORITY, 2); - String[] command = ContainerExecutor.getRunCommand("echo", "group1", conf); + String[] command = containerExecutor.getRunCommand("echo", "group1", "user", null, conf); if (Shell.WINDOWS) { // windows doesn't currently support assertEquals("first command should be the run command for the platform", @@ -54,7 +56,7 @@ public void testRunCommandwithPriority() throws Exception { // test with negative number conf.setInt(YarnConfiguration.NM_CONTAINER_EXECUTOR_SCHED_PRIORITY, -5); - command = ContainerExecutor.getRunCommand("echo", "group1", conf); + command = containerExecutor.getRunCommand("echo", "group1", "user", null, conf); if (Shell.WINDOWS) { // windows doesn't currently support assertEquals("first command should be the run command for the platform", diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestDefaultContainerExecutor.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestDefaultContainerExecutor.java index 63f3a9f..3763b65 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestDefaultContainerExecutor.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestDefaultContainerExecutor.java @@ -180,7 +180,7 @@ public void testDirPermissions() throws Exception { Assert.assertEquals(appDirPerm, stats.getPermission()); } - executor.createAppLogDirs(appId, logDirs); + executor.createAppLogDirs(appId, logDirs, user); for (String dir : logDirs) { FileStatus stats = lfs.getFileStatus(new Path(dir, appId));