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 00000000000..9026d22e71b --- /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,191 @@ +/* + * 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); + + /** + * 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; + } + } + + 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. + * @param disableFailureLogging disable logging for known rc failures. + * @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, + boolean disableFailureLogging) + 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); + if (disableFailureLogging) { + dockerOp.disableFailureLogging(); + } + if (LOG.isDebugEnabled()) { + LOG.debug("Running docker command: " + + dockerCommand.getCommandWithArguments()); + } + 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()); + } + } + + /** + * 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 static 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 if (currentContainerStatus + .equals(DockerContainerStatus.NONEXISTENT.getName())) { + dockerContainerStatus = DockerContainerStatus.NONEXISTENT; + } else { + dockerContainerStatus = DockerContainerStatus.UNKNOWN; + } + if (LOG.isDebugEnabled()) { + LOG.debug("Container Status: " + dockerContainerStatus.getName() + + " ContainerId: " + containerId); + } + return dockerContainerStatus; + } catch (ContainerExecutionException e) { + if (LOG.isDebugEnabled()) { + 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. + */ + private static String executeStatusCommand(String containerId, + Configuration conf, + PrivilegedOperationExecutor privilegedOperationExecutor) + throws ContainerExecutionException { + DockerInspectCommand dockerInspectCommand = + new DockerInspectCommand(containerId).getContainerStatus(); + try { + return DockerCommandExecutor.executeDockerCommand(dockerInspectCommand, + containerId, null, conf, privilegedOperationExecutor, false); + } catch (ContainerExecutionException e) { + throw new ContainerExecutionException(e); + } + } +} \ 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/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 00000000000..189167c6d3e --- /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 00000000000..187da6b4d4f --- /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 32533940fc7..ee1f25c5af6 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 @@ -816,7 +816,7 @@ public class TestDockerContainerRuntime { .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 +870,7 @@ public class TestDockerContainerRuntime { .setExecutionAttribute(USER, user) .setExecutionAttribute(PID, signalPid) .setExecutionAttribute(SIGNAL, signal); - runtime.initialize(getConfigurationWithMockContainerExecutor()); + runtime.initialize(enableMockContainerExecutor(conf)); runtime.signalContainer(builder.build()); PrivilegedOperation op = capturePrivilegedOperation(); @@ -881,7 +881,14 @@ public class TestDockerContainerRuntime { 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)) { FileUtil.setExecutable(f, true); 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 00000000000..60fce406605 --- /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,218 @@ +/* + * 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.Assert; +import org.junit.Before; +import org.junit.Test; + +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.HashMap; +import java.util.List; + +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.docker.DockerCommandExecutor.DockerContainerStatus; +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +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, false); + 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 { + DockerRmCommand dockerCommand = new DockerRmCommand(MOCK_CONTAINER_ID); + DockerCommandExecutor + .executeDockerCommand(dockerCommand, MOCK_CONTAINER_ID, env, + configuration, mockExecutor, false); + List ops = MockPrivilegedOperationCaptor + .capturePrivilegedOperations(mockExecutor, 1, true); + List dockerCommands = 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 { + DockerStopCommand dockerCommand = new DockerStopCommand(MOCK_CONTAINER_ID); + DockerCommandExecutor + .executeDockerCommand(dockerCommand, MOCK_CONTAINER_ID, env, + configuration, mockExecutor, false); + List ops = MockPrivilegedOperationCaptor + .capturePrivilegedOperations(mockExecutor, 1, true); + List dockerCommands = 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 testExecuteDockerInspectStatus() throws Exception { + DockerInspectCommand dockerCommand = + new DockerInspectCommand(MOCK_CONTAINER_ID).getContainerStatus(); + DockerCommandExecutor + .executeDockerCommand(dockerCommand, MOCK_CONTAINER_ID, env, + configuration, mockExecutor, false); + List ops = MockPrivilegedOperationCaptor + .capturePrivilegedOperations(mockExecutor, 1, true); + List dockerCommands = 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 { + DockerPullCommand dockerCommand = + new DockerPullCommand(MOCK_IMAGE_NAME); + DockerCommandExecutor + .executeDockerCommand(dockerCommand, MOCK_CONTAINER_ID, env, + configuration, mockExecutor, false); + List ops = MockPrivilegedOperationCaptor + .capturePrivilegedOperations(mockExecutor, 1, true); + List dockerCommands = 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 { + DockerLoadCommand dockerCommand = + new DockerLoadCommand(MOCK_LOCAL_IMAGE_NAME); + DockerCommandExecutor + .executeDockerCommand(dockerCommand, MOCK_CONTAINER_ID, env, + configuration, mockExecutor, false); + List ops = MockPrivilegedOperationCaptor + .capturePrivilegedOperations(mockExecutor, 1, true); + List dockerCommands = 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)); + } + + @Test + public void testGetContainerStatus() throws Exception { + for (DockerContainerStatus status : DockerContainerStatus.values()) { + when(mockExecutor.executePrivilegedOperation(eq(null), + any(PrivilegedOperation.class), eq(null), any(), eq(true), eq(false))) + .thenReturn(status.getName()); + assertEquals(status, DockerCommandExecutor + .getContainerStatus(MOCK_CONTAINER_ID, configuration, mockExecutor)); + } + } + + private 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/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 00000000000..85fa0f8f733 --- /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 00000000000..85bccd2d51d --- /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,63 @@ +/* + * 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.List; + +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 testCommandArguments() { + String sourceDevice = "source"; + String destDevice = "dest"; + dockerRunCommand.addDevice(sourceDevice, destDevice); + List commands = new ArrayList<>(); + commands.add("launch_command"); + dockerRunCommand.setOverrideCommandWithArgs(commands); + dockerRunCommand.removeContainerOnExit(); + assertEquals("run --name=foo --user=user_id --device=source:dest --rm " + + "image_name launch_command", + dockerRunCommand.getCommandWithArguments()); + } +} \ No newline at end of file