YARN-8777. Container Executor C binary change to execute interactive docker command. Contributed by Eric Yang
This commit is contained in:
parent
47eeb3eb7b
commit
96d28b4750
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -268,6 +268,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
|
||||
*/
|
||||
@ -295,4 +300,4 @@ char* flatten(char **args);
|
||||
/**
|
||||
* Remove docker container
|
||||
*/
|
||||
int remove_docker_container(char **argv, int argc);
|
||||
int remove_docker_container(char **argv, int argc);
|
||||
|
@ -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. */
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 };
|
||||
|
||||
@ -175,6 +176,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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user