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 2015/07/28 20:52:39 UTC

mesos git commit: Renamed containerizer_tests.cpp to mesos_containerizer_tests.cpp.

Repository: mesos
Updated Branches:
  refs/heads/master cf8ab5e45 -> 4b4cba24d


Renamed containerizer_tests.cpp to mesos_containerizer_tests.cpp.

Review: https://reviews.apache.org/r/36892


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/4b4cba24
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/4b4cba24
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/4b4cba24

Branch: refs/heads/master
Commit: 4b4cba24df5d9b9d04f61430e97c75c9103ec2cd
Parents: cf8ab5e
Author: Jie Yu <yu...@gmail.com>
Authored: Tue Jul 28 11:45:32 2015 -0700
Committer: Jie Yu <yu...@gmail.com>
Committed: Tue Jul 28 11:45:47 2015 -0700

----------------------------------------------------------------------
 src/Makefile.am                                 |   4 +-
 src/tests/containerizer/containerizer_tests.cpp | 802 -------------------
 .../containerizer/mesos_containerizer_tests.cpp | 802 +++++++++++++++++++
 3 files changed, 804 insertions(+), 804 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/4b4cba24/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index a7104bb..0794969 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1536,12 +1536,12 @@ mesos_tests_SOURCES =						\
   tests/zookeeper_url_tests.cpp					\
   tests/common/http_tests.cpp					\
   tests/containerizer/composing_containerizer_tests.cpp		\
-  tests/containerizer/containerizer_tests.cpp			\
   tests/containerizer/docker_containerizer_tests.cpp		\
   tests/containerizer/docker_tests.cpp				\
   tests/containerizer/external_containerizer_test.cpp		\
   tests/containerizer/isolator_tests.cpp			\
-  tests/containerizer/memory_test_helper.cpp
+  tests/containerizer/memory_test_helper.cpp			\
+  tests/containerizer/mesos_containerizer_tests.cpp
 
 mesos_tests_CPPFLAGS = $(MESOS_CPPFLAGS)
 mesos_tests_CPPFLAGS += -DSOURCE_DIR=\"$(abs_top_srcdir)\"

