TEST_F(SharedFilesystemIsolatorTest, ROOT_AbsoluteVolume) { slave::Flags flags = CreateSlaveFlags(); flags.isolation = "filesystem/shared"; Try<Isolator*> isolator = SharedFilesystemIsolatorProcess::create(flags); CHECK_SOME(isolator); Try<Launcher*> launcher = LinuxLauncher::create(flags); CHECK_SOME(launcher); // We'll mount the absolute test work directory as /var/tmp in the // container. const string hostPath = flags.work_dir; const string containerPath = "/var/tmp"; ContainerInfo containerInfo; containerInfo.set_type(ContainerInfo::MESOS); containerInfo.add_volumes()->CopyFrom( CREATE_VOLUME(containerPath, hostPath, Volume::RW)); ExecutorInfo executorInfo; executorInfo.mutable_container()->CopyFrom(containerInfo); ContainerID containerId; containerId.set_value(UUID::random().toString()); Future<Option<CommandInfo> > prepare = isolator.get()->prepare(containerId, executorInfo, flags.work_dir, None()); AWAIT_READY(prepare); ASSERT_SOME(prepare.get()); // Test the volume mounting by touching a file in the container's // /tmp, which should then be in flags.work_dir. const string filename = UUID::random().toString(); ASSERT_FALSE(os::exists(path::join(containerPath, filename))); vector<string> args; args.push_back("/bin/sh"); args.push_back("-x"); args.push_back("-c"); args.push_back(prepare.get().get().value() + " && touch " + path::join(containerPath, filename)); Try<pid_t> pid = launcher.get()->fork( containerId, "/bin/sh", args, Subprocess::FD(STDIN_FILENO), Subprocess::FD(STDOUT_FILENO), Subprocess::FD(STDERR_FILENO), None(), None(), None()); ASSERT_SOME(pid); // Set up the reaper to wait on the forked child. Future<Option<int> > status = process::reap(pid.get()); AWAIT_READY(status); EXPECT_SOME_EQ(0, status.get()); // Check the file was created in flags.work_dir. EXPECT_TRUE(os::exists(path::join(hostPath, filename))); // Check it didn't get created in the host's view of containerPath. EXPECT_FALSE(os::exists(path::join(containerPath, filename))); delete launcher.get(); delete isolator.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"; Fetcher fetcher(flags); Try<MesosContainerizer*> create = MesosContainerizer::create(flags, true, &fetcher); ASSERT_SOME(create); Owned<Containerizer> containerizer(create.get()); ContainerID containerId; containerId.set_value(id::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<Containerizer::LaunchResult> launch = containerizer->launch( containerId, createContainerConfig(None(), executor, directory), map<string, string>(), None()); AWAIT_ASSERT_EQ(Containerizer::LaunchResult::SUCCESS, launch); Future<Option<ContainerTermination>> wait = containerizer->wait(containerId); if (nesting) { ContainerID nestedContainerId; nestedContainerId.mutable_parent()->CopyFrom(containerId); nestedContainerId.set_value(id::UUID::random().toString()); launch = containerizer->launch( nestedContainerId, createContainerConfig(command, container), map<string, string>(), None()); AWAIT_ASSERT_EQ(Containerizer::LaunchResult::SUCCESS, 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) { Future<Option<ContainerTermination>> termination = containerizer->destroy(containerId); AWAIT_READY(termination); ASSERT_SOME(termination.get()); ASSERT_TRUE(termination->get().has_status()); EXPECT_WTERMSIG_EQ(SIGKILL, termination.get()->status()); } }
// Test that a container can create a private view of a system // directory (/var/tmp). Check that a file written by a process inside // the container doesn't appear on the host filesystem but does appear // under the container's work directory. TEST_F(SharedFilesystemIsolatorTest, ROOT_RelativeVolume) { slave::Flags flags = CreateSlaveFlags(); flags.isolation = "filesystem/shared"; Try<Isolator*> isolator = SharedFilesystemIsolatorProcess::create(flags); CHECK_SOME(isolator); Try<Launcher*> launcher = LinuxLauncher::create(flags); CHECK_SOME(launcher); // Use /var/tmp so we don't mask the work directory (under /tmp). const string containerPath = "/var/tmp"; ASSERT_TRUE(os::isdir(containerPath)); // Use a host path relative to the container work directory. const string hostPath = strings::remove(containerPath, "/", strings::PREFIX); ContainerInfo containerInfo; containerInfo.set_type(ContainerInfo::MESOS); containerInfo.add_volumes()->CopyFrom( CREATE_VOLUME(containerPath, hostPath, Volume::RW)); ExecutorInfo executorInfo; executorInfo.mutable_container()->CopyFrom(containerInfo); ContainerID containerId; containerId.set_value(UUID::random().toString()); Future<Option<CommandInfo> > prepare = isolator.get()->prepare(containerId, executorInfo, flags.work_dir, None()); AWAIT_READY(prepare); ASSERT_SOME(prepare.get()); // The test will touch a file in container path. const string file = path::join(containerPath, UUID::random().toString()); ASSERT_FALSE(os::exists(file)); // Manually run the isolator's preparation command first, then touch // the file. vector<string> args; args.push_back("/bin/sh"); args.push_back("-x"); args.push_back("-c"); args.push_back(prepare.get().get().value() + " && touch " + file); Try<pid_t> pid = launcher.get()->fork( containerId, "/bin/sh", args, Subprocess::FD(STDIN_FILENO), Subprocess::FD(STDOUT_FILENO), Subprocess::FD(STDERR_FILENO), None(), None(), None()); ASSERT_SOME(pid); // Set up the reaper to wait on the forked child. Future<Option<int> > status = process::reap(pid.get()); AWAIT_READY(status); EXPECT_SOME_EQ(0, status.get()); // Check the correct hierarchy was created under the container work // directory. string dir = "/"; foreach (const string& subdir, strings::tokenize(containerPath, "/")) { dir = path::join(dir, subdir); struct stat hostStat; EXPECT_EQ(0, ::stat(dir.c_str(), &hostStat)); struct stat containerStat; EXPECT_EQ(0, ::stat(path::join(flags.work_dir, dir).c_str(), &containerStat)); EXPECT_EQ(hostStat.st_mode, containerStat.st_mode); EXPECT_EQ(hostStat.st_uid, containerStat.st_uid); EXPECT_EQ(hostStat.st_gid, containerStat.st_gid); } // Check it did *not* create a file in the host namespace. EXPECT_FALSE(os::exists(file)); // Check it did create the file under the container's work directory // on the host. EXPECT_TRUE(os::exists(path::join(flags.work_dir, file))); delete launcher.get(); delete isolator.get(); }
// This test verifies that if a persistent volume and SANDBOX_PATH // volume are both specified and the 'path' of the SANDBOX_PATH volume // is the same relative path as the persistent volume's container // path, the persistent volume will not be neglect and is mounted // correctly. This is a regression test for MESOS-7770. TEST_F(LinuxFilesystemIsolatorTest, ROOT_PersistentVolumeAndHostVolumeWithRootFilesystem) { string registry = path::join(sandbox.get(), "registry"); AWAIT_READY(DockerArchive::create(registry, "test_image")); slave::Flags flags = CreateSlaveFlags(); flags.isolation = "filesystem/linux,docker/runtime"; flags.docker_registry = registry; flags.docker_store_dir = path::join(sandbox.get(), "store"); flags.image_providers = "docker"; Fetcher fetcher(flags); Try<MesosContainerizer*> create = MesosContainerizer::create(flags, true, &fetcher); ASSERT_SOME(create); Owned<Containerizer> containerizer(create.get()); ContainerID containerId; containerId.set_value(id::UUID::random().toString()); // Write to an absolute path in the container's mount namespace to // verify mounts of the SANDBOX_PATH volume and the persistent // volume are done in the proper order. ExecutorInfo executor = createExecutorInfo( "test_executor", "echo abc > /absolute_path/file"); executor.add_resources()->CopyFrom(createPersistentVolume( Megabytes(32), "test_role", "persistent_volume_id", "volume")); executor.mutable_container()->CopyFrom(createContainerInfo( "test_image", {createVolumeSandboxPath("/absolute_path", "volume", Volume::RW)})); // Create a persistent volume. string volume = slave::paths::getPersistentVolumePath( flags.work_dir, "test_role", "persistent_volume_id"); ASSERT_SOME(os::mkdir(volume)); string directory = path::join(flags.work_dir, "sandbox"); ASSERT_SOME(os::mkdir(directory)); Future<Containerizer::LaunchResult> launch = containerizer->launch( containerId, createContainerConfig(None(), executor, directory), map<string, string>(), None()); AWAIT_ASSERT_EQ(Containerizer::LaunchResult::SUCCESS, launch); Future<Option<ContainerTermination>> wait = containerizer->wait(containerId); AWAIT_READY(wait); ASSERT_SOME(wait.get()); ASSERT_TRUE(wait->get().has_status()); EXPECT_WEXITSTATUS_EQ(0, wait->get().status()); EXPECT_SOME_EQ("abc\n", os::read(path::join(volume, "file"))); }