diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java index 3bb73f5..5edaf6f 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java @@ -1431,6 +1431,14 @@ public static boolean isAclEnabled(Configuration conf) { public static final String DEFAULT_NM_DOCKER_DEFAULT_CONTAINER_NETWORK = "host"; + /** The docker client config used when launching containers, this allows for + * overriding the $HOME/.docker/config.json path used when running or pulling + * containers. This is needed to allow YARN to automatically download + * the image from a docker registry that requires authentication. + */ + public static final String NM_DOCKER_CLIENT_CONFIG_DIRECTORY = + DOCKER_CONTAINER_RUNTIME_PREFIX + "client-config-directory"; + /** The path to the Linux container executor.*/ public static final String NM_LINUX_CONTAINER_EXECUTOR_PATH = NM_PREFIX + "linux-container-executor.path"; diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/test/java/org/apache/hadoop/yarn/conf/TestYarnConfigurationFields.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/test/java/org/apache/hadoop/yarn/conf/TestYarnConfigurationFields.java index a4e5b0a..7a534a1 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/test/java/org/apache/hadoop/yarn/conf/TestYarnConfigurationFields.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/test/java/org/apache/hadoop/yarn/conf/TestYarnConfigurationFields.java @@ -123,6 +123,8 @@ public void initializeMemberVariables() { .add(YarnConfiguration.NM_MEMORY_RESOURCE_PREFIX); configurationPrefixToSkipCompare .add(YarnConfiguration.NM_CPU_RESOURCE_ENABLED); + configurationPropsToSkipCompare + .add(YarnConfiguration.NM_DOCKER_CLIENT_CONFIG_DIRECTORY); // Ignore Distributed Scheduling Related Configurations. // Since it is still a "work in progress" feature 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 0cfdd05..0e90540 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 @@ -347,6 +347,12 @@ public void launchContainer(ContainerRuntimeContext ctx) .addMountLocation("/etc/passwd", "/etc/password:ro"); List allDirs = new ArrayList<>(containerLocalDirs); + String clientConfigDir = conf.get( + YarnConfiguration.NM_DOCKER_CLIENT_CONFIG_DIRECTORY); + if(clientConfigDir != null) { + runCommand.setClientConfigDir(clientConfigDir); + } + allDirs.addAll(filecacheDirs); allDirs.add(containerWorkDir.toString()); allDirs.addAll(containerLogDirs); @@ -465,6 +471,13 @@ public void signalContainer(ContainerRuntimeContext ctx) } else { String containerId = ctx.getContainer().getContainerId().toString(); DockerStopCommand stopCommand = new DockerStopCommand(containerId); + + String clientConfigDir = conf.get( + YarnConfiguration.NM_DOCKER_CLIENT_CONFIG_DIRECTORY); + if(clientConfigDir != null) { + stopCommand.setClientConfigDir(clientConfigDir); + } + String commandFile = dockerClient.writeCommandToTempFile(stopCommand, containerId); privOp = new PrivilegedOperation( 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/DockerCommand.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/DockerCommand.java index 3b76a5c..e821f4b 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/DockerCommand.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/DockerCommand.java @@ -60,7 +60,27 @@ protected final void addCommandArguments(String... arguments) { this.commandWithArguments.addAll(Arrays.asList(arguments)); } + /** Add commands to the beginning of the commandWithArguments. + * This is needed in cases such as prepending --config or + * -D/--debug to the docker CLI. + * This method is only meant for use by sub-classes. + * @param arguments to be prepended + */ + protected final void prependCommandArguments(String... arguments) { + this.commandWithArguments.addAll(0, Arrays.asList(arguments)); + } + public String getCommandWithArguments() { return StringUtils.join(" ", commandWithArguments); } + + /** Add the client configuration to the docker command. + * The client configuration goes before any of the docker subcommands (such + * as run, load, pull, etc). + * @param clientConfigDir - directory containing the docker client config + */ + public void setClientConfigDir(String clientConfigDir) { + prependCommandArguments("--config=" + clientConfigDir); + } + } \ No newline at end of file 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/TestDockerContainerRuntime.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/TestDockerContainerRuntime.java index a29b174..10f132c 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/TestDockerContainerRuntime.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/TestDockerContainerRuntime.java @@ -879,4 +879,40 @@ private Configuration getConfigurationWithMockContainerExecutor() { return conf; } + @Test + public void testDockerConfigDirDefault() throws Exception { + String command = getAndValidateDockerCommandsWithConf(conf); + Assert.assertFalse(command.startsWith("--config=")); + } + + @Test + public void testDockerConfigDirNonDefault() throws Exception { + String dockerConfDir = "/etc/docker"; + conf.set(YarnConfiguration.NM_DOCKER_CLIENT_CONFIG_DIRECTORY, + dockerConfDir); + + String command = getAndValidateDockerCommandsWithConf(conf); + Assert.assertTrue(command.startsWith("--config=" + dockerConfDir)); + } + + private String getAndValidateDockerCommandsWithConf(Configuration + config) + throws Exception { + DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime( + mockExecutor, mockCGroupsHandler); + runtime.initialize(config); + runtime.launchContainer(builder.build()); + + PrivilegedOperation op = capturePrivilegedOperation(); + Assert.assertEquals(op.getOperationType(), + PrivilegedOperation.OperationType.LAUNCH_DOCKER_CONTAINER); + + String dockerCommandFile = op.getArguments().get(11); + List dockerCommands = Files.readAllLines( + Paths.get(dockerCommandFile), Charset.forName("UTF-8")); + Assert.assertEquals(1, dockerCommands.size()); + + return dockerCommands.get(0); + } + }