YARN-8777. Container Executor C binary change to execute interactive docker 
command. Contributed by Eric Yang


Project: http://git-wip-us.apache.org/repos/asf/hadoop/repo
Commit: http://git-wip-us.apache.org/repos/asf/hadoop/commit/96d28b47
Tree: http://git-wip-us.apache.org/repos/asf/hadoop/tree/96d28b47
Diff: http://git-wip-us.apache.org/repos/asf/hadoop/diff/96d28b47

Branch: refs/heads/HDFS-12943
Commit: 96d28b4750f1088f2a10c83991b5a8149c97ef86
Parents: 47eeb3e
Author: Billie Rinaldi <bil...@apache.org>
Authored: Thu Oct 11 09:25:21 2018 -0700
Committer: Billie Rinaldi <bil...@apache.org>
Committed: Thu Oct 11 09:25:21 2018 -0700

----------------------------------------------------------------------
 .../impl/container-executor.c                   | 164 ++++++++++++++++++-
 .../impl/container-executor.h                   |   7 +-
 .../main/native/container-executor/impl/util.h  |   3 +-
 .../container-executor/impl/utils/docker-util.c |  53 ++++++
 .../container-executor/impl/utils/docker-util.h |  10 ++
 .../test/utils/test_docker_util.cc              |  29 ++++
 6 files changed, 263 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hadoop/blob/96d28b47/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.c
----------------------------------------------------------------------
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 7765308..1f7ae3f 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
@@ -47,6 +47,7 @@
 #include <sys/wait.h>
 #include <getopt.h>
 #include <sys/param.h>
+#include <termios.h>
 
 #ifndef HAVE_FCHMODAT
 #include "compat/fchmodat.h"
