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 918c30a..ef5c6bd 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 @@ -183,11 +183,12 @@ public abstract void deleteAsUser(DeletionAsUserContext ctx) /** * Create a symlink file which points to the target. - * @param target The target for symlink + * @param src The source file for symlink * @param symlink the symlink file + * @param container the container for which to create the symlink * @throws IOException Error when creating symlinks */ - public abstract void symLink(String target, String symlink) + public abstract void symLink(String src, String symlink, Container container) throws IOException; /** 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 9a0549d..1f6507c 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 @@ -513,8 +513,9 @@ public void deleteAsUser(DeletionAsUserContext ctx) } @Override - public void symLink(String target, String symlink) throws IOException { - FileUtil.symLink(target, symlink); + public void symLink(String src, String symlink, Container container) + throws IOException { + FileUtil.symLink(src, symlink); } /** Permissions for user dir. diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/DockerContainerExecutor.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/DockerContainerExecutor.java index 1390214..7b8235a 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/DockerContainerExecutor.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/DockerContainerExecutor.java @@ -491,7 +491,7 @@ public void deleteAsUser(DeletionAsUserContext ctx) } @Override - public void symLink(String target, String symlink) + public void symLink(String src, String symlink, Container container) throws IOException { } 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 cc12b20..6bd844c 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 @@ -32,6 +32,7 @@ import org.apache.hadoop.yarn.conf.YarnConfiguration; import org.apache.hadoop.yarn.server.nodemanager.containermanager.container.Container; import org.apache.hadoop.yarn.server.nodemanager.containermanager.container.ContainerDiagnosticsUpdateEvent; +import org.apache.hadoop.yarn.server.nodemanager.containermanager.launcher.ContainerLaunch; import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.PrivilegedOperation; import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.PrivilegedOperationException; import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.PrivilegedOperationExecutor; @@ -55,7 +56,11 @@ import org.apache.hadoop.yarn.server.nodemanager.util.DefaultLCEResourcesHandler; import org.apache.hadoop.yarn.server.nodemanager.util.LCEResourcesHandler; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.Writer; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Arrays; @@ -683,8 +688,52 @@ public void deleteAsUser(DeletionAsUserContext ctx) { } @Override - public void symLink(String target, String symlink) { + public void symLink(String src, String symlink, Container container) { + ContainerLaunch.ShellScriptBuilder scriptBuilder = + ContainerLaunch.ShellScriptBuilder.create(); + try { + scriptBuilder.symlink(new Path(src), new Path(symlink)); + File srcFile = + writeScriptToNMPrivateDir(container.getNMPrivateDir().toString(), + scriptBuilder.toString()); + String dstFile = + container.getWorkDir() + Path.SEPARATOR + srcFile.getName(); + runScriptAsUser(container.getUser(), srcFile, dstFile); + srcFile.delete(); + } catch (IOException e) { + LOG.error("Error when creating symlink for " + container.getContainerId() + + ": " + src + " -> " + symlink, e); + } + } + + protected void runScriptAsUser(String user, File srcFile, String dstFile) { + String runAsUser = getRunAsUser(user); + PrivilegedOperation op = new PrivilegedOperation( + PrivilegedOperation.OperationType.RUN_SCRIPT); + op.appendArgs(runAsUser); + op.appendArgs(srcFile.getAbsolutePath()); + op.appendArgs(dstFile); + + try { + LOG.info("Copy " + srcFile + " to " + dstFile); + PrivilegedOperationExecutor executor = + PrivilegedOperationExecutor.getInstance(getConf()); + executor.executePrivilegedOperation(null, op, null, null, false, true); + } catch (PrivilegedOperationException e) { + int exitCode = e.getExitCode(); + LOG.error("Error when running script [" + dstFile + "], exitcode = " + + exitCode + ", output: " + e.getOutput(), e); + } + } + private File writeScriptToNMPrivateDir(String nmPrivateDir, String command) + throws IOException { + File tmp = File.createTempFile("cmd", ".tmp", new File(nmPrivateDir)); + Writer writer = new OutputStreamWriter(new FileOutputStream(tmp), "UTF-8"); + PrintWriter printWriter = new PrintWriter(writer); + printWriter.print(command); + printWriter.close(); + return tmp; } @Override diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/container/Container.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/container/Container.java index c4cea18..a68a151 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/container/Container.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/container/Container.java @@ -71,7 +71,9 @@ void setIpAndHost(String[] ipAndHost); - String toString(); + void setNMPrivateDir(Path path); + + Path getNMPrivateDir(); Priority getPriority(); diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/container/ContainerImpl.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/container/ContainerImpl.java index ce9e581..ea2d3dc 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/container/ContainerImpl.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/container/ContainerImpl.java @@ -117,6 +117,7 @@ private String logDir; private String host; private String ips; + private Path nmPrivateDir; /** The NM-wide configuration - not specific to this container */ private final Configuration daemonConf; @@ -589,6 +590,16 @@ public ResourceSet getResourceSet() { return this.resourceSet; } + @Override + public void setNMPrivateDir(Path nmPrivateDir) { + this.nmPrivateDir = nmPrivateDir; + } + + @Override + public Path getNMPrivateDir() { + return nmPrivateDir; + } + @SuppressWarnings("unchecked") private void sendFinishedEvents() { // Inform the application @@ -840,8 +851,8 @@ public void transition(ContainerImpl container, ContainerEvent event) { if (new File(linkFile).exists()) { LOG.info("Symlink file already exists: " + linkFile); } else { - container.context.getContainerExecutor() - .symLink(rsrcEvent.getLocation().toString(), linkFile); + container.context.getContainerExecutor().symLink( + rsrcEvent.getLocation().toString(), linkFile, container); LOG.info("Created symlink: " + linkFile + " -> " + rsrcEvent .getLocation()); } 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 d8239ef..8859c27 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 @@ -221,7 +221,7 @@ public Integer call() { + Path.SEPARATOR + containerIdStr, LocalDirAllocator.SIZE_UNKNOWN, false); recordContainerWorkDir(containerID, containerWorkDir.toString()); - + container.setNMPrivateDir(nmPrivateContainerScriptPath.getParent()); String pidFileSubpath = getPidFileSubpath(appIdStr, containerIdStr); // pid file should be in nm private dir so that it is not @@ -811,9 +811,6 @@ public final void symlink(Path src, Path dst) throws IOException { if (!src.isAbsolute()) { throw new IOException("Source must be absolute"); } - if (dst.isAbsolute()) { - throw new IOException("Destination must be relative"); - } if (dst.toUri().getPath().indexOf('/') != -1) { mkdir(dst.getParent()); } 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/privileged/PrivilegedOperation.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/privileged/PrivilegedOperation.java index 8402a16..23a22e8 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/privileged/PrivilegedOperation.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/privileged/PrivilegedOperation.java @@ -51,7 +51,8 @@ TC_READ_STATS("--tc-read-stats"), ADD_PID_TO_CGROUP(""), //no CLI switch supported yet. RUN_DOCKER_CMD("--run-docker"), - LIST_AS_USER(""); //no CLI switch supported yet. + LIST_AS_USER(""), //no CLI switch supported yet. + RUN_SCRIPT("--run-script"); private final String option; 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 ca3847e..2598951 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 @@ -901,6 +901,8 @@ static int open_file_as_nm(const char* filename) { uid_t user = geteuid(); gid_t group = getegid(); if (change_effective_user(nm_uid, nm_gid) != 0) { + fprintf(ERRORFILE, "Failed to change effective user as NM. \n"); + fflush(ERRORFILE); return -1; } int result = open(filename, O_RDONLY); @@ -909,6 +911,8 @@ static int open_file_as_nm(const char* filename) { strerror(errno)); } if (change_effective_user(user, group)) { + fprintf(ERRORFILE, "Failed to change user to %i - %i\n", user, group); + fflush(ERRORFILE); result = -1; } return result; @@ -1155,6 +1159,46 @@ int run_docker(const char *command_file) { return exit_code; } +int run_script_as_user(const char *src_script_file, const char *dst_script_file) { + int exit_code = -1; + int src_file_fd = open_file_as_nm(src_script_file); + if (src_file_fd == -1) { + exit_code = INVALID_NM_ROOT_DIRS; + fprintf(ERRORFILE, "Could not open file %s\n", src_script_file); + fflush(ERRORFILE); + unlink(src_script_file); + return exit_code; + } + + // give up root privs + if (change_user(user_detail->pw_uid, user_detail->pw_gid) != 0) { + unlink(src_script_file); + return -1; + } + + // 700 + if (copy_file(src_file_fd, src_script_file, dst_script_file, S_IRWXU) != 0) { + fprintf(ERRORFILE, "Could not copy file: %s to %s\n", src_script_file, + dst_script_file); + fflush(ERRORFILE); + exit_code = INVALID_COMMAND_PROVIDED; + unlink(src_script_file); + return exit_code; + } + + if (execlp(dst_script_file, dst_script_file, NULL) != 0) { + fprintf(ERRORFILE, "Couldn't execute the script file: %s - %s", + dst_script_file, strerror(errno)); + fflush(ERRORFILE); + unlink(src_script_file); + unlink(dst_script_file); + return UNABLE_TO_EXECUTE_CONTAINER_SCRIPT; + } + unlink(src_script_file); + unlink(dst_script_file); + return 0; +} + int create_script_paths(const char *work_dir, const char *script_name, const char *cred_file, char** script_file_dest, char** cred_file_dest, diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.h hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.h index 1858555..a3732c8 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.h +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.h @@ -82,6 +82,7 @@ enum operations { RUN_AS_USER_LAUNCH_DOCKER_CONTAINER = 10, RUN_DOCKER = 11, RUN_AS_USER_LIST = 12 + RUN_SCRIPT = 13 }; #define NM_GROUP_KEY "yarn.nodemanager.linux-container-executor.group" @@ -287,6 +288,11 @@ int traffic_control_read_stats(char *command_file); int run_docker(const char *command_file); /** + * Run a linux command. + */ +int run_script_as_user(const char *src_script_file, const char *dst_script_file); + +/** * 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/main/native/container-executor/impl/main.c hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/main.c index 56215ca..5903be9 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/main.c +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/main.c @@ -50,6 +50,7 @@ static void display_usage(FILE *stream) { " container-executor --tc-read-state \n" \ " container-executor --tc-read-stats \n" \ " container-executor --run-docker \n" \ + " container-executor --run-script \n" \ " container-executor \n" \ " where command and command-args: \n" \ " initialize container: %2d appid tokens nm-local-dirs nm-log-dirs cmd app...\n" \ @@ -186,7 +187,7 @@ static struct { const char *target_dir; int container_pid; int signal; - const char *docker_command_file; + const char *command_file; } cmd_input; static int validate_run_as_user_commands(int argc, char **argv, int *operation); @@ -260,10 +261,23 @@ static int validate_arguments(int argc, char **argv , int *operation) { return INVALID_ARGUMENT_NUMBER; } optind++; - cmd_input.docker_command_file = argv[optind++]; + cmd_input.command_file = argv[optind++]; *operation = RUN_DOCKER; return 0; } + + if (strcmp("--run-script", argv[1]) == 0) { + if (argc != 5) { + display_usage(stdout); + return INVALID_ARGUMENT_NUMBER; + } + optind++; + cmd_input.run_as_user_name = argv[optind++]; + cmd_input.script_file = argv[optind++]; // script_file in nmPrivateDir + cmd_input.command_file = argv[optind++];// script_file in container workDir + *operation = RUN_SCRIPT; + return 0; + } /* Now we have to validate 'run as user' operations that don't use a 'long option' - we should fix this at some point. The validation/argument parsing here is extensive enough that it done in a separate function */ @@ -324,7 +338,7 @@ static int validate_run_as_user_commands(int argc, char **argv, int *operation) cmd_input.pid_file = argv[optind++]; cmd_input.local_dirs = argv[optind++];// good local dirs as a comma separated list cmd_input.log_dirs = argv[optind++];// good log dirs as a comma separated list - cmd_input.docker_command_file = argv[optind++]; + cmd_input.command_file = argv[optind++]; resources = argv[optind++];// key,value pair describing resources resources_key = malloc(strlen(resources)); resources_value = malloc(strlen(resources)); @@ -469,7 +483,14 @@ int main(int argc, char **argv) { exit_code = traffic_control_read_stats(cmd_input.traffic_control_command_file); break; case RUN_DOCKER: - exit_code = run_docker(cmd_input.docker_command_file); + exit_code = run_docker(cmd_input.command_file); + break; + case RUN_SCRIPT: + exit_code = set_user(cmd_input.run_as_user_name); + if (exit_code != 0) { + break; + } + exit_code = run_script_as_user(cmd_input.script_file, cmd_input.command_file); break; case RUN_AS_USER_INITIALIZE_CONTAINER: exit_code = set_user(cmd_input.run_as_user_name); @@ -508,7 +529,7 @@ int main(int argc, char **argv) { cmd_input.pid_file, extract_values(cmd_input.local_dirs), extract_values(cmd_input.log_dirs), - cmd_input.docker_command_file, + cmd_input.command_file, cmd_input.resources_key, cmd_input.resources_values); break; diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/monitor/TestContainersMonitorResourceChange.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/monitor/TestContainersMonitorResourceChange.java index d24f89d..b58d6ee 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/monitor/TestContainersMonitorResourceChange.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/monitor/TestContainersMonitorResourceChange.java @@ -91,7 +91,7 @@ public void deleteAsUser(DeletionAsUserContext ctx) } @Override - public void symLink(String target, String symlink) + public void symLink(String src, String symlink, Container container) throws IOException { } diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/webapp/MockContainer.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/webapp/MockContainer.java index c176556..5f46327 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/webapp/MockContainer.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/webapp/MockContainer.java @@ -190,4 +190,14 @@ public Priority getPriority() { public void setIpAndHost(String[] ipAndHost) { } + + @Override + public void setNMPrivateDir(Path path) { + + } + + @Override + public Path getNMPrivateDir() { + return null; + } }