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/privileged/PrivilegedOperation.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/privileged/PrivilegedOperation.java index 189c0d09b90..92a82e8fbcd 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/privileged/PrivilegedOperation.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/privileged/PrivilegedOperation.java @@ -54,7 +54,9 @@ GPU("--module-gpu"), FPGA("--module-fpga"), LIST_AS_USER(""), // no CLI switch supported yet. - ADD_NUMA_PARAMS(""); // no CLI switch supported yet. + ADD_NUMA_PARAMS(""), // no CLI switch supported yet. + REMOVE_DOCKER_CONTAINER("--remove-docker-container"), + INSPECT_DOCKER_CONTAINER("--inspect-docker-container"); private final String option; 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 999b343628a..9b7959b3444 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 @@ -22,6 +22,7 @@ import com.google.common.annotations.VisibleForTesting; import org.apache.hadoop.security.Credentials; +import org.apache.hadoop.yarn.api.records.ContainerId; import org.apache.hadoop.yarn.server.nodemanager.Context; import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker.DockerCommand; import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker.DockerCommandExecutor; @@ -383,7 +384,7 @@ private String runDockerVolumeCommand(DockerVolumeCommand dockerVolumeCommand, Container container) throws ContainerExecutionException { try { String commandFile = dockerClient.writeCommandToTempFile( - dockerVolumeCommand, container, nmContext); + dockerVolumeCommand, container.getContainerId(), nmContext); PrivilegedOperation privOp = new PrivilegedOperation( PrivilegedOperation.OperationType.RUN_DOCKER_CMD); privOp.appendArgs(commandFile); @@ -728,6 +729,7 @@ private String getUserIdInfo(String userName) public void launchContainer(ContainerRuntimeContext ctx) throws ContainerExecutionException { Container container = ctx.getContainer(); + ContainerId containerId = container.getContainerId(); Map environment = container.getLaunchContext() .getEnvironment(); String imageName = environment.get(ENV_DOCKER_CONTAINER_IMAGE); @@ -744,7 +746,7 @@ public void launchContainer(ContainerRuntimeContext ctx) validateImageName(imageName); - String containerIdStr = container.getContainerId().toString(); + String containerIdStr = containerId.toString(); String runAsUser = ctx.getExecutionAttribute(RUN_AS_USER); String dockerRunAsUser = runAsUser; Path containerWorkDir = ctx.getExecutionAttribute(CONTAINER_WORK_DIR); @@ -902,7 +904,7 @@ public void launchContainer(ContainerRuntimeContext ctx) } String commandFile = dockerClient.writeCommandToTempFile(runCommand, - container, nmContext); + containerId, nmContext); PrivilegedOperation launchOp = buildLaunchOp(ctx, commandFile, runCommand); @@ -921,8 +923,8 @@ public void launchContainer(ContainerRuntimeContext ctx) @Override public void relaunchContainer(ContainerRuntimeContext ctx) throws ContainerExecutionException { - Container container = ctx.getContainer(); - String containerIdStr = container.getContainerId().toString(); + ContainerId containerId = ctx.getContainer().getContainerId(); + String containerIdStr = containerId.toString(); // Check to see if the container already exists for relaunch DockerCommandExecutor.DockerContainerStatus containerStatus = DockerCommandExecutor.getContainerStatus(containerIdStr, conf, @@ -931,7 +933,7 @@ public void relaunchContainer(ContainerRuntimeContext ctx) DockerCommandExecutor.isStartable(containerStatus)) { DockerStartCommand startCommand = new DockerStartCommand(containerIdStr); String commandFile = dockerClient.writeCommandToTempFile(startCommand, - container, nmContext); + containerId, nmContext); PrivilegedOperation launchOp = buildLaunchOp(ctx, commandFile, startCommand); @@ -1036,12 +1038,13 @@ public void reapContainer(ContainerRuntimeContext ctx) // ipAndHost[1] contains the hostname. @Override public String[] getIpAndHost(Container container) { - String containerId = container.getContainerId().toString(); + ContainerId containerId = container.getContainerId(); + String containerIdStr = containerId.toString(); DockerInspectCommand inspectCommand = - new DockerInspectCommand(containerId).getIpAndHost(); + new DockerInspectCommand(containerIdStr).getIpAndHost(); try { String commandFile = dockerClient.writeCommandToTempFile(inspectCommand, - container, nmContext); + containerId, nmContext); PrivilegedOperation privOp = new PrivilegedOperation( PrivilegedOperation.OperationType.RUN_DOCKER_CMD); privOp.appendArgs(commandFile); 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/docker/DockerClient.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/docker/DockerClient.java index c55b83b16f7..e04969349cb 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/docker/DockerClient.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/docker/DockerClient.java @@ -104,9 +104,8 @@ public String writeCommandToTempFile(DockerCommand cmd, String filePrefix) } } - public String writeCommandToTempFile(DockerCommand cmd, Container container, + public String writeCommandToTempFile(DockerCommand cmd, ContainerId containerId, Context nmContext) throws ContainerExecutionException { - ContainerId containerId = container.getContainerId(); String filePrefix = containerId.toString(); ApplicationId appId = containerId.getApplicationAttemptId() .getApplicationId(); 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/docker/DockerCommandExecutor.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/docker/DockerCommandExecutor.java index 6abe1cb5766..d698e127b18 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/docker/DockerCommandExecutor.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/docker/DockerCommandExecutor.java @@ -80,14 +80,27 @@ public static String executeDockerCommand(DockerCommand dockerCommand, PrivilegedOperationExecutor privilegedOperationExecutor, boolean disableFailureLogging, Context nmContext) throws ContainerExecutionException { - DockerClient dockerClient = new DockerClient(conf); - String commandFile = - dockerClient.writeCommandToTempFile(dockerCommand, - nmContext.getContainers().get(ContainerId.fromString(containerId)), - nmContext); - PrivilegedOperation dockerOp = new PrivilegedOperation( - PrivilegedOperation.OperationType.RUN_DOCKER_CMD); - dockerOp.appendArgs(commandFile); + PrivilegedOperation dockerOp; + if(dockerCommand instanceof DockerRmCommand) { + dockerOp = new PrivilegedOperation( + PrivilegedOperation.OperationType.REMOVE_DOCKER_CONTAINER); + dockerOp.appendArgs(containerId); + } else if(dockerCommand instanceof DockerInspectCommand) { + DockerInspectCommand dockerInspectCommand = + (DockerInspectCommand) dockerCommand; + dockerOp = new PrivilegedOperation( + PrivilegedOperation.OperationType.INSPECT_DOCKER_CONTAINER); + dockerOp.appendArgs(dockerInspectCommand.getCommandArguments(), containerId); + } else { + DockerClient dockerClient = new DockerClient(conf); + String commandFile = + dockerClient.writeCommandToTempFile(dockerCommand, + ContainerId.fromString(containerId), + nmContext); + dockerOp = new PrivilegedOperation( + PrivilegedOperation.OperationType.RUN_DOCKER_CMD); + dockerOp.appendArgs(commandFile); + } if (disableFailureLogging) { dockerOp.disableFailureLogging(); } @@ -97,7 +110,7 @@ public static String executeDockerCommand(DockerCommand dockerCommand, try { String result = privilegedOperationExecutor .executePrivilegedOperation(null, dockerOp, null, - env, true, false); + env, true, false); if (result != null && !result.isEmpty()) { result = result.trim(); } 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/docker/DockerInspectCommand.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/docker/DockerInspectCommand.java index d27f74d0f16..b310b2c2b07 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/docker/DockerInspectCommand.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/docker/DockerInspectCommand.java @@ -26,14 +26,18 @@ */ public class DockerInspectCommand extends DockerCommand { private static final String INSPECT_COMMAND = "inspect"; + private String containerId; + private String commandArguments; public DockerInspectCommand(String containerName) { super(INSPECT_COMMAND); super.addCommandArguments("name", containerName); + this.containerId = containerName; } public DockerInspectCommand getContainerStatus() { super.addCommandArguments("format", "{{.State.Status}}"); + this.commandArguments = "--format={{.State.Status}}"; return this; } @@ -45,4 +49,12 @@ public DockerInspectCommand getIpAndHost() { + "{{.IPAddress}},{{end}}{{.Config.Hostname}}"); return this; } + + public String getContainerId() { + return this.containerId; + } + + public String getCommandArguments() { + return this.commandArguments; + } } 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/docker/DockerRmCommand.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/docker/DockerRmCommand.java index dcfe7779dbc..ced54a516c2 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/docker/DockerRmCommand.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/docker/DockerRmCommand.java @@ -22,9 +22,15 @@ */ public class DockerRmCommand extends DockerCommand { private static final String RM_COMMAND = "rm"; + private String containerId; public DockerRmCommand(String containerName) { super(RM_COMMAND); super.addCommandArguments("name", containerName); + this.containerId = containerName; + } + + public String getContainerId() { + return this.containerId; } } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.c b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.c index d9ed0702cd3..33bb339102d 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.c +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.c @@ -1332,6 +1332,32 @@ int run_docker(const char *command_file) { return exit_code; } +int invoke_docker_command(const char *docker_command, const char *arguments) { + char* docker_binary = get_docker_binary(&CFG); + size_t command_size = MIN(sysconf(_SC_ARG_MAX), 128*1024); + + char* docker_command_with_binary = alloc_and_clear_memory(command_size, sizeof(char)); + snprintf(docker_command_with_binary, command_size, "%s %s %s", docker_binary, docker_command, arguments); + fprintf(LOGFILE, "Invoking '%s'\n", docker_command_with_binary); + + char **args = split_delimiter(docker_command_with_binary, " "); + + int exit_code = -1; + if (execvp(docker_binary, args) != 0) { + fprintf(ERRORFILE, "Couldn't execute the container launch with args %s - %s", + docker_binary, strerror(errno)); + fflush(LOGFILE); + fflush(ERRORFILE); + free(docker_binary); + free(docker_command_with_binary); + free(args); + exit_code = DOCKER_RUN_FAILED; + } else { + exit_code = 0; + } + return exit_code; +} + 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 a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.h b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.h index 7c3ed773537..cd18ca70a6a 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.h +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.h @@ -47,7 +47,9 @@ enum operations { RUN_AS_USER_DELETE = 9, RUN_AS_USER_LAUNCH_DOCKER_CONTAINER = 10, RUN_DOCKER = 11, - RUN_AS_USER_LIST = 12 + RUN_AS_USER_LIST = 12, + REMOVE_DOCKER_CONTAINER = 13, + INSPECT_DOCKER_CONTAINER = 14 }; #define NM_GROUP_KEY "yarn.nodemanager.linux-container-executor.group" @@ -263,6 +265,11 @@ int is_docker_support_enabled(); */ int run_docker(const char *command_file); +/** + * Run a docker command without a command file + */ +int invoke_docker_command(const char *docker_command, const char *arguments); + /* * Compile the regex_str and determine if the input string matches. * Return 0 on match, 1 of non-match. diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/main.c b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/main.c index b69546a5455..eb15721ecde 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/main.c +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/main.c @@ -36,7 +36,7 @@ static void display_usage(FILE *stream) { fprintf(stream, "Usage: container-executor --checksetup\n" " container-executor --mount-cgroups " - "...\n" ); + "\n" ); if(is_tc_support_enabled()) { fprintf(stream, @@ -52,10 +52,14 @@ static void display_usage(FILE *stream) { if(is_docker_support_enabled()) { fprintf(stream, - " container-executor --run-docker \n"); + " container-executor --run-docker \n" + " container-executor --remove-docker-container \n" + " container-executor --inspect-docker-container \n"); } else { fprintf(stream, - "[DISABLED] container-executor --run-docker \n"); + "[DISABLED] container-executor --run-docker \n" + "[DISABLED] container-executor --remove-docker-container \n" + "[DISABLED] container-executor --inspect-docker-container \n"); } fprintf(stream, @@ -331,6 +335,36 @@ static int validate_arguments(int argc, char **argv , int *operation) { } } + if (strcmp("--remove-docker-container", argv[1]) == 0) { + if(is_docker_support_enabled()) { + if (argc != 3) { + display_usage(stdout); + return INVALID_ARGUMENT_NUMBER; + } + optind++; + *operation = REMOVE_DOCKER_CONTAINER; + return 0; + } else { + display_feature_disabled_message("docker"); + return FEATURE_DISABLED; + } + } + + if (strcmp("--inspect-docker-container", argv[1]) == 0) { + if(is_docker_support_enabled()) { + if (argc != 4) { + display_usage(stdout); + return INVALID_ARGUMENT_NUMBER; + } + optind++; + *operation = INSPECT_DOCKER_CONTAINER; + return 0; + } else { + display_feature_disabled_message("docker"); + return FEATURE_DISABLED; + } + } + /* 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 */ @@ -528,6 +562,7 @@ int main(int argc, char **argv) { int operation = -1; int ret = validate_arguments(argc, argv, &operation); + char* arguments; if (ret != 0) { flush_and_close_log_files(); @@ -561,6 +596,18 @@ int main(int argc, char **argv) { case RUN_DOCKER: exit_code = run_docker(cmd_input.docker_command_file); break; + case REMOVE_DOCKER_CONTAINER: + exit_code = invoke_docker_command("rm", argv[optind++]); + break; + case INSPECT_DOCKER_CONTAINER: + arguments = malloc(MIN(sysconf(_SC_ARG_MAX), 128*1024)); + while (optind < argc) { + strcat(arguments, argv[optind++]); + strcat(arguments, " "); + } + exit_code = invoke_docker_command("inspect", arguments); + free(arguments); + break; case RUN_AS_USER_INITIALIZE_CONTAINER: exit_code = set_user(cmd_input.run_as_user_name); if (exit_code != 0) { 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/docker/TestDockerCommandExecutor.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/docker/TestDockerCommandExecutor.java index a230d4df9cc..850bf299ec4 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/docker/TestDockerCommandExecutor.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/docker/TestDockerCommandExecutor.java @@ -153,14 +153,13 @@ public void testExecuteDockerRm() throws Exception { env, configuration, mockExecutor, false, nmContext); List ops = MockPrivilegedOperationCaptor .capturePrivilegedOperations(mockExecutor, 1, true); - List dockerCommands = getValidatedDockerCommands(ops); + PrivilegedOperation privOp = ops.get(0); + List args = privOp.getArguments(); assertEquals(1, ops.size()); - assertEquals(PrivilegedOperation.OperationType.RUN_DOCKER_CMD.name(), - ops.get(0).getOperationType().name()); - assertEquals(3, dockerCommands.size()); - assertEquals("[docker-command-execution]", dockerCommands.get(0)); - assertEquals(" docker-command=rm", dockerCommands.get(1)); - assertEquals(" name=" + MOCK_CONTAINER_ID, dockerCommands.get(2)); + assertEquals(PrivilegedOperation.OperationType.REMOVE_DOCKER_CONTAINER.name(), + privOp.getOperationType().name()); + assertEquals(1, args.size()); + assertEquals(MOCK_CONTAINER_ID, args.get(0)); } @Test @@ -188,16 +187,14 @@ public void testExecuteDockerInspectStatus() throws Exception { env, configuration, mockExecutor, false, nmContext); List ops = MockPrivilegedOperationCaptor .capturePrivilegedOperations(mockExecutor, 1, true); - List dockerCommands = getValidatedDockerCommands(ops); + PrivilegedOperation privOp = ops.get(0); + List args = privOp.getArguments(); assertEquals(1, ops.size()); - assertEquals(PrivilegedOperation.OperationType.RUN_DOCKER_CMD.name(), - ops.get(0).getOperationType().name()); - assertEquals(4, dockerCommands.size()); - assertEquals("[docker-command-execution]", dockerCommands.get(0)); - assertEquals(" docker-command=inspect", dockerCommands.get(1)); - assertEquals(" format={{.State.Status}}", dockerCommands.get(2)); - assertEquals(" name=" + MOCK_CONTAINER_ID, dockerCommands.get(3)); - + assertEquals(PrivilegedOperation.OperationType.INSPECT_DOCKER_CONTAINER.name(), + privOp.getOperationType().name()); + assertEquals(2, args.size()); + assertEquals("--format={{.State.Status}}", args.get(0)); + assertEquals(MOCK_CONTAINER_ID, args.get(1)); } @Test