TEST_F(NamespacesIsolatorTest, ROOT_PidNamespace) { Try<Owned<MesosContainerizer>> containerizer = createContainerizer("filesystem/linux,namespaces/pid"); ASSERT_SOME(containerizer); // Write the command's pid namespace inode and init name to files. const string command = "stat -c %i /proc/self/ns/pid > ns && (cat /proc/1/comm > init)"; process::Future<bool> launch = containerizer.get()->launch( containerId, None(), createExecutorInfo("executor", command), directory, None(), SlaveID(), std::map<string, string>(), false); AWAIT_READY(launch); ASSERT_TRUE(launch.get()); // Wait on the container. Future<Option<ContainerTermination>> wait = containerizer.get()->wait(containerId); AWAIT_READY(wait); ASSERT_SOME(wait.get()); // Check the executor exited correctly. EXPECT_TRUE(wait->get().has_status()); EXPECT_EQ(0, wait->get().status()); // Check that the command was run in a different pid namespace. Try<ino_t> testPidNamespace = ns::getns(::getpid(), "pid"); ASSERT_SOME(testPidNamespace); Try<string> containerPidNamespace = os::read(path::join(directory, "ns")); ASSERT_SOME(containerPidNamespace); EXPECT_NE(stringify(testPidNamespace.get()), strings::trim(containerPidNamespace.get())); // Check that the word 'mesos' is the part of the name for the // container's 'init' process. This verifies that /proc has been // correctly mounted for the container. Try<string> init = os::read(path::join(directory, "init")); ASSERT_SOME(init); EXPECT_TRUE(strings::contains(init.get(), "mesos")); }
// Test that the environment decorator hook adds a new environment // variable to the executor runtime. // Test hook adds a new environment variable "FOO" to the executor // with a value "bar". We validate the hook by verifying the value // of this environment variable. TEST_F(HookTest, VerifySlaveExecutorEnvironmentDecorator) { const string& directory = os::getcwd(); // We're inside a temporary sandbox. Fetcher fetcher; Try<MesosContainerizer*> containerizer = MesosContainerizer::create(CreateSlaveFlags(), false, &fetcher); ASSERT_SOME(containerizer); ContainerID containerId; containerId.set_value("test_container"); // Test hook adds a new environment variable "FOO" to the executor // with a value "bar". A '0' (success) exit status for the following // command validates the hook. process::Future<bool> launch = containerizer.get()->launch( containerId, CREATE_EXECUTOR_INFO("executor", "test $FOO = 'bar'"), directory, None(), SlaveID(), process::PID<Slave>(), false); AWAIT_READY(launch); ASSERT_TRUE(launch.get()); // Wait on the container. process::Future<containerizer::Termination> wait = containerizer.get()->wait(containerId); AWAIT_READY(wait); // Check the executor exited correctly. EXPECT_TRUE(wait.get().has_status()); EXPECT_EQ(0, wait.get().status()); delete containerizer.get(); }
// This test verifies that sandbox path volume allows two containers // nested under the same parent container to share data. // TODO(jieyu): Parameterize this test to test both linux and posix // launcher and filesystem isolator. TEST_F(VolumeSandboxPathIsolatorTest, SharedVolume) { slave::Flags flags = CreateSlaveFlags(); flags.isolation = "volume/sandbox_path"; Fetcher fetcher; Try<MesosContainerizer*> create = MesosContainerizer::create( flags, true, &fetcher); ASSERT_SOME(create); Owned<MesosContainerizer> containerizer(create.get()); SlaveState state; state.id = SlaveID(); AWAIT_READY(containerizer->recover(state)); ContainerID containerId; containerId.set_value(UUID::random().toString()); ExecutorInfo executor = createExecutorInfo("executor", "sleep 99", "cpus:1"); Try<string> directory = environment->mkdtemp(); ASSERT_SOME(directory); Future<bool> launch = containerizer->launch( containerId, None(), executor, directory.get(), None(), state.id, map<string, string>(), true); // TODO(benh): Ever want to check not-checkpointing? AWAIT_ASSERT_TRUE(launch); ContainerID nestedContainerId1; nestedContainerId1.mutable_parent()->CopyFrom(containerId); nestedContainerId1.set_value(UUID::random().toString()); ContainerInfo containerInfo; containerInfo.set_type(ContainerInfo::MESOS); Volume* volume = containerInfo.add_volumes(); volume->set_mode(Volume::RW); volume->set_container_path("parent"); Volume::Source* source = volume->mutable_source(); source->set_type(Volume::Source::SANDBOX_PATH); Volume::Source::SandboxPath* sandboxPath = source->mutable_sandbox_path(); sandboxPath->set_type(Volume::Source::SandboxPath::PARENT); sandboxPath->set_path("shared"); launch = containerizer->launch( nestedContainerId1, createCommandInfo("touch parent/file; sleep 1000"), containerInfo, None(), state.id); AWAIT_ASSERT_TRUE(launch); ContainerID nestedContainerId2; nestedContainerId2.mutable_parent()->CopyFrom(containerId); nestedContainerId2.set_value(UUID::random().toString()); launch = containerizer->launch( nestedContainerId2, createCommandInfo( "while true; do if [ -f parent/file ]; then exit 0; fi; done"), containerInfo, None(), state.id); AWAIT_ASSERT_TRUE(launch); Future<Option<ContainerTermination>> wait = containerizer->wait(nestedContainerId2); AWAIT_READY(wait); ASSERT_SOME(wait.get()); ASSERT_TRUE(wait.get()->has_status()); EXPECT_WEXITSTATUS_EQ(0, wait.get()->status()); wait = containerizer->wait(containerId); containerizer->destroy(containerId); AWAIT_READY(wait); ASSERT_SOME(wait.get()); ASSERT_TRUE(wait.get()->has_status()); EXPECT_WTERMSIG_EQ(SIGKILL, wait.get()->status()); }
// The IPC namespace has its own copy of the svipc(7) tunables. We verify // that we are correctly entering the IPC namespace by verifying that we // can set shmmax some different value than that of the host namespace. TEST_F(NamespacesIsolatorTest, ROOT_IPCNamespace) { Try<Owned<MesosContainerizer>> containerizer = createContainerizer("namespaces/ipc"); ASSERT_SOME(containerizer); // Value we will set the child namespace shmmax to. uint64_t shmmaxValue = static_cast<uint64_t>(::getpid()); Try<uint64_t> hostShmmax = readValue("/proc/sys/kernel/shmmax"); ASSERT_SOME(hostShmmax); // Verify that the host namespace shmmax is different. ASSERT_NE(hostShmmax.get(), shmmaxValue); const string command = "stat -c %i /proc/self/ns/ipc > ns;" "echo " + stringify(shmmaxValue) + " > /proc/sys/kernel/shmmax;" "cp /proc/sys/kernel/shmmax shmmax"; process::Future<bool> launch = containerizer.get()->launch( containerId, None(), createExecutorInfo("executor", command), directory, None(), SlaveID(), std::map<string, string>(), false); AWAIT_READY(launch); ASSERT_TRUE(launch.get()); // Wait on the container. Future<Option<ContainerTermination>> wait = containerizer.get()->wait(containerId); AWAIT_READY(wait); ASSERT_SOME(wait.get()); // Check the executor exited correctly. EXPECT_TRUE(wait->get().has_status()); EXPECT_EQ(0, wait->get().status()); // Check that the command was run in a different IPC namespace. Try<ino_t> testIPCNamespace = ns::getns(::getpid(), "ipc"); ASSERT_SOME(testIPCNamespace); Try<string> containerIPCNamespace = os::read(path::join(directory, "ns")); ASSERT_SOME(containerIPCNamespace); EXPECT_NE(stringify(testIPCNamespace.get()), strings::trim(containerIPCNamespace.get())); // Check that we modified the IPC shmmax of the namespace, not the host. Try<uint64_t> childShmmax = readValue("shmmax"); ASSERT_SOME(childShmmax); // Verify that we didn't modify shmmax in the host namespace. ASSERT_EQ(hostShmmax.get(), readValue("/proc/sys/kernel/shmmax").get()); EXPECT_NE(hostShmmax.get(), childShmmax.get()); EXPECT_EQ(shmmaxValue, childShmmax.get()); }
// This test verifies that the image specified in the volume will be // properly provisioned and mounted into the container if container // root filesystem is not specified. TEST_P(VolumeImageIsolatorTest, ROOT_ImageInVolumeWithoutRootFilesystem) { string registry = path::join(sandbox.get(), "registry"); AWAIT_READY(DockerArchive::create(registry, "test_image")); slave::Flags flags = CreateSlaveFlags(); flags.isolation = "filesystem/linux,volume/image,docker/runtime"; flags.docker_registry = registry; flags.docker_store_dir = path::join(sandbox.get(), "store"); flags.image_providers = "docker"; Try<MesosContainerizer*> create = MesosContainerizer::create(flags, true, &fetcher); ASSERT_SOME(create); Owned<Containerizer> containerizer(create.get()); ContainerID containerId; containerId.set_value(UUID::random().toString()); ContainerInfo container = createContainerInfo( None(), {createVolumeFromDockerImage("rootfs", "test_image", Volume::RW)}); CommandInfo command = createCommandInfo("test -d rootfs/bin"); ExecutorInfo executor = createExecutorInfo( "test_executor", nesting ? createCommandInfo("sleep 1000") : command); if (!nesting) { executor.mutable_container()->CopyFrom(container); } string directory = path::join(flags.work_dir, "sandbox"); ASSERT_SOME(os::mkdir(directory)); Future<bool> launch = containerizer->launch( containerId, None(), executor, directory, None(), SlaveID(), map<string, string>(), false); AWAIT_ASSERT_TRUE(launch); Future<Option<ContainerTermination>> wait = containerizer->wait(containerId); if (nesting) { ContainerID nestedContainerId; nestedContainerId.mutable_parent()->CopyFrom(containerId); nestedContainerId.set_value(UUID::random().toString()); launch = containerizer->launch( nestedContainerId, command, container, None(), SlaveID()); AWAIT_ASSERT_TRUE(launch); wait = containerizer->wait(nestedContainerId); } AWAIT_READY(wait); ASSERT_SOME(wait.get()); ASSERT_TRUE(wait->get().has_status()); EXPECT_WEXITSTATUS_EQ(0, wait->get().status()); if (nesting) { wait = containerizer->wait(containerId); containerizer->destroy(containerId); AWAIT_READY(wait); ASSERT_SOME(wait.get()); ASSERT_TRUE(wait->get().has_status()); EXPECT_WTERMSIG_EQ(SIGKILL, wait.get()->status()); } }