blob: 5920be35a624680187d4327556395cc333454ce0 [file] [log] [blame]
/*
* *
* 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.FileUtil;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.registry.client.binding.RegistryPathUtils;
import org.apache.hadoop.util.Shell;
import org.apache.hadoop.util.StringUtils;
import org.apache.hadoop.yarn.api.records.ContainerId;
import org.apache.hadoop.yarn.api.records.ContainerLaunchContext;
import org.apache.hadoop.yarn.conf.YarnConfiguration;
import org.apache.hadoop.yarn.server.nodemanager.ContainerExecutor;
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.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.DockerRunCommand;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
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 static final Logger LOG =
LoggerFactory.getLogger(TestDockerContainerRuntime.class);
private Configuration conf;
private PrivilegedOperationExecutor mockExecutor;
private CGroupsHandler mockCGroupsHandler;
private String containerId;
private String defaultHostname;
private Container container;
private ContainerId cId;
private ContainerLaunchContext context;
private HashMap<String, String> env;
private String image;
private String uidGidPair;
private String runAsUser = System.getProperty("user.name");
private String[] groups = {};
private String groupGids;
private String user;
private String appId;
private String containerIdStr = containerId;
private Path containerWorkDir;
private Path nmPrivateContainerScriptPath;
private Path nmPrivateTokensPath;
private Path pidFilePath;
private List<String> localDirs;
private List<String> logDirs;
private List<String> filecacheDirs;
private List<String> userLocalDirs;
private List<String> containerLocalDirs;
private List<String> containerLogDirs;
private Map<Path, List<String>> localizedResources;
private String resourcesOptions;
private ContainerRuntimeContext.Builder builder;
private final String submittingUser = "anakin";
private final String whitelistedUser = "yoda";
private String[] testCapabilities;
private final String signalPid = "1234";
@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);
mockCGroupsHandler = Mockito.mock(CGroupsHandler.class);
containerId = "container_id";
defaultHostname = RegistryPathUtils.encodeYarnID(containerId);
container = mock(Container.class);
cId = mock(ContainerId.class);
context = mock(ContainerLaunchContext.class);
env = new HashMap<String, String>();
env.put("FROM_CLIENT", "1");
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);
when(container.getUser()).thenReturn(submittingUser);
// Get the running user's uid and gid for remap
String uid = "";
String gid = "";
Shell.ShellCommandExecutor shexec1 = new Shell.ShellCommandExecutor(
new String[]{"id", "-u", runAsUser});
Shell.ShellCommandExecutor shexec2 = new Shell.ShellCommandExecutor(
new String[]{"id", "-g", runAsUser});
Shell.ShellCommandExecutor shexec3 = new Shell.ShellCommandExecutor(
new String[]{"id", "-G", runAsUser});
try {
shexec1.execute();
// get rid of newline at the end
uid = shexec1.getOutput().replaceAll("\n$", "");
} catch (Exception e) {
LOG.info("Could not run id -u command: " + e);
}
try {
shexec2.execute();
// get rid of newline at the end
gid = shexec2.getOutput().replaceAll("\n$", "");
} catch (Exception e) {
LOG.info("Could not run id -g command: " + e);
}
try {
shexec3.execute();
groups = shexec3.getOutput().replace("\n", " ").split(" ");
} catch (Exception e) {
LOG.info("Could not run id -G command: " + e);
}
uidGidPair = uid + ":" + gid;
StringBuilder sb = new StringBuilder();
for (String group : groups) {
sb.append(group).append(",");
}
groupGids = sb.toString().replaceAll(",$", "");
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<>();
filecacheDirs = new ArrayList<>();
resourcesOptions = "cgroups=none";
userLocalDirs = new ArrayList<>();
containerLocalDirs = new ArrayList<>();
containerLogDirs = new ArrayList<>();
localizedResources = new HashMap<>();
localDirs.add("/test_local_dir");
logDirs.add("/test_log_dir");
filecacheDirs.add("/test_filecache_dir");
userLocalDirs.add("/test_user_local_dir");
containerLocalDirs.add("/test_container_local_dir");
containerLogDirs.add("/test_container_log_dir");
localizedResources.put(new Path("/test_local_dir/test_resource_file"),
Collections.singletonList("test_dir/test_resource_file"));
testCapabilities = new String[] {"NET_BIND_SERVICE", "SYS_CHROOT"};
conf.setStrings(YarnConfiguration.NM_DOCKER_CONTAINER_CAPABILITIES,
testCapabilities);
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(FILECACHE_DIRS, filecacheDirs)
.setExecutionAttribute(USER_LOCAL_DIRS, userLocalDirs)
.setExecutionAttribute(CONTAINER_LOCAL_DIRS, containerLocalDirs)
.setExecutionAttribute(CONTAINER_LOG_DIRS, containerLogDirs)
.setExecutionAttribute(LOCALIZED_RESOURCES, localizedResources)
.setExecutionAttribute(RESOURCES_OPTIONS, resourcesOptions);
}
@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));
}
@SuppressWarnings("unchecked")
private PrivilegedOperation capturePrivilegedOperation()
throws PrivilegedOperationException {
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), eq((Map<String, String>)null),
eq(false), eq(false));
//verification completed. we need to isolate specific invications.
// hence, reset mock here
Mockito.reset(mockExecutor);
return opCaptor.getValue();
}
@SuppressWarnings("unchecked")
private PrivilegedOperation capturePrivilegedOperationAndVerifyArgs()
throws PrivilegedOperationException {
PrivilegedOperation op = capturePrivilegedOperation();
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(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));
return op;
}
private String getExpectedTestCapabilitiesArgumentString() {
/* Ordering of capabilities depends on HashSet ordering. */
Set<String> capabilitySet = new HashSet<>(Arrays.asList(testCapabilities));
StringBuilder expectedCapabilitiesString = new StringBuilder(
"--cap-drop=ALL ");
for(String capability : capabilitySet) {
expectedCapabilitiesString.append("--cap-add=").append(capability)
.append(" ");
}
return expectedCapabilitiesString.toString();
}
private String getExpectedCGroupsMountString() {
CGroupsHandler cgroupsHandler = ResourceHandlerModule.getCGroupsHandler();
if(cgroupsHandler == null) {
return "";
}
String cgroupMountPath = cgroupsHandler.getCGroupMountPath();
boolean cGroupsMountExists = new File(
cgroupMountPath).exists();
if(cGroupsMountExists) {
return "-v " + cgroupMountPath
+ ":" + cgroupMountPath + ":ro ";
} else {
return "";
}
}
@Test
public void testDockerContainerLaunch()
throws ContainerExecutionException, PrivilegedOperationException,
IOException {
DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime(
mockExecutor, mockCGroupsHandler);
runtime.initialize(conf);
runtime.launchContainer(builder.build());
PrivilegedOperation op = capturePrivilegedOperationAndVerifyArgs();
List<String> args = op.getArguments();
String dockerCommandFile = args.get(11);
List<String> dockerCommands = Files.readAllLines(Paths.get
(dockerCommandFile), Charset.forName("UTF-8"));
int expected = 14;
int counter = 0;
Assert.assertEquals(expected, dockerCommands.size());
Assert.assertEquals("[docker-command-execution]",
dockerCommands.get(counter++));
Assert.assertEquals(" cap-add=SYS_CHROOT,NET_BIND_SERVICE",
dockerCommands.get(counter++));
Assert.assertEquals(" cap-drop=ALL", dockerCommands.get(counter++));
Assert.assertEquals(" detach=true", dockerCommands.get(counter++));
Assert.assertEquals(" docker-command=run", dockerCommands.get(counter++));
Assert.assertEquals(" group-add=" + groupGids,
dockerCommands.get(counter++));
Assert.assertEquals(" hostname=ctr-id", dockerCommands.get(counter++));
Assert
.assertEquals(" image=busybox:latest", dockerCommands.get(counter++));
Assert.assertEquals(
" launch-command=bash,/test_container_work_dir/launch_container.sh",
dockerCommands.get(counter++));
Assert.assertEquals(" name=container_id", dockerCommands.get(counter++));
Assert.assertEquals(" net=host", dockerCommands.get(counter++));
Assert.assertEquals(
" rw-mounts=/test_container_local_dir:/test_container_local_dir,"
+ "/test_filecache_dir:/test_filecache_dir,"
+ "/test_container_work_dir:/test_container_work_dir,"
+ "/test_container_log_dir:/test_container_log_dir,"
+ "/test_user_local_dir:/test_user_local_dir",
dockerCommands.get(counter++));
Assert.assertEquals(" user=" + uidGidPair, dockerCommands.get(counter++));
Assert.assertEquals(" workdir=/test_container_work_dir",
dockerCommands.get(counter++));
}
@Test
public void testContainerLaunchWithUserRemapping()
throws ContainerExecutionException, PrivilegedOperationException,
IOException {
conf.setBoolean(YarnConfiguration.NM_DOCKER_ENABLE_USER_REMAPPING,
true);
Shell.ShellCommandExecutor shexec = new Shell.ShellCommandExecutor(
new String[]{"whoami"});
shexec.execute();
// get rid of newline at the end
runAsUser = shexec.getOutput().replaceAll("\n$", "");
builder.setExecutionAttribute(RUN_AS_USER, runAsUser);
DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime(
mockExecutor, mockCGroupsHandler);
runtime.initialize(conf);
runtime.launchContainer(builder.build());
PrivilegedOperation op = capturePrivilegedOperationAndVerifyArgs();
List<String> args = op.getArguments();
String dockerCommandFile = args.get(11);
String uid = "";
String gid = "";
String[] groups = {};
Shell.ShellCommandExecutor shexec1 = new Shell.ShellCommandExecutor(
new String[]{"id", "-u", runAsUser});
Shell.ShellCommandExecutor shexec2 = new Shell.ShellCommandExecutor(
new String[]{"id", "-g", runAsUser});
Shell.ShellCommandExecutor shexec3 = new Shell.ShellCommandExecutor(
new String[]{"id", "-G", runAsUser});
try {
shexec1.execute();
// get rid of newline at the end
uid = shexec1.getOutput().replaceAll("\n$", "");
} catch (Exception e) {
LOG.info("Could not run id -u command: " + e);
}
try {
shexec2.execute();
// get rid of newline at the end
gid = shexec2.getOutput().replaceAll("\n$", "");
} catch (Exception e) {
LOG.info("Could not run id -g command: " + e);
}
try {
shexec3.execute();
groups = shexec3.getOutput().replace("\n", " ").split(" ");
} catch (Exception e) {
LOG.info("Could not run id -G command: " + e);
}
uidGidPair = uid + ":" + gid;
List<String> dockerCommands = Files.readAllLines(
Paths.get(dockerCommandFile), Charset.forName("UTF-8"));
Assert.assertEquals(14, dockerCommands.size());
int counter = 0;
Assert.assertEquals("[docker-command-execution]",
dockerCommands.get(counter++));
Assert.assertEquals(" cap-add=SYS_CHROOT,NET_BIND_SERVICE",
dockerCommands.get(counter++));
Assert.assertEquals(" cap-drop=ALL", dockerCommands.get(counter++));
Assert.assertEquals(" detach=true", dockerCommands.get(counter++));
Assert.assertEquals(" docker-command=run", dockerCommands.get(counter++));
Assert.assertEquals(" group-add=" + StringUtils.join(",", groups),
dockerCommands.get(counter++));
Assert.assertEquals(" hostname=ctr-id",
dockerCommands.get(counter++));
Assert
.assertEquals(" image=busybox:latest", dockerCommands.get(counter++));
Assert.assertEquals(
" launch-command=bash,/test_container_work_dir/launch_container.sh",
dockerCommands.get(counter++));
Assert.assertEquals(" name=container_id", dockerCommands.get(counter++));
Assert
.assertEquals(" net=host", dockerCommands.get(counter++));
Assert.assertEquals(
" rw-mounts=/test_container_local_dir:/test_container_local_dir,"
+ "/test_filecache_dir:/test_filecache_dir,"
+ "/test_container_work_dir:/test_container_work_dir,"
+ "/test_container_log_dir:/test_container_log_dir,"
+ "/test_user_local_dir:/test_user_local_dir",
dockerCommands.get(counter++));
Assert.assertEquals(" user=" + uidGidPair, dockerCommands.get(counter++));
Assert.assertEquals(" workdir=/test_container_work_dir",
dockerCommands.get(counter++));
}
@Test
public void testAllowedNetworksConfiguration() throws
ContainerExecutionException {
//the default network configuration should cause
// no exception should be thrown.
DockerLinuxContainerRuntime runtime =
new DockerLinuxContainerRuntime(mockExecutor, mockCGroupsHandler);
runtime.initialize(conf);
//invalid default network configuration - sdn2 is included in allowed
// networks
String[] networks = {"host", "none", "bridge", "sdn1"};
String invalidDefaultNetwork = "sdn2";
conf.setStrings(YarnConfiguration.NM_DOCKER_ALLOWED_CONTAINER_NETWORKS,
networks);
conf.set(YarnConfiguration.NM_DOCKER_DEFAULT_CONTAINER_NETWORK,
invalidDefaultNetwork);
try {
runtime =
new DockerLinuxContainerRuntime(mockExecutor, mockCGroupsHandler);
runtime.initialize(conf);
Assert.fail("Invalid default network configuration should did not "
+ "trigger initialization failure.");
} catch (ContainerExecutionException e) {
LOG.info("Caught expected exception : " + e);
}
//valid default network configuration - sdn1 is included in allowed
// networks - no exception should be thrown.
String validDefaultNetwork = "sdn1";
conf.set(YarnConfiguration.NM_DOCKER_DEFAULT_CONTAINER_NETWORK,
validDefaultNetwork);
runtime =
new DockerLinuxContainerRuntime(mockExecutor, mockCGroupsHandler);
runtime.initialize(conf);
}
@Test
@SuppressWarnings("unchecked")
public void testContainerLaunchWithNetworkingDefaults()
throws ContainerExecutionException, IOException,
PrivilegedOperationException {
DockerLinuxContainerRuntime runtime =
new DockerLinuxContainerRuntime(mockExecutor, mockCGroupsHandler);
runtime.initialize(conf);
Random randEngine = new Random();
String disallowedNetwork = "sdn" + Integer.toString(randEngine.nextInt());
try {
env.put(DockerLinuxContainerRuntime.ENV_DOCKER_CONTAINER_NETWORK,
disallowedNetwork);
runtime.launchContainer(builder.build());
Assert.fail("Network was expected to be disallowed: " +
disallowedNetwork);
} catch (ContainerExecutionException e) {
LOG.info("Caught expected exception: " + e);
}
int size = YarnConfiguration
.DEFAULT_NM_DOCKER_ALLOWED_CONTAINER_NETWORKS.length;
String allowedNetwork = YarnConfiguration
.DEFAULT_NM_DOCKER_ALLOWED_CONTAINER_NETWORKS[randEngine.nextInt(size)];
env.put(DockerLinuxContainerRuntime.ENV_DOCKER_CONTAINER_NETWORK,
allowedNetwork);
String expectedHostname = "test.hostname";
env.put(DockerLinuxContainerRuntime.ENV_DOCKER_CONTAINER_HOSTNAME,
expectedHostname);
//this should cause no failures.
runtime.launchContainer(builder.build());
PrivilegedOperation op = capturePrivilegedOperationAndVerifyArgs();
List<String> args = op.getArguments();
String dockerCommandFile = args.get(11);
//This is the expected docker invocation for this case
List<String> dockerCommands = Files
.readAllLines(Paths.get(dockerCommandFile), Charset.forName("UTF-8"));
int expected = 14;
int counter = 0;
Assert.assertEquals(expected, dockerCommands.size());
Assert.assertEquals("[docker-command-execution]",
dockerCommands.get(counter++));
Assert.assertEquals(" cap-add=SYS_CHROOT,NET_BIND_SERVICE",
dockerCommands.get(counter++));
Assert.assertEquals(" cap-drop=ALL", dockerCommands.get(counter++));
Assert.assertEquals(" detach=true", dockerCommands.get(counter++));
Assert.assertEquals(" docker-command=run", dockerCommands.get(counter++));
Assert.assertEquals(" group-add=" + groupGids,
dockerCommands.get(counter++));
Assert.assertEquals(" hostname=test.hostname",
dockerCommands.get(counter++));
Assert
.assertEquals(" image=busybox:latest", dockerCommands.get(counter++));
Assert.assertEquals(
" launch-command=bash,/test_container_work_dir/launch_container.sh",
dockerCommands.get(counter++));
Assert.assertEquals(" name=container_id", dockerCommands.get(counter++));
Assert
.assertEquals(" net=" + allowedNetwork, dockerCommands.get(counter++));
Assert.assertEquals(
" rw-mounts=/test_container_local_dir:/test_container_local_dir,"
+ "/test_filecache_dir:/test_filecache_dir,"
+ "/test_container_work_dir:/test_container_work_dir,"
+ "/test_container_log_dir:/test_container_log_dir,"
+ "/test_user_local_dir:/test_user_local_dir",
dockerCommands.get(counter++));
Assert.assertEquals(" user=" + uidGidPair, dockerCommands.get(counter++));
Assert.assertEquals(" workdir=/test_container_work_dir",
dockerCommands.get(counter++));
}
@Test
@SuppressWarnings("unchecked")
public void testContainerLaunchWithCustomNetworks()
throws ContainerExecutionException, IOException,
PrivilegedOperationException {
DockerLinuxContainerRuntime runtime =
new DockerLinuxContainerRuntime(mockExecutor, mockCGroupsHandler);
String customNetwork1 = "sdn1";
String customNetwork2 = "sdn2";
String customNetwork3 = "sdn3";
String[] networks = {"host", "none", "bridge", customNetwork1,
customNetwork2};
//customized set of allowed networks
conf.setStrings(YarnConfiguration.NM_DOCKER_ALLOWED_CONTAINER_NETWORKS,
networks);
//default network is "sdn1"
conf.set(YarnConfiguration.NM_DOCKER_DEFAULT_CONTAINER_NETWORK,
customNetwork1);
//this should cause no failures.
runtime.initialize(conf);
runtime.launchContainer(builder.build());
PrivilegedOperation op = capturePrivilegedOperationAndVerifyArgs();
List<String> args = op.getArguments();
String dockerCommandFile = args.get(11);
//This is the expected docker invocation for this case. customNetwork1
// ("sdn1") is the expected network to be used in this case
List<String> dockerCommands = Files
.readAllLines(Paths.get(dockerCommandFile), Charset.forName("UTF-8"));
int expected = 14;
int counter = 0;
Assert.assertEquals(expected, dockerCommands.size());
Assert.assertEquals("[docker-command-execution]",
dockerCommands.get(counter++));
Assert.assertEquals(" cap-add=SYS_CHROOT,NET_BIND_SERVICE",
dockerCommands.get(counter++));
Assert.assertEquals(" cap-drop=ALL", dockerCommands.get(counter++));
Assert.assertEquals(" detach=true", dockerCommands.get(counter++));
Assert.assertEquals(" docker-command=run", dockerCommands.get(counter++));
Assert.assertEquals(" group-add=" + groupGids,
dockerCommands.get(counter++));
Assert.assertEquals(" hostname=ctr-id", dockerCommands.get(counter++));
Assert
.assertEquals(" image=busybox:latest", dockerCommands.get(counter++));
Assert.assertEquals(
" launch-command=bash,/test_container_work_dir/launch_container.sh",
dockerCommands.get(counter++));
Assert.assertEquals(" name=container_id", dockerCommands.get(counter++));
Assert.assertEquals(" net=sdn1", dockerCommands.get(counter++));
Assert.assertEquals(
" rw-mounts=/test_container_local_dir:/test_container_local_dir,"
+ "/test_filecache_dir:/test_filecache_dir,"
+ "/test_container_work_dir:/test_container_work_dir,"
+ "/test_container_log_dir:/test_container_log_dir,"
+ "/test_user_local_dir:/test_user_local_dir",
dockerCommands.get(counter++));
Assert.assertEquals(" user=" + uidGidPair, dockerCommands.get(counter++));
Assert.assertEquals(" workdir=/test_container_work_dir",
dockerCommands.get(counter++));
//now set an explicit (non-default) allowedNetwork and ensure that it is
// used.
env.put(DockerLinuxContainerRuntime.ENV_DOCKER_CONTAINER_NETWORK,
customNetwork2);
runtime.launchContainer(builder.build());
op = capturePrivilegedOperationAndVerifyArgs();
args = op.getArguments();
dockerCommandFile = args.get(11);
//This is the expected docker invocation for this case. customNetwork2
// ("sdn2") is the expected network to be used in this case
dockerCommands = Files
.readAllLines(Paths.get(dockerCommandFile), Charset.forName("UTF-8"));
counter = 0;
Assert.assertEquals(expected, dockerCommands.size());
Assert.assertEquals("[docker-command-execution]",
dockerCommands.get(counter++));
Assert.assertEquals(" cap-add=SYS_CHROOT,NET_BIND_SERVICE",
dockerCommands.get(counter++));
Assert.assertEquals(" cap-drop=ALL", dockerCommands.get(counter++));
Assert.assertEquals(" detach=true", dockerCommands.get(counter++));
Assert.assertEquals(" docker-command=run", dockerCommands.get(counter++));
Assert.assertEquals(" group-add=" + groupGids,
dockerCommands.get(counter++));
Assert.assertEquals(" hostname=ctr-id", dockerCommands.get(counter++));
Assert
.assertEquals(" image=busybox:latest", dockerCommands.get(counter++));
Assert.assertEquals(
" launch-command=bash,/test_container_work_dir/launch_container.sh",
dockerCommands.get(counter++));
Assert.assertEquals(" name=container_id", dockerCommands.get(counter++));
Assert.assertEquals(" net=sdn2", dockerCommands.get(counter++));
Assert.assertEquals(
" rw-mounts=/test_container_local_dir:/test_container_local_dir,"
+ "/test_filecache_dir:/test_filecache_dir,"
+ "/test_container_work_dir:/test_container_work_dir,"
+ "/test_container_log_dir:/test_container_log_dir,"
+ "/test_user_local_dir:/test_user_local_dir",
dockerCommands.get(counter++));
Assert.assertEquals(" user=" + uidGidPair, dockerCommands.get(counter++));
Assert.assertEquals(" workdir=/test_container_work_dir",
dockerCommands.get(counter++));
//disallowed network should trigger a launch failure
env.put(DockerLinuxContainerRuntime.ENV_DOCKER_CONTAINER_NETWORK,
customNetwork3);
try {
runtime.launchContainer(builder.build());
Assert.fail("Disallowed network : " + customNetwork3
+ "did not trigger launch failure.");
} catch (ContainerExecutionException e) {
LOG.info("Caught expected exception : " + e);
}
}
@Test
public void testLaunchPrivilegedContainersInvalidEnvVar()
throws ContainerExecutionException, PrivilegedOperationException,
IOException{
DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime(
mockExecutor, mockCGroupsHandler);
runtime.initialize(conf);
env.put(DockerLinuxContainerRuntime
.ENV_DOCKER_CONTAINER_RUN_PRIVILEGED_CONTAINER, "invalid-value");
runtime.launchContainer(builder.build());
PrivilegedOperation op = capturePrivilegedOperationAndVerifyArgs();
List<String> args = op.getArguments();
String dockerCommandFile = args.get(11);
List<String> dockerCommands = Files.readAllLines(Paths.get
(dockerCommandFile), Charset.forName("UTF-8"));
int expected = 14;
Assert.assertEquals(expected, dockerCommands.size());
String command = dockerCommands.get(0);
//ensure --privileged isn't in the invocation
Assert.assertTrue("Unexpected --privileged in docker run args : " + command,
!command.contains("--privileged"));
}
@Test
public void testLaunchPrivilegedContainersWithDisabledSetting()
throws ContainerExecutionException, PrivilegedOperationException,
IOException{
DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime(
mockExecutor, mockCGroupsHandler);
runtime.initialize(conf);
env.put(DockerLinuxContainerRuntime
.ENV_DOCKER_CONTAINER_RUN_PRIVILEGED_CONTAINER, "true");
try {
runtime.launchContainer(builder.build());
Assert.fail("Expected a privileged launch container failure.");
} catch (ContainerExecutionException e) {
LOG.info("Caught expected exception : " + e);
}
}
@Test
public void testLaunchPrivilegedContainersWithEnabledSettingAndDefaultACL()
throws ContainerExecutionException, PrivilegedOperationException,
IOException{
//Enable privileged containers.
conf.setBoolean(YarnConfiguration.NM_DOCKER_ALLOW_PRIVILEGED_CONTAINERS,
true);
DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime(
mockExecutor, mockCGroupsHandler);
runtime.initialize(conf);
env.put(DockerLinuxContainerRuntime
.ENV_DOCKER_CONTAINER_RUN_PRIVILEGED_CONTAINER, "true");
//By default
// yarn.nodemanager.runtime.linux.docker.privileged-containers.acl
// is empty. So we expect this launch to fail.
try {
runtime.launchContainer(builder.build());
Assert.fail("Expected a privileged launch container failure.");
} catch (ContainerExecutionException e) {
LOG.info("Caught expected exception : " + e);
}
}
@Test
public void
testLaunchPrivilegedContainersEnabledAndUserNotInWhitelist()
throws ContainerExecutionException, PrivilegedOperationException,
IOException{
//Enable privileged containers.
conf.setBoolean(YarnConfiguration.NM_DOCKER_ALLOW_PRIVILEGED_CONTAINERS,
true);
//set whitelist of users.
conf.set(YarnConfiguration.NM_DOCKER_PRIVILEGED_CONTAINERS_ACL,
whitelistedUser);
DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime(
mockExecutor, mockCGroupsHandler);
runtime.initialize(conf);
env.put(DockerLinuxContainerRuntime
.ENV_DOCKER_CONTAINER_RUN_PRIVILEGED_CONTAINER, "true");
try {
runtime.launchContainer(builder.build());
Assert.fail("Expected a privileged launch container failure.");
} catch (ContainerExecutionException e) {
LOG.info("Caught expected exception : " + e);
}
}
@Test
public void
testLaunchPrivilegedContainersEnabledAndUserInWhitelist()
throws ContainerExecutionException, PrivilegedOperationException,
IOException{
//Enable privileged containers.
conf.setBoolean(YarnConfiguration.NM_DOCKER_ALLOW_PRIVILEGED_CONTAINERS,
true);
//Add submittingUser to whitelist.
conf.set(YarnConfiguration.NM_DOCKER_PRIVILEGED_CONTAINERS_ACL,
submittingUser);
DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime(
mockExecutor, mockCGroupsHandler);
runtime.initialize(conf);
env.put(DockerLinuxContainerRuntime
.ENV_DOCKER_CONTAINER_RUN_PRIVILEGED_CONTAINER, "true");
runtime.launchContainer(builder.build());
PrivilegedOperation op = capturePrivilegedOperationAndVerifyArgs();
List<String> args = op.getArguments();
String dockerCommandFile = args.get(11);
List<String> dockerCommands = Files.readAllLines(Paths.get
(dockerCommandFile), Charset.forName("UTF-8"));
int expected = 15;
int counter = 0;
Assert.assertEquals(expected, dockerCommands.size());
Assert.assertEquals("[docker-command-execution]",
dockerCommands.get(counter++));
Assert.assertEquals(" cap-add=SYS_CHROOT,NET_BIND_SERVICE",
dockerCommands.get(counter++));
Assert.assertEquals(" cap-drop=ALL", dockerCommands.get(counter++));
Assert.assertEquals(" detach=true", dockerCommands.get(counter++));
Assert.assertEquals(" docker-command=run", dockerCommands.get(counter++));
Assert.assertEquals(" group-add=" + groupGids,
dockerCommands.get(counter++));
Assert.assertEquals(" hostname=ctr-id", dockerCommands.get(counter++));
Assert
.assertEquals(" image=busybox:latest", dockerCommands.get(counter++));
Assert.assertEquals(
" launch-command=bash,/test_container_work_dir/launch_container.sh",
dockerCommands.get(counter++));
Assert.assertEquals(" name=container_id", dockerCommands.get(counter++));
Assert.assertEquals(" net=host", dockerCommands.get(counter++));
Assert.assertEquals(" privileged=true", dockerCommands.get(counter++));
Assert.assertEquals(
" rw-mounts=/test_container_local_dir:/test_container_local_dir,"
+ "/test_filecache_dir:/test_filecache_dir,"
+ "/test_container_work_dir:/test_container_work_dir,"
+ "/test_container_log_dir:/test_container_log_dir,"
+ "/test_user_local_dir:/test_user_local_dir",
dockerCommands.get(counter++));
Assert.assertEquals(" user=" + uidGidPair, dockerCommands.get(counter++));
Assert.assertEquals(" workdir=/test_container_work_dir",
dockerCommands.get(counter++));
}
@Test
public void testCGroupParent() throws ContainerExecutionException {
String hierarchy = "hadoop-yarn-test";
conf.set(YarnConfiguration.NM_LINUX_CONTAINER_CGROUPS_HIERARCHY,
hierarchy);
DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime
(mockExecutor, mockCGroupsHandler);
runtime.initialize(conf);
String resourceOptionsNone = "cgroups=none";
DockerRunCommand command = Mockito.mock(DockerRunCommand.class);
Mockito.when(mockCGroupsHandler.getRelativePathForCGroup(containerId))
.thenReturn(hierarchy + "/" + containerIdStr);
runtime.addCGroupParentIfRequired(resourceOptionsNone, containerIdStr,
command);
//no --cgroup-parent should be added here
Mockito.verifyZeroInteractions(command);
String resourceOptionsCpu = "/sys/fs/cgroup/cpu/" + hierarchy +
containerIdStr;
runtime.addCGroupParentIfRequired(resourceOptionsCpu, containerIdStr,
command);
//--cgroup-parent should be added for the containerId in question
String expectedPath = "/" + hierarchy + "/" + containerIdStr;
Mockito.verify(command).setCGroupParent(expectedPath);
//create a runtime with a 'null' cgroups handler - i.e no
// cgroup-based resource handlers are in use.
runtime = new DockerLinuxContainerRuntime
(mockExecutor, null);
runtime.initialize(conf);
runtime.addCGroupParentIfRequired(resourceOptionsNone, containerIdStr,
command);
runtime.addCGroupParentIfRequired(resourceOptionsCpu, containerIdStr,
command);
//no --cgroup-parent should be added in either case
Mockito.verifyZeroInteractions(command);
}
@Test
public void testMountSourceOnly()
throws ContainerExecutionException, PrivilegedOperationException,
IOException{
DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime(
mockExecutor, mockCGroupsHandler);
runtime.initialize(conf);
env.put(
DockerLinuxContainerRuntime.ENV_DOCKER_CONTAINER_LOCAL_RESOURCE_MOUNTS,
"source");
try {
runtime.launchContainer(builder.build());
Assert.fail("Expected a launch container failure due to invalid mount.");
} catch (ContainerExecutionException e) {
LOG.info("Caught expected exception : " + e);
}
}
@Test
public void testMountSourceTarget()
throws ContainerExecutionException, PrivilegedOperationException,
IOException{
DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime(
mockExecutor, mockCGroupsHandler);
runtime.initialize(conf);
env.put(
DockerLinuxContainerRuntime.ENV_DOCKER_CONTAINER_LOCAL_RESOURCE_MOUNTS,
"test_dir/test_resource_file:test_mount");
runtime.launchContainer(builder.build());
PrivilegedOperation op = capturePrivilegedOperationAndVerifyArgs();
List<String> args = op.getArguments();
String dockerCommandFile = args.get(11);
List<String> dockerCommands = Files.readAllLines(Paths.get
(dockerCommandFile), Charset.forName("UTF-8"));
int expected = 15;
int counter = 0;
Assert.assertEquals(expected, dockerCommands.size());
Assert.assertEquals("[docker-command-execution]",
dockerCommands.get(counter++));
Assert.assertEquals(" cap-add=SYS_CHROOT,NET_BIND_SERVICE",
dockerCommands.get(counter++));
Assert.assertEquals(" cap-drop=ALL", dockerCommands.get(counter++));
Assert.assertEquals(" detach=true", dockerCommands.get(counter++));
Assert.assertEquals(" docker-command=run", dockerCommands.get(counter++));
Assert.assertEquals(" group-add=" + groupGids,
dockerCommands.get(counter++));
Assert.assertEquals(" hostname=ctr-id", dockerCommands.get(counter++));
Assert.assertEquals(" image=busybox:latest",
dockerCommands.get(counter++));
Assert.assertEquals(
" launch-command=bash,/test_container_work_dir/launch_container.sh",
dockerCommands.get(counter++));
Assert.assertEquals(" name=container_id", dockerCommands.get(counter++));
Assert.assertEquals(" net=host", dockerCommands.get(counter++));
Assert.assertEquals(
" ro-mounts=/test_local_dir/test_resource_file:test_mount",
dockerCommands.get(counter++));
Assert.assertEquals(
" rw-mounts=/test_container_local_dir:/test_container_local_dir,"
+ "/test_filecache_dir:/test_filecache_dir,"
+ "/test_container_work_dir:/test_container_work_dir,"
+ "/test_container_log_dir:/test_container_log_dir,"
+ "/test_user_local_dir:/test_user_local_dir",
dockerCommands.get(counter++));
Assert.assertEquals(" user=" + uidGidPair, dockerCommands.get(counter++));
Assert.assertEquals(" workdir=/test_container_work_dir",
dockerCommands.get(counter));
}
@Test
public void testMountInvalid()
throws ContainerExecutionException, PrivilegedOperationException,
IOException{
DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime(
mockExecutor, mockCGroupsHandler);
runtime.initialize(conf);
env.put(
DockerLinuxContainerRuntime.ENV_DOCKER_CONTAINER_LOCAL_RESOURCE_MOUNTS,
"source:target:other");
try {
runtime.launchContainer(builder.build());
Assert.fail("Expected a launch container failure due to invalid mount.");
} catch (ContainerExecutionException e) {
LOG.info("Caught expected exception : " + e);
}
}
@Test
public void testMountMultiple()
throws ContainerExecutionException, PrivilegedOperationException,
IOException{
DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime(
mockExecutor, mockCGroupsHandler);
runtime.initialize(conf);
env.put(
DockerLinuxContainerRuntime.ENV_DOCKER_CONTAINER_LOCAL_RESOURCE_MOUNTS,
"test_dir/test_resource_file:test_mount1," +
"test_dir/test_resource_file:test_mount2");
runtime.launchContainer(builder.build());
PrivilegedOperation op = capturePrivilegedOperationAndVerifyArgs();
List<String> args = op.getArguments();
String dockerCommandFile = args.get(11);
List<String> dockerCommands = Files.readAllLines(Paths.get
(dockerCommandFile), Charset.forName("UTF-8"));
int expected = 15;
int counter = 0;
Assert.assertEquals(expected, dockerCommands.size());
Assert.assertEquals("[docker-command-execution]",
dockerCommands.get(counter++));
Assert.assertEquals(" cap-add=SYS_CHROOT,NET_BIND_SERVICE",
dockerCommands.get(counter++));
Assert.assertEquals(" cap-drop=ALL", dockerCommands.get(counter++));
Assert.assertEquals(" detach=true", dockerCommands.get(counter++));
Assert.assertEquals(" docker-command=run", dockerCommands.get(counter++));
Assert.assertEquals(" group-add=" + groupGids,
dockerCommands.get(counter++));
Assert.assertEquals(" hostname=ctr-id", dockerCommands.get(counter++));
Assert.assertEquals(" image=busybox:latest",
dockerCommands.get(counter++));
Assert.assertEquals(
" launch-command=bash,/test_container_work_dir/launch_container.sh",
dockerCommands.get(counter++));
Assert.assertEquals(" name=container_id", dockerCommands.get(counter++));
Assert.assertEquals(" net=host", dockerCommands.get(counter++));
Assert.assertEquals(
" ro-mounts=/test_local_dir/test_resource_file:test_mount1,"
+ "/test_local_dir/test_resource_file:test_mount2",
dockerCommands.get(counter++));
Assert.assertEquals(
" rw-mounts=/test_container_local_dir:/test_container_local_dir,"
+ "/test_filecache_dir:/test_filecache_dir,"
+ "/test_container_work_dir:/test_container_work_dir,"
+ "/test_container_log_dir:/test_container_log_dir,"
+ "/test_user_local_dir:/test_user_local_dir",
dockerCommands.get(counter++));
Assert.assertEquals(" user=" + uidGidPair, dockerCommands.get(counter++));
Assert.assertEquals(" workdir=/test_container_work_dir",
dockerCommands.get(counter));
}
@Test
public void testContainerLivelinessCheck()
throws ContainerExecutionException, PrivilegedOperationException {
DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime(
mockExecutor, mockCGroupsHandler);
builder.setExecutionAttribute(RUN_AS_USER, runAsUser)
.setExecutionAttribute(USER, user)
.setExecutionAttribute(PID, signalPid)
.setExecutionAttribute(SIGNAL, ContainerExecutor.Signal.NULL);
runtime.initialize(enableMockContainerExecutor(conf));
runtime.signalContainer(builder.build());
PrivilegedOperation op = capturePrivilegedOperation();
Assert.assertEquals(op.getOperationType(),
PrivilegedOperation.OperationType.SIGNAL_CONTAINER);
Assert.assertEquals(runAsUser, op.getArguments().get(0));
Assert.assertEquals("user", op.getArguments().get(1));
Assert.assertEquals("2", op.getArguments().get(2));
Assert.assertEquals("1234", op.getArguments().get(3));
Assert.assertEquals("0", op.getArguments().get(4));
}
@Test
public void testDockerStopOnTermSignal()
throws ContainerExecutionException, PrivilegedOperationException,
IOException {
List<String> dockerCommands = getDockerCommandsForSignal(
ContainerExecutor.Signal.TERM);
Assert.assertEquals(3, dockerCommands.size());
Assert.assertEquals("[docker-command-execution]", dockerCommands.get(0));
Assert.assertEquals(" docker-command=stop", dockerCommands.get(1));
Assert.assertEquals(" name=container_id", dockerCommands.get(2));
}
@Test
public void testDockerStopOnKillSignal()
throws ContainerExecutionException, PrivilegedOperationException,
IOException {
List<String> dockerCommands = getDockerCommandsForSignal(
ContainerExecutor.Signal.KILL);
Assert.assertEquals(3, dockerCommands.size());
Assert.assertEquals("[docker-command-execution]", dockerCommands.get(0));
Assert.assertEquals(" docker-command=stop", dockerCommands.get(1));
Assert.assertEquals(" name=container_id", dockerCommands.get(2));
}
@Test
public void testDockerStopOnQuitSignal()
throws ContainerExecutionException, PrivilegedOperationException,
IOException {
List<String> dockerCommands = getDockerCommandsForSignal(
ContainerExecutor.Signal.QUIT);
Assert.assertEquals(3, dockerCommands.size());
Assert.assertEquals("[docker-command-execution]", dockerCommands.get(0));
Assert.assertEquals(" docker-command=stop", dockerCommands.get(1));
Assert.assertEquals(" name=container_id", dockerCommands.get(2));
}
private List<String> getDockerCommandsForSignal(
ContainerExecutor.Signal signal)
throws ContainerExecutionException, PrivilegedOperationException,
IOException {
DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime(
mockExecutor, mockCGroupsHandler);
builder.setExecutionAttribute(RUN_AS_USER, runAsUser)
.setExecutionAttribute(USER, user)
.setExecutionAttribute(PID, signalPid)
.setExecutionAttribute(SIGNAL, signal);
runtime.initialize(enableMockContainerExecutor(conf));
runtime.signalContainer(builder.build());
PrivilegedOperation op = capturePrivilegedOperation();
Assert.assertEquals(op.getOperationType(),
PrivilegedOperation.OperationType.RUN_DOCKER_CMD);
String dockerCommandFile = op.getArguments().get(0);
return Files.readAllLines(Paths.get(dockerCommandFile),
Charset.forName("UTF-8"));
}
/**
* 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);
}
String executorPath = f.getAbsolutePath();
conf.set(YarnConfiguration.NM_LINUX_CONTAINER_EXECUTOR_PATH, executorPath);
return conf;
}
@Test
public void testDockerImageNamePattern() throws Exception {
String[] validNames =
{ "ubuntu", "fedora/httpd:version1.0",
"fedora/httpd:version1.0.test",
"fedora/httpd:version1.0.TEST",
"myregistryhost:5000/ubuntu",
"myregistryhost:5000/fedora/httpd:version1.0",
"myregistryhost:5000/fedora/httpd:version1.0.test",
"myregistryhost:5000/fedora/httpd:version1.0.TEST"};
String[] invalidNames = { "Ubuntu", "ubuntu || fedora", "ubuntu#",
"myregistryhost:50AB0/ubuntu", "myregistry#host:50AB0/ubuntu",
":8080/ubuntu"
};
for (String name : validNames) {
DockerLinuxContainerRuntime.validateImageName(name);
}
for (String name : invalidNames) {
try {
DockerLinuxContainerRuntime.validateImageName(name);
Assert.fail(name + " is an invalid name and should fail the regex");
} catch (ContainerExecutionException ce) {
continue;
}
}
}
@Test
public void testDockerHostnamePattern() throws Exception {
String[] validNames = {"ab", "a.b.c.d", "a1-b.cd.ef", "0AB.", "C_D-"};
String[] invalidNames = {"a", "a#.b.c", "-a.b.c", "a@b.c", "a/b/c"};
for (String name : validNames) {
DockerLinuxContainerRuntime.validateHostname(name);
}
for (String name : invalidNames) {
try {
DockerLinuxContainerRuntime.validateHostname(name);
Assert.fail(name + " is an invalid hostname and should fail the regex");
} catch (ContainerExecutionException ce) {
continue;
}
}
}
@Test
public void testDockerCapabilities()
throws ContainerExecutionException, PrivilegedOperationException,
IOException {
DockerLinuxContainerRuntime runtime = new DockerLinuxContainerRuntime(
mockExecutor, mockCGroupsHandler);
try {
conf.setStrings(YarnConfiguration.NM_DOCKER_CONTAINER_CAPABILITIES,
"none", "CHOWN", "DAC_OVERRIDE");
runtime.initialize(conf);
Assert.fail("Initialize didn't fail with invalid capabilities " +
"'none', 'CHOWN', 'DAC_OVERRIDE'");
} catch (ContainerExecutionException e) {
}
try {
conf.setStrings(YarnConfiguration.NM_DOCKER_CONTAINER_CAPABILITIES,
"CHOWN", "DAC_OVERRIDE", "NONE");
runtime.initialize(conf);
Assert.fail("Initialize didn't fail with invalid capabilities " +
"'CHOWN', 'DAC_OVERRIDE', 'NONE'");
} catch (ContainerExecutionException e) {
}
conf.setStrings(YarnConfiguration.NM_DOCKER_CONTAINER_CAPABILITIES,
"NONE");
runtime.initialize(conf);
Assert.assertEquals(0, runtime.getCapabilities().size());
conf.setStrings(YarnConfiguration.NM_DOCKER_CONTAINER_CAPABILITIES,
"none");
runtime.initialize(conf);
Assert.assertEquals(0, runtime.getCapabilities().size());
conf.setStrings(YarnConfiguration.NM_DOCKER_CONTAINER_CAPABILITIES,
"CHOWN", "DAC_OVERRIDE");
runtime.initialize(conf);
Iterator<String> it = runtime.getCapabilities().iterator();
Assert.assertEquals("CHOWN", it.next());
Assert.assertEquals("DAC_OVERRIDE", it.next());
}
}