diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/ApplicationConstants.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/ApplicationConstants.java index 64bcc440d00..abcbb3bb79d 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/ApplicationConstants.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/ApplicationConstants.java @@ -234,7 +234,14 @@ * Comma separate list of directories that the container should use for * logging. */ - LOG_DIRS("LOG_DIRS"); + LOG_DIRS("LOG_DIRS"), + + /** + * $CONTAINER_HOSTNAME + * Final, exported by NodeManager and non-modifiable by users. + * Contains the hostname for the container being launched + */ + CONTAINER_HOSTNAME("CONTAINER_HOSTNAME"); private final String variable; private Environment(String variable) { 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 de225e6a2b1..b9b2e0f09ee 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.ApplicationConstants.Environment; import org.apache.hadoop.yarn.server.nodemanager.Context; import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker.DockerCommandExecutor; import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker.DockerKillCommand; @@ -428,6 +429,7 @@ public void prepareContainer(ContainerRuntimeContext ctx) } } } + setHostnameinEnv(container); } private void checkDockerVolumeCreated( @@ -536,22 +538,51 @@ public static void validateHostname(String hostname) throws } } - /** Set a DNS friendly hostname. */ - private void setHostname(DockerRunCommand runCommand, String - containerIdStr, String name) + /** Construct a DNS friendly hostname */ + private String constructFQHostName(String hostName, String containerIdStr) throws ContainerExecutionException { - if (name == null || name.isEmpty()) { - name = RegistryPathUtils.encodeYarnID(containerIdStr); + if (hostName == null || hostName.isEmpty()) { + hostName = RegistryPathUtils.encodeYarnID(containerIdStr); String domain = conf.get(RegistryConstants.KEY_DNS_DOMAIN); if (domain != null) { - name += ("." + domain); + hostName += ("." + domain); } - validateHostname(name); + validateHostname(hostName); } + return hostName; + } + + /** Set a DNS friendly hostname. */ + private void setHostname(DockerRunCommand runCommand, String + containerIdStr, + String name, Map environment) + throws ContainerExecutionException { + + String hostName = null; + //Pick up from the environment if already set in prepareContainer + if (environment.get(Environment.CONTAINER_HOSTNAME.name()) != null) { + hostName = environment.get(Environment + .CONTAINER_HOSTNAME + .name()); + } else{ + hostName = constructFQHostName(name, containerIdStr); + } + LOG.info("setting hostname in container to: " + hostName); + runCommand.setHostname(hostName); + } + + private void setHostnameinEnv(Container container) + throws ContainerExecutionException { + //Update container launch environment to set the FQ Hostname + Map environment = container.getLaunchContext() + .getEnvironment(); + String specifiedHostName = environment.get(ENV_DOCKER_CONTAINER_HOSTNAME); + String containerIdStr = container.getContainerId().toString(); + String hostName = constructFQHostName(specifiedHostName, containerIdStr); - LOG.info("setting hostname in container to: " + name); - runCommand.setHostname(name); + environment.put(Environment.CONTAINER_HOSTNAME.name(), + hostName); } /** @@ -785,7 +816,7 @@ public void launchContainer(ContainerRuntimeContext ctx) .detachOnRun() .setContainerWorkDir(containerWorkDir.toString()) .setNetworkType(network); - setHostname(runCommand, containerIdStr, hostname); + setHostname(runCommand, containerIdStr, hostname, environment); runCommand.setCapabilities(capabilities); runCommand.addAllReadWriteMountLocations(containerLogDirs); 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 4c53eb117c3..a711aace7d8 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 @@ -29,8 +29,10 @@ import org.apache.hadoop.security.Credentials; import org.apache.hadoop.util.Shell; import org.apache.hadoop.util.StringUtils; +import org.apache.hadoop.yarn.api.ApplicationConstants.Environment; import org.apache.hadoop.yarn.api.records.ContainerId; import org.apache.hadoop.yarn.api.records.ContainerLaunchContext; +import org.apache.hadoop.yarn.server.nodemanager.LinuxContainerExecutor; import org.apache.hadoop.yarn.util.DockerClientConfigHandler; import org.apache.hadoop.yarn.conf.YarnConfiguration; import org.apache.hadoop.yarn.security.TestDockerClientConfigHandler; @@ -64,10 +66,13 @@ import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; +import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.attribute.FileAttribute; @@ -77,11 +82,13 @@ import java.util.Collections; import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; +import static org.apache.hadoop.test.PlatformAssumptions.assumeNotWindows; import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.APPID; import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.APPLICATION_LOCAL_DIRS; import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.CONTAINER_ID_STR; @@ -331,7 +338,9 @@ public void testDockerContainerLaunch() DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime( mockExecutor, mockCGroupsHandler); runtime.initialize(conf, null); - runtime.launchContainer(builder.build()); + ContainerRuntimeContext runtimeContext = builder.build(); + runtime.prepareContainer(runtimeContext); + runtime.launchContainer(runtimeContext); PrivilegedOperation op = capturePrivilegedOperationAndVerifyArgs(); List args = op.getArguments(); @@ -353,6 +362,15 @@ public void testDockerContainerLaunch() Assert.assertEquals(" group-add=" + String.join(",", groups), dockerCommands.get(counter++)); Assert.assertEquals(" hostname=ctr-id", dockerCommands.get(counter++)); + + + Container container = runtimeContext.getContainer(); + Map environment = container.getLaunchContext() + .getEnvironment(); + Assert + .assertEquals("Container Hostname : ", environment.get( + Environment.CONTAINER_HOSTNAME.name()), + "ctr-id"); Assert .assertEquals(" image=busybox:latest", dockerCommands.get(counter++)); Assert.assertEquals( @@ -1874,4 +1892,39 @@ public void reapContainer(ContainerRuntimeContext ctx) } } } + + @Test(timeout = 20000) + public void testWriteEnvExportDocker() throws Exception { + // Valid only for unix + assumeNotWindows(); + File tmpDir = new File("target", this.getClass().getSimpleName() + + "-tmpDir"); + tmpDir.mkdir(); + File shellFile = Shell.appendScriptExtension(tmpDir, "hello"); + FileOutputStream fos = new FileOutputStream(shellFile); + DockerLinuxContainerRuntime dockerRuntime = + new DockerLinuxContainerRuntime( + mock(PrivilegedOperationExecutor.class)); + dockerRuntime.initialize(conf, null); + ContainerRuntimeContext runtimeContext = builder.build(); + + dockerRuntime.prepareContainer(runtimeContext); + LinuxContainerExecutor lce = + new LinuxContainerExecutor(dockerRuntime); + YarnConfiguration conf = new YarnConfiguration(); + lce.setConf(conf); + List commands = new ArrayList(); + Map> resources = new HashMap>(); + lce.writeLaunchEnv(fos, runtimeContext.getContainer().getLaunchContext().getEnvironment(), resources, commands, + new Path(logDirs.get(0)), "user", new LinkedHashSet<>()); + + String shellContent = + new String(Files.readAllBytes(Paths.get(shellFile.getAbsolutePath())), + StandardCharsets.UTF_8); + + Assert.assertTrue(shellContent + .contains("export CONTAINER_HOSTNAME=\"ctr-id\"")); + fos.flush(); + fos.close(); + } }