http://git-wip-us.apache.org/repos/asf/mesos/blob/4b4cba24/src/tests/containerizer/containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/containerizer_tests.cpp b/src/tests/containerizer/containerizer_tests.cpp
deleted file mode 100644
index 213fa4b..0000000
--- a/src/tests/containerizer/containerizer_tests.cpp
+++ /dev/null
@@ -1,802 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <list>
-#include <map>
-#include <string>
-#include <vector>
-
-#include <gmock/gmock.h>
-
-#include <mesos/mesos.hpp>
-
-#include <mesos/slave/isolator.hpp>
-
-#include <process/future.hpp>
-#include <process/owned.hpp>
-
-#include <stout/strings.hpp>
-
-#include "slave/flags.hpp"
-
-#include "slave/containerizer/fetcher.hpp"
-#include "slave/containerizer/launcher.hpp"
-
-#include "slave/containerizer/mesos/containerizer.hpp"
-
-#include "tests/flags.hpp"
-#include "tests/mesos.hpp"
-#include "tests/utils.hpp"
-
-#include "tests/containerizer/isolator.hpp"
-#include "tests/containerizer/launcher.hpp"
-
-using namespace process;
-
-using mesos::internal::master::Master;
-
-using mesos::internal::slave::Fetcher;
-using mesos::internal::slave::Launcher;
-using mesos::internal::slave::MesosContainerizer;
-using mesos::internal::slave::MesosContainerizerProcess;
-using mesos::internal::slave::PosixLauncher;
-using mesos::internal::slave::Provisioner;
-using mesos::internal::slave::Slave;
-
-using mesos::internal::slave::state::ExecutorState;
-using mesos::internal::slave::state::FrameworkState;
-using mesos::internal::slave::state::RunState;
-using mesos::internal::slave::state::SlaveState;
-
-using mesos::slave::ContainerLimitation;
-using mesos::slave::ContainerPrepareInfo;
-using mesos::slave::ContainerState;
-using mesos::slave::Isolator;
-
-using std::list;
-using std::map;
-using std::string;
-using std::vector;
-
-using testing::_;
-using testing::DoAll;
-using testing::Invoke;
-using testing::Return;
-
-namespace mesos {
-namespace internal {
-namespace tests {
-
-class MesosContainerizerIsolatorPreparationTest :
-  public TemporaryDirectoryTest
-{
-public:
-  // Construct a MesosContainerizer with TestIsolator(s) which use the provided
-  // 'prepare' command(s).
-  Try<MesosContainerizer*> CreateContainerizer(
-      Fetcher* fetcher,
-      const vector<Option<ContainerPrepareInfo>>& prepares)
-  {
-    vector<Owned<Isolator>> isolators;
-
-    foreach (const Option<ContainerPrepareInfo>& prepare, prepares) {
-      Try<Isolator*> isolator = TestIsolatorProcess::create(prepare);
-      if (isolator.isError()) {
-        return Error(isolator.error());
-      }
-
-      isolators.push_back(Owned<Isolator>(isolator.get()));
-    }
-
-    slave::Flags flags;
-    flags.launcher_dir = path::join(tests::flags.build_dir, "src");
-
-    Try<Launcher*> launcher = PosixLauncher::create(flags);
-    if (launcher.isError()) {
-      return Error(launcher.error());
-    }
-
-    return new MesosContainerizer(
-        flags,
-        false,
-        fetcher,
-        Owned<Launcher>(launcher.get()),
-        isolators,
-        hashmap<ContainerInfo::Image::Type, Owned<Provisioner>>());
-  }
-
-  Try<MesosContainerizer*> CreateContainerizer(
-      Fetcher* fetcher,
-      const Option<ContainerPrepareInfo>& prepare)
-  {
-    vector<Option<ContainerPrepareInfo>> prepares;
-    prepares.push_back(prepare);
-
-    return CreateContainerizer(fetcher, prepares);
-  }
-};
-
-
-// The isolator has a prepare command that succeeds.
-TEST_F(MesosContainerizerIsolatorPreparationTest, ScriptSucceeds)
-{
-  string directory = os::getcwd(); // We're inside a temporary sandbox.
-  string file = path::join(directory, "child.script.executed");
-
-  Fetcher fetcher;
-
-  ContainerPrepareInfo prepareInfo;
-  prepareInfo.add_commands()->set_value("touch " + file);
-
-  Try<MesosContainerizer*> containerizer = CreateContainerizer(
-      &fetcher,
-      prepareInfo);
-  CHECK_SOME(containerizer);
-
-  ContainerID containerId;
-  containerId.set_value("test_container");
-
-  Future<bool> launch = containerizer.get()->launch(
-      containerId,
-      CREATE_EXECUTOR_INFO("executor", "exit 0"),
-      directory,
-      None(),
-      SlaveID(),
-      PID<Slave>(),
-      false);
-
-  // Wait until the launch completes.
-  AWAIT_READY(launch);
-
-  // Wait for the child (preparation script + executor) to complete.
-  Future<containerizer::Termination> wait =
-    containerizer.get()->wait(containerId);
-
-  AWAIT_READY(wait);
-
-  // Check the child exited correctly.
-  EXPECT_TRUE(wait.get().has_status());
-  EXPECT_EQ(0, wait.get().status());
-
-  // Check the preparation script actually ran.
-  EXPECT_TRUE(os::exists(file));
-
-  // Destroy the container.
-  containerizer.get()->destroy(containerId);
-
-  delete containerizer.get();
-}
-
-
-// The isolator has a prepare command that fails.
-TEST_F(MesosContainerizerIsolatorPreparationTest, ScriptFails)
-{
-  string directory = os::getcwd(); // We're inside a temporary sandbox.
-  string file = path::join(directory, "child.script.executed");
-
-  Fetcher fetcher;
-
-  ContainerPrepareInfo prepareInfo;
-  prepareInfo.add_commands()->set_value("touch " + file + " && exit 1");
-
-  Try<MesosContainerizer*> containerizer = CreateContainerizer(
-      &fetcher,
-      prepareInfo);
-  CHECK_SOME(containerizer);
-
-  ContainerID containerId;
-  containerId.set_value("test_container");
-
-  Future<bool> launch = containerizer.get()->launch(
-      containerId,
-      CREATE_EXECUTOR_INFO("executor", "exit 0"),
-      directory,
-      None(),
-      SlaveID(),
-      PID<Slave>(),
-      false);
-
-  // Wait until the launch completes.
-  AWAIT_READY(launch);
-
-  // Wait for the child (preparation script + executor) to complete.
-  Future<containerizer::Termination> wait =
-    containerizer.get()->wait(containerId);
-
-  AWAIT_READY(wait);
-
-  // Check the child failed to exit correctly.
-  EXPECT_TRUE(wait.get().has_status());
-  EXPECT_NE(0, wait.get().status());
-
-  // Check the preparation script actually ran.
-  EXPECT_TRUE(os::exists(file));
-
-  // Destroy the container.
-  containerizer.get()->destroy(containerId);
-
-  delete containerizer.get();
-}
-
-
-// There are two isolators, one with a prepare command that succeeds
-// and another that fails. The execution order is not defined but the
-// launch should fail from the failing prepare command.
-TEST_F(MesosContainerizerIsolatorPreparationTest, MultipleScripts)
-{
-  string directory = os::getcwd(); // We're inside a temporary sandbox.
-  string file1 = path::join(directory, "child.script.executed.1");
-  string file2 = path::join(directory, "child.script.executed.2");
-
-  vector<Option<ContainerPrepareInfo>> prepares;
-
-  // This isolator prepare command one will succeed if called first, otherwise
-  // it won't get run.
-  ContainerPrepareInfo prepare1;
-  prepare1.add_commands()->set_value("touch " + file1 + " && exit 0");
-  prepares.push_back(prepare1);
-
-  // This will fail, either first or after the successful command.
-  ContainerPrepareInfo prepare2;
-  prepare2.add_commands()->set_value("touch " + file2 + " && exit 1");
-  prepares.push_back(prepare2);
-
-  Fetcher fetcher;
-
-  Try<MesosContainerizer*> containerizer =
-    CreateContainerizer(&fetcher, prepares);
-
-  CHECK_SOME(containerizer);
-
-  ContainerID containerId;
-  containerId.set_value("test_container");
-
-  Future<bool> launch = containerizer.get()->launch(
-      containerId,
-      CREATE_EXECUTOR_INFO("executor", "exit 0"),
-      directory,
-      None(),
-      SlaveID(),
-      PID<Slave>(),
-      false);
-
-  // Wait until the launch completes.
-  AWAIT_READY(launch);
-
-  // Wait for the child (preparation script(s) + executor) to complete.
-  Future<containerizer::Termination> wait =
-    containerizer.get()->wait(containerId);
-  AWAIT_READY(wait);
-
-  // Check the child failed to exit correctly.
-  EXPECT_TRUE(wait.get().has_status());
-  EXPECT_NE(0, wait.get().status());
-
-  // Check the failing preparation script has actually ran.
-  EXPECT_TRUE(os::exists(file2));
-
-  // Destroy the container.
-  containerizer.get()->destroy(containerId);
-
-  delete containerizer.get();
-}
-
-
-// The isolator sets an environment variable for the Executor. The
-// Executor then creates a file as pointed to by environment
-// varialble. Finally, after the executor has terminated, we check for
-// the existence of the file.
-TEST_F(MesosContainerizerIsolatorPreparationTest, ExecutorEnvironmentVariable)
-{
-  string directory = os::getcwd(); // We're inside a temporary sandbox.
-  string file = path::join(directory, "child.script.executed");
-
-  Fetcher fetcher;
-
-  ContainerPrepareInfo prepareInfo;
-
-  Environment::Variable* variable =
-    prepareInfo.mutable_environment()->add_variables();
-
-  variable->set_name("TEST_ENVIRONMENT");
-  variable->set_value(file);
-
-  Try<MesosContainerizer*> containerizer = CreateContainerizer(
-      &fetcher,
-      prepareInfo);
-
-  CHECK_SOME(containerizer);
-
-  ContainerID containerId;
-  containerId.set_value("test_container");
-
-  Future<bool> launch = containerizer.get()->launch(
-      containerId,
-      CREATE_EXECUTOR_INFO("executor", "touch $TEST_ENVIRONMENT"),
-      directory,
-      None(),
-      SlaveID(),
-      PID<Slave>(),
-      false);
-
-  // Wait until the launch completes.
-  AWAIT_READY(launch);
-
-  // Wait for the child (preparation script + executor) to complete.
-  Future<containerizer::Termination> wait =
-    containerizer.get()->wait(containerId);
-
-  AWAIT_READY(wait);
-
-  // Check the child exited correctly.
-  EXPECT_TRUE(wait.get().has_status());
-  EXPECT_EQ(0, wait.get().status());
-
-  // Check the preparation script actually ran.
-  EXPECT_TRUE(os::exists(file));
-
-  // Destroy the container.
-  containerizer.get()->destroy(containerId);
-
-  delete containerizer.get();
-}
-
-
-class MesosContainerizerExecuteTest : public TemporaryDirectoryTest {};
-
-
-TEST_F(MesosContainerizerExecuteTest, IoRedirection)
-{
-  string directory = os::getcwd(); // We're inside a temporary sandbox.
-
-  slave::Flags flags;
-  flags.launcher_dir = path::join(tests::flags.build_dir, "src");
-
-  Fetcher fetcher;
-
-  // Use local=false so std{err,out} are redirected to files.
-  Try<MesosContainerizer*> containerizer =
-    MesosContainerizer::create(flags, false, &fetcher);
-
-  ASSERT_SOME(containerizer);
-
-  ContainerID containerId;
-  containerId.set_value("test_container");
-
-  string errMsg = "this is stderr";
-  string outMsg = "this is stdout";
-  string command =
-    "(echo '" + errMsg + "' 1>&2) && echo '" + outMsg + "'";
-
-  Future<bool> launch = containerizer.get()->launch(
-      containerId,
-      CREATE_EXECUTOR_INFO("executor", command),
-      directory,
-      None(),
-      SlaveID(),
-      PID<Slave>(),
-      false);
-
-  // Wait for the launch to complete.
-  AWAIT_READY(launch);
-
-  // Wait on the container.
-  Future<containerizer::Termination> wait =
-    containerizer.get()->wait(containerId);
-
-  AWAIT_READY(wait);
-
-  // Check the executor exited correctly.
-  EXPECT_TRUE(wait.get().has_status());
-  EXPECT_EQ(0, wait.get().status());
-
-  // Check that std{err, out} was redirected.
-  // NOTE: Fetcher uses GLOG, which outputs extra information to
-  // stderr.
-  Try<string> stderr = os::read(path::join(directory, "stderr"));
-  ASSERT_SOME(stderr);
-  EXPECT_TRUE(strings::contains(stderr.get(), errMsg));
-
-  EXPECT_SOME_EQ(outMsg + "\n", os::read(path::join(directory, "stdout")));
-
-  delete containerizer.get();
-}
-
-
-class MesosContainerizerDestroyTest : public MesosTest {};
-
-
-class MockMesosContainerizerProcess : public MesosContainerizerProcess
-{
-public:
-  MockMesosContainerizerProcess(
-      const slave::Flags& flags,
-      bool local,
-      Fetcher* fetcher,
-      const Owned<Launcher>& launcher,
-      const vector<Owned<Isolator>>& isolators,
-      const hashmap<ContainerInfo::Image::Type,
-                    Owned<Provisioner>>& provisioners)
-    : MesosContainerizerProcess(
-          flags,
-          local,
-          fetcher,
-          launcher,
-          isolators,
-          provisioners)
-  {
-    // NOTE: See TestContainerizer::setup for why we use
-    // 'EXPECT_CALL' and 'WillRepeatedly' here instead of
-    // 'ON_CALL' and 'WillByDefault'.
-    EXPECT_CALL(*this, exec(_, _))
-      .WillRepeatedly(Invoke(this, &MockMesosContainerizerProcess::_exec));
-  }
-
-  MOCK_METHOD2(
-      exec,
-      Future<bool>(
-          const ContainerID& containerId,
-          int pipeWrite));
-
-  Future<bool> _exec(
-      const ContainerID& containerId,
-      int pipeWrite)
-  {
-    return MesosContainerizerProcess::exec(
-        containerId,
-        pipeWrite);
-  }
-};
-
-
-class MockIsolator : public mesos::slave::Isolator
-{
-public:
-  MockIsolator()
-  {
-    EXPECT_CALL(*this, watch(_))
-      .WillRepeatedly(Return(watchPromise.future()));
-
-    EXPECT_CALL(*this, isolate(_, _))
-      .WillRepeatedly(Return(Nothing()));
-
-    EXPECT_CALL(*this, cleanup(_))
-      .WillRepeatedly(Return(Nothing()));
-
-    EXPECT_CALL(*this, prepare(_, _, _, _, _))
-      .WillRepeatedly(Invoke(this, &MockIsolator::_prepare));
-  }
-
-  MOCK_METHOD2(
-      recover,
-      Future<Nothing>(
-          const list<ContainerState>&,
-          const hashset<ContainerID>&));
-
-  MOCK_METHOD5(
-      prepare,
-      Future<Option<ContainerPrepareInfo>>(
-          const ContainerID&,
-          const ExecutorInfo&,
-          const string&,
-          const Option<string>&,
-          const Option<string>&));
-
-  virtual Future<Option<ContainerPrepareInfo>> _prepare(
-      const ContainerID& containerId,
-      const ExecutorInfo& executorInfo,
-      const string& directory,
-      const Option<string>& rootfs,
-      const Option<string>& user)
-  {
-    return None();
-  }
-
-  MOCK_METHOD2(
-      isolate,
-      Future<Nothing>(const ContainerID&, pid_t));
-
-  MOCK_METHOD1(
-      watch,
-      Future<mesos::slave::ContainerLimitation>(const ContainerID&));
-
-  MOCK_METHOD2(
-      update,
-      Future<Nothing>(const ContainerID&, const Resources&));
-
-  MOCK_METHOD1(
-      usage,
-      Future<ResourceStatistics>(const ContainerID&));
-
-  MOCK_METHOD1(
-      cleanup,
-      Future<Nothing>(const ContainerID&));
-
-  Promise<mesos::slave::ContainerLimitation> watchPromise;
-};
-
-
-// Destroying a mesos containerizer while it is fetching should
-// complete without waiting for the fetching to finish.
-TEST_F(MesosContainerizerDestroyTest, DestroyWhileFetching)
-{
-  slave::Flags flags = CreateSlaveFlags();
-
-  Try<Launcher*> launcher = PosixLauncher::create(flags);
-  ASSERT_SOME(launcher);
-
-  Fetcher fetcher;
-
-  MockMesosContainerizerProcess* process = new MockMesosContainerizerProcess(
-      flags,
-      true,
-      &fetcher,
-      Owned<Launcher>(launcher.get()),
-      vector<Owned<Isolator>>(),
-      hashmap<ContainerInfo::Image::Type, Owned<Provisioner>>());
-
-  Future<Nothing> exec;
-  Promise<bool> promise;
-
-  // Letting exec hang to simulate a long fetch.
-  EXPECT_CALL(*process, exec(_, _))
-    .WillOnce(DoAll(FutureSatisfy(&exec),
-                    Return(promise.future())));
-
-  MesosContainerizer containerizer((Owned<MesosContainerizerProcess>(process)));
-
-  ContainerID containerId;
-  containerId.set_value("test_container");
-
-  TaskInfo taskInfo;
-  CommandInfo commandInfo;
-  taskInfo.mutable_command()->MergeFrom(commandInfo);
-
-  containerizer.launch(
-      containerId,
-      taskInfo,
-      CREATE_EXECUTOR_INFO("executor", "exit 0"),
-      os::getcwd(),
-      None(),
-      SlaveID(),
-      PID<Slave>(),
-      false);
-
-  Future<containerizer::Termination> wait = containerizer.wait(containerId);
-
-  AWAIT_READY(exec);
-
-  containerizer.destroy(containerId);
-
-  // The container should still exit even if fetch didn't complete.
-  AWAIT_READY(wait);
-}
-
-
-// Destroying a mesos containerizer while it is preparing should wait
-// until isolators are finished preparing before destroying.
-TEST_F(MesosContainerizerDestroyTest, DestroyWhilePreparing)
-{
-  slave::Flags flags = CreateSlaveFlags();
-
-  Try<Launcher*> launcher = PosixLauncher::create(flags);
-  ASSERT_SOME(launcher);
-
-  MockIsolator* isolator = new MockIsolator();
-
-  Future<Nothing> prepare;
-  Promise<Option<ContainerPrepareInfo>> promise;
-
-  // Simulate a long prepare from the isolator.
-  EXPECT_CALL(*isolator, prepare(_, _, _, _, _))
-    .WillOnce(DoAll(FutureSatisfy(&prepare),
-                    Return(promise.future())));
-
-  Fetcher fetcher;
-
-  MockMesosContainerizerProcess* process = new MockMesosContainerizerProcess(
-      flags,
-      true,
-      &fetcher,
-      Owned<Launcher>(launcher.get()),
-      {Owned<Isolator>(isolator)},
-      hashmap<ContainerInfo::Image::Type, Owned<Provisioner>>());
-
-  MesosContainerizer containerizer((Owned<MesosContainerizerProcess>(process)));
-
-  ContainerID containerId;
-  containerId.set_value("test_container");
-
-  TaskInfo taskInfo;
-  CommandInfo commandInfo;
-  taskInfo.mutable_command()->MergeFrom(commandInfo);
-
-  containerizer.launch(
-      containerId,
-      taskInfo,
-      CREATE_EXECUTOR_INFO("executor", "exit 0"),
-      os::getcwd(),
-      None(),
-      SlaveID(),
-      PID<Slave>(),
-      false);
-
-  Future<containerizer::Termination> wait = containerizer.wait(containerId);
-
-  AWAIT_READY(prepare);
-
-  containerizer.destroy(containerId);
-
-  // The container should not exit until prepare is complete.
-  ASSERT_TRUE(wait.isPending());
-
-  // Need to help the compiler to disambiguate between overloads.
-  ContainerPrepareInfo prepareInfo;
-  prepareInfo.add_commands()->CopyFrom(commandInfo);
-  Option<ContainerPrepareInfo> option = prepareInfo;
-  promise.set(option);
-
-  AWAIT_READY(wait);
-
-  containerizer::Termination termination = wait.get();
-
-  EXPECT_EQ(
-      "Container destroyed while preparing isolators",
-      termination.message());
-
-  EXPECT_TRUE(termination.killed());
-  EXPECT_FALSE(termination.has_status());
-}
-
-
-// This action destroys the container using the real launcher and
-// waits until the destroy is complete.
-ACTION_P(InvokeDestroyAndWait, launcher)
-{
-  Future<Nothing> destroy = launcher->real->destroy(arg0);
-  AWAIT_READY(destroy);
-}
-
-
-// This test verifies that when a container destruction fails the
-// 'container_destroy_errors' metric is updated.
-TEST_F(MesosContainerizerDestroyTest, LauncherDestroyFailure)
-{
-  // Create a TestLauncher backed by PosixLauncher.
-  slave::Flags flags = CreateSlaveFlags();
-
-  Try<Launcher*> launcher_ = PosixLauncher::create(flags);
-  ASSERT_SOME(launcher_);
-
-  TestLauncher* launcher = new TestLauncher(Owned<Launcher>(launcher_.get()));
-
-  Fetcher fetcher;
-
-  MesosContainerizerProcess* process = new MesosContainerizerProcess(
-      flags,
-      true,
-      &fetcher,
-      Owned<Launcher>(launcher),
-      vector<Owned<Isolator>>(),
-      hashmap<ContainerInfo::Image::Type, Owned<Provisioner>>());
-
-  MesosContainerizer containerizer((Owned<MesosContainerizerProcess>(process)));
-
-  ContainerID containerId;
-  containerId.set_value("test_container");
-
-  TaskInfo taskInfo;
-  CommandInfo commandInfo;
-  taskInfo.mutable_command()->MergeFrom(commandInfo);
-
-  // Destroy the container using the PosixLauncher but return a failed
-  // future to the containerizer.
-  EXPECT_CALL(*launcher, destroy(_))
-    .WillOnce(DoAll(InvokeDestroyAndWait(launcher),
-                    Return(Failure("Destroy failure"))));
-
-  Future<bool> launch = containerizer.launch(
-      containerId,
-      taskInfo,
-      CREATE_EXECUTOR_INFO("executor", "sleep 1000"),
-      os::getcwd(),
-      None(),
-      SlaveID(),
-      PID<Slave>(),
-      false);
-
-  AWAIT_READY(launch);
-
-  Future<containerizer::Termination> wait = containerizer.wait(containerId);
-
-  containerizer.destroy(containerId);
-
-  // The container destroy should fail.
-  AWAIT_FAILED(wait);
-
-  // We settle the clock here to ensure that the processing of
-  // 'MesosContainerizerProcess::__destroy()' is complete and the
-  // metric is updated.
-  Clock::pause();
-  Clock::settle();
-  Clock::resume();
-
-  // Ensure that the metric is updated.
-  JSON::Object metrics = Metrics();
-  ASSERT_EQ(
-      1u,
-      metrics.values.count("containerizer/mesos/container_destroy_errors"));
-  ASSERT_EQ(
-      1u,
-      metrics.values["containerizer/mesos/container_destroy_errors"]);
-}
-
-
-class MesosContainerizerRecoverTest : public MesosTest {};
-
-
-// This test checks that MesosContainerizer doesn't recover executors
-// that were started by another containerizer (e.g: Docker).
-TEST_F(MesosContainerizerRecoverTest, SkipRecoverNonMesosContainers)
-{
-  slave::Flags flags = CreateSlaveFlags();
-  Fetcher fetcher;
-
-  Try<MesosContainerizer*> containerizer =
-    MesosContainerizer::create(flags, true, &fetcher);
-
-  ASSERT_SOME(containerizer);
-
-  ExecutorID executorId;
-  executorId.set_value(UUID::random().toString());
-
-  ContainerID containerId;
-  containerId.set_value(UUID::random().toString());
-
-  ExecutorInfo executorInfo;
-  executorInfo.mutable_container()->set_type(ContainerInfo::DOCKER);
-
-  ExecutorState executorState;
-  executorState.info = executorInfo;
-  executorState.latest = containerId;
-
-  RunState runState;
-  runState.id = containerId;
-  executorState.runs.put(containerId, runState);
-
-  FrameworkState frameworkState;
-  frameworkState.executors.put(executorId, executorState);
-
-  SlaveState slaveState;
-  FrameworkID frameworkId;
-  frameworkId.set_value(UUID::random().toString());
-  slaveState.frameworks.put(frameworkId, frameworkState);
-
-  Future<Nothing> recover = containerizer.get()->recover(slaveState);
-  AWAIT_READY(recover);
-
-  Future<hashset<ContainerID>> containers = containerizer.get()->containers();
-  AWAIT_READY(containers);
-  EXPECT_EQ(0u, containers.get().size());
-
-  delete containerizer.get();
-}
-
-} // namespace tests {
-} // namespace internal {
-} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/4b4cba24/src/tests/containerizer/mesos_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/mesos_containerizer_tests.cpp b/src/tests/containerizer/mesos_containerizer_tests.cpp
new file mode 100644
index 0000000..213fa4b
--- /dev/null
+++ b/src/tests/containerizer/mesos_containerizer_tests.cpp
@@ -0,0 +1,802 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <list>
+#include <map>
+#include <string>
+#include <vector>
+
+#include <gmock/gmock.h>
+
+#include <mesos/mesos.hpp>
+
+#include <mesos/slave/isolator.hpp>
+
+#include <process/future.hpp>
+#include <process/owned.hpp>
+
+#include <stout/strings.hpp>
+
+#include "slave/flags.hpp"
+
+#include "slave/containerizer/fetcher.hpp"
+#include "slave/containerizer/launcher.hpp"
+
+#include "slave/containerizer/mesos/containerizer.hpp"
+
+#include "tests/flags.hpp"
+#include "tests/mesos.hpp"
+#include "tests/utils.hpp"
+
+#include "tests/containerizer/isolator.hpp"
+#include "tests/containerizer/launcher.hpp"
+
+using namespace process;
+
+using mesos::internal::master::Master;
+
+using mesos::internal::slave::Fetcher;
+using mesos::internal::slave::Launcher;
+using mesos::internal::slave::MesosContainerizer;
+using mesos::internal::slave::MesosContainerizerProcess;
+using mesos::internal::slave::PosixLauncher;
+using mesos::internal::slave::Provisioner;
+using mesos::internal::slave::Slave;
+
+using mesos::internal::slave::state::ExecutorState;
+using mesos::internal::slave::state::FrameworkState;
+using mesos::internal::slave::state::RunState;
+using mesos::internal::slave::state::SlaveState;
+
+using mesos::slave::ContainerLimitation;
+using mesos::slave::ContainerPrepareInfo;
+using mesos::slave::ContainerState;
+using mesos::slave::Isolator;
+
+using std::list;
+using std::map;
+using std::string;
+using std::vector;
+
+using testing::_;
+using testing::DoAll;
+using testing::Invoke;
+using testing::Return;
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+class MesosContainerizerIsolatorPreparationTest :
+  public TemporaryDirectoryTest
+{
+public:
+  // Construct a MesosContainerizer with TestIsolator(s) which use the provided
+  // 'prepare' command(s).
+  Try<MesosContainerizer*> CreateContainerizer(
+      Fetcher* fetcher,
+      const vector<Option<ContainerPrepareInfo>>& prepares)
+  {
+    vector<Owned<Isolator>> isolators;
+
+    foreach (const Option<ContainerPrepareInfo>& prepare, prepares) {
+      Try<Isolator*> isolator = TestIsolatorProcess::create(prepare);
+      if (isolator.isError()) {
+        return Error(isolator.error());
+      }
+
+      isolators.push_back(Owned<Isolator>(isolator.get()));
+    }
+
+    slave::Flags flags;
+    flags.launcher_dir = path::join(tests::flags.build_dir, "src");
+
+    Try<Launcher*> launcher = PosixLauncher::create(flags);
+    if (launcher.isError()) {
+      return Error(launcher.error());
+    }
+
+    return new MesosContainerizer(
+        flags,
+        false,
+        fetcher,
+        Owned<Launcher>(launcher.get()),
+        isolators,
+        hashmap<ContainerInfo::Image::Type, Owned<Provisioner>>());
+  }
+
+  Try<MesosContainerizer*> CreateContainerizer(
+      Fetcher* fetcher,
+      const Option<ContainerPrepareInfo>& prepare)
+  {
+    vector<Option<ContainerPrepareInfo>> prepares;
+    prepares.push_back(prepare);
+
+    return CreateContainerizer(fetcher, prepares);
+  }
+};
+
+
+// The isolator has a prepare command that succeeds.
+TEST_F(MesosContainerizerIsolatorPreparationTest, ScriptSucceeds)
+{
+  string directory = os::getcwd(); // We're inside a temporary sandbox.
+  string file = path::join(directory, "child.script.executed");
+
+  Fetcher fetcher;
+
+  ContainerPrepareInfo prepareInfo;
+  prepareInfo.add_commands()->set_value("touch " + file);
+
+  Try<MesosContainerizer*> containerizer = CreateContainerizer(
+      &fetcher,
+      prepareInfo);
+  CHECK_SOME(containerizer);
+
+  ContainerID containerId;
+  containerId.set_value("test_container");
+
+  Future<bool> launch = containerizer.get()->launch(
+      containerId,
+      CREATE_EXECUTOR_INFO("executor", "exit 0"),
+      directory,
+      None(),
+      SlaveID(),
+      PID<Slave>(),
+      false);
+
+  // Wait until the launch completes.
+  AWAIT_READY(launch);
+
+  // Wait for the child (preparation script + executor) to complete.
+  Future<containerizer::Termination> wait =
+    containerizer.get()->wait(containerId);
+
+  AWAIT_READY(wait);
+
+  // Check the child exited correctly.
+  EXPECT_TRUE(wait.get().has_status());
+  EXPECT_EQ(0, wait.get().status());
+
+  // Check the preparation script actually ran.
+  EXPECT_TRUE(os::exists(file));
+
+  // Destroy the container.
+  containerizer.get()->destroy(containerId);
+
+  delete containerizer.get();
+}
+
+
+// The isolator has a prepare command that fails.
+TEST_F(MesosContainerizerIsolatorPreparationTest, ScriptFails)
+{
+  string directory = os::getcwd(); // We're inside a temporary sandbox.
+  string file = path::join(directory, "child.script.executed");
+
+  Fetcher fetcher;
+
+  ContainerPrepareInfo prepareInfo;
+  prepareInfo.add_commands()->set_value("touch " + file + " && exit 1");
+
+  Try<MesosContainerizer*> containerizer = CreateContainerizer(
+      &fetcher,
+      prepareInfo);
+  CHECK_SOME(containerizer);
+
+  ContainerID containerId;
+  containerId.set_value("test_container");
+
+  Future<bool> launch = containerizer.get()->launch(
+      containerId,
+      CREATE_EXECUTOR_INFO("executor", "exit 0"),
+      directory,
+      None(),
+      SlaveID(),
+      PID<Slave>(),
+      false);
+
+  // Wait until the launch completes.
+  AWAIT_READY(launch);
+
+  // Wait for the child (preparation script + executor) to complete.
+  Future<containerizer::Termination> wait =
+    containerizer.get()->wait(containerId);
+
+  AWAIT_READY(wait);
+
+  // Check the child failed to exit correctly.
+  EXPECT_TRUE(wait.get().has_status());
+  EXPECT_NE(0, wait.get().status());
+
+  // Check the preparation script actually ran.
+  EXPECT_TRUE(os::exists(file));
+
+  // Destroy the container.
+  containerizer.get()->destroy(containerId);
+
+  delete containerizer.get();
+}
+
+
+// There are two isolators, one with a prepare command that succeeds
+// and another that fails. The execution order is not defined but the
+// launch should fail from the failing prepare command.
+TEST_F(MesosContainerizerIsolatorPreparationTest, MultipleScripts)
+{
+  string directory = os::getcwd(); // We're inside a temporary sandbox.
+  string file1 = path::join(directory, "child.script.executed.1");
+  string file2 = path::join(directory, "child.script.executed.2");
+
+  vector<Option<ContainerPrepareInfo>> prepares;
+
+  // This isolator prepare command one will succeed if called first, otherwise
+  // it won't get run.
+  ContainerPrepareInfo prepare1;
+  prepare1.add_commands()->set_value("touch " + file1 + " && exit 0");
+  prepares.push_back(prepare1);
+
+  // This will fail, either first or after the successful command.
+  ContainerPrepareInfo prepare2;
+  prepare2.add_commands()->set_value("touch " + file2 + " && exit 1");
+  prepares.push_back(prepare2);
+
+  Fetcher fetcher;
+
+  Try<MesosContainerizer*> containerizer =
+    CreateContainerizer(&fetcher, prepares);
+
+  CHECK_SOME(containerizer);
+
+  ContainerID containerId;
+  containerId.set_value("test_container");
+
+  Future<bool> launch = containerizer.get()->launch(
+      containerId,
+      CREATE_EXECUTOR_INFO("executor", "exit 0"),
+      directory,
+      None(),
+      SlaveID(),
+      PID<Slave>(),
+      false);
+
+  // Wait until the launch completes.
+  AWAIT_READY(launch);
+
+  // Wait for the child (preparation script(s) + executor) to complete.
+  Future<containerizer::Termination> wait =
+    containerizer.get()->wait(containerId);
+  AWAIT_READY(wait);
+
+  // Check the child failed to exit correctly.
+  EXPECT_TRUE(wait.get().has_status());
+  EXPECT_NE(0, wait.get().status());
+
+  // Check the failing preparation script has actually ran.
+  EXPECT_TRUE(os::exists(file2));
+
+  // Destroy the container.
+  containerizer.get()->destroy(containerId);
+
+  delete containerizer.get();
+}
+
+
+// The isolator sets an environment variable for the Executor. The
+// Executor then creates a file as pointed to by environment
+// varialble. Finally, after the executor has terminated, we check for
+// the existence of the file.
+TEST_F(MesosContainerizerIsolatorPreparationTest, ExecutorEnvironmentVariable)
+{
+  string directory = os::getcwd(); // We're inside a temporary sandbox.
+  string file = path::join(directory, "child.script.executed");
+
+  Fetcher fetcher;
+
+  ContainerPrepareInfo prepareInfo;
+
+  Environment::Variable* variable =
+    prepareInfo.mutable_environment()->add_variables();
+
+  variable->set_name("TEST_ENVIRONMENT");
+  variable->set_value(file);
+
+  Try<MesosContainerizer*> containerizer = CreateContainerizer(
+      &fetcher,
+      prepareInfo);
+
+  CHECK_SOME(containerizer);
+
+  ContainerID containerId;
+  containerId.set_value("test_container");
+
+  Future<bool> launch = containerizer.get()->launch(
+      containerId,
+      CREATE_EXECUTOR_INFO("executor", "touch $TEST_ENVIRONMENT"),
+      directory,
+      None(),
+      SlaveID(),
+      PID<Slave>(),
+      false);
+
+  // Wait until the launch completes.
+  AWAIT_READY(launch);
+
+  // Wait for the child (preparation script + executor) to complete.
+  Future<containerizer::Termination> wait =
+    containerizer.get()->wait(containerId);
+
+  AWAIT_READY(wait);
+
+  // Check the child exited correctly.
+  EXPECT_TRUE(wait.get().has_status());
+  EXPECT_EQ(0, wait.get().status());
+
+  // Check the preparation script actually ran.
+  EXPECT_TRUE(os::exists(file));
+
+  // Destroy the container.
+  containerizer.get()->destroy(containerId);
+
+  delete containerizer.get();
+}
+
+
+class MesosContainerizerExecuteTest : public TemporaryDirectoryTest {};
+
+
+TEST_F(MesosContainerizerExecuteTest, IoRedirection)
+{
+  string directory = os::getcwd(); // We're inside a temporary sandbox.
+
+  slave::Flags flags;
+  flags.launcher_dir = path::join(tests::flags.build_dir, "src");
+
+  Fetcher fetcher;
+
+  // Use local=false so std{err,out} are redirected to files.
+  Try<MesosContainerizer*> containerizer =
+    MesosContainerizer::create(flags, false, &fetcher);
+
+  ASSERT_SOME(containerizer);
+
+  ContainerID containerId;
+  containerId.set_value("test_container");
+
+  string errMsg = "this is stderr";
+  string outMsg = "this is stdout";
+  string command =
+    "(echo '" + errMsg + "' 1>&2) && echo '" + outMsg + "'";
+
+  Future<bool> launch = containerizer.get()->launch(
+      containerId,
+      CREATE_EXECUTOR_INFO("executor", command),
+      directory,
+      None(),
+      SlaveID(),
+      PID<Slave>(),
+      false);
+
+  // Wait for the launch to complete.
+  AWAIT_READY(launch);
+
+  // Wait on the container.
+  Future<containerizer::Termination> wait =
+    containerizer.get()->wait(containerId);
+
+  AWAIT_READY(wait);
+
+  // Check the executor exited correctly.
+  EXPECT_TRUE(wait.get().has_status());
+  EXPECT_EQ(0, wait.get().status());
+
+  // Check that std{err, out} was redirected.
+  // NOTE: Fetcher uses GLOG, which outputs extra information to
+  // stderr.
+  Try<string> stderr = os::read(path::join(directory, "stderr"));
+  ASSERT_SOME(stderr);
+  EXPECT_TRUE(strings::contains(stderr.get(), errMsg));
+
+  EXPECT_SOME_EQ(outMsg + "\n", os::read(path::join(directory, "stdout")));
+
+  delete containerizer.get();
+}
+
+
+class MesosContainerizerDestroyTest : public MesosTest {};
+
+
+class MockMesosContainerizerProcess : public MesosContainerizerProcess
+{
+public:
+  MockMesosContainerizerProcess(
+      const slave::Flags& flags,
+      bool local,
+      Fetcher* fetcher,
+      const Owned<Launcher>& launcher,
+      const vector<Owned<Isolator>>& isolators,
+      const hashmap<ContainerInfo::Image::Type,
+                    Owned<Provisioner>>& provisioners)
+    : MesosContainerizerProcess(
+          flags,
+          local,
+          fetcher,
+          launcher,
+          isolators,
+          provisioners)
+  {
+    // NOTE: See TestContainerizer::setup for why we use
+    // 'EXPECT_CALL' and 'WillRepeatedly' here instead of
+    // 'ON_CALL' and 'WillByDefault'.
+    EXPECT_CALL(*this, exec(_, _))
+      .WillRepeatedly(Invoke(this, &MockMesosContainerizerProcess::_exec));
+  }
+
+  MOCK_METHOD2(
+      exec,
+      Future<bool>(
+          const ContainerID& containerId,
+          int pipeWrite));
+
+  Future<bool> _exec(
+      const ContainerID& containerId,
+      int pipeWrite)
+  {
+    return MesosContainerizerProcess::exec(
+        containerId,
+        pipeWrite);
+  }
+};
+
+
+class MockIsolator : public mesos::slave::Isolator
+{
+public:
+  MockIsolator()
+  {
+    EXPECT_CALL(*this, watch(_))
+      .WillRepeatedly(Return(watchPromise.future()));
+
+    EXPECT_CALL(*this, isolate(_, _))
+      .WillRepeatedly(Return(Nothing()));
+
+    EXPECT_CALL(*this, cleanup(_))
+      .WillRepeatedly(Return(Nothing()));
+
+    EXPECT_CALL(*this, prepare(_, _, _, _, _))
+      .WillRepeatedly(Invoke(this, &MockIsolator::_prepare));
+  }
+
+  MOCK_METHOD2(
+      recover,
+      Future<Nothing>(
+          const list<ContainerState>&,
+          const hashset<ContainerID>&));
+
+  MOCK_METHOD5(
+      prepare,
+      Future<Option<ContainerPrepareInfo>>(
+          const ContainerID&,
+          const ExecutorInfo&,
+          const string&,
+          const Option<string>&,
+          const Option<string>&));
+
+  virtual Future<Option<ContainerPrepareInfo>> _prepare(
+      const ContainerID& containerId,
+      const ExecutorInfo& executorInfo,
+      const string& directory,
+      const Option<string>& rootfs,
+      const Option<string>& user)
+  {
+    return None();
+  }
+
+  MOCK_METHOD2(
+      isolate,
+      Future<Nothing>(const ContainerID&, pid_t));
+
+  MOCK_METHOD1(
+      watch,
+      Future<mesos::slave::ContainerLimitation>(const ContainerID&));
+
+  MOCK_METHOD2(
+      update,
+      Future<Nothing>(const ContainerID&, const Resources&));
+
+  MOCK_METHOD1(
+      usage,
+      Future<ResourceStatistics>(const ContainerID&));
+
+  MOCK_METHOD1(
+      cleanup,
+      Future<Nothing>(const ContainerID&));
+
+  Promise<mesos::slave::ContainerLimitation> watchPromise;
+};
+
+
+// Destroying a mesos containerizer while it is fetching should
+// complete without waiting for the fetching to finish.
+TEST_F(MesosContainerizerDestroyTest, DestroyWhileFetching)
+{
+  slave::Flags flags = CreateSlaveFlags();
+
+  Try<Launcher*> launcher = PosixLauncher::create(flags);
+  ASSERT_SOME(launcher);
+
+  Fetcher fetcher;
+
+  MockMesosContainerizerProcess* process = new MockMesosContainerizerProcess(
+      flags,
+      true,
+      &fetcher,
+      Owned<Launcher>(launcher.get()),
+      vector<Owned<Isolator>>(),
+      hashmap<ContainerInfo::Image::Type, Owned<Provisioner>>());
+
+  Future<Nothing> exec;
+  Promise<bool> promise;
+
+  // Letting exec hang to simulate a long fetch.
+  EXPECT_CALL(*process, exec(_, _))
+    .WillOnce(DoAll(FutureSatisfy(&exec),
+                    Return(promise.future())));
+
+  MesosContainerizer containerizer((Owned<MesosContainerizerProcess>(process)));
+
+  ContainerID containerId;
+  containerId.set_value("test_container");
+
+  TaskInfo taskInfo;
+  CommandInfo commandInfo;
+  taskInfo.mutable_command()->MergeFrom(commandInfo);
+
+  containerizer.launch(
+      containerId,
+      taskInfo,
+      CREATE_EXECUTOR_INFO("executor", "exit 0"),
+      os::getcwd(),
+      None(),
+      SlaveID(),
+      PID<Slave>(),
+      false);
+
+  Future<containerizer::Termination> wait = containerizer.wait(containerId);
+
+  AWAIT_READY(exec);
+
+  containerizer.destroy(containerId);
+
+  // The container should still exit even if fetch didn't complete.
+  AWAIT_READY(wait);
+}
+
+
+// Destroying a mesos containerizer while it is preparing should wait
+// until isolators are finished preparing before destroying.
+TEST_F(MesosContainerizerDestroyTest, DestroyWhilePreparing)
+{
+  slave::Flags flags = CreateSlaveFlags();
+
+  Try<Launcher*> launcher = PosixLauncher::create(flags);
+  ASSERT_SOME(launcher);
+
+  MockIsolator* isolator = new MockIsolator();
+
+  Future<Nothing> prepare;
+  Promise<Option<ContainerPrepareInfo>> promise;
+
+  // Simulate a long prepare from the isolator.
+  EXPECT_CALL(*isolator, prepare(_, _, _, _, _))
+    .WillOnce(DoAll(FutureSatisfy(&prepare),
+                    Return(promise.future())));
+
+  Fetcher fetcher;
+
+  MockMesosContainerizerProcess* process = new MockMesosContainerizerProcess(
+      flags,
+      true,
+      &fetcher,
+      Owned<Launcher>(launcher.get()),
+      {Owned<Isolator>(isolator)},
+      hashmap<ContainerInfo::Image::Type, Owned<Provisioner>>());
+
+  MesosContainerizer containerizer((Owned<MesosContainerizerProcess>(process)));
+
+  ContainerID containerId;
+  containerId.set_value("test_container");
+
+  TaskInfo taskInfo;
+  CommandInfo commandInfo;
+  taskInfo.mutable_command()->MergeFrom(commandInfo);
+
+  containerizer.launch(
+      containerId,
+      taskInfo,
+      CREATE_EXECUTOR_INFO("executor", "exit 0"),
+      os::getcwd(),
+      None(),
+      SlaveID(),
+      PID<Slave>(),
+      false);
+
+  Future<containerizer::Termination> wait = containerizer.wait(containerId);
+
+  AWAIT_READY(prepare);
+
+  containerizer.destroy(containerId);
+
+  // The container should not exit until prepare is complete.
+  ASSERT_TRUE(wait.isPending());
+
+  // Need to help the compiler to disambiguate between overloads.
+  ContainerPrepareInfo prepareInfo;
+  prepareInfo.add_commands()->CopyFrom(commandInfo);
+  Option<ContainerPrepareInfo> option = prepareInfo;
+  promise.set(option);
+
+  AWAIT_READY(wait);
+
+  containerizer::Termination termination = wait.get();
+
+  EXPECT_EQ(
+      "Container destroyed while preparing isolators",
+      termination.message());
+
+  EXPECT_TRUE(termination.killed());
+  EXPECT_FALSE(termination.has_status());
+}
+
+
+// This action destroys the container using the real launcher and
+// waits until the destroy is complete.
+ACTION_P(InvokeDestroyAndWait, launcher)
+{
+  Future<Nothing> destroy = launcher->real->destroy(arg0);
+  AWAIT_READY(destroy);
+}
+
+
+// This test verifies that when a container destruction fails the
+// 'container_destroy_errors' metric is updated.
+TEST_F(MesosContainerizerDestroyTest, LauncherDestroyFailure)
+{
+  // Create a TestLauncher backed by PosixLauncher.
+  slave::Flags flags = CreateSlaveFlags();
+
+  Try<Launcher*> launcher_ = PosixLauncher::create(flags);
+  ASSERT_SOME(launcher_);
+
+  TestLauncher* launcher = new TestLauncher(Owned<Launcher>(launcher_.get()));
+
+  Fetcher fetcher;
+
+  MesosContainerizerProcess* process = new MesosContainerizerProcess(
+      flags,
+      true,
+      &fetcher,
+      Owned<Launcher>(launcher),
+      vector<Owned<Isolator>>(),
+      hashmap<ContainerInfo::Image::Type, Owned<Provisioner>>());
+
+  MesosContainerizer containerizer((Owned<MesosContainerizerProcess>(process)));
+
+  ContainerID containerId;
+  containerId.set_value("test_container");
+
+  TaskInfo taskInfo;
+  CommandInfo commandInfo;
+  taskInfo.mutable_command()->MergeFrom(commandInfo);
+
+  // Destroy the container using the PosixLauncher but return a failed
+  // future to the containerizer.
+  EXPECT_CALL(*launcher, destroy(_))
+    .WillOnce(DoAll(InvokeDestroyAndWait(launcher),
+                    Return(Failure("Destroy failure"))));
+
+  Future<bool> launch = containerizer.launch(
+      containerId,
+      taskInfo,
+      CREATE_EXECUTOR_INFO("executor", "sleep 1000"),
+      os::getcwd(),
+      None(),
+      SlaveID(),
+      PID<Slave>(),
+      false);
+
+  AWAIT_READY(launch);
+
+  Future<containerizer::Termination> wait = containerizer.wait(containerId);
+
+  containerizer.destroy(containerId);
+
+  // The container destroy should fail.
+  AWAIT_FAILED(wait);
+
+  // We settle the clock here to ensure that the processing of
+  // 'MesosContainerizerProcess::__destroy()' is complete and the
+  // metric is updated.
+  Clock::pause();
+  Clock::settle();
+  Clock::resume();
+
+  // Ensure that the metric is updated.
+  JSON::Object metrics = Metrics();
+  ASSERT_EQ(
+      1u,
+      metrics.values.count("containerizer/mesos/container_destroy_errors"));
+  ASSERT_EQ(
+      1u,
+      metrics.values["containerizer/mesos/container_destroy_errors"]);
+}
+
+
+class MesosContainerizerRecoverTest : public MesosTest {};
+
+
+// This test checks that MesosContainerizer doesn't recover executors
+// that were started by another containerizer (e.g: Docker).
+TEST_F(MesosContainerizerRecoverTest, SkipRecoverNonMesosContainers)
+{
+  slave::Flags flags = CreateSlaveFlags();
+  Fetcher fetcher;
+
+  Try<MesosContainerizer*> containerizer =
+    MesosContainerizer::create(flags, true, &fetcher);
+
+  ASSERT_SOME(containerizer);
+
+  ExecutorID executorId;
+  executorId.set_value(UUID::random().toString());
+
+  ContainerID containerId;
+  containerId.set_value(UUID::random().toString());
+
+  ExecutorInfo executorInfo;
+  executorInfo.mutable_container()->set_type(ContainerInfo::DOCKER);
+
+  ExecutorState executorState;
+  executorState.info = executorInfo;
+  executorState.latest = containerId;
+
+  RunState runState;
+  runState.id = containerId;
+  executorState.runs.put(containerId, runState);
+
+  FrameworkState frameworkState;
+  frameworkState.executors.put(executorId, executorState);
+
+  SlaveState slaveState;
+  FrameworkID frameworkId;
+  frameworkId.set_value(UUID::random().toString());
+  slaveState.frameworks.put(frameworkId, frameworkState);
+
+  Future<Nothing> recover = containerizer.get()->recover(slaveState);
+  AWAIT_READY(recover);
+
+  Future<hashset<ContainerID>> containers = containerizer.get()->containers();
+  AWAIT_READY(containers);
+  EXPECT_EQ(0u, containers.get().size());
+
+  delete containerizer.get();
+}
+
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {