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..a980eef 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..8af186c --- /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 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..74095df --- /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,22 @@ +/* + * 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. + */ +@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..de4d801 --- /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,174 @@ +/* + * 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; + +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..0cb6eca --- /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,44 @@ +/* + * 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; + +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..5a5909e --- /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,151 @@ +package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.docker;/* + * 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. + */ + +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; + +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