http://git-wip-us.apache.org/repos/asf/hadoop/blob/3e6fce91/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/DockerRunCommand.java ---------------------------------------------------------------------- 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/DockerRunCommand.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/DockerRunCommand.java new file mode 100644 index 0000000..f9a890e --- /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/DockerRunCommand.java @@ -0,0 +1,107 @@ +/* + * * + * 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.util.StringUtils; + +import java.util.ArrayList; +import java.util.List; + +public class DockerRunCommand extends DockerCommand { + private static final String RUN_COMMAND = "run"; + private final String image; + private List<String> overrrideCommandWithArgs; + + /** The following are mandatory: */ + public DockerRunCommand(String containerId, String user, String image) { + super(RUN_COMMAND); + super.addCommandArguments("--name=" + containerId, "--user=" + user); + this.image = image; + } + + public DockerRunCommand removeContainerOnExit() { + super.addCommandArguments("--rm"); + return this; + } + + public DockerRunCommand detachOnRun() { + super.addCommandArguments("-d"); + return this; + } + + public DockerRunCommand setContainerWorkDir(String workdir) { + super.addCommandArguments("--workdir=" + workdir); + return this; + } + + public DockerRunCommand setNetworkType(String type) { + super.addCommandArguments("--net=" + type); + return this; + } + + public DockerRunCommand addMountLocation(String sourcePath, String + destinationPath) { + super.addCommandArguments("-v", sourcePath + ":" + destinationPath); + return this; + } + + public DockerRunCommand setCGroupParent(String parentPath) { + super.addCommandArguments("--cgroup-parent=" + parentPath); + return this; + } + + public DockerRunCommand addDevice(String sourceDevice, String + destinationDevice) { + super.addCommandArguments("--device=" + sourceDevice + ":" + + destinationDevice); + return this; + } + + public DockerRunCommand enableDetach() { + super.addCommandArguments("--detach=true"); + return this; + } + + public DockerRunCommand disableDetach() { + super.addCommandArguments("--detach=false"); + return this; + } + + public DockerRunCommand setOverrideCommandWithArgs( + List<String> overrideCommandWithArgs) { + this.overrrideCommandWithArgs = overrideCommandWithArgs; + return this; + } + + @Override + public String getCommandWithArguments() { + List<String> argList = new ArrayList<>(); + + argList.add(super.getCommandWithArguments()); + argList.add(image); + + if (overrrideCommandWithArgs != null) { + argList.addAll(overrrideCommandWithArgs); + } + + return StringUtils.join(" ", argList); + } +}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/3e6fce91/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/runtime/ContainerExecutionException.java ---------------------------------------------------------------------- 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/runtime/ContainerExecutionException.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/runtime/ContainerExecutionException.java new file mode 100644 index 0000000..1fbece2 --- /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/runtime/ContainerExecutionException.java @@ -0,0 +1,85 @@ +/* + * * + * 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.runtime; + +import org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.classification.InterfaceStability; +import org.apache.hadoop.yarn.exceptions.YarnException; + +/** Exception caused in a container runtime impl. 'Runtime' is not used in + * the class name to avoid confusion with a java RuntimeException + */ + +@InterfaceAudience.Private +@InterfaceStability.Unstable +public class ContainerExecutionException extends YarnException { + private static final long serialVersionUID = 1L; + private static final Integer EXIT_CODE_UNSET = -1; + private static final String OUTPUT_UNSET = "<unknown>"; + + private Integer exitCode; + private String output; + private String errorOutput; + + public ContainerExecutionException(String message) { + super(message); + exitCode = EXIT_CODE_UNSET; + output = OUTPUT_UNSET; + errorOutput = OUTPUT_UNSET; + } + + public ContainerExecutionException(Throwable throwable) { + super(throwable); + exitCode = EXIT_CODE_UNSET; + output = OUTPUT_UNSET; + errorOutput = OUTPUT_UNSET; + } + + + public ContainerExecutionException(String message, Integer exitCode, String + output, String errorOutput) { + super(message); + this.exitCode = exitCode; + this.output = output; + this.errorOutput = errorOutput; + } + + public ContainerExecutionException(Throwable cause, Integer exitCode, String + output, String errorOutput) { + super(cause); + this.exitCode = exitCode; + this.output = output; + this.errorOutput = errorOutput; + } + + public Integer getExitCode() { + return exitCode; + } + + public String getOutput() { + return output; + } + + public String getErrorOutput() { + return errorOutput; + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/hadoop/blob/3e6fce91/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/runtime/ContainerRuntime.java ---------------------------------------------------------------------- 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/runtime/ContainerRuntime.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/runtime/ContainerRuntime.java new file mode 100644 index 0000000..e05f3fc --- /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/runtime/ContainerRuntime.java @@ -0,0 +1,50 @@ +/* + * * + * 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.runtime; + +import org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.classification.InterfaceStability; + +/** An abstraction for various container runtime implementations. Examples + * include Process Tree, Docker, Appc runtimes etc., These implementations + * are meant for low-level OS container support - dependencies on + * higher-level nodemananger constructs should be avoided. + */ + +@InterfaceAudience.Private +@InterfaceStability.Unstable +public interface ContainerRuntime { + /** Prepare a container to be ready for launch */ + void prepareContainer(ContainerRuntimeContext ctx) + throws ContainerExecutionException; + + /** Launch a container. */ + void launchContainer(ContainerRuntimeContext ctx) + throws ContainerExecutionException; + + /** Signal a container - request to terminate, status check etc., */ + void signalContainer(ContainerRuntimeContext ctx) + throws ContainerExecutionException; + + /** Any container cleanup that may be required. */ + void reapContainer(ContainerRuntimeContext ctx) + throws ContainerExecutionException; +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/hadoop/blob/3e6fce91/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/runtime/ContainerRuntimeConstants.java ---------------------------------------------------------------------- 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/runtime/ContainerRuntimeConstants.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/runtime/ContainerRuntimeConstants.java new file mode 100644 index 0000000..4473856 --- /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/runtime/ContainerRuntimeConstants.java @@ -0,0 +1,33 @@ +/* + * * + * 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.runtime; + +import org.apache.hadoop.classification.InterfaceAudience.Private; + +public class ContainerRuntimeConstants { + + /* Switch container runtimes. Work in progress: These + * parameters may be changed/removed in the future. */ + + @Private + public static final String ENV_CONTAINER_TYPE = + "YARN_CONTAINER_RUNTIME_TYPE"; +} http://git-wip-us.apache.org/repos/asf/hadoop/blob/3e6fce91/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/runtime/ContainerRuntimeContext.java ---------------------------------------------------------------------- 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/runtime/ContainerRuntimeContext.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/runtime/ContainerRuntimeContext.java new file mode 100644 index 0000000..4194b99 --- /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/runtime/ContainerRuntimeContext.java @@ -0,0 +1,105 @@ +/* + * * + * 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.runtime; + +import org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.classification.InterfaceStability; +import org.apache.hadoop.yarn.server.nodemanager.containermanager.container.Container; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +@InterfaceAudience.Private +@InterfaceStability.Unstable +public final class ContainerRuntimeContext { + private final Container container; + private final Map<Attribute<?>, Object> executionAttributes; + + /** An attribute class that attempts to provide better type safety as compared + * with using a map of string to object. + * @param <T> + */ + public static final class Attribute<T> { + private final Class<T> valueClass; + private final String id; + + private Attribute(Class<T> valueClass, String id) { + this.valueClass = valueClass; + this.id = id; + } + + @Override + public int hashCode() { + return valueClass.hashCode() + 31 * id.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null || !(obj instanceof Attribute)){ + return false; + } + + Attribute<?> attribute = (Attribute<?>) obj; + + return valueClass.equals(attribute.valueClass) && id.equals(attribute.id); + } + public static <T> Attribute<T> attribute(Class<T> valueClass, String id) { + return new Attribute<T>(valueClass, id); + } + } + + public static final class Builder { + private final Container container; + private Map<Attribute<?>, Object> executionAttributes; + + public Builder(Container container) { + executionAttributes = new HashMap<>(); + this.container = container; + } + + public <E> Builder setExecutionAttribute(Attribute<E> attribute, E value) { + this.executionAttributes.put(attribute, attribute.valueClass.cast(value)); + return this; + } + + public ContainerRuntimeContext build() { + return new ContainerRuntimeContext(this); + } + } + + private ContainerRuntimeContext(Builder builder) { + this.container = builder.container; + this.executionAttributes = builder.executionAttributes; + } + + public Container getContainer() { + return this.container; + } + + public Map<Attribute<?>, Object> getExecutionAttributes() { + return Collections.unmodifiableMap(this.executionAttributes); + } + + public <E> E getExecutionAttribute(Attribute<E> attribute) { + return attribute.valueClass.cast(executionAttributes.get(attribute)); + } +} http://git-wip-us.apache.org/repos/asf/hadoop/blob/3e6fce91/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/executor/ContainerLivenessContext.java ---------------------------------------------------------------------- diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/executor/ContainerLivenessContext.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/executor/ContainerLivenessContext.java index acadae9..43113ef 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/executor/ContainerLivenessContext.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/executor/ContainerLivenessContext.java @@ -22,6 +22,7 @@ package org.apache.hadoop.yarn.server.nodemanager.executor; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceStability; +import org.apache.hadoop.yarn.server.nodemanager.containermanager.container.Container; /** * Encapsulates information required for container liveness checks. @@ -30,16 +31,23 @@ import org.apache.hadoop.classification.InterfaceStability; @InterfaceAudience.Private @InterfaceStability.Unstable public final class ContainerLivenessContext { + private final Container container; private final String user; private final String pid; public static final class Builder { + private Container container; private String user; private String pid; public Builder() { } + public Builder setContainer(Container container) { + this.container = container; + return this; + } + public Builder setUser(String user) { this.user = user; return this; @@ -56,10 +64,15 @@ public final class ContainerLivenessContext { } private ContainerLivenessContext(Builder builder) { + this.container = builder.container; this.user = builder.user; this.pid = builder.pid; } + public Container getContainer() { + return this.container; + } + public String getUser() { return this.user; } http://git-wip-us.apache.org/repos/asf/hadoop/blob/3e6fce91/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/executor/ContainerReacquisitionContext.java ---------------------------------------------------------------------- diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/executor/ContainerReacquisitionContext.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/executor/ContainerReacquisitionContext.java index 8adcab7..d93cdaf 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/executor/ContainerReacquisitionContext.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/executor/ContainerReacquisitionContext.java @@ -23,6 +23,7 @@ package org.apache.hadoop.yarn.server.nodemanager.executor; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.yarn.api.records.ContainerId; +import org.apache.hadoop.yarn.server.nodemanager.containermanager.container.Container; /** * Encapsulates information required for container reacquisition. @@ -31,16 +32,23 @@ import org.apache.hadoop.yarn.api.records.ContainerId; @InterfaceAudience.Private @InterfaceStability.Unstable public final class ContainerReacquisitionContext { + private final Container container; private final String user; private final ContainerId containerId; public static final class Builder { + private Container container; private String user; private ContainerId containerId; public Builder() { } + public Builder setContainer(Container container) { + this.container = container; + return this; + } + public Builder setUser(String user) { this.user = user; return this; @@ -57,10 +65,15 @@ public final class ContainerReacquisitionContext { } private ContainerReacquisitionContext(Builder builder) { + this.container = builder.container; this.user = builder.user; this.containerId = builder.containerId; } + public Container getContainer() { + return this.container; + } + public String getUser() { return this.user; } http://git-wip-us.apache.org/repos/asf/hadoop/blob/3e6fce91/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/executor/ContainerSignalContext.java ---------------------------------------------------------------------- diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/executor/ContainerSignalContext.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/executor/ContainerSignalContext.java index cc40af5..56b571b 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/executor/ContainerSignalContext.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/executor/ContainerSignalContext.java @@ -23,6 +23,7 @@ package org.apache.hadoop.yarn.server.nodemanager.executor; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.yarn.server.nodemanager.ContainerExecutor.Signal; +import org.apache.hadoop.yarn.server.nodemanager.containermanager.container.Container; /** * Encapsulates information required for container signaling. @@ -31,11 +32,13 @@ import org.apache.hadoop.yarn.server.nodemanager.ContainerExecutor.Signal; @InterfaceAudience.Private @InterfaceStability.Unstable public final class ContainerSignalContext { + private final Container container; private final String user; private final String pid; private final Signal signal; public static final class Builder { + private Container container; private String user; private String pid; private Signal signal; @@ -43,6 +46,11 @@ public final class ContainerSignalContext { public Builder() { } + public Builder setContainer(Container container) { + this.container = container; + return this; + } + public Builder setUser(String user) { this.user = user; return this; @@ -64,11 +72,16 @@ public final class ContainerSignalContext { } private ContainerSignalContext(Builder builder) { + this.container = builder.container; this.user = builder.user; this.pid = builder.pid; this.signal = builder.signal; } + public Container getContainer() { + return this.container; + } + public String getUser() { return this.user; } http://git-wip-us.apache.org/repos/asf/hadoop/blob/3e6fce91/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/executor/ContainerStartContext.java ---------------------------------------------------------------------- diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/executor/ContainerStartContext.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/executor/ContainerStartContext.java index 7dfff02..ffcc519 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/executor/ContainerStartContext.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/executor/ContainerStartContext.java @@ -25,7 +25,9 @@ import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.fs.Path; import org.apache.hadoop.yarn.server.nodemanager.containermanager.container.Container; +import java.util.Collections; import java.util.List; +import java.util.Map; /** * Encapsulates information required for starting/launching containers. @@ -35,6 +37,7 @@ import java.util.List; @InterfaceStability.Unstable public final class ContainerStartContext { private final Container container; + private final Map<Path, List<String>> localizedResources; private final Path nmPrivateContainerScriptPath; private final Path nmPrivateTokensPath; private final String user; @@ -45,6 +48,7 @@ public final class ContainerStartContext { public static final class Builder { private Container container; + private Map<Path, List<String>> localizedResources; private Path nmPrivateContainerScriptPath; private Path nmPrivateTokensPath; private String user; @@ -61,6 +65,12 @@ public final class ContainerStartContext { return this; } + public Builder setLocalizedResources(Map<Path, + List<String>> localizedResources) { + this.localizedResources = localizedResources; + return this; + } + public Builder setNmPrivateContainerScriptPath( Path nmPrivateContainerScriptPath) { this.nmPrivateContainerScriptPath = nmPrivateContainerScriptPath; @@ -104,6 +114,7 @@ public final class ContainerStartContext { private ContainerStartContext(Builder builder) { this.container = builder.container; + this.localizedResources = builder.localizedResources; this.nmPrivateContainerScriptPath = builder.nmPrivateContainerScriptPath; this.nmPrivateTokensPath = builder.nmPrivateTokensPath; this.user = builder.user; @@ -117,6 +128,14 @@ public final class ContainerStartContext { return this.container; } + public Map<Path, List<String>> getLocalizedResources() { + if (this.localizedResources != null) { + return Collections.unmodifiableMap(this.localizedResources); + } else { + return null; + } + } + public Path getNmPrivateContainerScriptPath() { return this.nmPrivateContainerScriptPath; } @@ -138,10 +157,10 @@ public final class ContainerStartContext { } public List<String> getLocalDirs() { - return this.localDirs; + return Collections.unmodifiableList(this.localDirs); } public List<String> getLogDirs() { - return this.logDirs; + return Collections.unmodifiableList(this.logDirs); } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/hadoop/blob/3e6fce91/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestLinuxContainerExecutorWithMocks.java ---------------------------------------------------------------------- diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestLinuxContainerExecutorWithMocks.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestLinuxContainerExecutorWithMocks.java index 82b7fd9..0ef788b 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestLinuxContainerExecutorWithMocks.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestLinuxContainerExecutorWithMocks.java @@ -32,6 +32,8 @@ import java.io.FileReader; import java.io.IOException; import java.io.LineNumberReader; import java.net.InetSocketAddress; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -50,6 +52,10 @@ import org.apache.hadoop.yarn.conf.YarnConfiguration; import org.apache.hadoop.yarn.server.nodemanager.containermanager.container.Container; import org.apache.hadoop.yarn.server.nodemanager.containermanager.container.ContainerDiagnosticsUpdateEvent; 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.runtime.DefaultLinuxContainerRuntime; +import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntime; +import org.apache.hadoop.yarn.server.nodemanager.containermanager.runtime.ContainerExecutionException; import org.apache.hadoop.yarn.server.nodemanager.executor.ContainerSignalContext; import org.apache.hadoop.yarn.server.nodemanager.executor.ContainerStartContext; import org.apache.hadoop.yarn.server.nodemanager.executor.DeletionAsUserContext; @@ -61,11 +67,19 @@ import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; + public class TestLinuxContainerExecutorWithMocks { private static final Log LOG = LogFactory .getLog(TestLinuxContainerExecutorWithMocks.class); + private static final String MOCK_EXECUTOR = + "./src/test/resources/mock-container-executor"; + private static final String MOCK_EXECUTOR_WITH_ERROR = + "./src/test/resources/mock-container-executer-with-error"; + + private String tmpMockExecutor; private LinuxContainerExecutor mockExec = null; private final File mockParamFile = new File("./params.txt"); private LocalDirsHandlerService dirsHandler; @@ -88,20 +102,42 @@ public class TestLinuxContainerExecutorWithMocks { reader.close(); return ret; } - + + private void setupMockExecutor(String executorPath, Configuration conf) + throws IOException { + //we'll always use the tmpMockExecutor - since + // PrivilegedOperationExecutor can only be initialized once. + + Files.copy(Paths.get(executorPath), Paths.get(tmpMockExecutor), + REPLACE_EXISTING); + + File executor = new File(tmpMockExecutor); + + if (!FileUtil.canExecute(executor)) { + FileUtil.setExecutable(executor, true); + } + String executorAbsolutePath = executor.getAbsolutePath(); + conf.set(YarnConfiguration.NM_LINUX_CONTAINER_EXECUTOR_PATH, + executorAbsolutePath); + } + @Before - public void setup() { + public void setup() throws IOException, ContainerExecutionException { assumeTrue(!Path.WINDOWS); - File f = new File("./src/test/resources/mock-container-executor"); - if(!FileUtil.canExecute(f)) { - FileUtil.setExecutable(f, true); - } - String executorPath = f.getAbsolutePath(); + + tmpMockExecutor = System.getProperty("test.build.data") + + "/tmp-mock-container-executor"; + Configuration conf = new Configuration(); - conf.set(YarnConfiguration.NM_LINUX_CONTAINER_EXECUTOR_PATH, executorPath); - mockExec = new LinuxContainerExecutor(); + LinuxContainerRuntime linuxContainerRuntime; + + setupMockExecutor(MOCK_EXECUTOR, conf); + linuxContainerRuntime = new DefaultLinuxContainerRuntime( + PrivilegedOperationExecutor.getInstance(conf)); dirsHandler = new LocalDirsHandlerService(); dirsHandler.init(conf); + linuxContainerRuntime.initialize(conf); + mockExec = new LinuxContainerExecutor(linuxContainerRuntime); mockExec.setConf(conf); } @@ -114,7 +150,7 @@ public class TestLinuxContainerExecutorWithMocks { public void testContainerLaunch() throws IOException { String appSubmitter = "nobody"; String cmd = String.valueOf( - LinuxContainerExecutor.Commands.LAUNCH_CONTAINER.getValue()); + PrivilegedOperation.RunAsUserCommand.LAUNCH_CONTAINER.getValue()); String appId = "APP_ID"; String containerId = "CONTAINER_ID"; Container container = mock(Container.class); @@ -161,13 +197,8 @@ public class TestLinuxContainerExecutorWithMocks { public void testContainerLaunchWithPriority() throws IOException { // set the scheduler priority to make sure still works with nice -n prio - File f = new File("./src/test/resources/mock-container-executor"); - if (!FileUtil.canExecute(f)) { - FileUtil.setExecutable(f, true); - } - String executorPath = f.getAbsolutePath(); Configuration conf = new Configuration(); - conf.set(YarnConfiguration.NM_LINUX_CONTAINER_EXECUTOR_PATH, executorPath); + setupMockExecutor(MOCK_EXECUTOR, conf); conf.setInt(YarnConfiguration.NM_CONTAINER_EXECUTOR_SCHED_PRIORITY, 2); mockExec.setConf(conf); @@ -175,8 +206,8 @@ public class TestLinuxContainerExecutorWithMocks { mockExec.addSchedPriorityCommand(command); assertEquals("first should be nice", "nice", command.get(0)); assertEquals("second should be -n", "-n", command.get(1)); - assertEquals("third should be the priority", Integer.toString(2), - command.get(2)); + assertEquals("third should be the priority", Integer.toString(2), + command.get(2)); testContainerLaunch(); } @@ -185,11 +216,10 @@ public class TestLinuxContainerExecutorWithMocks { public void testLaunchCommandWithoutPriority() throws IOException { // make sure the command doesn't contain the nice -n since priority // not specified - List<String> command = new ArrayList<String>(); + List<String> command = new ArrayList<String>(); mockExec.addSchedPriorityCommand(command); assertEquals("addSchedPriority should be empty", 0, command.size()); } - @Test (timeout = 5000) public void testStartLocalizer() throws IOException { @@ -232,20 +262,25 @@ public class TestLinuxContainerExecutorWithMocks { @Test - public void testContainerLaunchError() throws IOException { + public void testContainerLaunchError() + throws IOException, ContainerExecutionException { // reinitialize executer - File f = new File("./src/test/resources/mock-container-executer-with-error"); - if (!FileUtil.canExecute(f)) { - FileUtil.setExecutable(f, true); - } - String executorPath = f.getAbsolutePath(); Configuration conf = new Configuration(); - conf.set(YarnConfiguration.NM_LINUX_CONTAINER_EXECUTOR_PATH, executorPath); + setupMockExecutor(MOCK_EXECUTOR_WITH_ERROR, conf); conf.set(YarnConfiguration.NM_LOCAL_DIRS, "file:///bin/echo"); conf.set(YarnConfiguration.NM_LOG_DIRS, "file:///dev/null"); - mockExec = spy(new LinuxContainerExecutor()); + + LinuxContainerExecutor exec; + LinuxContainerRuntime linuxContainerRuntime = new + DefaultLinuxContainerRuntime(PrivilegedOperationExecutor.getInstance + (conf)); + + linuxContainerRuntime.initialize(conf); + exec = new LinuxContainerExecutor(linuxContainerRuntime); + + mockExec = spy(exec); doAnswer( new Answer() { @Override @@ -264,7 +299,7 @@ public class TestLinuxContainerExecutorWithMocks { String appSubmitter = "nobody"; String cmd = String - .valueOf(LinuxContainerExecutor.Commands.LAUNCH_CONTAINER.getValue()); + .valueOf(PrivilegedOperation.RunAsUserCommand.LAUNCH_CONTAINER.getValue()); String appId = "APP_ID"; String containerId = "CONTAINER_ID"; Container container = mock(Container.class); @@ -300,6 +335,7 @@ public class TestLinuxContainerExecutorWithMocks { Path pidFile = new Path(workDir, "pid.txt"); mockExec.activateContainer(cId, pidFile); + int ret = mockExec.launchContainer(new ContainerStartContext.Builder() .setContainer(container) .setNmPrivateContainerScriptPath(scriptPath) @@ -331,16 +367,23 @@ public class TestLinuxContainerExecutorWithMocks { } - @Test public void testContainerKill() throws IOException { String appSubmitter = "nobody"; String cmd = String.valueOf( - LinuxContainerExecutor.Commands.SIGNAL_CONTAINER.getValue()); + PrivilegedOperation.RunAsUserCommand.SIGNAL_CONTAINER.getValue()); ContainerExecutor.Signal signal = ContainerExecutor.Signal.QUIT; String sigVal = String.valueOf(signal.getValue()); - + + Container container = mock(Container.class); + ContainerId cId = mock(ContainerId.class); + ContainerLaunchContext context = mock(ContainerLaunchContext.class); + + when(container.getContainerId()).thenReturn(cId); + when(container.getLaunchContext()).thenReturn(context); + mockExec.signalContainer(new ContainerSignalContext.Builder() + .setContainer(container) .setUser(appSubmitter) .setPid("1000") .setSignal(signal) @@ -354,7 +397,7 @@ public class TestLinuxContainerExecutorWithMocks { public void testDeleteAsUser() throws IOException { String appSubmitter = "nobody"; String cmd = String.valueOf( - LinuxContainerExecutor.Commands.DELETE_AS_USER.getValue()); + PrivilegedOperation.RunAsUserCommand.DELETE_AS_USER.getValue()); Path dir = new Path("/tmp/testdir"); Path testFile = new Path("testfile"); Path baseDir0 = new Path("/grid/0/BaseDir"); @@ -396,14 +439,9 @@ public class TestLinuxContainerExecutorWithMocks { Arrays.asList(YarnConfiguration.DEFAULT_NM_NONSECURE_MODE_LOCAL_USER, appSubmitter, cmd, "", baseDir0.toString(), baseDir1.toString()), readMockParams()); - - File f = new File("./src/test/resources/mock-container-executer-with-error"); - if (!FileUtil.canExecute(f)) { - FileUtil.setExecutable(f, true); - } - String executorPath = f.getAbsolutePath(); + ; Configuration conf = new Configuration(); - conf.set(YarnConfiguration.NM_LINUX_CONTAINER_EXECUTOR_PATH, executorPath); + setupMockExecutor(MOCK_EXECUTOR, conf); mockExec.setConf(conf); mockExec.deleteAsUser(new DeletionAsUserContext.Builder() http://git-wip-us.apache.org/repos/asf/hadoop/blob/3e6fce91/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/privileged/TestPrivilegedOperationExecutor.java ---------------------------------------------------------------------- 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/TestPrivilegedOperationExecutor.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/TestPrivilegedOperationExecutor.java index 8f297ed..849dbab 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/privileged/TestPrivilegedOperationExecutor.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/TestPrivilegedOperationExecutor.java @@ -118,7 +118,7 @@ public class TestPrivilegedOperationExecutor { PrivilegedOperationExecutor exec = PrivilegedOperationExecutor .getInstance(confWithExecutorPath); PrivilegedOperation op = new PrivilegedOperation(PrivilegedOperation - .OperationType.LAUNCH_CONTAINER, (String) null); + .OperationType.TC_MODIFY_STATE, (String) null); String[] cmdArray = exec.getPrivilegedOperationExecutionCommand(null, op); //No arguments added - so the resulting array should consist of @@ -127,10 +127,8 @@ public class TestPrivilegedOperationExecutor { Assert.assertEquals(customExecutorPath, cmdArray[0]); Assert.assertEquals(op.getOperationType().getOption(), cmdArray[1]); - //other (dummy) arguments to launch container - String[] additionalArgs = { "test_user", "yarn", "1", "app_01", - "container_01", "workdir", "launch_script.sh", "tokens", "pidfile", - "nm-local-dirs", "nm-log-dirs", "resource-spec" }; + //other (dummy) arguments to tc modify state + String[] additionalArgs = { "cmd_file_1", "cmd_file_2", "cmd_file_3"}; op.appendArgs(additionalArgs); cmdArray = exec.getPrivilegedOperationExecutionCommand(null, op); http://git-wip-us.apache.org/repos/asf/hadoop/blob/3e6fce91/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 ---------------------------------------------------------------------- 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 new file mode 100644 index 0000000..31ed496 --- /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/TestDockerContainerRuntime.java @@ -0,0 +1,219 @@ +/* + * * + * 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; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.yarn.api.records.ContainerId; +import org.apache.hadoop.yarn.api.records.ContainerLaunchContext; +import org.apache.hadoop.yarn.server.nodemanager.LocalDirsHandlerService; +import org.apache.hadoop.yarn.server.nodemanager.containermanager.container.Container; +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 org.apache.hadoop.yarn.server.nodemanager.containermanager.runtime.ContainerRuntimeConstants; +import org.apache.hadoop.yarn.server.nodemanager.containermanager.runtime.ContainerRuntimeContext; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; + +import java.io.File; +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 java.util.Map; + +import static org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.LinuxContainerRuntimeConstants.*; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.*; + +public class TestDockerContainerRuntime { + private Configuration conf; + PrivilegedOperationExecutor mockExecutor; + String containerId; + Container container; + ContainerId cId; + ContainerLaunchContext context; + HashMap<String, String> env; + String image; + String runAsUser; + String user; + String appId; + String containerIdStr = containerId; + Path containerWorkDir; + Path nmPrivateContainerScriptPath; + Path nmPrivateTokensPath; + Path pidFilePath; + List<String> localDirs; + List<String> logDirs; + String resourcesOptions; + + @Before + public void setup() { + String tmpPath = new StringBuffer(System.getProperty("test.build.data")) + .append + ('/').append("hadoop.tmp.dir").toString(); + + conf = new Configuration(); + conf.set("hadoop.tmp.dir", tmpPath); + + mockExecutor = Mockito + .mock(PrivilegedOperationExecutor.class); + containerId = "container_id"; + container = mock(Container.class); + cId = mock(ContainerId.class); + context = mock(ContainerLaunchContext.class); + env = new HashMap<String, String>(); + image = "busybox:latest"; + + env.put(DockerLinuxContainerRuntime.ENV_DOCKER_CONTAINER_IMAGE, image); + when(container.getContainerId()).thenReturn(cId); + when(cId.toString()).thenReturn(containerId); + when(container.getLaunchContext()).thenReturn(context); + when(context.getEnvironment()).thenReturn(env); + + runAsUser = "run_as_user"; + user = "user"; + appId = "app_id"; + containerIdStr = containerId; + containerWorkDir = new Path("/test_container_work_dir"); + nmPrivateContainerScriptPath = new Path("/test_script_path"); + nmPrivateTokensPath = new Path("/test_private_tokens_path"); + pidFilePath = new Path("/test_pid_file_path"); + localDirs = new ArrayList<>(); + logDirs = new ArrayList<>(); + resourcesOptions = "cgroups:none"; + + localDirs.add("/test_local_dir"); + logDirs.add("/test_log_dir"); + } + + @Test + public void testSelectDockerContainerType() { + Map<String, String> envDockerType = new HashMap<>(); + Map<String, String> envOtherType = new HashMap<>(); + + envDockerType.put(ContainerRuntimeConstants.ENV_CONTAINER_TYPE, "docker"); + envOtherType.put(ContainerRuntimeConstants.ENV_CONTAINER_TYPE, "other"); + + Assert.assertEquals(false, DockerLinuxContainerRuntime + .isDockerContainerRequested(null)); + Assert.assertEquals(true, DockerLinuxContainerRuntime + .isDockerContainerRequested(envDockerType)); + Assert.assertEquals(false, DockerLinuxContainerRuntime + .isDockerContainerRequested(envOtherType)); + } + + @Test + @SuppressWarnings("unchecked") + public void testDockerContainerLaunch() + throws ContainerExecutionException, PrivilegedOperationException, + IOException { + DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime( + mockExecutor); + runtime.initialize(conf); + + ContainerRuntimeContext.Builder builder = new ContainerRuntimeContext + .Builder(container); + + builder.setExecutionAttribute(RUN_AS_USER, runAsUser) + .setExecutionAttribute(USER, user) + .setExecutionAttribute(APPID, appId) + .setExecutionAttribute(CONTAINER_ID_STR, containerIdStr) + .setExecutionAttribute(CONTAINER_WORK_DIR, containerWorkDir) + .setExecutionAttribute(NM_PRIVATE_CONTAINER_SCRIPT_PATH, + nmPrivateContainerScriptPath) + .setExecutionAttribute(NM_PRIVATE_TOKENS_PATH, nmPrivateTokensPath) + .setExecutionAttribute(PID_FILE_PATH, pidFilePath) + .setExecutionAttribute(LOCAL_DIRS, localDirs) + .setExecutionAttribute(LOG_DIRS, logDirs) + .setExecutionAttribute(RESOURCES_OPTIONS, resourcesOptions); + + runtime.launchContainer(builder.build()); + + ArgumentCaptor<PrivilegedOperation> opCaptor = ArgumentCaptor.forClass( + PrivilegedOperation.class); + + //single invocation expected + //due to type erasure + mocking, this verification requires a suppress + // warning annotation on the entire method + verify(mockExecutor, times(1)) + .executePrivilegedOperation(anyList(), opCaptor.capture(), any( + File.class), any(Map.class), eq(false)); + + PrivilegedOperation op = opCaptor.getValue(); + + Assert.assertEquals(PrivilegedOperation.OperationType + .LAUNCH_DOCKER_CONTAINER, op.getOperationType()); + + List<String> args = op.getArguments(); + + //This invocation of container-executor should use 13 arguments in a + // specific order (sigh.) + Assert.assertEquals(13, args.size()); + + //verify arguments + Assert.assertEquals(runAsUser, args.get(0)); + Assert.assertEquals(user, args.get(1)); + Assert.assertEquals(Integer.toString(PrivilegedOperation.RunAsUserCommand + .LAUNCH_DOCKER_CONTAINER.getValue()), args.get(2)); + Assert.assertEquals(appId, args.get(3)); + Assert.assertEquals(containerId, args.get(4)); + Assert.assertEquals(containerWorkDir.toString(), args.get(5)); + Assert.assertEquals(nmPrivateContainerScriptPath.toUri() + .toString(), args.get(6)); + Assert.assertEquals(nmPrivateTokensPath.toUri().getPath(), args.get(7)); + Assert.assertEquals(pidFilePath.toString(), args.get(8)); + Assert.assertEquals(localDirs.get(0), args.get(9)); + Assert.assertEquals(logDirs.get(0), args.get(10)); + Assert.assertEquals(resourcesOptions, args.get(12)); + + String dockerCommandFile = args.get(11); + + //This is the expected docker invocation for this case + StringBuffer expectedCommandTemplate = new StringBuffer("run --name=%1$s ") + .append("--user=%2$s -d ") + .append("--workdir=%3$s ") + .append("--net=host -v /etc/passwd:/etc/password:ro ") + .append("-v %4$s:%4$s ") + .append("-v %5$s:%5$s ") + .append("-v %6$s:%6$s ") + .append("%7$s ") + .append("bash %8$s/launch_container.sh"); + + String expectedCommand = String.format(expectedCommandTemplate.toString(), + containerId, runAsUser, containerWorkDir, localDirs.get(0), + containerWorkDir, logDirs.get(0), image, containerWorkDir); + + List<String> dockerCommands = Files.readAllLines(Paths.get + (dockerCommandFile), Charset.forName("UTF-8")); + + Assert.assertEquals(1, dockerCommands.size()); + Assert.assertEquals(expectedCommand, dockerCommands.get(0)); + } +}