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();
 }