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 b70a4e1..9ea8ca4 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
@@ -40,6 +40,7 @@
import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.resources.CGroupsHandler;
import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.resources.ResourceHandlerModule;
import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker.DockerClient;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker.DockerContainerStatusHandler;
import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker.DockerInspectCommand;
import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker.DockerRunCommand;
import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker.DockerStopCommand;
@@ -58,7 +59,24 @@
import java.util.Map.Entry;
import java.util.Set;
-import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.*;
+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.CONTAINER_LOCAL_DIRS;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.CONTAINER_LOG_DIRS;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.CONTAINER_WORK_DIR;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.FILECACHE_DIRS;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.LOCALIZED_RESOURCES;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.LOCAL_DIRS;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.LOG_DIRS;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.NM_PRIVATE_CONTAINER_SCRIPT_PATH;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.NM_PRIVATE_TOKENS_PATH;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.PID;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.PID_FILE_PATH;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.RESOURCES_OPTIONS;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.RUN_AS_USER;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.SIGNAL;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.TC_COMMAND_FILE;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.USER;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.USER_LOCAL_DIRS;
/**
*
This class is a {@link ContainerRuntime} implementation that uses the
@@ -155,6 +173,7 @@
private String defaultNetwork;
private CGroupsHandler cGroupsHandler;
private AccessControlList privilegedContainersAcl;
+ private DockerContainerStatusHandler dockerContainerStatusHandler;
/**
* Return whether the given environment variables indicate that the operation
@@ -185,8 +204,8 @@ public static boolean isDockerContainerRequested(
*/
public DockerLinuxContainerRuntime(PrivilegedOperationExecutor
privilegedOperationExecutor) {
- this(privilegedOperationExecutor,
- ResourceHandlerModule.getCGroupsHandler());
+ this(privilegedOperationExecutor, ResourceHandlerModule
+ .getCGroupsHandler(), new DockerContainerStatusHandler());
}
/**
@@ -202,13 +221,38 @@ public DockerLinuxContainerRuntime(PrivilegedOperationExecutor
public DockerLinuxContainerRuntime(PrivilegedOperationExecutor
privilegedOperationExecutor, CGroupsHandler cGroupsHandler) {
this.privilegedOperationExecutor = privilegedOperationExecutor;
+ this.dockerContainerStatusHandler = new DockerContainerStatusHandler();
+ setCGroupHandler(cGroupsHandler);
+ }
- if (cGroupsHandler == null) {
+ /**
+ * Create an instance using the given {@link PrivilegedOperationExecutor}
+ * instance for performing operations and the given {@link CGroupsHandler}
+ * instance. Inject a {@link DockerContainerStatusHandler} for testing. This
+ * constructor is intended for use in testing.
+ *
+ * @param privilegedOperationExecutor {@link PrivilegedOperationExecutor}
+ * instance.
+ * @param cGroupsHandler {@link CGroupsHandler} instance.
+ * @param dockerContainerStatusHandler {@link DockerContainerStatusHandler}
+ * instance.
+ */
+ @VisibleForTesting
+ public DockerLinuxContainerRuntime(PrivilegedOperationExecutor
+ privilegedOperationExecutor, CGroupsHandler cGroupsHandler,
+ DockerContainerStatusHandler dockerContainerStatusHandler) {
+ this.privilegedOperationExecutor = privilegedOperationExecutor;
+ this.dockerContainerStatusHandler = dockerContainerStatusHandler;
+ setCGroupHandler(cGroupsHandler);
+ }
+
+ private void setCGroupHandler(CGroupsHandler cGroupHandlerInstance) {
+ if (cGroupHandlerInstance == null) {
if (LOG.isInfoEnabled()) {
LOG.info("cGroupsHandler is null - cgroups not in use.");
}
} else {
- this.cGroupsHandler = cGroupsHandler;
+ this.cGroupsHandler = cGroupHandlerInstance;
}
}
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
new file mode 100644
index 0000000..4b3e55f
--- /dev/null
+++ 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
@@ -0,0 +1,172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.PrivilegedOperation;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.PrivilegedOperationException;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.PrivilegedOperationExecutor;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.runtime.ContainerExecutionException;
+
+import java.util.Map;
+
+/**
+ * Utility class for executing common docker operations.
+ */
+public final class DockerCommandExecutor {
+ private static final Log LOG = LogFactory.getLog(DockerCommandExecutor.class);
+
+ private DockerCommandExecutor() {
+ }
+
+ /**
+ * Execute a docker command and return the output.
+ *
+ * @param dockerCommand the docker command to run.
+ * @param containerId the id of the container.
+ * @param env environment for the container.
+ * @param conf the hadoop configuration.
+ * @param privilegedOperationExecutor the privileged operations executor.
+ * @return the output of the operation.
+ * @throws ContainerExecutionException if the operation fails.
+ */
+ public static String executeDockerCommand(DockerCommand dockerCommand,
+ String containerId, Map env, Configuration conf,
+ PrivilegedOperationExecutor privilegedOperationExecutor)
+ throws ContainerExecutionException {
+ DockerClient dockerClient = new DockerClient(conf);
+ String commandFile =
+ dockerClient.writeCommandToTempFile(dockerCommand, containerId);
+ PrivilegedOperation dockerOp = new PrivilegedOperation(
+ PrivilegedOperation.OperationType.RUN_DOCKER_CMD);
+ dockerOp.appendArgs(commandFile);
+ dockerOp.disableFailureLogging();
+ try {
+ String result = privilegedOperationExecutor
+ .executePrivilegedOperation(null, dockerOp, null,
+ env, true, false);
+ if (result != null && !result.isEmpty()) {
+ result = result.trim();
+ }
+ return result;
+ } catch (PrivilegedOperationException e) {
+ throw new ContainerExecutionException("Docker operation failed",
+ e.getExitCode(), e.getOutput(), e.getErrorOutput());
+ }
+ }
+
+ /**
+ * Execute the docker rm command.
+ *
+ * @param containerId the id of the container.
+ * @param env environment for the container.
+ * @param conf the hadoop configuration.
+ * @param privilegedOperationExecutor the privileged operations executor.
+ * @throws ContainerExecutionException if the operation fails.
+ */
+ public static void executeDockerRm(String containerId,
+ Map env, Configuration conf,
+ PrivilegedOperationExecutor privilegedOperationExecutor)
+ throws ContainerExecutionException {
+ DockerRmCommand rmCommand = new DockerRmCommand(containerId);
+ LOG.debug("Running docker rm. ContainerId: " + containerId);
+ executeDockerCommand(rmCommand, containerId, env, conf,
+ privilegedOperationExecutor);
+ }
+
+ /**
+ * Execute the docker stop command.
+ *
+ * @param containerId the id of the container.
+ * @param env environment for the container.
+ * @param conf the hadoop configuration.
+ * @param privilegedOperationExecutor the privileged operations executor.
+ * @throws ContainerExecutionException if the operation fails.
+ */
+ public static void executeDockerStop(String containerId,
+ Map env, Configuration conf,
+ PrivilegedOperationExecutor privilegedOperationExecutor)
+ throws ContainerExecutionException {
+ DockerStopCommand stopCommand = new DockerStopCommand(containerId);
+ LOG.debug("Running docker stop. ContainerId: " + containerId);
+ executeDockerCommand(stopCommand, containerId, env, conf,
+ privilegedOperationExecutor);
+ }
+
+ /**
+ * Execute the docker inspect command.
+ *
+ * @param dockerInspectCommand the docker inspect command to execute.
+ * @param containerId the id of the container.
+ * @param env environment for the container.
+ * @param conf the hadoop configuration.
+ * @param privilegedOperationExecutor the privileged operations executor.
+ * @return the result of the docker inspect command.
+ * @throws ContainerExecutionException if the operation fails.
+ */
+ public static String executeDockerInspect(
+ DockerInspectCommand dockerInspectCommand, String containerId,
+ Map env, Configuration conf,
+ PrivilegedOperationExecutor privilegedOperationExecutor)
+ throws ContainerExecutionException {
+ LOG.debug("Running docker inspect. ContainerId: " + containerId);
+ return executeDockerCommand(dockerInspectCommand, containerId, env, conf,
+ privilegedOperationExecutor);
+ }
+
+ /**
+ * Execute the docker load command.
+ *
+ * @param containerId the id of the container.
+ * @param localImageFile the name of the local image file.
+ * @param env environment for the container.
+ * @param conf the hadoop configuration.
+ * @param privilegedOperationExecutor the privileged operations executor.
+ * @throws ContainerExecutionException if the operation fails.
+ */
+ public static void executeDockerLoad(String containerId,
+ String localImageFile, Map env, Configuration conf,
+ PrivilegedOperationExecutor privilegedOperationExecutor)
+ throws ContainerExecutionException {
+ DockerLoadCommand loadCommand = new DockerLoadCommand(localImageFile);
+ LOG.debug("Running docker load. Image File: " + localImageFile);
+ executeDockerCommand(loadCommand, containerId, env, conf,
+ privilegedOperationExecutor);
+ }
+
+ /**
+ * Execute the docker pull command.
+ *
+ * @param containerId the id of the container.
+ * @param imageName the full image name to pull.
+ * @param env environment for the container.
+ * @param conf the hadoop configuration.
+ * @param privilegedOperationExecutor the privileged operations executor.
+ * @throws ContainerExecutionException if the operation fails.
+ */
+ public static void executeDockerPull(String containerId, String imageName,
+ Map env, Configuration conf,
+ PrivilegedOperationExecutor privilegedOperationExecutor)
+ throws ContainerExecutionException {
+ DockerPullCommand pullCommand = new DockerPullCommand(imageName);
+ LOG.debug("Running docker pull. Image Name: " + imageName);
+ executeDockerCommand(pullCommand, containerId, env, conf,
+ privilegedOperationExecutor);
+ }
+}
\ No newline at end of file
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/DockerContainerStatusHandler.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/DockerContainerStatusHandler.java
new file mode 100644
index 0000000..1a6c892
--- /dev/null
+++ 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/DockerContainerStatusHandler.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.PrivilegedOperationExecutor;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.runtime.ContainerExecutionException;
+
+/**
+ * Encapsulate docker container status.
+ */
+public class DockerContainerStatusHandler {
+ private static final Log LOG =
+ LogFactory.getLog(DockerContainerStatusHandler.class);
+
+ /**
+ * Potential states that the docker status can return.
+ */
+ public enum DockerContainerStatus {
+ CREATED("created"),
+ RUNNING("running"),
+ STOPPED("stopped"),
+ RESTARTING("restarting"),
+ REMOVING("removing"),
+ DEAD("dead"),
+ EXITED("exited"),
+ NONEXISTENT("nonexistent"),
+ UNKNOWN("unknown");
+
+ private final String name;
+
+ DockerContainerStatus(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+ /**
+ * Get the status of the docker container. This runs a docker inspect to
+ * get the status. If the container no longer exists, docker inspect throws
+ * an exception and the nonexistent status is returned.
+ *
+ * @param containerId the id of the container.
+ * @param conf the hadoop configuration.
+ * @param privilegedOperationExecutor the privileged operations executor.
+ * @return a {@link DockerContainerStatus} representing the current status.
+ */
+ public DockerContainerStatus getContainerStatus(String containerId,
+ Configuration conf,
+ PrivilegedOperationExecutor privilegedOperationExecutor) {
+ try {
+ DockerContainerStatus dockerContainerStatus;
+ String currentContainerStatus =
+ executeStatusCommand(containerId, conf, privilegedOperationExecutor);
+ if (currentContainerStatus == null) {
+ dockerContainerStatus = DockerContainerStatus.UNKNOWN;
+ } else if (currentContainerStatus
+ .equals(DockerContainerStatus.CREATED.getName())) {
+ dockerContainerStatus = DockerContainerStatus.CREATED;
+ } else if (currentContainerStatus
+ .equals(DockerContainerStatus.RUNNING.getName())) {
+ dockerContainerStatus = DockerContainerStatus.RUNNING;
+ } else if (currentContainerStatus
+ .equals(DockerContainerStatus.STOPPED.getName())) {
+ dockerContainerStatus = DockerContainerStatus.STOPPED;
+ } else if (currentContainerStatus
+ .equals(DockerContainerStatus.RESTARTING.getName())) {
+ dockerContainerStatus = DockerContainerStatus.RESTARTING;
+ } else if (currentContainerStatus
+ .equals(DockerContainerStatus.REMOVING.getName())) {
+ dockerContainerStatus = DockerContainerStatus.REMOVING;
+ } else if (currentContainerStatus
+ .equals(DockerContainerStatus.DEAD.getName())) {
+ dockerContainerStatus = DockerContainerStatus.DEAD;
+ } else if (currentContainerStatus
+ .equals(DockerContainerStatus.EXITED.getName())) {
+ dockerContainerStatus = DockerContainerStatus.EXITED;
+ } else {
+ dockerContainerStatus = DockerContainerStatus.UNKNOWN;
+ }
+ LOG.debug("Container Status: " + dockerContainerStatus.getName()
+ + " ContainerId: " + containerId);
+ return dockerContainerStatus;
+ } catch (ContainerExecutionException e) {
+ LOG.debug(
+ "Container Status: " + DockerContainerStatus.NONEXISTENT.getName() +
+ " ContainerId: " + containerId);
+ return DockerContainerStatus.NONEXISTENT;
+ }
+ }
+
+ /**
+ * Execute the docker inspect command to retrieve the docker container's
+ * status.
+ *
+ * @param containerId the id of the container.
+ * @param conf the hadoop configuration.
+ * @param privilegedOperationExecutor the privileged operations executor.
+ * @return the current container status.
+ * @throws ContainerExecutionException if the docker operation fails to run.
+ */
+ protected String executeStatusCommand(String containerId,
+ Configuration conf,
+ PrivilegedOperationExecutor privilegedOperationExecutor)
+ throws ContainerExecutionException {
+ DockerInspectCommand dockerInspectCommand =
+ new DockerInspectCommand(containerId).getContainerStatus();
+ try {
+ return DockerCommandExecutor.executeDockerInspect(dockerInspectCommand,
+ containerId, null, conf, privilegedOperationExecutor);
+ } catch (ContainerExecutionException e) {
+ throw new ContainerExecutionException(e);
+ }
+ }
+}
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/package-info.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/package-info.java
new file mode 100644
index 0000000..189167c
--- /dev/null
+++ 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/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Package containing classes related to Docker commands and common operations
+ * used within the @{link DockerLinuxContainerRuntime}.
+ */
+@InterfaceAudience.Private
+@InterfaceStability.Unstable
+package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
\ 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/privileged/MockPrivilegedOperationCaptor.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/privileged/MockPrivilegedOperationCaptor.java
new file mode 100644
index 0000000..187da6b
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/privileged/MockPrivilegedOperationCaptor.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged;
+
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyList;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Captures operations from mock {@link PrivilegedOperation} instances.
+ */
+public final class MockPrivilegedOperationCaptor {
+
+ private MockPrivilegedOperationCaptor() {}
+
+ /**
+ * Capture the operation that should be performed by the
+ * PrivilegedOperationExecutor.
+ *
+ * @param mockExecutor mock PrivilegedOperationExecutor.
+ * @param invocationCount number of invocations expected.
+ * @return a list of operations that were invoked.
+ * @throws PrivilegedOperationException when the operation fails to execute.
+ */
+ @SuppressWarnings("unchecked")
+ public static List capturePrivilegedOperations(
+ PrivilegedOperationExecutor mockExecutor, int invocationCount,
+ boolean grabOutput) throws PrivilegedOperationException {
+ ArgumentCaptor opCaptor =
+ ArgumentCaptor.forClass(PrivilegedOperation.class);
+
+ //one or more invocations expected
+ //due to type erasure + mocking, this verification requires a suppress
+ // warning annotation on the entire method
+ verify(mockExecutor, times(invocationCount))
+ .executePrivilegedOperation(anyList(), opCaptor.capture(),
+ any(File.class), any(Map.class), eq(grabOutput), eq(false));
+
+ //verification completed. we need to isolate specific invications.
+ // hence, reset mock here
+ Mockito.reset(mockExecutor);
+
+ return opCaptor.getAllValues();
+ }
+}
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 3253394..826a1af 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
@@ -49,18 +49,43 @@
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
-import java.util.*;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Random;
import java.util.Set;
-import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.*;
+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.CONTAINER_ID_STR;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.CONTAINER_LOCAL_DIRS;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.CONTAINER_LOG_DIRS;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.CONTAINER_WORK_DIR;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.FILECACHE_DIRS;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.LOCALIZED_RESOURCES;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.LOCAL_DIRS;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.LOG_DIRS;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.NM_PRIVATE_CONTAINER_SCRIPT_PATH;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.NM_PRIVATE_TOKENS_PATH;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.PID;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.PID_FILE_PATH;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.RESOURCES_OPTIONS;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.RUN_AS_USER;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.SIGNAL;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.USER;
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.USER_LOCAL_DIRS;
import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyList;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+
public class TestDockerContainerRuntime {
private static final Log LOG = LogFactory
@@ -816,7 +841,7 @@ public void testContainerLivelinessCheck()
.setExecutionAttribute(USER, user)
.setExecutionAttribute(PID, signalPid)
.setExecutionAttribute(SIGNAL, ContainerExecutor.Signal.NULL);
- runtime.initialize(getConfigurationWithMockContainerExecutor());
+ runtime.initialize(enableMockContainerExecutor(conf));
runtime.signalContainer(builder.build());
PrivilegedOperation op = capturePrivilegedOperation();
@@ -870,7 +895,7 @@ public void testDockerStopOnQuitSignal()
.setExecutionAttribute(USER, user)
.setExecutionAttribute(PID, signalPid)
.setExecutionAttribute(SIGNAL, signal);
- runtime.initialize(getConfigurationWithMockContainerExecutor());
+ runtime.initialize(enableMockContainerExecutor(conf));
runtime.signalContainer(builder.build());
PrivilegedOperation op = capturePrivilegedOperation();
@@ -881,9 +906,16 @@ public void testDockerStopOnQuitSignal()
Charset.forName("UTF-8"));
}
- private Configuration getConfigurationWithMockContainerExecutor() {
+ /**
+ * Return a configuration object with the mock container executor binary
+ * preconfigured.
+ *
+ * @param conf The hadoop configuration.
+ * @return The hadoop configuration.
+ */
+ public static Configuration enableMockContainerExecutor(Configuration conf) {
File f = new File("./src/test/resources/mock-container-executor");
- if(!FileUtil.canExecute(f)) {
+ if (!FileUtil.canExecute(f)) {
FileUtil.setExecutable(f, true);
}
String executorPath = f.getAbsolutePath();
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/DockerRuntimeTestingUtils.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/DockerRuntimeTestingUtils.java
new file mode 100644
index 0000000..ac20294
--- /dev/null
+++ 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/DockerRuntimeTestingUtils.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker;
+
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.PrivilegedOperation;
+import org.junit.Assert;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utility class for commonly used docker runtime testing helpers.
+ */
+public final class DockerRuntimeTestingUtils {
+
+ private DockerRuntimeTestingUtils() {
+ }
+
+ /**
+ * Ensure that the contents of the docker command file are correct and
+ * return the docker commands found in the command file.
+ *
+ * @param ops list of {@link PrivilegedOperation} from the operation
+ * invocation.
+ * @return the docker commands found in the docker command file.
+ * @throws IOException if the docker command file cannot be read.
+ */
+ public static List getValidatedDockerCommands(
+ List ops) throws IOException {
+ try {
+ List dockerCommands = new ArrayList<>();
+ for (PrivilegedOperation op : ops) {
+ Assert.assertEquals(op.getOperationType(),
+ PrivilegedOperation.OperationType.RUN_DOCKER_CMD);
+ String dockerCommandFile = op.getArguments().get(0);
+ List dockerCommandFileContents = Files
+ .readAllLines(Paths.get(dockerCommandFile),
+ Charset.forName("UTF-8"));
+ dockerCommands.addAll(dockerCommandFileContents);
+ }
+ return dockerCommands;
+ } catch (IOException e) {
+ throw new IOException("Unable to read the docker command file.", e);
+ }
+ }
+}
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/MockDockerContainerStatusHandler.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/MockDockerContainerStatusHandler.java
new file mode 100644
index 0000000..9ccef1c
--- /dev/null
+++ 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/MockDockerContainerStatusHandler.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.PrivilegedOperationExecutor;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.runtime.ContainerExecutionException;
+
+/**
+ * Mock container status handler.
+ */
+public class MockDockerContainerStatusHandler
+ extends DockerContainerStatusHandler {
+
+ private static final Log LOG =
+ LogFactory.getLog(MockDockerContainerStatusHandler.class);
+
+ private DockerContainerStatus status;
+
+ /**
+ * Set the container status. This is intended for testing only.
+ *
+ * @param containerStatus the status state to inject.
+ */
+ @VisibleForTesting
+ public void setDockerContainerStatus(DockerContainerStatus containerStatus) {
+ status = containerStatus;
+ }
+
+ /**
+ * Reset the container status so that previous state does not impact future
+ * tests.
+ */
+ @VisibleForTesting
+ public void resetDockerContainerStatus() {
+ status = null;
+ }
+
+ /**
+ * Execute the docker inspect command to retrieve the docker container's
+ * status.
+ *
+ * @param containerId the id of the container.
+ * @param conf the hadoop configuration.
+ * @param privilegedOperationExecutor the privileged operations executor.
+ * @return a string representing the current container status.
+ * @throws ContainerExecutionException if the docker operation fails to run.
+ */
+ @Override
+ protected String executeStatusCommand(String containerId,
+ Configuration conf,
+ PrivilegedOperationExecutor privilegedOperationExecutor)
+ throws ContainerExecutionException {
+
+ String containerStatus;
+ DockerInspectCommand dockerInspectCommand =
+ new DockerInspectCommand(containerId).getContainerStatus();
+ try {
+ containerStatus = DockerCommandExecutor
+ .executeDockerInspect(dockerInspectCommand, containerId, null, conf,
+ privilegedOperationExecutor);
+ LOG.debug("Container Status: " + containerStatus + " ContainerId: "
+ + containerId);
+ } catch (ContainerExecutionException e) {
+ throw new ContainerExecutionException(
+ "Container Status: " + DockerContainerStatus.NONEXISTENT.getName() +
+ "ContainerId: " + containerId);
+ }
+
+ // Allow for injecting the container's status for testing.
+ if (status != null) {
+ containerStatus = status.getName();
+ }
+
+ return containerStatus;
+ }
+}
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
new file mode 100644
index 0000000..df805b9
--- /dev/null
+++ 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
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.yarn.api.records.ContainerId;
+import org.apache.hadoop.yarn.api.records.ContainerLaunchContext;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.container.Container;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.MockPrivilegedOperationCaptor;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.PrivilegedOperation;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.PrivilegedOperationExecutor;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.resources.CGroupsHandler;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.DockerLinuxContainerRuntime;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.TestDockerContainerRuntime;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.runtime.ContainerRuntimeContext;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.List;
+
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.CONTAINER_ID_STR;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Test common docker commands.
+ */
+public class TestDockerCommandExecutor {
+
+ private static final String MOCK_CONTAINER_ID = "container_id";
+ private static final String MOCK_LOCAL_IMAGE_NAME = "local_image_name";
+ private static final String MOCK_IMAGE_NAME = "image_name";
+
+ private PrivilegedOperationExecutor mockExecutor;
+ private CGroupsHandler mockCGroupsHandler;
+ private Configuration configuration;
+ private ContainerRuntimeContext.Builder builder;
+ private DockerLinuxContainerRuntime runtime;
+ private Container container;
+ private ContainerId cId;
+ private ContainerLaunchContext context;
+ private HashMap env;
+
+ @Before
+ public void setUp() throws Exception {
+ mockExecutor = mock(PrivilegedOperationExecutor.class);
+ mockCGroupsHandler = mock(CGroupsHandler.class);
+ configuration = new Configuration();
+ runtime = new DockerLinuxContainerRuntime(mockExecutor, mockCGroupsHandler);
+ container = mock(Container.class);
+ cId = mock(ContainerId.class);
+ context = mock(ContainerLaunchContext.class);
+ env = new HashMap<>();
+ builder = new ContainerRuntimeContext.Builder(container);
+
+ when(container.getContainerId()).thenReturn(cId);
+ when(cId.toString()).thenReturn(MOCK_CONTAINER_ID);
+ when(container.getLaunchContext()).thenReturn(context);
+ when(context.getEnvironment()).thenReturn(env);
+
+ builder.setExecutionAttribute(CONTAINER_ID_STR, MOCK_CONTAINER_ID);
+ runtime.initialize(
+ TestDockerContainerRuntime.enableMockContainerExecutor(configuration));
+ }
+
+ @Test
+ public void testExecuteDockerCommand() throws Exception {
+ DockerStopCommand dockerStopCommand =
+ new DockerStopCommand(MOCK_CONTAINER_ID);
+ DockerCommandExecutor
+ .executeDockerCommand(dockerStopCommand, cId.toString(), env,
+ configuration, mockExecutor);
+ List ops = MockPrivilegedOperationCaptor
+ .capturePrivilegedOperations(mockExecutor, 1, true);
+ assertEquals(1, ops.size());
+ assertEquals(PrivilegedOperation.OperationType.RUN_DOCKER_CMD.name(),
+ ops.get(0).getOperationType().name());
+ }
+
+ @Test
+ public void testExecuteDockerRm() throws Exception {
+ DockerCommandExecutor
+ .executeDockerRm(MOCK_CONTAINER_ID, env, configuration, mockExecutor);
+ List ops = MockPrivilegedOperationCaptor
+ .capturePrivilegedOperations(mockExecutor, 1, true);
+ List dockerCommands =
+ DockerRuntimeTestingUtils.getValidatedDockerCommands(ops);
+ assertEquals(1, ops.size());
+ assertEquals(PrivilegedOperation.OperationType.RUN_DOCKER_CMD.name(),
+ ops.get(0).getOperationType().name());
+ assertEquals(1, dockerCommands.size());
+ assertEquals("rm " + MOCK_CONTAINER_ID, dockerCommands.get(0));
+ }
+
+ @Test
+ public void testExecuteDockerStop() throws Exception {
+ DockerCommandExecutor
+ .executeDockerStop(MOCK_CONTAINER_ID, env, configuration, mockExecutor);
+ List ops = MockPrivilegedOperationCaptor
+ .capturePrivilegedOperations(mockExecutor, 1, true);
+ List dockerCommands =
+ DockerRuntimeTestingUtils.getValidatedDockerCommands(ops);
+ assertEquals(1, ops.size());
+ assertEquals(PrivilegedOperation.OperationType.RUN_DOCKER_CMD.name(),
+ ops.get(0).getOperationType().name());
+ assertEquals(1, dockerCommands.size());
+ assertEquals("stop " + MOCK_CONTAINER_ID, dockerCommands.get(0));
+ }
+
+ @Test
+ public void testExecuteDockerInspect() throws Exception {
+ DockerCommandExecutor
+ .executeDockerInspect(new DockerInspectCommand(MOCK_CONTAINER_ID),
+ MOCK_CONTAINER_ID, env, configuration, mockExecutor);
+ List ops = MockPrivilegedOperationCaptor
+ .capturePrivilegedOperations(mockExecutor, 1, true);
+ List dockerCommands =
+ DockerRuntimeTestingUtils.getValidatedDockerCommands(ops);
+ assertEquals(1, ops.size());
+ assertEquals(PrivilegedOperation.OperationType.RUN_DOCKER_CMD.name(),
+ ops.get(0).getOperationType().name());
+ assertEquals(1, dockerCommands.size());
+ assertEquals("inspect", dockerCommands.get(0));
+ }
+
+ @Test
+ public void testExecuteDockerInspectStatus() throws Exception {
+ DockerInspectCommand dockerInspectCommand =
+ new DockerInspectCommand(MOCK_CONTAINER_ID).getContainerStatus();
+ DockerCommandExecutor
+ .executeDockerInspect(dockerInspectCommand, MOCK_CONTAINER_ID, env,
+ configuration, mockExecutor);
+ List ops = MockPrivilegedOperationCaptor
+ .capturePrivilegedOperations(mockExecutor, 1, true);
+ List dockerCommands =
+ DockerRuntimeTestingUtils.getValidatedDockerCommands(ops);
+ assertEquals(1, ops.size());
+ assertEquals(PrivilegedOperation.OperationType.RUN_DOCKER_CMD.name(),
+ ops.get(0).getOperationType().name());
+ assertEquals(1, dockerCommands.size());
+ assertEquals("inspect --format='{{.State.Status}}' " + MOCK_CONTAINER_ID,
+ dockerCommands.get(0));
+ }
+
+ @Test
+ public void testExecuteDockerPull() throws Exception {
+ DockerCommandExecutor.executeDockerPull(MOCK_CONTAINER_ID, MOCK_IMAGE_NAME,
+ env, configuration, mockExecutor);
+ List ops = MockPrivilegedOperationCaptor
+ .capturePrivilegedOperations(mockExecutor, 1, true);
+ List dockerCommands =
+ DockerRuntimeTestingUtils.getValidatedDockerCommands(ops);
+ assertEquals(1, ops.size());
+ assertEquals(PrivilegedOperation.OperationType.RUN_DOCKER_CMD.name(),
+ ops.get(0).getOperationType().name());
+ assertEquals(1, dockerCommands.size());
+ assertEquals("pull " + MOCK_IMAGE_NAME,
+ dockerCommands.get(0));
+ }
+
+ @Test
+ public void testExecuteDockerLoad() throws Exception {
+ DockerCommandExecutor.executeDockerLoad(MOCK_CONTAINER_ID,
+ MOCK_LOCAL_IMAGE_NAME, env, configuration, mockExecutor);
+ List ops = MockPrivilegedOperationCaptor
+ .capturePrivilegedOperations(mockExecutor, 1, true);
+ List dockerCommands =
+ DockerRuntimeTestingUtils.getValidatedDockerCommands(ops);
+ assertEquals(1, ops.size());
+ assertEquals(PrivilegedOperation.OperationType.RUN_DOCKER_CMD.name(),
+ ops.get(0).getOperationType().name());
+ assertEquals(1, dockerCommands.size());
+ assertEquals("load --i=" + MOCK_LOCAL_IMAGE_NAME,
+ dockerCommands.get(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/TestDockerContainerStatusHandler.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/TestDockerContainerStatusHandler.java
new file mode 100644
index 0000000..a87249f
--- /dev/null
+++ 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/TestDockerContainerStatusHandler.java
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.yarn.api.records.ContainerId;
+import org.apache.hadoop.yarn.api.records.ContainerLaunchContext;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.container.Container;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.PrivilegedOperationExecutor;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.resources.CGroupsHandler;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.DockerLinuxContainerRuntime;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.TestDockerContainerRuntime;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.runtime.ContainerRuntimeContext;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.HashMap;
+
+import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.CONTAINER_ID_STR;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Test operations for the {@link DockerContainerStatusHandler}.
+ */
+public class TestDockerContainerStatusHandler {
+ private static final String MOCK_CONTAINER_ID = "container_id";
+
+ private PrivilegedOperationExecutor mockExecutor;
+ private CGroupsHandler mockCGroupsHandler;
+ private MockDockerContainerStatusHandler mockDockerContainerStatusHandler;
+ private Configuration configuration;
+ private ContainerRuntimeContext.Builder builder;
+ private DockerLinuxContainerRuntime runtime;
+ private Container container;
+ private ContainerId cId;
+ private ContainerLaunchContext context;
+ private HashMap env;
+
+ @Before
+ public void setUp() throws Exception {
+ mockExecutor = mock(PrivilegedOperationExecutor.class);
+ mockCGroupsHandler = mock(CGroupsHandler.class);
+ mockDockerContainerStatusHandler = new MockDockerContainerStatusHandler();
+ configuration = new Configuration();
+ runtime = new DockerLinuxContainerRuntime(mockExecutor, mockCGroupsHandler,
+ mockDockerContainerStatusHandler);
+ container = mock(Container.class);
+ cId = mock(ContainerId.class);
+ context = mock(ContainerLaunchContext.class);
+ env = new HashMap<>();
+ builder = new ContainerRuntimeContext.Builder(container);
+
+ when(container.getContainerId()).thenReturn(cId);
+ when(cId.toString()).thenReturn(MOCK_CONTAINER_ID);
+ when(container.getLaunchContext()).thenReturn(context);
+ when(context.getEnvironment()).thenReturn(env);
+
+ builder.setExecutionAttribute(CONTAINER_ID_STR, MOCK_CONTAINER_ID);
+ runtime.initialize(
+ TestDockerContainerRuntime.enableMockContainerExecutor(configuration));
+ }
+
+ @Test
+ public void testSetDockerContainerStatusState() throws Exception {
+ mockDockerContainerStatusHandler.setDockerContainerStatus(
+ DockerContainerStatusHandler.DockerContainerStatus.RUNNING);
+ Assert.assertEquals(
+ DockerContainerStatusHandler.DockerContainerStatus.RUNNING,
+ mockDockerContainerStatusHandler.getContainerStatus(MOCK_CONTAINER_ID,
+ configuration, mockExecutor));
+ }
+
+ @Test
+ public void testResetDockerContainerStatusState() throws Exception {
+ mockDockerContainerStatusHandler.resetDockerContainerStatus();
+ Assert.assertEquals(
+ DockerContainerStatusHandler.DockerContainerStatus.UNKNOWN,
+ mockDockerContainerStatusHandler.getContainerStatus(MOCK_CONTAINER_ID,
+ configuration, mockExecutor));
+ }
+
+ @Test
+ public void testGetContainerStatusForCreated() throws Exception {
+ mockDockerContainerStatusHandler.setDockerContainerStatus(
+ DockerContainerStatusHandler.DockerContainerStatus.CREATED);
+ Assert.assertEquals(
+ DockerContainerStatusHandler.DockerContainerStatus.CREATED,
+ mockDockerContainerStatusHandler.getContainerStatus(MOCK_CONTAINER_ID,
+ configuration, mockExecutor));
+ }
+
+ @Test
+ public void testGetContainerStatusForRunning() throws Exception {
+ mockDockerContainerStatusHandler.setDockerContainerStatus(
+ DockerContainerStatusHandler.DockerContainerStatus.RUNNING);
+ Assert.assertEquals(
+ DockerContainerStatusHandler.DockerContainerStatus.RUNNING,
+ mockDockerContainerStatusHandler.getContainerStatus(MOCK_CONTAINER_ID,
+ configuration, mockExecutor));
+ }
+
+ @Test
+ public void testGetContainerStatusForStopped() throws Exception {
+ mockDockerContainerStatusHandler.setDockerContainerStatus(
+ DockerContainerStatusHandler.DockerContainerStatus.STOPPED);
+ Assert.assertEquals(
+ DockerContainerStatusHandler.DockerContainerStatus.STOPPED,
+ mockDockerContainerStatusHandler.getContainerStatus(MOCK_CONTAINER_ID,
+ configuration, mockExecutor));
+ }
+
+ @Test
+ public void testGetContainerStatusForRestarting() throws Exception {
+ mockDockerContainerStatusHandler.setDockerContainerStatus(
+ DockerContainerStatusHandler.DockerContainerStatus.RESTARTING);
+ Assert.assertEquals(
+ DockerContainerStatusHandler.DockerContainerStatus.RESTARTING,
+ mockDockerContainerStatusHandler.getContainerStatus(MOCK_CONTAINER_ID,
+ configuration, mockExecutor));
+ }
+
+ @Test
+ public void testGetContainerStatusForRemoving() throws Exception {
+ mockDockerContainerStatusHandler.setDockerContainerStatus(
+ DockerContainerStatusHandler.DockerContainerStatus.REMOVING);
+ Assert.assertEquals(
+ DockerContainerStatusHandler.DockerContainerStatus.REMOVING,
+ mockDockerContainerStatusHandler.getContainerStatus(MOCK_CONTAINER_ID,
+ configuration, mockExecutor));
+ }
+
+ @Test
+ public void testGetContainerStatusForDead() throws Exception {
+ mockDockerContainerStatusHandler.setDockerContainerStatus(
+ DockerContainerStatusHandler.DockerContainerStatus.DEAD);
+ Assert.assertEquals(
+ DockerContainerStatusHandler.DockerContainerStatus.DEAD,
+ mockDockerContainerStatusHandler.getContainerStatus(MOCK_CONTAINER_ID,
+ configuration, mockExecutor));
+ }
+
+ @Test
+ public void testGetContainerStatusForExited() throws Exception {
+ mockDockerContainerStatusHandler.setDockerContainerStatus(
+ DockerContainerStatusHandler.DockerContainerStatus.EXITED);
+ Assert.assertEquals(
+ DockerContainerStatusHandler.DockerContainerStatus.EXITED,
+ mockDockerContainerStatusHandler.getContainerStatus(MOCK_CONTAINER_ID,
+ configuration, mockExecutor));
+ }
+
+ @Test
+ public void testGetContainerStatusForUnknown() throws Exception {
+ mockDockerContainerStatusHandler.setDockerContainerStatus(
+ DockerContainerStatusHandler.DockerContainerStatus.UNKNOWN);
+ Assert.assertEquals(
+ DockerContainerStatusHandler.DockerContainerStatus.UNKNOWN,
+ mockDockerContainerStatusHandler.getContainerStatus(MOCK_CONTAINER_ID,
+ configuration, mockExecutor));
+ }
+}
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/TestDockerLoadCommand.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/TestDockerLoadCommand.java
new file mode 100644
index 0000000..85fa0f8
--- /dev/null
+++ 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/TestDockerLoadCommand.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests the docker load command and its command
+ * line arguments.
+ */
+public class TestDockerLoadCommand {
+ private DockerLoadCommand dockerLoadCommand;
+
+ private static final String LOCAL_IMAGE_NAME = "foo";
+
+ @Before
+ public void setup() {
+ dockerLoadCommand = new DockerLoadCommand(LOCAL_IMAGE_NAME);
+ }
+
+ @Test
+ public void testGetCommandOption() {
+ assertEquals("load", dockerLoadCommand.getCommandOption());
+ }
+
+ @Test
+ public void testGetCommandWithArguments() {
+ assertEquals("load --i=foo",
+ dockerLoadCommand.getCommandWithArguments());
+ }
+}
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/TestDockerRunCommand.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/TestDockerRunCommand.java
new file mode 100644
index 0000000..9c6f0c0
--- /dev/null
+++ 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/TestDockerRunCommand.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests the docker run command and its command
+ * line arguments.
+ */
+
+public class TestDockerRunCommand {
+ private DockerRunCommand dockerRunCommand;
+
+ private static final String CONTAINER_NAME = "foo";
+ private static final String USER_ID = "user_id";
+ private static final String IMAGE_NAME = "image_name";
+
+ @Before
+ public void setUp() throws Exception {
+ dockerRunCommand = new DockerRunCommand(CONTAINER_NAME, USER_ID,
+ IMAGE_NAME);
+ }
+
+ @Test
+ public void testGetCommandOption() {
+ assertEquals("run", dockerRunCommand.getCommandOption());
+ }
+
+ @Test
+ public void testGetCommandWithArguments() {
+ assertEquals("run --name=foo --user=user_id image_name",
+ dockerRunCommand.getCommandWithArguments());
+ }
+
+ @Test
+ public void testRemoveContainerOnExit() throws Exception {
+ dockerRunCommand.removeContainerOnExit();
+ assertEquals("run --name=foo --user=user_id --rm image_name",
+ dockerRunCommand.getCommandWithArguments());
+ }
+
+ @Test
+ public void testDetachOnRun() throws Exception {
+ dockerRunCommand.detachOnRun();
+ assertEquals("run --name=foo --user=user_id -d image_name",
+ dockerRunCommand.getCommandWithArguments());
+ }
+
+ @Test
+ public void testSetContainerWorkDir() throws Exception {
+ String workDir = "work_dir";
+ dockerRunCommand.setContainerWorkDir(workDir);
+ assertEquals("run --name=foo --user=user_id --workdir=work_dir image_name",
+ dockerRunCommand.getCommandWithArguments());
+ }
+
+ @Test
+ public void testSetNetworkType() throws Exception {
+ String networkType = "bridge";
+ dockerRunCommand.setNetworkType(networkType);
+ assertEquals("run --name=foo --user=user_id --net=bridge image_name",
+ dockerRunCommand.getCommandWithArguments());
+ }
+
+ @Test
+ public void testAddMountLocation() throws Exception {
+ String sourcePath = "/source";
+ String destPath = "/dest";
+ Boolean createSource = true;
+ dockerRunCommand.addMountLocation(sourcePath, destPath, createSource);
+ assertEquals("run --name=foo --user=user_id -v /source:/dest image_name",
+ dockerRunCommand.getCommandWithArguments());
+ }
+
+ @Test
+ public void testSetCGroupParent() throws Exception {
+ String parentDir = "/sys/fs/cgroup/yarn";
+ dockerRunCommand.setCGroupParent(parentDir);
+ assertEquals("run --name=foo --user=user_id "
+ + "--cgroup-parent=/sys/fs/cgroup/yarn image_name",
+ dockerRunCommand.getCommandWithArguments());
+ }
+
+ @Test
+ public void testSetPrivileged() throws Exception {
+ dockerRunCommand.setPrivileged();
+ assertEquals("run --name=foo --user=user_id --privileged image_name",
+ dockerRunCommand.getCommandWithArguments());
+ }
+
+ @Test
+ public void testSetCapabilities() throws Exception {
+ Set capabilities = new HashSet<>();
+ capabilities.add("CHOWN");
+ capabilities.add("KILL");
+ dockerRunCommand.setCapabilities(capabilities);
+ assertEquals("run --name=foo --user=user_id "
+ + "--cap-drop=ALL --cap-add=CHOWN --cap-add=KILL image_name",
+ dockerRunCommand.getCommandWithArguments());
+ }
+
+ @Test
+ public void testAddDevice() throws Exception {
+ String sourceDevice = "source";
+ String destDevice = "dest";
+ dockerRunCommand.addDevice(sourceDevice, destDevice);
+ assertEquals("run --name=foo --user=user_id "
+ + "--device=source:dest image_name",
+ dockerRunCommand.getCommandWithArguments());
+ }
+
+ @Test
+ public void testEnableDetach() throws Exception {
+ dockerRunCommand.enableDetach();
+ assertEquals("run --name=foo --user=user_id --detach=true image_name",
+ dockerRunCommand.getCommandWithArguments());
+ }
+
+ @Test
+ public void testDisableDetach() throws Exception {
+ dockerRunCommand.disableDetach();
+ assertEquals("run --name=foo --user=user_id --detach=false image_name",
+ dockerRunCommand.getCommandWithArguments());
+ }
+
+ @Test
+ public void testSetOverrideCommandWithArgs() throws Exception {
+ List commands = new ArrayList<>();
+ commands.add("launch_command");
+ dockerRunCommand.setOverrideCommandWithArgs(commands);
+ assertEquals("run --name=foo --user=user_id image_name launch_command",
+ dockerRunCommand.getCommandWithArguments());
+ }
+}
\ No newline at end of file