You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by ji...@apache.org on 2016/10/07 16:38:54 UTC
[04/10] mesos git commit: Reordered filesystem isolator tests.
Reordered filesystem isolator tests.
This is a pure code movement. Moved all the end to end tests to the
bottom of the file.
Review: https://reviews.apache.org/r/52538
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/351d2326
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/351d2326
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/351d2326
Branch: refs/heads/master
Commit: 351d23266fa042dff4cb2bec920a41c17fcc460e
Parents: 4ea9651
Author: Jie Yu <yu...@gmail.com>
Authored: Mon Oct 3 22:23:47 2016 -0700
Committer: Jie Yu <yu...@gmail.com>
Committed: Fri Oct 7 09:31:17 2016 -0700
----------------------------------------------------------------------
.../containerizer/filesystem_isolator_tests.cpp | 1664 +++++++++---------
1 file changed, 832 insertions(+), 832 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/351d2326/src/tests/containerizer/filesystem_isolator_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/filesystem_isolator_tests.cpp b/src/tests/containerizer/filesystem_isolator_tests.cpp
index 5b6a1d4..ad0506e 100644
--- a/src/tests/containerizer/filesystem_isolator_tests.cpp
+++ b/src/tests/containerizer/filesystem_isolator_tests.cpp
@@ -274,16 +274,12 @@ TEST_F(LinuxFilesystemIsolatorTest, ROOT_ChangeRootFilesystem)
}
-// This test verifies that the root filesystem of the container is
-// properly changed to the one that's provisioned by the provisioner.
-// Also runs the command executor with the new root filesystem.
-TEST_F(LinuxFilesystemIsolatorTest, ROOT_ChangeRootFilesystemCommandExecutor)
+TEST_F(LinuxFilesystemIsolatorTest, ROOT_Metrics)
{
- Try<Owned<cluster::Master>> master = StartMaster();
- ASSERT_SOME(master);
-
slave::Flags flags = CreateSlaveFlags();
- flags.image_provisioner_backend = "copy";
+
+ ContainerID containerId;
+ containerId.set_value(UUID::random().toString());
Try<Owned<MesosContainerizer>> containerizer = createContainerizer(
flags,
@@ -291,81 +287,61 @@ TEST_F(LinuxFilesystemIsolatorTest, ROOT_ChangeRootFilesystemCommandExecutor)
ASSERT_SOME(containerizer);
- Owned<MasterDetector> detector = master.get()->createDetector();
-
- Try<Owned<cluster::Slave>> slave =
- StartSlave(detector.get(), containerizer.get().get(), flags);
- ASSERT_SOME(slave);
-
- MockScheduler sched;
-
- MesosSchedulerDriver driver(
- &sched,
- DEFAULT_FRAMEWORK_INFO,
- master.get()->pid,
- DEFAULT_CREDENTIAL);
-
- Future<FrameworkID> frameworkId;
- EXPECT_CALL(sched, registered(&driver, _, _))
- .WillOnce(FutureArg<1>(&frameworkId));
-
- Future<vector<Offer>> offers;
- EXPECT_CALL(sched, resourceOffers(&driver, _))
- .WillOnce(FutureArg<1>(&offers))
- .WillRepeatedly(Return()); // Ignore subsequent offers.
-
- driver.start();
-
- AWAIT_READY(frameworkId);
+ // Use a long running task so we can reliably capture the moment it's alive.
+ ExecutorInfo executor = createExecutorInfo(
+ "test_executor",
+ "sleep 1000");
- AWAIT_READY(offers);
- ASSERT_NE(0u, offers->size());
+ executor.mutable_container()->CopyFrom(createContainerInfo("test_image"));
- const Offer& offer = offers.get()[0];
+ string directory = path::join(os::getcwd(), "sandbox");
+ ASSERT_SOME(os::mkdir(directory));
- TaskInfo task = createTask(
- offer.slave_id(),
- offer.resources(),
- "test -d " + flags.sandbox_directory);
+ Future<bool> launch = containerizer.get()->launch(
+ containerId,
+ None(),
+ executor,
+ directory,
+ None(),
+ SlaveID(),
+ map<string, string>(),
+ false);
- ContainerInfo containerInfo;
- Image* image = containerInfo.mutable_mesos()->mutable_image();
- image->set_type(Image::APPC);
- image->mutable_appc()->set_name("test_image");
- containerInfo.set_type(ContainerInfo::MESOS);
- task.mutable_container()->CopyFrom(containerInfo);
+ // Wait for the launch to complete.
+ // Need to wait for Rootfs copying.
+ AWAIT_READY_FOR(launch, Seconds(60));
- driver.launchTasks(offer.id(), {task});
+ // Check metrics.
+ JSON::Object stats = Metrics();
+ EXPECT_EQ(1u, stats.values.count(
+ "containerizer/mesos/filesystem/containers_new_rootfs"));
+ EXPECT_EQ(
+ 1, stats.values["containerizer/mesos/filesystem/containers_new_rootfs"]);
- Future<TaskStatus> statusRunning;
- Future<TaskStatus> statusFinished;
+ containerizer.get()->destroy(containerId);
- EXPECT_CALL(sched, statusUpdate(&driver, _))
- .WillOnce(FutureArg<1>(&statusRunning))
- .WillOnce(FutureArg<1>(&statusFinished));
+ // Wait on the container.
+ Future<Option<ContainerTermination>> wait =
+ containerizer.get()->wait(containerId);
- // Need to wait for Rootfs copying.
- AWAIT_READY_FOR(statusRunning, Seconds(60));
- EXPECT_EQ(TASK_RUNNING, statusRunning->state());
- AWAIT_READY(statusFinished);
- EXPECT_EQ(TASK_FINISHED, statusFinished->state());
+ AWAIT_READY(wait);
+ ASSERT_SOME(wait.get());
- driver.stop();
- driver.join();
+ // Executor was killed.
+ EXPECT_TRUE(wait->get().has_status());
+ EXPECT_EQ(9, wait->get().status());
}
-// This test verifies that the root filesystem of the container is
-// properly changed to the one that's provisioned by the provisioner.
-// Also runs the command executor with the new root filesystem.
-TEST_F(LinuxFilesystemIsolatorTest,
- ROOT_ChangeRootFilesystemCommandExecutorWithVolumes)
+// This test verifies that a volume with a relative host path is
+// properly created in the container's sandbox and is properly mounted
+// in the container's mount namespace.
+TEST_F(LinuxFilesystemIsolatorTest, ROOT_VolumeFromSandbox)
{
- Try<Owned<cluster::Master>> master = StartMaster();
- ASSERT_SOME(master);
-
slave::Flags flags = CreateSlaveFlags();
- flags.image_provisioner_backend = "copy";
+
+ ContainerID containerId;
+ containerId.set_value(UUID::random().toString());
Try<Owned<MesosContainerizer>> containerizer = createContainerizer(
flags,
@@ -373,104 +349,109 @@ TEST_F(LinuxFilesystemIsolatorTest,
ASSERT_SOME(containerizer);
- Owned<MasterDetector> detector = master.get()->createDetector();
+ ExecutorInfo executor = createExecutorInfo(
+ "test_executor",
+ "echo abc > /tmp/file");
- Try<Owned<cluster::Slave>> slave =
- StartSlave(detector.get(), containerizer.get().get(), flags);
- ASSERT_SOME(slave);
+ executor.mutable_container()->CopyFrom(createContainerInfo(
+ "test_image",
+ {createVolumeFromHostPath("/tmp", "tmp", Volume::RW)}));
- MockScheduler sched;
+ string directory = path::join(os::getcwd(), "sandbox");
+ ASSERT_SOME(os::mkdir(directory));
- MesosSchedulerDriver driver(
- &sched,
- DEFAULT_FRAMEWORK_INFO,
- master.get()->pid,
- DEFAULT_CREDENTIAL);
+ Future<bool> launch = containerizer.get()->launch(
+ containerId,
+ None(),
+ executor,
+ directory,
+ None(),
+ SlaveID(),
+ map<string, string>(),
+ false);
- Future<FrameworkID> frameworkId;
- EXPECT_CALL(sched, registered(&driver, _, _))
- .WillOnce(FutureArg<1>(&frameworkId));
+ // Wait for the launch to complete.
+ // Need to wait for Rootfs copying.
+ AWAIT_READY_FOR(launch, Seconds(60));
- Future<vector<Offer>> offers;
- EXPECT_CALL(sched, resourceOffers(&driver, _))
- .WillOnce(FutureArg<1>(&offers))
- .WillRepeatedly(Return()); // Ignore subsequent offers.
+ // Wait on the container.
+ Future<Option<ContainerTermination>> wait =
+ containerizer.get()->wait(containerId);
- driver.start();
+ AWAIT_READY(wait);
+ ASSERT_SOME(wait.get());
- AWAIT_READY(frameworkId);
+ // Check the executor exited correctly.
+ EXPECT_TRUE(wait->get().has_status());
+ EXPECT_EQ(0, wait->get().status());
- AWAIT_READY(offers);
- ASSERT_NE(0u, offers->size());
+ EXPECT_SOME_EQ("abc\n", os::read(path::join(directory, "tmp", "file")));
+}
- const Offer& offer = offers.get()[0];
- // Preparing two volumes:
- // - host_path: dir1, container_path: /tmp
- // - host_path: dir2, container_path: relative_dir
- const string dir1 = path::join(os::getcwd(), "dir1");
- ASSERT_SOME(os::mkdir(dir1));
+// This test verifies that a volume with an absolute host path as
+// well as an absolute container path is properly mounted in the
+// container's mount namespace.
+TEST_F(LinuxFilesystemIsolatorTest, ROOT_VolumeFromHost)
+{
+ slave::Flags flags = CreateSlaveFlags();
- const string testFile = path::join(dir1, "testfile");
- ASSERT_SOME(os::touch(testFile));
+ ContainerID containerId;
+ containerId.set_value(UUID::random().toString());
- const string dir2 = path::join(os::getcwd(), "dir2");
- ASSERT_SOME(os::mkdir(dir2));
+ Try<Owned<MesosContainerizer>> containerizer = createContainerizer(
+ flags,
+ {{"test_image", path::join(os::getcwd(), "test_image")}});
- TaskInfo task = createTask(
- offer.slave_id(),
- offer.resources(),
- "test -f /tmp/testfile && test -d " +
- path::join(flags.sandbox_directory, "relative_dir"));
+ ASSERT_SOME(containerizer);
- ContainerInfo containerInfo;
- Image* image = containerInfo.mutable_mesos()->mutable_image();
- image->set_type(Image::APPC);
- image->mutable_appc()->set_name("test_image");
- containerInfo.set_type(ContainerInfo::MESOS);
+ ExecutorInfo executor = createExecutorInfo(
+ "test_executor",
+ "test -d /tmp/sandbox");
- // We are assuming the image created by the tests have /tmp to be
- // able to mount the directory.
- containerInfo.add_volumes()->CopyFrom(
- createVolumeFromHostPath("/tmp", dir1, Volume::RW));
- containerInfo.add_volumes()->CopyFrom(
- createVolumeFromHostPath("relative_dir", dir2, Volume::RW));
- task.mutable_container()->CopyFrom(containerInfo);
+ executor.mutable_container()->CopyFrom(createContainerInfo(
+ "test_image",
+ {createVolumeFromHostPath("/tmp", os::getcwd(), Volume::RW)}));
- driver.launchTasks(offer.id(), {task});
+ string directory = path::join(os::getcwd(), "sandbox");
+ ASSERT_SOME(os::mkdir(directory));
- Future<TaskStatus> statusRunning;
- Future<TaskStatus> statusFinished;
+ Future<bool> launch = containerizer.get()->launch(
+ containerId,
+ None(),
+ executor,
+ directory,
+ None(),
+ SlaveID(),
+ map<string, string>(),
+ false);
- EXPECT_CALL(sched, statusUpdate(&driver, _))
- .WillOnce(FutureArg<1>(&statusRunning))
- .WillOnce(FutureArg<1>(&statusFinished));
+ // Wait for the launch to complete.
+ // Need to wait for Rootfs copying.
+ AWAIT_READY_FOR(launch, Seconds(60));
- AWAIT_READY(statusRunning);
- EXPECT_EQ(TASK_RUNNING, statusRunning->state());
- AWAIT_READY(statusFinished);
- EXPECT_EQ(TASK_FINISHED, statusFinished->state());
+ // Wait on the container.
+ Future<Option<ContainerTermination>> wait =
+ containerizer.get()->wait(containerId);
- driver.stop();
- driver.join();
+ AWAIT_READY(wait);
+ ASSERT_SOME(wait.get());
+
+ // Check the executor exited correctly.
+ EXPECT_TRUE(wait->get().has_status());
+ EXPECT_EQ(0, wait->get().status());
}
-// This test verifies that a command task with new root filesystem
-// with persistent volumes works correctly.
-TEST_F(LinuxFilesystemIsolatorTest,
- ROOT_ChangeRootFilesystemCommandExecutorPersistentVolume)
+// This test verifies that a file volume with an absolute host
+// path as well as an absolute container path is properly mounted
+// in the container's mount namespace.
+TEST_F(LinuxFilesystemIsolatorTest, ROOT_FileVolumeFromHost)
{
- Try<Owned<cluster::Master>> master = StartMaster();
- ASSERT_SOME(master);
-
slave::Flags flags = CreateSlaveFlags();
- flags.image_provisioner_backend = "copy";
- flags.resources = "cpus:2;mem:1024;disk(role1):1024";
- // Need this otherwise the persistent volumes are not created
- // within the slave work_dir and thus not retrievable.
- flags.work_dir = os::getcwd();
+ ContainerID containerId;
+ containerId.set_value(UUID::random().toString());
Try<Owned<MesosContainerizer>> containerizer = createContainerizer(
flags,
@@ -478,131 +459,107 @@ TEST_F(LinuxFilesystemIsolatorTest,
ASSERT_SOME(containerizer);
- Owned<MasterDetector> detector = master.get()->createDetector();
-
- Try<Owned<cluster::Slave>> slave =
- StartSlave(detector.get(), containerizer.get().get(), flags);
- ASSERT_SOME(slave);
-
- MockScheduler sched;
- FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO;
- frameworkInfo.set_role("role1");
-
- MesosSchedulerDriver driver(
- &sched,
- frameworkInfo,
- master.get()->pid,
- DEFAULT_CREDENTIAL);
+ ExecutorInfo executor = createExecutorInfo(
+ "test_executor",
+ "test -f /tmp/test/file.txt");
- Future<FrameworkID> frameworkId;
- EXPECT_CALL(sched, registered(&driver, _, _))
- .WillOnce(FutureArg<1>(&frameworkId));
+ const string file = path::join(os::getcwd(), "file");
+ ASSERT_SOME(os::touch(file));
- Future<vector<Offer>> offers;
- EXPECT_CALL(sched, resourceOffers(&driver, _))
- .WillOnce(FutureArg<1>(&offers))
- .WillRepeatedly(Return()); // Ignore subsequent offers.
+ executor.mutable_container()->CopyFrom(createContainerInfo(
+ "test_image",
+ {createVolumeFromHostPath("/tmp/test/file.txt", file, Volume::RW)}));
- driver.start();
+ const string directory = path::join(os::getcwd(), "sandbox");
+ ASSERT_SOME(os::mkdir(directory));
- AWAIT_READY(frameworkId);
+ Future<bool> launch = containerizer.get()->launch(
+ containerId,
+ None(),
+ executor,
+ directory,
+ None(),
+ SlaveID(),
+ map<string, string>(),
+ false);
- AWAIT_READY(offers);
- ASSERT_NE(0u, offers->size());
+ AWAIT_READY_FOR(launch, Seconds(60));
- Offer offer = offers.get()[0];
+ Future<Option<ContainerTermination>> wait =
+ containerizer.get()->wait(containerId);
- const string dir1 = path::join(os::getcwd(), "dir1");
- ASSERT_SOME(os::mkdir(dir1));
+ AWAIT_READY(wait);
+ ASSERT_SOME(wait.get());
- Resource persistentVolume = createPersistentVolume(
- Megabytes(64),
- "role1",
- "id1",
- "path1",
- None(),
- None(),
- frameworkInfo.principal());
+ // Check the executor exited correctly.
+ EXPECT_TRUE(wait->get().has_status());
+ EXPECT_EQ(0, wait->get().status());
+}
- // We use the filter explicitly here so that the resources will not
- // be filtered for 5 seconds (the default).
- Filters filters;
- filters.set_refuse_seconds(0);
- TaskInfo task = createTask(
- offer.slave_id(),
- Resources::parse("cpus:1;mem:512").get() + persistentVolume,
- "echo abc > path1/file");
- ContainerInfo containerInfo;
- Image* image = containerInfo.mutable_mesos()->mutable_image();
- image->set_type(Image::APPC);
- image->mutable_appc()->set_name("test_image");
- containerInfo.set_type(ContainerInfo::MESOS);
+// This test verifies that a volume with an absolute host path and a
+// relative container path is properly mounted in the container's
+// mount namespace. The mount point will be created in the sandbox.
+TEST_F(LinuxFilesystemIsolatorTest, ROOT_VolumeFromHostSandboxMountPoint)
+{
+ slave::Flags flags = CreateSlaveFlags();
- // We are assuming the image created by the tests have /tmp to be
- // able to mount the directory.
- containerInfo.add_volumes()->CopyFrom(
- createVolumeFromHostPath("/tmp", dir1, Volume::RW));
- task.mutable_container()->CopyFrom(containerInfo);
+ ContainerID containerId;
+ containerId.set_value(UUID::random().toString());
- // Create the persistent volumes and launch task via `acceptOffers`.
- driver.acceptOffers(
- {offer.id()},
- {CREATE(persistentVolume), LAUNCH({task})},
- filters);
+ Try<Owned<MesosContainerizer>> containerizer = createContainerizer(
+ flags,
+ {{"test_image", path::join(os::getcwd(), "test_image")}});
- Future<TaskStatus> statusRunning;
- Future<TaskStatus> statusFinished;
+ ASSERT_SOME(containerizer);
- EXPECT_CALL(sched, statusUpdate(&driver, _))
- .WillOnce(FutureArg<1>(&statusRunning))
- .WillOnce(FutureArg<1>(&statusFinished));
+ ExecutorInfo executor = createExecutorInfo(
+ "test_executor",
+ "test -d mountpoint/sandbox");
- AWAIT_READY(statusRunning);
- EXPECT_EQ(TASK_RUNNING, statusRunning->state());
- AWAIT_READY(statusFinished);
- EXPECT_EQ(TASK_FINISHED, statusFinished->state());
+ executor.mutable_container()->CopyFrom(createContainerInfo(
+ "test_image",
+ {createVolumeFromHostPath("mountpoint", os::getcwd(), Volume::RW)}));
- // NOTE: The command executor's id is the same as the task id.
- ExecutorID executorId;
- executorId.set_value(task.task_id().value());
+ string directory = path::join(os::getcwd(), "sandbox");
+ ASSERT_SOME(os::mkdir(directory));
- const string& directory = slave::paths::getExecutorLatestRunPath(
- flags.work_dir,
- offer.slave_id(),
- frameworkId.get(),
- executorId);
+ Future<bool> launch = containerizer.get()->launch(
+ containerId,
+ None(),
+ executor,
+ directory,
+ None(),
+ SlaveID(),
+ map<string, string>(),
+ false);
- EXPECT_FALSE(os::exists(path::join(directory, "path1")));
+ // Wait for the launch to complete.
+ // Need to wait for Rootfs copying.
+ AWAIT_READY_FOR(launch, Seconds(60));
- const string& volumePath = slave::paths::getPersistentVolumePath(
- flags.work_dir,
- "role1",
- "id1");
+ // Wait on the container.
+ Future<Option<ContainerTermination>> wait =
+ containerizer.get()->wait(containerId);
- EXPECT_SOME_EQ("abc\n", os::read(path::join(volumePath, "file")));
+ AWAIT_READY(wait);
+ ASSERT_SOME(wait.get());
- driver.stop();
- driver.join();
+ // Check the executor exited correctly.
+ EXPECT_TRUE(wait->get().has_status());
+ EXPECT_EQ(0, wait->get().status());
}
-// This test verifies that persistent volumes are unmounted properly
-// after a checkpointed framework disappears and the slave restarts.
-//
-// TODO(jieyu): Even though the command task specifies a new
-// filesystem root, the executor (command executor) itself does not
-// change filesystem root (uses the host filesystem). We need to add a
-// test to test the scenario that the executor itself changes rootfs.
-TEST_F(LinuxFilesystemIsolatorTest, ROOT_RecoverOrphanedPersistentVolume)
+// This test verifies that a file volume with an absolute host path
+// and a relative container path is properly mounted in the container's
+// mount namespace. The mount point will be created in the sandbox.
+TEST_F(LinuxFilesystemIsolatorTest, ROOT_FileVolumeFromHostSandboxMountPoint)
{
- Try<Owned<cluster::Master>> master = StartMaster();
- ASSERT_SOME(master);
-
slave::Flags flags = CreateSlaveFlags();
- flags.image_provisioner_backend = "copy";
- flags.resources = "cpus:2;mem:1024;disk(role1):1024";
- flags.isolation = "disk/du,filesystem/linux";
+
+ ContainerID containerId;
+ containerId.set_value(UUID::random().toString());
Try<Owned<MesosContainerizer>> containerizer = createContainerizer(
flags,
@@ -610,140 +567,53 @@ TEST_F(LinuxFilesystemIsolatorTest, ROOT_RecoverOrphanedPersistentVolume)
ASSERT_SOME(containerizer);
- Owned<MasterDetector> detector = master.get()->createDetector();
+ ExecutorInfo executor = createExecutorInfo(
+ "test_executor",
+ "test -f mountpoint/file.txt");
- Try<Owned<cluster::Slave>> slave =
- StartSlave(detector.get(), containerizer.get().get(), flags);
- ASSERT_SOME(slave);
+ const string file = path::join(os::getcwd(), "file");
+ ASSERT_SOME(os::touch(file));
- MockScheduler sched;
- FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO;
- frameworkInfo.set_role("role1");
- frameworkInfo.set_checkpoint(true);
+ executor.mutable_container()->CopyFrom(createContainerInfo(
+ "test_image",
+ {createVolumeFromHostPath("mountpoint/file.txt", file, Volume::RW)}));
- MesosSchedulerDriver driver(
- &sched,
- frameworkInfo,
- master.get()->pid,
- DEFAULT_CREDENTIAL);
+ const string directory = path::join(os::getcwd(), "sandbox");
+ ASSERT_SOME(os::mkdir(directory));
- Future<FrameworkID> frameworkId;
- EXPECT_CALL(sched, registered(&driver, _, _))
- .WillOnce(FutureArg<1>(&frameworkId));
+ Future<bool> launch = containerizer.get()->launch(
+ containerId,
+ None(),
+ executor,
+ directory,
+ None(),
+ SlaveID(),
+ map<string, string>(),
+ false);
- Future<vector<Offer>> offers;
- EXPECT_CALL(sched, resourceOffers(&driver, _))
- .WillOnce(FutureArg<1>(&offers))
- .WillRepeatedly(Return()); // Ignore subsequent offers.
+ AWAIT_READY_FOR(launch, Seconds(60));
- driver.start();
+ Future<Option<ContainerTermination>> wait =
+ containerizer.get()->wait(containerId);
- AWAIT_READY(frameworkId);
+ AWAIT_READY(wait);
+ ASSERT_SOME(wait.get());
- AWAIT_READY(offers);
- EXPECT_FALSE(offers->empty());
+ // Check the executor exited correctly.
+ EXPECT_TRUE(wait->get().has_status());
+ EXPECT_EQ(0, wait->get().status());
+}
- Offer offer = offers.get()[0];
- const string dir1 = path::join(os::getcwd(), "dir1");
- ASSERT_SOME(os::mkdir(dir1));
+// This test verifies that persistent volumes are properly mounted in
+// the container's root filesystem.
+TEST_F(LinuxFilesystemIsolatorTest, ROOT_PersistentVolumeWithRootFilesystem)
+{
+ slave::Flags flags = CreateSlaveFlags();
- Resource persistentVolume = createPersistentVolume(
- Megabytes(64),
- "role1",
- "id1",
- "path1",
- None(),
- None(),
- frameworkInfo.principal());
-
- // Create a task that does nothing for a long time.
- TaskInfo task = createTask(
- offer.slave_id(),
- Resources::parse("cpus:1;mem:512").get() + persistentVolume,
- "sleep 1000");
-
- ContainerInfo containerInfo;
- Image* image = containerInfo.mutable_mesos()->mutable_image();
- image->set_type(Image::APPC);
- image->mutable_appc()->set_name("test_image");
- containerInfo.set_type(ContainerInfo::MESOS);
-
- // We are assuming the image created by the tests have /tmp to be
- // able to mount the directory.
- containerInfo.add_volumes()->CopyFrom(
- createVolumeFromHostPath("/tmp", dir1, Volume::RW));
- task.mutable_container()->CopyFrom(containerInfo);
-
- Future<TaskStatus> status;
- EXPECT_CALL(sched, statusUpdate(&driver, _))
- .WillOnce(FutureArg<1>(&status))
- .WillRepeatedly(DoDefault());
-
- Future<Nothing> ack =
- FUTURE_DISPATCH(_, &Slave::_statusUpdateAcknowledgement);
-
- // Create the persistent volumes and launch task via `acceptOffers`.
- driver.acceptOffers(
- {offer.id()},
- {CREATE(persistentVolume), LAUNCH({task})});
-
- AWAIT_READY(status);
- EXPECT_EQ(TASK_RUNNING, status.get().state());
-
- // Wait for the ACK to be checkpointed.
- AWAIT_READY(ack);
-
- Future<hashset<ContainerID>> containers = containerizer.get()->containers();
-
- AWAIT_READY(containers);
- EXPECT_EQ(1u, containers.get().size());
-
- ContainerID containerId = *containers.get().begin();
-
- // Restart the slave.
- slave.get()->terminate();
-
- // Wipe the slave meta directory so that the slave will treat the
- // above running task as an orphan.
- ASSERT_SOME(os::rmdir(slave::paths::getMetaRootDir(flags.work_dir)));
-
- // Recreate the containerizer using the same helper as above.
- containerizer = createContainerizer(
- flags,
- {{"test_image", path::join(os::getcwd(), "test_image")}});
-
- slave = StartSlave(detector.get(), containerizer.get().get(), flags);
- ASSERT_SOME(slave);
-
- // Wait until slave recovery is complete.
- Future<Nothing> _recover = FUTURE_DISPATCH(_, &Slave::_recover);
- AWAIT_READY_FOR(_recover, Seconds(60));
-
- // Wait until the orphan containers are cleaned up.
- AWAIT_READY_FOR(containerizer.get()->wait(containerId), Seconds(60));
-
- Try<fs::MountInfoTable> table = fs::MountInfoTable::read();
- ASSERT_SOME(table);
-
- // All mount targets should be under this directory.
- const string directory = slave::paths::getSandboxRootDir(flags.work_dir);
-
- // Verify that the orphaned container's persistent volume and
- // the rootfs are unmounted.
- foreach (const fs::MountInfoTable::Entry& entry, table.get().entries) {
- EXPECT_FALSE(strings::contains(entry.target, directory))
- << "Target was not unmounted: " << entry.target;
- }
-
- driver.stop();
- driver.join();
-}
-
-
-TEST_F(LinuxFilesystemIsolatorTest, ROOT_Metrics)
-{
- slave::Flags flags = CreateSlaveFlags();
+ // Need this otherwise the persistent volumes are not created
+ // within the slave work_dir and thus not retrievable.
+ flags.work_dir = os::getcwd();
ContainerID containerId;
containerId.set_value(UUID::random().toString());
@@ -754,13 +624,26 @@ TEST_F(LinuxFilesystemIsolatorTest, ROOT_Metrics)
ASSERT_SOME(containerizer);
- // Use a long running task so we can reliably capture the moment it's alive.
ExecutorInfo executor = createExecutorInfo(
"test_executor",
- "sleep 1000");
+ "echo abc > volume/file");
+
+ executor.add_resources()->CopyFrom(createPersistentVolume(
+ Megabytes(32),
+ "test_role",
+ "persistent_volume_id",
+ "volume"));
executor.mutable_container()->CopyFrom(createContainerInfo("test_image"));
+ // Create a persistent volume.
+ string volume = slave::paths::getPersistentVolumePath(
+ os::getcwd(),
+ "test_role",
+ "persistent_volume_id");
+
+ ASSERT_SOME(os::mkdir(volume));
+
string directory = path::join(os::getcwd(), "sandbox");
ASSERT_SOME(os::mkdir(directory));
@@ -778,15 +661,6 @@ TEST_F(LinuxFilesystemIsolatorTest, ROOT_Metrics)
// Need to wait for Rootfs copying.
AWAIT_READY_FOR(launch, Seconds(60));
- // Check metrics.
- JSON::Object stats = Metrics();
- EXPECT_EQ(1u, stats.values.count(
- "containerizer/mesos/filesystem/containers_new_rootfs"));
- EXPECT_EQ(
- 1, stats.values["containerizer/mesos/filesystem/containers_new_rootfs"]);
-
- containerizer.get()->destroy(containerId);
-
// Wait on the container.
Future<Option<ContainerTermination>> wait =
containerizer.get()->wait(containerId);
@@ -794,19 +668,22 @@ TEST_F(LinuxFilesystemIsolatorTest, ROOT_Metrics)
AWAIT_READY(wait);
ASSERT_SOME(wait.get());
- // Executor was killed.
+ // Check the executor exited correctly.
EXPECT_TRUE(wait->get().has_status());
- EXPECT_EQ(9, wait->get().status());
+ EXPECT_EQ(0, wait->get().status());
+
+ EXPECT_SOME_EQ("abc\n", os::read(path::join(volume, "file")));
}
-// This test verifies that a volume with a relative host path is
-// properly created in the container's sandbox and is properly mounted
-// in the container's mount namespace.
-TEST_F(LinuxFilesystemIsolatorTest, ROOT_VolumeFromSandbox)
+TEST_F(LinuxFilesystemIsolatorTest, ROOT_PersistentVolumeWithoutRootFilesystem)
{
slave::Flags flags = CreateSlaveFlags();
+ // Need this otherwise the persistent volumes are not created
+ // within the slave work_dir and thus not retrievable.
+ flags.work_dir = os::getcwd();
+
ContainerID containerId;
containerId.set_value(UUID::random().toString());
@@ -818,14 +695,32 @@ TEST_F(LinuxFilesystemIsolatorTest, ROOT_VolumeFromSandbox)
ExecutorInfo executor = createExecutorInfo(
"test_executor",
- "echo abc > /tmp/file");
+ "echo abc > volume/file");
- executor.mutable_container()->CopyFrom(createContainerInfo(
- "test_image",
- {createVolumeFromHostPath("/tmp", "tmp", Volume::RW)}));
+ executor.add_resources()->CopyFrom(createPersistentVolume(
+ Megabytes(32),
+ "test_role",
+ "persistent_volume_id",
+ "volume"));
- string directory = path::join(os::getcwd(), "sandbox");
- ASSERT_SOME(os::mkdir(directory));
+ executor.mutable_container()->CopyFrom(createContainerInfo());
+
+ // Create a persistent volume.
+ string volume = slave::paths::getPersistentVolumePath(
+ os::getcwd(),
+ "test_role",
+ "persistent_volume_id");
+
+ ASSERT_SOME(os::mkdir(volume));
+
+ // To make sure the sandbox directory has the container ID in its
+ // path so it doesn't get unmounted by the launcher.
+ string directory = slave::paths::createExecutorDirectory(
+ flags.work_dir,
+ SlaveID(),
+ FrameworkID(),
+ executor.executor_id(),
+ containerId);
Future<bool> launch = containerizer.get()->launch(
containerId,
@@ -852,14 +747,14 @@ TEST_F(LinuxFilesystemIsolatorTest, ROOT_VolumeFromSandbox)
EXPECT_TRUE(wait->get().has_status());
EXPECT_EQ(0, wait->get().status());
- EXPECT_SOME_EQ("abc\n", os::read(path::join(directory, "tmp", "file")));
+ EXPECT_SOME_EQ("abc\n", os::read(path::join(volume, "file")));
}
-// This test verifies that a volume with an absolute host path as
-// well as an absolute container path is properly mounted in the
-// container's mount namespace.
-TEST_F(LinuxFilesystemIsolatorTest, ROOT_VolumeFromHost)
+// 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_F(LinuxFilesystemIsolatorTest, ROOT_ImageInVolumeWithoutRootFilesystem)
{
slave::Flags flags = CreateSlaveFlags();
@@ -874,11 +769,11 @@ TEST_F(LinuxFilesystemIsolatorTest, ROOT_VolumeFromHost)
ExecutorInfo executor = createExecutorInfo(
"test_executor",
- "test -d /tmp/sandbox");
+ "test -d rootfs/bin");
executor.mutable_container()->CopyFrom(createContainerInfo(
- "test_image",
- {createVolumeFromHostPath("/tmp", os::getcwd(), Volume::RW)}));
+ None(),
+ {createVolumeFromAppcImage("rootfs", "test_image", Volume::RW)}));
string directory = path::join(os::getcwd(), "sandbox");
ASSERT_SOME(os::mkdir(directory));
@@ -910,34 +805,33 @@ TEST_F(LinuxFilesystemIsolatorTest, ROOT_VolumeFromHost)
}
-// This test verifies that a file volume with an absolute host
-// path as well as an absolute container path is properly mounted
-// in the container's mount namespace.
-TEST_F(LinuxFilesystemIsolatorTest, ROOT_FileVolumeFromHost)
+// This test verifies that the image specified in the volume will be
+// properly provisioned and mounted into the container if container
+// root filesystem is specified.
+TEST_F(LinuxFilesystemIsolatorTest, ROOT_ImageInVolumeWithRootFilesystem)
{
slave::Flags flags = CreateSlaveFlags();
ContainerID containerId;
containerId.set_value(UUID::random().toString());
- Try<Owned<MesosContainerizer>> containerizer = createContainerizer(
- flags,
- {{"test_image", path::join(os::getcwd(), "test_image")}});
+ Try<Owned<MesosContainerizer>> containerizer =
+ createContainerizer(
+ flags,
+ {{"test_image_rootfs", path::join(os::getcwd(), "test_image_rootfs")},
+ {"test_image_volume", path::join(os::getcwd(), "test_image_volume")}});
ASSERT_SOME(containerizer);
ExecutorInfo executor = createExecutorInfo(
"test_executor",
- "test -f /tmp/test/file.txt");
-
- const string file = path::join(os::getcwd(), "file");
- ASSERT_SOME(os::touch(file));
+ "[ ! -d '" + os::getcwd() + "' ] && [ -d rootfs/bin ]");
executor.mutable_container()->CopyFrom(createContainerInfo(
- "test_image",
- {createVolumeFromHostPath("/tmp/test/file.txt", file, Volume::RW)}));
+ "test_image_rootfs",
+ {createVolumeFromAppcImage("rootfs", "test_image_volume", Volume::RW)}));
- const string directory = path::join(os::getcwd(), "sandbox");
+ string directory = path::join(os::getcwd(), "sandbox");
ASSERT_SOME(os::mkdir(directory));
Future<bool> launch = containerizer.get()->launch(
@@ -950,12 +844,16 @@ TEST_F(LinuxFilesystemIsolatorTest, ROOT_FileVolumeFromHost)
map<string, string>(),
false);
- AWAIT_READY_FOR(launch, Seconds(60));
+ // Wait for the launch to complete.
+ // Need to wait for Rootfs copy.
+ AWAIT_READY_FOR(launch, Seconds(240));
+ // Wait on the container.
Future<Option<ContainerTermination>> wait =
containerizer.get()->wait(containerId);
- AWAIT_READY(wait);
+ // Because destroy rootfs spents a lot of time, we use 30s as timeout here.
+ AWAIT_READY_FOR(wait, Seconds(30));
ASSERT_SOME(wait.get());
// Check the executor exited correctly.
@@ -964,64 +862,134 @@ TEST_F(LinuxFilesystemIsolatorTest, ROOT_FileVolumeFromHost)
}
-// This test verifies that a volume with an absolute host path and a
-// relative container path is properly mounted in the container's
-// mount namespace. The mount point will be created in the sandbox.
-TEST_F(LinuxFilesystemIsolatorTest, ROOT_VolumeFromHostSandboxMountPoint)
+// This test verifies that multiple containers with images can be
+// launched simultaneously with no interference.
+TEST_F(LinuxFilesystemIsolatorTest, ROOT_MultipleContainers)
{
slave::Flags flags = CreateSlaveFlags();
- ContainerID containerId;
- containerId.set_value(UUID::random().toString());
+ // Need this otherwise the persistent volumes are not created
+ // within the slave work_dir and thus not retrievable.
+ flags.work_dir = os::getcwd();
- Try<Owned<MesosContainerizer>> containerizer = createContainerizer(
- flags,
- {{"test_image", path::join(os::getcwd(), "test_image")}});
+ ContainerID containerId1;
+ containerId1.set_value(UUID::random().toString());
+
+ ContainerID containerId2;
+ containerId2.set_value(UUID::random().toString());
+
+ Try<Owned<MesosContainerizer>> containerizer =
+ createContainerizer(
+ flags,
+ {{"test_image1", path::join(os::getcwd(), "test_image1")},
+ {"test_image2", path::join(os::getcwd(), "test_image2")}});
ASSERT_SOME(containerizer);
- ExecutorInfo executor = createExecutorInfo(
- "test_executor",
- "test -d mountpoint/sandbox");
+ SlaveID slaveId;
+ slaveId.set_value("test_agent");
- executor.mutable_container()->CopyFrom(createContainerInfo(
- "test_image",
- {createVolumeFromHostPath("mountpoint", os::getcwd(), Volume::RW)}));
+ // First launch container 1 which has a long running task which
+ // guarantees that its work directory mount is in the host mount
+ // table when container 2 is launched.
+ ExecutorInfo executor1 = createExecutorInfo(
+ "test_executor1",
+ "sleep 1000"); // Long running task.
- string directory = path::join(os::getcwd(), "sandbox");
- ASSERT_SOME(os::mkdir(directory));
+ executor1.mutable_container()->CopyFrom(createContainerInfo("test_image1"));
- Future<bool> launch = containerizer.get()->launch(
- containerId,
- None(),
- executor,
- directory,
- None(),
- SlaveID(),
+ // Create a persistent volume for container 1. We do this because
+ // we want to test container 2 cleaning up multiple mounts.
+ executor1.add_resources()->CopyFrom(createPersistentVolume(
+ Megabytes(32),
+ "test_role",
+ "persistent_volume_id",
+ "volume"));
+
+ string volume = slave::paths::getPersistentVolumePath(
+ os::getcwd(),
+ "test_role",
+ "persistent_volume_id");
+
+ ASSERT_SOME(os::mkdir(volume));
+
+ string directory1 = slave::paths::createExecutorDirectory(
+ flags.work_dir,
+ slaveId,
+ DEFAULT_FRAMEWORK_INFO.id(),
+ executor1.executor_id(),
+ containerId1);
+
+ Future<bool> launch1 = containerizer.get()->launch(
+ containerId1,
+ None(),
+ executor1,
+ directory1,
+ None(),
+ slaveId,
map<string, string>(),
false);
// Wait for the launch to complete.
- // Need to wait for Rootfs copying.
- AWAIT_READY_FOR(launch, Seconds(60));
+ // Need to wait for Rootfs copy.
+ AWAIT_READY_FOR(launch1, Seconds(120));
- // Wait on the container.
- Future<Option<ContainerTermination>> wait =
- containerizer.get()->wait(containerId);
+ // Now launch container 2 which will copy the host mount table with
+ // container 1's work directory mount in it.
+ ExecutorInfo executor2 = createExecutorInfo(
+ "test_executor2",
+ "[ ! -d '" + os::getcwd() + "' ]");
- AWAIT_READY(wait);
- ASSERT_SOME(wait.get());
+ executor2.mutable_container()->CopyFrom(createContainerInfo("test_image2"));
- // Check the executor exited correctly.
- EXPECT_TRUE(wait->get().has_status());
- EXPECT_EQ(0, wait->get().status());
+ string directory2 = slave::paths::createExecutorDirectory(
+ flags.work_dir,
+ slaveId,
+ DEFAULT_FRAMEWORK_INFO.id(),
+ executor2.executor_id(),
+ containerId2);
+
+ Future<bool> launch2 = containerizer.get()->launch(
+ containerId2,
+ None(),
+ executor2,
+ directory2,
+ None(),
+ slaveId,
+ map<string, string>(),
+ false);
+
+ // Need to wait for Rootfs copy.
+ AWAIT_READY_FOR(launch1, Seconds(60));
+
+ containerizer.get()->destroy(containerId1);
+
+ // Wait on the containers.
+ Future<Option<ContainerTermination>> wait1 =
+ containerizer.get()->wait(containerId1);
+
+ Future<Option<ContainerTermination>> wait2 =
+ containerizer.get()->wait(containerId2);
+
+ AWAIT_READY_FOR(wait1, Seconds(60));
+ ASSERT_SOME(wait1.get());
+
+ AWAIT_READY_FOR(wait2, Seconds(60));
+ ASSERT_SOME(wait2.get());
+
+ // Executor 1 was forcefully killed.
+ EXPECT_TRUE(wait1->get().has_status());
+ EXPECT_EQ(9, wait1->get().status());
+
+ // Executor 2 exited normally.
+ EXPECT_TRUE(wait2->get().has_status());
+ EXPECT_EQ(0, wait2->get().status());
}
-// This test verifies that a file volume with an absolute host path
-// and a relative container path is properly mounted in the container's
-// mount namespace. The mount point will be created in the sandbox.
-TEST_F(LinuxFilesystemIsolatorTest, ROOT_FileVolumeFromHostSandboxMountPoint)
+// This test verifies that the environment variables for sandbox
+// (i.e., MESOS_DIRECTORY and MESOS_SANDBOX) are set properly.
+TEST_F(LinuxFilesystemIsolatorTest, ROOT_SandboxEnvironmentVariable)
{
slave::Flags flags = CreateSlaveFlags();
@@ -1034,19 +1002,30 @@ TEST_F(LinuxFilesystemIsolatorTest, ROOT_FileVolumeFromHostSandboxMountPoint)
ASSERT_SOME(containerizer);
+ string directory = path::join(os::getcwd(), "sandbox");
+ ASSERT_SOME(os::mkdir(directory));
+
+ Try<string> script = strings::format(
+ "if [ \"$MESOS_DIRECTORY\" != \"%s\" ]; then exit 1; fi &&"
+ "if [ \"$MESOS_SANDBOX\" != \"%s\" ]; then exit 1; fi",
+ directory,
+ flags.sandbox_directory);
+
+ ASSERT_SOME(script);
+
ExecutorInfo executor = createExecutorInfo(
"test_executor",
- "test -f mountpoint/file.txt");
-
- const string file = path::join(os::getcwd(), "file");
- ASSERT_SOME(os::touch(file));
+ script.get());
- executor.mutable_container()->CopyFrom(createContainerInfo(
- "test_image",
- {createVolumeFromHostPath("mountpoint/file.txt", file, Volume::RW)}));
+ executor.mutable_container()->CopyFrom(createContainerInfo("test_image"));
- const string directory = path::join(os::getcwd(), "sandbox");
- ASSERT_SOME(os::mkdir(directory));
+ map<string, string> environment = executorEnvironment(
+ flags,
+ executor,
+ directory,
+ SlaveID(),
+ PID<Slave>(),
+ false);
Future<bool> launch = containerizer.get()->launch(
containerId,
@@ -1055,11 +1034,14 @@ TEST_F(LinuxFilesystemIsolatorTest, ROOT_FileVolumeFromHostSandboxMountPoint)
directory,
None(),
SlaveID(),
- map<string, string>(),
+ environment,
false);
+ // Wait for the launch to complete.
+ // Need to wait for Rootfs copy.
AWAIT_READY_FOR(launch, Seconds(60));
+ // Wait on the container.
Future<Option<ContainerTermination>> wait =
containerizer.get()->wait(containerId);
@@ -1072,87 +1054,95 @@ TEST_F(LinuxFilesystemIsolatorTest, ROOT_FileVolumeFromHostSandboxMountPoint)
}
-// This test verifies that persistent volumes are properly mounted in
-// the container's root filesystem.
-TEST_F(LinuxFilesystemIsolatorTest, ROOT_PersistentVolumeWithRootFilesystem)
+// This test verifies the case where we don't need a bind mount for
+// slave's working directory because the mount containing it is
+// already a shared mount in its own peer group.
+TEST_F(LinuxFilesystemIsolatorTest, ROOT_WorkDirMountNotNeeded)
{
- slave::Flags flags = CreateSlaveFlags();
+ // Make 'sandbox' a shared mount in its own peer group.
+ ASSERT_SOME(os::shell(
+ "mount --bind %s %s && "
+ "mount --make-private %s &&"
+ "mount --make-shared %s",
+ sandbox->c_str(),
+ sandbox->c_str(),
+ sandbox->c_str(),
+ sandbox->c_str()));
- // Need this otherwise the persistent volumes are not created
- // within the slave work_dir and thus not retrievable.
- flags.work_dir = os::getcwd();
+ // Slave's working directory is under 'sandbox'.
+ slave::Flags flags = CreateSlaveFlags();
+ flags.work_dir = path::join(sandbox.get(), "slave");
- ContainerID containerId;
- containerId.set_value(UUID::random().toString());
+ ASSERT_SOME(os::mkdir(flags.work_dir));
- Try<Owned<MesosContainerizer>> containerizer = createContainerizer(
- flags,
- {{"test_image", path::join(os::getcwd(), "test_image")}});
+ Try<Isolator*> isolator = LinuxFilesystemIsolatorProcess::create(flags);
+ ASSERT_SOME(isolator);
- ASSERT_SOME(containerizer);
+ Try<fs::MountInfoTable> table = fs::MountInfoTable::read();
+ ASSERT_SOME(table);
- ExecutorInfo executor = createExecutorInfo(
- "test_executor",
- "echo abc > volume/file");
+ // Verifies that there's no mount for slave's working directory.
+ bool mountFound = false;
+ foreach (const fs::MountInfoTable::Entry& entry, table->entries) {
+ if (entry.target == flags.work_dir) {
+ mountFound = true;
+ }
+ }
- executor.add_resources()->CopyFrom(createPersistentVolume(
- Megabytes(32),
- "test_role",
- "persistent_volume_id",
- "volume"));
+ EXPECT_FALSE(mountFound);
- executor.mutable_container()->CopyFrom(createContainerInfo("test_image"));
+ delete isolator.get();
+}
- // Create a persistent volume.
- string volume = slave::paths::getPersistentVolumePath(
- os::getcwd(),
- "test_role",
- "persistent_volume_id");
- ASSERT_SOME(os::mkdir(volume));
+// This test verifies the case where we do need a bind mount for
+// slave's working directory because the mount containing it is not a
+// shared mount in its own peer group.
+TEST_F(LinuxFilesystemIsolatorTest, ROOT_WorkDirMountNeeded)
+{
+ // Make 'sandbox' a private mount.
+ ASSERT_SOME(os::shell(
+ "mount --bind %s %s && "
+ "mount --make-private %s",
+ sandbox->c_str(),
+ sandbox->c_str(),
+ sandbox->c_str()));
- string directory = path::join(os::getcwd(), "sandbox");
- ASSERT_SOME(os::mkdir(directory));
+ slave::Flags flags = CreateSlaveFlags();
+ flags.work_dir = path::join(sandbox.get(), "slave");
- Future<bool> launch = containerizer.get()->launch(
- containerId,
- None(),
- executor,
- directory,
- None(),
- SlaveID(),
- map<string, string>(),
- false);
+ ASSERT_SOME(os::mkdir(flags.work_dir));
- // Wait for the launch to complete.
- // Need to wait for Rootfs copying.
- AWAIT_READY_FOR(launch, Seconds(60));
+ Try<Isolator*> isolator = LinuxFilesystemIsolatorProcess::create(flags);
+ ASSERT_SOME(isolator);
- // Wait on the container.
- Future<Option<ContainerTermination>> wait =
- containerizer.get()->wait(containerId);
+ Try<fs::MountInfoTable> table = fs::MountInfoTable::read();
+ ASSERT_SOME(table);
- AWAIT_READY(wait);
- ASSERT_SOME(wait.get());
+ bool mountFound = false;
+ foreach (const fs::MountInfoTable::Entry& entry, table->entries) {
+ if (entry.target == flags.work_dir) {
+ EXPECT_SOME(entry.shared());
+ mountFound = true;
+ }
+ }
- // Check the executor exited correctly.
- EXPECT_TRUE(wait->get().has_status());
- EXPECT_EQ(0, wait->get().status());
+ EXPECT_TRUE(mountFound);
- EXPECT_SOME_EQ("abc\n", os::read(path::join(volume, "file")));
+ delete isolator.get();
}
-TEST_F(LinuxFilesystemIsolatorTest, ROOT_PersistentVolumeWithoutRootFilesystem)
+// This test verifies that the root filesystem of the container is
+// properly changed to the one that's provisioned by the provisioner.
+// Also runs the command executor with the new root filesystem.
+TEST_F(LinuxFilesystemIsolatorTest, ROOT_ChangeRootFilesystemCommandExecutor)
{
- slave::Flags flags = CreateSlaveFlags();
-
- // Need this otherwise the persistent volumes are not created
- // within the slave work_dir and thus not retrievable.
- flags.work_dir = os::getcwd();
+ Try<Owned<cluster::Master>> master = StartMaster();
+ ASSERT_SOME(master);
- ContainerID containerId;
- containerId.set_value(UUID::random().toString());
+ slave::Flags flags = CreateSlaveFlags();
+ flags.image_provisioner_backend = "copy";
Try<Owned<MesosContainerizer>> containerizer = createContainerizer(
flags,
@@ -1160,73 +1150,81 @@ TEST_F(LinuxFilesystemIsolatorTest, ROOT_PersistentVolumeWithoutRootFilesystem)
ASSERT_SOME(containerizer);
- ExecutorInfo executor = createExecutorInfo(
- "test_executor",
- "echo abc > volume/file");
+ Owned<MasterDetector> detector = master.get()->createDetector();
- executor.add_resources()->CopyFrom(createPersistentVolume(
- Megabytes(32),
- "test_role",
- "persistent_volume_id",
- "volume"));
+ Try<Owned<cluster::Slave>> slave =
+ StartSlave(detector.get(), containerizer.get().get(), flags);
+ ASSERT_SOME(slave);
- executor.mutable_container()->CopyFrom(createContainerInfo());
+ MockScheduler sched;
- // Create a persistent volume.
- string volume = slave::paths::getPersistentVolumePath(
- os::getcwd(),
- "test_role",
- "persistent_volume_id");
+ MesosSchedulerDriver driver(
+ &sched,
+ DEFAULT_FRAMEWORK_INFO,
+ master.get()->pid,
+ DEFAULT_CREDENTIAL);
- ASSERT_SOME(os::mkdir(volume));
+ Future<FrameworkID> frameworkId;
+ EXPECT_CALL(sched, registered(&driver, _, _))
+ .WillOnce(FutureArg<1>(&frameworkId));
- // To make sure the sandbox directory has the container ID in its
- // path so it doesn't get unmounted by the launcher.
- string directory = slave::paths::createExecutorDirectory(
- flags.work_dir,
- SlaveID(),
- FrameworkID(),
- executor.executor_id(),
- containerId);
+ Future<vector<Offer>> offers;
+ EXPECT_CALL(sched, resourceOffers(&driver, _))
+ .WillOnce(FutureArg<1>(&offers))
+ .WillRepeatedly(Return()); // Ignore subsequent offers.
- Future<bool> launch = containerizer.get()->launch(
- containerId,
- None(),
- executor,
- directory,
- None(),
- SlaveID(),
- map<string, string>(),
- false);
+ driver.start();
- // Wait for the launch to complete.
- // Need to wait for Rootfs copying.
- AWAIT_READY_FOR(launch, Seconds(60));
+ AWAIT_READY(frameworkId);
- // Wait on the container.
- Future<Option<ContainerTermination>> wait =
- containerizer.get()->wait(containerId);
+ AWAIT_READY(offers);
+ ASSERT_NE(0u, offers->size());
- AWAIT_READY(wait);
- ASSERT_SOME(wait.get());
+ const Offer& offer = offers.get()[0];
- // Check the executor exited correctly.
- EXPECT_TRUE(wait->get().has_status());
- EXPECT_EQ(0, wait->get().status());
+ TaskInfo task = createTask(
+ offer.slave_id(),
+ offer.resources(),
+ "test -d " + flags.sandbox_directory);
- EXPECT_SOME_EQ("abc\n", os::read(path::join(volume, "file")));
+ ContainerInfo containerInfo;
+ Image* image = containerInfo.mutable_mesos()->mutable_image();
+ image->set_type(Image::APPC);
+ image->mutable_appc()->set_name("test_image");
+ containerInfo.set_type(ContainerInfo::MESOS);
+ task.mutable_container()->CopyFrom(containerInfo);
+
+ driver.launchTasks(offer.id(), {task});
+
+ Future<TaskStatus> statusRunning;
+ Future<TaskStatus> statusFinished;
+
+ EXPECT_CALL(sched, statusUpdate(&driver, _))
+ .WillOnce(FutureArg<1>(&statusRunning))
+ .WillOnce(FutureArg<1>(&statusFinished));
+
+ // Need to wait for Rootfs copying.
+ AWAIT_READY_FOR(statusRunning, Seconds(60));
+ EXPECT_EQ(TASK_RUNNING, statusRunning->state());
+ AWAIT_READY(statusFinished);
+ EXPECT_EQ(TASK_FINISHED, statusFinished->state());
+
+ driver.stop();
+ driver.join();
}
-// 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_F(LinuxFilesystemIsolatorTest, ROOT_ImageInVolumeWithoutRootFilesystem)
+// This test verifies that the root filesystem of the container is
+// properly changed to the one that's provisioned by the provisioner.
+// Also runs the command executor with the new root filesystem.
+TEST_F(LinuxFilesystemIsolatorTest,
+ ROOT_ChangeRootFilesystemCommandExecutorWithVolumes)
{
- slave::Flags flags = CreateSlaveFlags();
+ Try<Owned<cluster::Master>> master = StartMaster();
+ ASSERT_SOME(master);
- ContainerID containerId;
- containerId.set_value(UUID::random().toString());
+ slave::Flags flags = CreateSlaveFlags();
+ flags.image_provisioner_backend = "copy";
Try<Owned<MesosContainerizer>> containerizer = createContainerizer(
flags,
@@ -1234,234 +1232,236 @@ TEST_F(LinuxFilesystemIsolatorTest, ROOT_ImageInVolumeWithoutRootFilesystem)
ASSERT_SOME(containerizer);
- ExecutorInfo executor = createExecutorInfo(
- "test_executor",
- "test -d rootfs/bin");
+ Owned<MasterDetector> detector = master.get()->createDetector();
- executor.mutable_container()->CopyFrom(createContainerInfo(
- None(),
- {createVolumeFromAppcImage("rootfs", "test_image", Volume::RW)}));
+ Try<Owned<cluster::Slave>> slave =
+ StartSlave(detector.get(), containerizer.get().get(), flags);
+ ASSERT_SOME(slave);
- string directory = path::join(os::getcwd(), "sandbox");
- ASSERT_SOME(os::mkdir(directory));
+ MockScheduler sched;
- Future<bool> launch = containerizer.get()->launch(
- containerId,
- None(),
- executor,
- directory,
- None(),
- SlaveID(),
- map<string, string>(),
- false);
+ MesosSchedulerDriver driver(
+ &sched,
+ DEFAULT_FRAMEWORK_INFO,
+ master.get()->pid,
+ DEFAULT_CREDENTIAL);
- // Wait for the launch to complete.
- // Need to wait for Rootfs copying.
- AWAIT_READY_FOR(launch, Seconds(60));
+ Future<FrameworkID> frameworkId;
+ EXPECT_CALL(sched, registered(&driver, _, _))
+ .WillOnce(FutureArg<1>(&frameworkId));
- // Wait on the container.
- Future<Option<ContainerTermination>> wait =
- containerizer.get()->wait(containerId);
+ Future<vector<Offer>> offers;
+ EXPECT_CALL(sched, resourceOffers(&driver, _))
+ .WillOnce(FutureArg<1>(&offers))
+ .WillRepeatedly(Return()); // Ignore subsequent offers.
- AWAIT_READY(wait);
- ASSERT_SOME(wait.get());
+ driver.start();
- // Check the executor exited correctly.
- EXPECT_TRUE(wait->get().has_status());
- EXPECT_EQ(0, wait->get().status());
-}
+ AWAIT_READY(frameworkId);
+ AWAIT_READY(offers);
+ ASSERT_NE(0u, offers->size());
-// This test verifies that the image specified in the volume will be
-// properly provisioned and mounted into the container if container
-// root filesystem is specified.
-TEST_F(LinuxFilesystemIsolatorTest, ROOT_ImageInVolumeWithRootFilesystem)
-{
- slave::Flags flags = CreateSlaveFlags();
+ const Offer& offer = offers.get()[0];
- ContainerID containerId;
- containerId.set_value(UUID::random().toString());
+ // Preparing two volumes:
+ // - host_path: dir1, container_path: /tmp
+ // - host_path: dir2, container_path: relative_dir
+ const string dir1 = path::join(os::getcwd(), "dir1");
+ ASSERT_SOME(os::mkdir(dir1));
- Try<Owned<MesosContainerizer>> containerizer =
- createContainerizer(
- flags,
- {{"test_image_rootfs", path::join(os::getcwd(), "test_image_rootfs")},
- {"test_image_volume", path::join(os::getcwd(), "test_image_volume")}});
+ const string testFile = path::join(dir1, "testfile");
+ ASSERT_SOME(os::touch(testFile));
- ASSERT_SOME(containerizer);
+ const string dir2 = path::join(os::getcwd(), "dir2");
+ ASSERT_SOME(os::mkdir(dir2));
- ExecutorInfo executor = createExecutorInfo(
- "test_executor",
- "[ ! -d '" + os::getcwd() + "' ] && [ -d rootfs/bin ]");
+ TaskInfo task = createTask(
+ offer.slave_id(),
+ offer.resources(),
+ "test -f /tmp/testfile && test -d " +
+ path::join(flags.sandbox_directory, "relative_dir"));
- executor.mutable_container()->CopyFrom(createContainerInfo(
- "test_image_rootfs",
- {createVolumeFromAppcImage("rootfs", "test_image_volume", Volume::RW)}));
+ ContainerInfo containerInfo;
+ Image* image = containerInfo.mutable_mesos()->mutable_image();
+ image->set_type(Image::APPC);
+ image->mutable_appc()->set_name("test_image");
+ containerInfo.set_type(ContainerInfo::MESOS);
- string directory = path::join(os::getcwd(), "sandbox");
- ASSERT_SOME(os::mkdir(directory));
+ // We are assuming the image created by the tests have /tmp to be
+ // able to mount the directory.
+ containerInfo.add_volumes()->CopyFrom(
+ createVolumeFromHostPath("/tmp", dir1, Volume::RW));
+ containerInfo.add_volumes()->CopyFrom(
+ createVolumeFromHostPath("relative_dir", dir2, Volume::RW));
+ task.mutable_container()->CopyFrom(containerInfo);
- Future<bool> launch = containerizer.get()->launch(
- containerId,
- None(),
- executor,
- directory,
- None(),
- SlaveID(),
- map<string, string>(),
- false);
+ driver.launchTasks(offer.id(), {task});
- // Wait for the launch to complete.
- // Need to wait for Rootfs copy.
- AWAIT_READY_FOR(launch, Seconds(240));
+ Future<TaskStatus> statusRunning;
+ Future<TaskStatus> statusFinished;
- // Wait on the container.
- Future<Option<ContainerTermination>> wait =
- containerizer.get()->wait(containerId);
+ EXPECT_CALL(sched, statusUpdate(&driver, _))
+ .WillOnce(FutureArg<1>(&statusRunning))
+ .WillOnce(FutureArg<1>(&statusFinished));
- // Because destroy rootfs spents a lot of time, we use 30s as timeout here.
- AWAIT_READY_FOR(wait, Seconds(30));
- ASSERT_SOME(wait.get());
+ AWAIT_READY(statusRunning);
+ EXPECT_EQ(TASK_RUNNING, statusRunning->state());
+ AWAIT_READY(statusFinished);
+ EXPECT_EQ(TASK_FINISHED, statusFinished->state());
- // Check the executor exited correctly.
- EXPECT_TRUE(wait->get().has_status());
- EXPECT_EQ(0, wait->get().status());
+ driver.stop();
+ driver.join();
}
-// This test verifies that multiple containers with images can be
-// launched simultaneously with no interference.
-TEST_F(LinuxFilesystemIsolatorTest, ROOT_MultipleContainers)
+// This test verifies that a command task with new root filesystem
+// with persistent volumes works correctly.
+TEST_F(LinuxFilesystemIsolatorTest,
+ ROOT_ChangeRootFilesystemCommandExecutorPersistentVolume)
{
+ Try<Owned<cluster::Master>> master = StartMaster();
+ ASSERT_SOME(master);
+
slave::Flags flags = CreateSlaveFlags();
+ flags.image_provisioner_backend = "copy";
+ flags.resources = "cpus:2;mem:1024;disk(role1):1024";
// Need this otherwise the persistent volumes are not created
// within the slave work_dir and thus not retrievable.
flags.work_dir = os::getcwd();
- ContainerID containerId1;
- containerId1.set_value(UUID::random().toString());
+ Try<Owned<MesosContainerizer>> containerizer = createContainerizer(
+ flags,
+ {{"test_image", path::join(os::getcwd(), "test_image")}});
- ContainerID containerId2;
- containerId2.set_value(UUID::random().toString());
+ ASSERT_SOME(containerizer);
- Try<Owned<MesosContainerizer>> containerizer =
- createContainerizer(
- flags,
- {{"test_image1", path::join(os::getcwd(), "test_image1")},
- {"test_image2", path::join(os::getcwd(), "test_image2")}});
+ Owned<MasterDetector> detector = master.get()->createDetector();
- ASSERT_SOME(containerizer);
+ Try<Owned<cluster::Slave>> slave =
+ StartSlave(detector.get(), containerizer.get().get(), flags);
+ ASSERT_SOME(slave);
- SlaveID slaveId;
- slaveId.set_value("test_agent");
+ MockScheduler sched;
+ FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO;
+ frameworkInfo.set_role("role1");
- // First launch container 1 which has a long running task which
- // guarantees that its work directory mount is in the host mount
- // table when container 2 is launched.
- ExecutorInfo executor1 = createExecutorInfo(
- "test_executor1",
- "sleep 1000"); // Long running task.
+ MesosSchedulerDriver driver(
+ &sched,
+ frameworkInfo,
+ master.get()->pid,
+ DEFAULT_CREDENTIAL);
- executor1.mutable_container()->CopyFrom(createContainerInfo("test_image1"));
+ Future<FrameworkID> frameworkId;
+ EXPECT_CALL(sched, registered(&driver, _, _))
+ .WillOnce(FutureArg<1>(&frameworkId));
- // Create a persistent volume for container 1. We do this because
- // we want to test container 2 cleaning up multiple mounts.
- executor1.add_resources()->CopyFrom(createPersistentVolume(
- Megabytes(32),
- "test_role",
- "persistent_volume_id",
- "volume"));
+ Future<vector<Offer>> offers;
+ EXPECT_CALL(sched, resourceOffers(&driver, _))
+ .WillOnce(FutureArg<1>(&offers))
+ .WillRepeatedly(Return()); // Ignore subsequent offers.
- string volume = slave::paths::getPersistentVolumePath(
- os::getcwd(),
- "test_role",
- "persistent_volume_id");
+ driver.start();
- ASSERT_SOME(os::mkdir(volume));
+ AWAIT_READY(frameworkId);
- string directory1 = slave::paths::createExecutorDirectory(
- flags.work_dir,
- slaveId,
- DEFAULT_FRAMEWORK_INFO.id(),
- executor1.executor_id(),
- containerId1);
+ AWAIT_READY(offers);
+ ASSERT_NE(0u, offers->size());
- Future<bool> launch1 = containerizer.get()->launch(
- containerId1,
+ Offer offer = offers.get()[0];
+
+ const string dir1 = path::join(os::getcwd(), "dir1");
+ ASSERT_SOME(os::mkdir(dir1));
+
+ Resource persistentVolume = createPersistentVolume(
+ Megabytes(64),
+ "role1",
+ "id1",
+ "path1",
None(),
- executor1,
- directory1,
None(),
- slaveId,
- map<string, string>(),
- false);
+ frameworkInfo.principal());
- // Wait for the launch to complete.
- // Need to wait for Rootfs copy.
- AWAIT_READY_FOR(launch1, Seconds(120));
+ // We use the filter explicitly here so that the resources will not
+ // be filtered for 5 seconds (the default).
+ Filters filters;
+ filters.set_refuse_seconds(0);
- // Now launch container 2 which will copy the host mount table with
- // container 1's work directory mount in it.
- ExecutorInfo executor2 = createExecutorInfo(
- "test_executor2",
- "[ ! -d '" + os::getcwd() + "' ]");
+ TaskInfo task = createTask(
+ offer.slave_id(),
+ Resources::parse("cpus:1;mem:512").get() + persistentVolume,
+ "echo abc > path1/file");
+ ContainerInfo containerInfo;
+ Image* image = containerInfo.mutable_mesos()->mutable_image();
+ image->set_type(Image::APPC);
+ image->mutable_appc()->set_name("test_image");
+ containerInfo.set_type(ContainerInfo::MESOS);
- executor2.mutable_container()->CopyFrom(createContainerInfo("test_image2"));
+ // We are assuming the image created by the tests have /tmp to be
+ // able to mount the directory.
+ containerInfo.add_volumes()->CopyFrom(
+ createVolumeFromHostPath("/tmp", dir1, Volume::RW));
+ task.mutable_container()->CopyFrom(containerInfo);
- string directory2 = slave::paths::createExecutorDirectory(
- flags.work_dir,
- slaveId,
- DEFAULT_FRAMEWORK_INFO.id(),
- executor2.executor_id(),
- containerId2);
+ // Create the persistent volumes and launch task via `acceptOffers`.
+ driver.acceptOffers(
+ {offer.id()},
+ {CREATE(persistentVolume), LAUNCH({task})},
+ filters);
- Future<bool> launch2 = containerizer.get()->launch(
- containerId2,
- None(),
- executor2,
- directory2,
- None(),
- slaveId,
- map<string, string>(),
- false);
+ Future<TaskStatus> statusRunning;
+ Future<TaskStatus> statusFinished;
- // Need to wait for Rootfs copy.
- AWAIT_READY_FOR(launch1, Seconds(60));
+ EXPECT_CALL(sched, statusUpdate(&driver, _))
+ .WillOnce(FutureArg<1>(&statusRunning))
+ .WillOnce(FutureArg<1>(&statusFinished));
- containerizer.get()->destroy(containerId1);
+ AWAIT_READY(statusRunning);
+ EXPECT_EQ(TASK_RUNNING, statusRunning->state());
+ AWAIT_READY(statusFinished);
+ EXPECT_EQ(TASK_FINISHED, statusFinished->state());
- // Wait on the containers.
- Future<Option<ContainerTermination>> wait1 =
- containerizer.get()->wait(containerId1);
+ // NOTE: The command executor's id is the same as the task id.
+ ExecutorID executorId;
+ executorId.set_value(task.task_id().value());
- Future<Option<ContainerTermination>> wait2 =
- containerizer.get()->wait(containerId2);
+ const string& directory = slave::paths::getExecutorLatestRunPath(
+ flags.work_dir,
+ offer.slave_id(),
+ frameworkId.get(),
+ executorId);
- AWAIT_READY_FOR(wait1, Seconds(60));
- ASSERT_SOME(wait1.get());
+ EXPECT_FALSE(os::exists(path::join(directory, "path1")));
- AWAIT_READY_FOR(wait2, Seconds(60));
- ASSERT_SOME(wait2.get());
+ const string& volumePath = slave::paths::getPersistentVolumePath(
+ flags.work_dir,
+ "role1",
+ "id1");
- // Executor 1 was forcefully killed.
- EXPECT_TRUE(wait1->get().has_status());
- EXPECT_EQ(9, wait1->get().status());
+ EXPECT_SOME_EQ("abc\n", os::read(path::join(volumePath, "file")));
- // Executor 2 exited normally.
- EXPECT_TRUE(wait2->get().has_status());
- EXPECT_EQ(0, wait2->get().status());
+ driver.stop();
+ driver.join();
}
-// This test verifies that the environment variables for sandbox
-// (i.e., MESOS_DIRECTORY and MESOS_SANDBOX) are set properly.
-TEST_F(LinuxFilesystemIsolatorTest, ROOT_SandboxEnvironmentVariable)
+// This test verifies that persistent volumes are unmounted properly
+// after a checkpointed framework disappears and the slave restarts.
+//
+// TODO(jieyu): Even though the command task specifies a new
+// filesystem root, the executor (command executor) itself does not
+// change filesystem root (uses the host filesystem). We need to add a
+// test to test the scenario that the executor itself changes rootfs.
+TEST_F(LinuxFilesystemIsolatorTest, ROOT_RecoverOrphanedPersistentVolume)
{
- slave::Flags flags = CreateSlaveFlags();
+ Try<Owned<cluster::Master>> master = StartMaster();
+ ASSERT_SOME(master);
- ContainerID containerId;
- containerId.set_value(UUID::random().toString());
+ slave::Flags flags = CreateSlaveFlags();
+ flags.image_provisioner_backend = "copy";
+ flags.resources = "cpus:2;mem:1024;disk(role1):1024";
+ flags.isolation = "disk/du,filesystem/linux";
Try<Owned<MesosContainerizer>> containerizer = createContainerizer(
flags,
@@ -1469,134 +1469,134 @@ TEST_F(LinuxFilesystemIsolatorTest, ROOT_SandboxEnvironmentVariable)
ASSERT_SOME(containerizer);
- string directory = path::join(os::getcwd(), "sandbox");
- ASSERT_SOME(os::mkdir(directory));
+ Owned<MasterDetector> detector = master.get()->createDetector();
- Try<string> script = strings::format(
- "if [ \"$MESOS_DIRECTORY\" != \"%s\" ]; then exit 1; fi &&"
- "if [ \"$MESOS_SANDBOX\" != \"%s\" ]; then exit 1; fi",
- directory,
- flags.sandbox_directory);
+ Try<Owned<cluster::Slave>> slave =
+ StartSlave(detector.get(), containerizer.get().get(), flags);
+ ASSERT_SOME(slave);
- ASSERT_SOME(script);
+ MockScheduler sched;
+ FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO;
+ frameworkInfo.set_role("role1");
+ frameworkInfo.set_checkpoint(true);
- ExecutorInfo executor = createExecutorInfo(
- "test_executor",
- script.get());
+ MesosSchedulerDriver driver(
+ &sched,
+ frameworkInfo,
+ master.get()->pid,
+ DEFAULT_CREDENTIAL);
- executor.mutable_container()->CopyFrom(createContainerInfo("test_image"));
+ Future<FrameworkID> frameworkId;
+ EXPECT_CALL(sched, registered(&driver, _, _))
+ .WillOnce(FutureArg<1>(&frameworkId));
- map<string, string> environment = executorEnvironment(
- flags,
- executor,
- directory,
- SlaveID(),
- PID<Slave>(),
- false);
+ Future<vector<Offer>> offers;
+ EXPECT_CALL(sched, resourceOffers(&driver, _))
+ .WillOnce(FutureArg<1>(&offers))
+ .WillRepeatedly(Return()); // Ignore subsequent offers.
- Future<bool> launch = containerizer.get()->launch(
- containerId,
+ driver.start();
+
+ AWAIT_READY(frameworkId);
+
+ AWAIT_READY(offers);
+ EXPECT_FALSE(offers->empty());
+
+ Offer offer = offers.get()[0];
+
+ const string dir1 = path::join(os::getcwd(), "dir1");
+ ASSERT_SOME(os::mkdir(dir1));
+
+ Resource persistentVolume = createPersistentVolume(
+ Megabytes(64),
+ "role1",
+ "id1",
+ "path1",
None(),
- executor,
- directory,
None(),
- SlaveID(),
- environment,
- false);
-
- // Wait for the launch to complete.
- // Need to wait for Rootfs copy.
- AWAIT_READY_FOR(launch, Seconds(60));
+ frameworkInfo.principal());
- // Wait on the container.
- Future<Option<ContainerTermination>> wait =
- containerizer.get()->wait(containerId);
+ // Create a task that does nothing for a long time.
+ TaskInfo task = createTask(
+ offer.slave_id(),
+ Resources::parse("cpus:1;mem:512").get() + persistentVolume,
+ "sleep 1000");
- AWAIT_READY(wait);
- ASSERT_SOME(wait.get());
+ ContainerInfo containerInfo;
+ Image* image = containerInfo.mutable_mesos()->mutable_image();
+ image->set_type(Image::APPC);
+ image->mutable_appc()->set_name("test_image");
+ containerInfo.set_type(ContainerInfo::MESOS);
- // Check the executor exited correctly.
- EXPECT_TRUE(wait->get().has_status());
- EXPECT_EQ(0, wait->get().status());
-}
+ // We are assuming the image created by the tests have /tmp to be
+ // able to mount the directory.
+ containerInfo.add_volumes()->CopyFrom(
+ createVolumeFromHostPath("/tmp", dir1, Volume::RW));
+ task.mutable_container()->CopyFrom(containerInfo);
+ Future<TaskStatus> status;
+ EXPECT_CALL(sched, statusUpdate(&driver, _))
+ .WillOnce(FutureArg<1>(&status))
+ .WillRepeatedly(DoDefault());
-// This test verifies the case where we don't need a bind mount for
-// slave's working directory because the mount containing it is
-// already a shared mount in its own peer group.
-TEST_F(LinuxFilesystemIsolatorTest, ROOT_WorkDirMountNotNeeded)
-{
- // Make 'sandbox' a shared mount in its own peer group.
- ASSERT_SOME(os::shell(
- "mount --bind %s %s && "
- "mount --make-private %s &&"
- "mount --make-shared %s",
- sandbox->c_str(),
- sandbox->c_str(),
- sandbox->c_str(),
- sandbox->c_str()));
+ Future<Nothing> ack =
+ FUTURE_DISPATCH(_, &Slave::_statusUpdateAcknowledgement);
- // Slave's working directory is under 'sandbox'.
- slave::Flags flags = CreateSlaveFlags();
- flags.work_dir = path::join(sandbox.get(), "slave");
+ // Create the persistent volumes and launch task via `acceptOffers`.
+ driver.acceptOffers(
+ {offer.id()},
+ {CREATE(persistentVolume), LAUNCH({task})});
- ASSERT_SOME(os::mkdir(flags.work_dir));
+ AWAIT_READY(status);
+ EXPECT_EQ(TASK_RUNNING, status.get().state());
- Try<Isolator*> isolator = LinuxFilesystemIsolatorProcess::create(flags);
- ASSERT_SOME(isolator);
+ // Wait for the ACK to be checkpointed.
+ AWAIT_READY(ack);
- Try<fs::MountInfoTable> table = fs::MountInfoTable::read();
- ASSERT_SOME(table);
+ Future<hashset<ContainerID>> containers = containerizer.get()->containers();
- // Verifies that there's no mount for slave's working directory.
- bool mountFound = false;
- foreach (const fs::MountInfoTable::Entry& entry, table->entries) {
- if (entry.target == flags.work_dir) {
- mountFound = true;
- }
- }
+ AWAIT_READY(containers);
+ EXPECT_EQ(1u, containers.get().size());
- EXPECT_FALSE(mountFound);
+ ContainerID containerId = *containers.get().begin();
- delete isolator.get();
-}
+ // Restart the slave.
+ slave.get()->terminate();
+ // Wipe the slave meta directory so that the slave will treat the
+ // above running task as an orphan.
+ ASSERT_SOME(os::rmdir(slave::paths::getMetaRootDir(flags.work_dir)));
-// This test verifies the case where we do need a bind mount for
-// slave's working directory because the mount containing it is not a
-// shared mount in its own peer group.
-TEST_F(LinuxFilesystemIsolatorTest, ROOT_WorkDirMountNeeded)
-{
- // Make 'sandbox' a private mount.
- ASSERT_SOME(os::shell(
- "mount --bind %s %s && "
- "mount --make-private %s",
- sandbox->c_str(),
- sandbox->c_str(),
- sandbox->c_str()));
+ // Recreate the containerizer using the same helper as above.
+ containerizer = createContainerizer(
+ flags,
+ {{"test_image", path::join(os::getcwd(), "test_image")}});
- slave::Flags flags = CreateSlaveFlags();
- flags.work_dir = path::join(sandbox.get(), "slave");
+ slave = StartSlave(detector.get(), containerizer.get().get(), flags);
+ ASSERT_SOME(slave);
- ASSERT_SOME(os::mkdir(flags.work_dir));
+ // Wait until slave recovery is complete.
+ Future<Nothing> _recover = FUTURE_DISPATCH(_, &Slave::_recover);
+ AWAIT_READY_FOR(_recover, Seconds(60));
- Try<Isolator*> isolator = LinuxFilesystemIsolatorProcess::create(flags);
- ASSERT_SOME(isolator);
+ // Wait until the orphan containers are cleaned up.
+ AWAIT_READY_FOR(containerizer.get()->wait(containerId), Seconds(60));
Try<fs::MountInfoTable> table = fs::MountInfoTable::read();
ASSERT_SOME(table);
- bool mountFound = false;
- foreach (const fs::MountInfoTable::Entry& entry, table->entries) {
- if (entry.target == flags.work_dir) {
- EXPECT_SOME(entry.shared());
- mountFound = true;
- }
- }
+ // All mount targets should be under this directory.
+ const string directory = slave::paths::getSandboxRootDir(flags.work_dir);
- EXPECT_TRUE(mountFound);
+ // Verify that the orphaned container's persistent volume and
+ // the rootfs are unmounted.
+ foreach (const fs::MountInfoTable::Entry& entry, table.get().entries) {
+ EXPECT_FALSE(strings::contains(entry.target, directory))
+ << "Target was not unmounted: " << entry.target;
+ }
- delete isolator.get();
+ driver.stop();
+ driver.join();
}