@@ -1357,8 +1358,25 @@ char **construct_docker_command(const char 
*command_file) {
 }
 
 int run_docker(const char *command_file) {
+  struct configuration command_config = {0, NULL};
+
+  int ret = read_config(command_file, &command_config);
+  if (ret != 0) {
+    free_configuration(&command_config);
+    return INVALID_COMMAND_FILE;
+  }
+  char *value = get_configuration_value("docker-command", 
DOCKER_COMMAND_FILE_SECTION, &command_config);
+  if (value != NULL && strcasecmp(value, "exec") == 0) {
+    free(value);
+    free_configuration(&command_config);
+    return run_docker_with_pty(command_file);
+  }
+  free_configuration(&command_config);
+  free(value);
+
   char **args = construct_docker_command(command_file);
   char* docker_binary = get_docker_binary(&CFG);
+
   int exit_code = -1;
   if (execvp(docker_binary, args) != 0) {
     fprintf(ERRORFILE, "Couldn't execute the container launch with args %s - 
%s",
@@ -1375,6 +1393,150 @@ int run_docker(const char *command_file) {
   return exit_code;
 }
 
+int run_docker_with_pty(const char *command_file) {
+  int exit_code = -1;
+  char **args = construct_docker_command(command_file);
+  char* docker_binary = get_docker_binary(&CFG);
+  int fdm, fds, rc;
+  char input[4000];
+
+  fdm = posix_openpt(O_RDWR);
+  if (fdm < 0) {
+    fprintf(stderr, "Error %d on posix_openpt()\n", errno);
+    return DOCKER_EXEC_FAILED;
+  }
+
+  rc = grantpt(fdm);
+  if (rc != 0) {
+    fprintf(stderr, "Error %d on grantpt()\n", errno);
+    return DOCKER_EXEC_FAILED;
+  }
+
+  rc = unlockpt(fdm);
+  if (rc != 0) {
+    fprintf(stderr, "Error %d on unlockpt()\n", errno);
+    return DOCKER_EXEC_FAILED;
+  }
+
+  // Open the slave PTY
+  fds = open(ptsname(fdm), O_RDWR);
+
+  // Creation of a child process
+  if (fork()) {
+    fd_set fd_in;
+    // Parent
+
+    // Close the slave side of the PTY
+    close(fds);
+    while (1) {
+      // Wait for data from standard input and master side of PTY
+      FD_ZERO(&fd_in);
+      FD_SET(0, &fd_in);
+      FD_SET(fdm, &fd_in);
+      rc = select(fdm + 1, &fd_in, NULL, NULL, NULL);
+      switch(rc) {
+        case -1 : fprintf(stderr, "Error %d on select()\n", errno);
+                  exit(1);
+        default :
+            {
+              // If data on standard input
+              if (FD_ISSET(0, &fd_in)) {
+                rc = read(0, input, sizeof(input));
+                if (rc > 0) {
+                  // Send data on the master side of PTY
+                  ssize_t written = write(fdm, input, rc);
+                  if (written == -1) {
+                    fprintf(stderr, "Error %d writing to container.\n", errno);
+                    exit(DOCKER_EXEC_FAILED);
+                  }
+                } else {
+                  if (rc < 0) {
+                    fprintf(stderr, "Error %d on read standard input\n", 
errno);
+                    exit(DOCKER_EXEC_FAILED);
+                  }
+                }
+              }
+
+              // If data on master side of PTY
+              if (FD_ISSET(fdm, &fd_in)) {
+                rc = read(fdm, input, sizeof(input));
+                if (rc > 0) {
+                  // Send data on standard output
+                  ssize_t written = write(1, input, rc);
+                  if (written == -1) {
+                    fprintf(stderr, "Error %d writing to terminal.\n", errno);
+                    exit(DOCKER_EXEC_FAILED);
+                  }
+                } else {
+                  if (rc < 0) {
+                    fprintf(stderr, "Error %d on read master PTY\n", errno);
+                    exit(DOCKER_EXEC_FAILED);
+                  }
+                }
+              }
+            }
+      } // End switch
+    } // End while
+  } else {
+    struct termios slave_orig_term_settings; // Saved terminal settings
+    struct termios new_term_settings; // Current terminal settings
+
+    // Child
+
+    // Close the master side of the PTY
+    close(fdm);
+
+    // Save the default parameters of the slave side of the PTY
+    rc = tcgetattr(fds, &slave_orig_term_settings);
+
+    // Set raw mode on the slave side of the PTY
+    new_term_settings = slave_orig_term_settings;
+    cfmakeraw (&new_term_settings);
+    tcsetattr (fds, TCSANOW, &new_term_settings);
+
+    // The slave side of the PTY becomes the standard input and outputs of the 
child process
+    close(0); // Close standard input (current terminal)
+    close(1); // Close standard output (current terminal)
+    close(2); // Close standard error (current terminal)
+
+    if (dup(fds) == -1) {
+      // PTY becomes standard input (0)
+      exit(DOCKER_EXEC_FAILED);
+    }
+    if (dup(fds) == -1) {
+      // PTY becomes standard output (1)
+      exit(DOCKER_EXEC_FAILED);
+    }
+    if (dup(fds) == -1) {
+      // PTY becomes standard error (2)
+      exit(DOCKER_EXEC_FAILED);
+    }
+
+    // Now the original file descriptor is useless
+    close(fds);
+
+    // Make the current process a new session leader
+    setsid();
+
+    // As the child is a session leader, set the controlling terminal to be 
the slave side of the PTY
+    // (Mandatory for programs like the shell to make them manage correctly 
their outputs)
+    ioctl(0, TIOCSCTTY, 1);
+
+    if (execvp(docker_binary, args) != 0) {
+      fprintf(ERRORFILE, "Couldn't execute the container launch with args %s - 
%s",
+              docker_binary, strerror(errno));
+      free(docker_binary);
+      free_values(args);
+      exit_code = DOCKER_EXEC_FAILED;
+    } else {
+      free_values(args);
+      exit_code = 0;
+    }
+  }
+
+  return exit_code;
+}
+
 int exec_docker_command(char *docker_command, char **argv, int argc) {
   int i;
   char* docker_binary = get_docker_binary(&CFG);
@@ -2708,4 +2870,4 @@ int remove_docker_container(char**argv, int argc) {
     exit_code = clean_docker_cgroups(yarn_hierarchy, container_id);
   }
   return exit_code;
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/96d28b47/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/container-executor.h
----------------------------------------------------------------------
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 cd09e1e..ce05c57 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
@@ -269,6 +269,11 @@ int is_docker_support_enabled();
 int run_docker(const char *command_file);
 
 /**
+ * Run a docker command passing the command file as an argument with terminal.
+ */
+int run_docker_with_pty(const char *command_file);
+
+/**
  * Run a docker command without a command file
  */
 int exec_docker_command(char *docker_command, char **argv, int argc);
@@ -295,4 +300,4 @@ char* flatten(char **args);
 /**
  * Remove docker container
  */
-int remove_docker_container(char **argv, int argc);
\ No newline at end of file
+int remove_docker_container(char **argv, int argc);

http://git-wip-us.apache.org/repos/asf/hadoop/blob/96d28b47/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/util.h
----------------------------------------------------------------------
diff --git 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/util.h
 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/util.h
index 6aac1fe..372c17a 100644
--- 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/util.h
+++ 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/util.h
@@ -68,7 +68,8 @@ enum errorcodes {
   DOCKER_IMAGE_INVALID = 40,
   // DOCKER_CONTAINER_NAME_INVALID = 41, (NOT USED)
   ERROR_COMPILING_REGEX = 42,
-  INVALID_CONTAINER_ID = 43
+  INVALID_CONTAINER_ID = 43,
+  DOCKER_EXEC_FAILED = 44
 };
 
 /* Macros for min/max. */

http://git-wip-us.apache.org/repos/asf/hadoop/blob/96d28b47/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/utils/docker-util.c
----------------------------------------------------------------------
diff --git 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/utils/docker-util.c
 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/utils/docker-util.c
index 69f27ba..c0aa80b 100644
--- 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/utils/docker-util.c
+++ 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/utils/docker-util.c
@@ -432,6 +432,8 @@ int get_docker_command(const char *command_file, const 
struct configuration *con
     ret = get_docker_volume_command(command_file, conf, args);
   } else if (strcmp(DOCKER_START_COMMAND, command) == 0) {
     ret = get_docker_start_command(command_file, conf, args);
+  } else if (strcmp(DOCKER_EXEC_COMMAND, command) == 0) {
+    ret = get_docker_exec_command(command_file, conf, args);
   } else {
     ret = UNKNOWN_DOCKER_COMMAND;
   }
@@ -820,6 +822,57 @@ free_and_exit:
   return ret;
 }
 
+int get_docker_exec_command(const char *command_file, const struct 
configuration *conf, args *args) {
+  int ret = 0, i = 0;
+  char *container_name = NULL;
+  char **launch_command = NULL;
+  struct configuration command_config = {0, NULL};
+  ret = read_and_verify_command_file(command_file, DOCKER_EXEC_COMMAND, 
&command_config);
+  if (ret != 0) {
+    goto free_and_exit;
+  }
+
+  container_name = get_configuration_value("name", 
DOCKER_COMMAND_FILE_SECTION, &command_config);
+  if (container_name == NULL || validate_container_name(container_name) != 0) {
+    ret = INVALID_DOCKER_CONTAINER_NAME;
+    goto free_and_exit;
+  }
+
+  ret = add_to_args(args, DOCKER_EXEC_COMMAND);
+  if (ret != 0) {
+    goto free_and_exit;
+  }
+
+  ret = add_to_args(args, "-it");
+  if (ret != 0) {
+    goto free_and_exit;
+  }
+
+  ret = add_to_args(args, container_name);
+  if (ret != 0) {
+    goto free_and_exit;
+  }
+
+  launch_command = get_configuration_values_delimiter("launch-command", 
DOCKER_COMMAND_FILE_SECTION, &command_config,
+                                                      ",");
+  if (launch_command != NULL) {
+    for (i = 0; launch_command[i] != NULL; ++i) {
+      ret = add_to_args(args, launch_command[i]);
+      if (ret != 0) {
+        ret = BUFFER_TOO_SMALL;
+        goto free_and_exit;
+      }
+    }
+  } else {
+    ret = INVALID_COMMAND_FILE;
+  }
+free_and_exit:
+  free(container_name);
+  free_configuration(&command_config);
+  free_values(launch_command);
+  return ret;
+}
+
 static int detach_container(const struct configuration *command_config, args 
*args) {
   return add_param_to_command(command_config, "detach", "-d", 0, args);
 }

http://git-wip-us.apache.org/repos/asf/hadoop/blob/96d28b47/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/utils/docker-util.h
----------------------------------------------------------------------
diff --git 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/utils/docker-util.h
 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/utils/docker-util.h
index 7b7322d..fc7b4cc 100644
--- 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/utils/docker-util.h
+++ 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/utils/docker-util.h
@@ -34,6 +34,7 @@
 #define DOCKER_KILL_COMMAND "kill"
 #define DOCKER_VOLUME_COMMAND "volume"
 #define DOCKER_START_COMMAND "start"
+#define DOCKER_EXEC_COMMAND "exec"
 #define DOCKER_ARG_MAX 1024
 #define ARGS_INITIAL_VALUE { 0 };
 
@@ -176,6 +177,15 @@ int get_docker_volume_command(const char *command_file, 
const struct configurati
 int get_docker_start_command(const char* command_file, const struct 
configuration* conf, args *args);
 
 /**
+ * Get the Docker exec command line string. The function will verify that the 
params file is meant for the exec command.
+ * @param command_file File containing the params for the Docker start command
+ * @param conf Configuration struct containing the container-executor.cfg 
details
+ * @param args Buffer to construct argv
+ * @return Return code with 0 indicating success and non-zero codes indicating 
error
+ */
+int get_docker_exec_command(const char* command_file, const struct 
configuration* conf, args *args);
+
+/**
  * Give an error message for the supplied error code
  * @param error_code the error code
  * @return const string containing the error message

http://git-wip-us.apache.org/repos/asf/hadoop/blob/96d28b47/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/utils/test_docker_util.cc
----------------------------------------------------------------------
diff --git 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/utils/test_docker_util.cc
 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/utils/test_docker_util.cc
index b289857..2817e0c 100644
--- 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/utils/test_docker_util.cc
+++ 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/test/utils/test_docker_util.cc
@@ -1762,4 +1762,33 @@ namespace ContainerExecutor {
       free_configuration(&container_executor_cfg);
     }
   }
+
+  TEST_F(TestDockerUtil, test_docker_exec) {
+    std::string container_executor_contents = "[docker]\n"
+        "  docker.allowed.devices=/dev/test\n  
docker.trusted.registries=hadoop\n";
+    write_file(container_executor_cfg_file, container_executor_contents);
+    int ret = read_config(container_executor_cfg_file.c_str(), 
&container_executor_cfg);
+    if (ret != 0) {
+      FAIL();
+    }
+    ret = create_ce_file();
+    if (ret != 0) {
+      std::cerr << "Could not create ce file, skipping test" << std::endl;
+      return;
+    }
+
+    std::vector<std::pair<std::string, std::string> > file_cmd_vec;
+    file_cmd_vec.push_back(std::make_pair<std::string, std::string>(
+        "[docker-command-execution]\n"
+            "  docker-command=exec\n  
name=container_e1_12312_11111_02_000001\n  launch-command=bash",
+        "exec -it container_e1_12312_11111_02_000001 bash"));
+
+    std::vector<std::pair<std::string, int> > bad_file_cmd_vec;
+
+    bad_file_cmd_vec.push_back(std::make_pair<std::string, int>(
+        "[docker-command-execution]\n  docker-command=exec\n  
image=hadoop/docker-image\n  user=nobody",
+        static_cast<int>(INVALID_DOCKER_CONTAINER_NAME)));
+    run_docker_command_test(file_cmd_vec, bad_file_cmd_vec, 
get_docker_exec_command);
+    free_configuration(&container_executor_cfg);
+  }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: common-commits-unsubscr...@hadoop.apache.org
For additional commands, e-mail: common-commits-h...@hadoop.apache.org

Reply via email to