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/08/09 20:13:44 UTC

[1/2] mesos git commit: Removed the external containerizer.

Repository: mesos
Updated Branches:
  refs/heads/master 751a48155 -> f6a0e72c5


http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/src/slave/containerizer/external_containerizer.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/external_containerizer.hpp b/src/slave/containerizer/external_containerizer.hpp
deleted file mode 100644
index feeb027..0000000
--- a/src/slave/containerizer/external_containerizer.hpp
+++ /dev/null
@@ -1,299 +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.
-
-#ifndef __EXTERNAL_CONTAINERIZER_HPP__
-#define __EXTERNAL_CONTAINERIZER_HPP__
-
-#include <list>
-#include <string>
-#include <tuple>
-
-#include <process/owned.hpp>
-#include <process/subprocess.hpp>
-
-#include <stout/hashmap.hpp>
-#include <stout/protobuf.hpp>
-#include <stout/try.hpp>
-
-#include "slave/state.hpp"
-
-#include "slave/containerizer/containerizer.hpp"
-
-#include "slave/containerizer/mesos/launcher.hpp"
-
-namespace mesos {
-namespace internal {
-namespace slave {
-
-// The scheme an external containerizer programs have to adhere to is;
-//
-// COMMAND < INPUT-PROTO > RESULT-PROTO
-//
-// launch < containerizer::Launch
-// update < containerizer::Update
-// usage < containerizer::Usage > mesos::ResourceStatistics
-// wait < containerizer::Wait > containerizer::Termination
-// destroy < containerizer::Destroy
-// containers > containerizer::Containers
-// recover
-//
-// 'wait' on the external containerizer side is expected to block
-// until the task command/executor has terminated.
-//
-// Additionally, we have the following environment variable setup
-// for external containerizer programs:
-// MESOS_LIBEXEC_DIRECTORY = path to mesos-executor, mesos-usage, ...
-// MESOS_WORK_DIRECTORY = slave work directory. This should be used
-// for distiguishing slave instances.
-// MESOS_DEFAULT_CONTAINER_IMAGE = default image as provided via
-// slave flags (default_container_image). This variable is provided
-// only in calls to 'launch'.
-
-// Check src/examples/python/test_containerizer.py for a rough
-// implementation template of this protocol.
-
-// For debugging purposes of an external containerizer program, it
-// might be helpful to enable verbose logging on the slave (GLOG_v=2).
-
-class ExternalContainerizerProcess;
-
-class ExternalContainerizer : public Containerizer
-{
-public:
-  static Try<ExternalContainerizer*> create(const Flags& flags);
-
-  ExternalContainerizer(const Flags& flags);
-
-  virtual ~ExternalContainerizer();
-
-  virtual process::Future<Nothing> recover(
-      const Option<state::SlaveState>& state);
-
-  virtual process::Future<bool> launch(
-      const ContainerID& containerId,
-      const ExecutorInfo& executorInfo,
-      const std::string& directory,
-      const Option<std::string>& user,
-      const SlaveID& slaveId,
-      const process::PID<Slave>& slavePid,
-      bool checkpoint);
-
-  virtual process::Future<bool> launch(
-      const ContainerID& containerId,
-      const TaskInfo& task,
-      const ExecutorInfo& executorInfo,
-      const std::string& directory,
-      const Option<std::string>& user,
-      const SlaveID& slaveId,
-      const process::PID<Slave>& slavePid,
-      bool checkpoint);
-
-  virtual process::Future<Nothing> update(
-      const ContainerID& containerId,
-      const Resources& resources);
-
-  virtual process::Future<ResourceStatistics> usage(
-      const ContainerID& containerId);
-
-  virtual process::Future<containerizer::Termination> wait(
-      const ContainerID& containerId);
-
-  virtual void destroy(const ContainerID& containerId);
-
-  virtual process::Future<hashset<ContainerID>> containers();
-
-private:
-  process::Owned<ExternalContainerizerProcess> process;
-};
-
-
-class ExternalContainerizerProcess
-  : public process::Process<ExternalContainerizerProcess>
-{
-public:
-  ExternalContainerizerProcess(const Flags& flags);
-
-  // Recover containerized executors as specified by state. See
-  // containerizer.hpp:recover for more.
-  process::Future<Nothing> recover(const Option<state::SlaveState>& state);
-
-  // Start the containerized executor.
-  process::Future<bool> launch(
-      const ContainerID& containerId,
-      const Option<TaskInfo>& taskInfo,
-      const ExecutorInfo& executorInfo,
-      const std::string& directory,
-      const Option<std::string>& user,
-      const SlaveID& slaveId,
-      const process::PID<Slave>& slavePid,
-      bool checkpoint);
-
-  // Update the container's resources.
-  process::Future<Nothing> update(
-      const ContainerID& containerId,
-      const Resources& resources);
-
-  // Gather resource usage statistics for the containerized executor.
-  process::Future<ResourceStatistics> usage(const ContainerID& containerId);
-
-  // Get a future on the containerized executor's Termination.
-  process::Future<containerizer::Termination> wait(
-      const ContainerID& containerId);
-
-  // Terminate the containerized executor.
-  void destroy(const ContainerID& containerId);
-
-  // Get all active container-id's.
-  process::Future<hashset<ContainerID>> containers();
-
-private:
-  // Startup flags.
-  const Flags flags;
-
-  // Information describing a container environment. A sandbox has to
-  // be prepared before the external containerizer can be invoked.
-  struct Sandbox
-  {
-    Sandbox(const std::string& directory, const Option<std::string>& user)
-      : directory(directory), user(user) {}
-
-    const std::string directory;
-    const Option<std::string> user;
-  };
-
-  // Information describing a running container.
-  struct Container
-  {
-    Container(const Option<Sandbox>& sandbox)
-      : sandbox(sandbox), pid(None()), destroying(false) {}
-
-    // Keep sandbox information available for subsequent containerizer
-    // invocations.
-    Option<Sandbox> sandbox;
-
-    // External containerizer pid as per wait-invocation.
-    // Wait should block on the external containerizer side, hence we
-    // need to keep its pid for terminating if needed.
-    Option<pid_t> pid;
-
-    process::Promise<containerizer::Termination> termination;
-
-    // Is set when container is being destroyed.
-    bool destroying;
-
-    // As described in MESOS-1251, we need to make sure that events
-    // that are triggered before launch has completed, are in fact
-    // queued until then to reduce complexity within external
-    // containerizer program implementations. To achieve that, we
-    // simply queue all events onto this promise.
-    // TODO(tillt): Consider adding a timeout when queuing onto this
-    // promise to account for external containerizer launch
-    // invocations that got stuck.
-    process::Promise<Nothing> launched;
-
-    Resources resources;
-  };
-
-  // Stores all active containers.
-  hashmap<ContainerID, process::Owned<Container>> actives;
-
-  process::Future<Nothing> _recover(
-      const Option<state::SlaveState>& state,
-      const process::Future<Option<int>>& future);
-
-  process::Future<Nothing> __recover(
-      const Option<state::SlaveState>& state,
-      const hashset<ContainerID>& containers);
-
-  process::Future<Nothing> ___recover();
-
-  process::Future<bool> _launch(
-      const ContainerID& containerId,
-      const process::Future<Option<int>>& future);
-
-  void __launch(
-      const ContainerID& containerId,
-      const process::Future<bool>& future);
-
-  process::Future<containerizer::Termination> _wait(
-      const ContainerID& containerId);
-
-  void __wait(
-      const ContainerID& containerId,
-      const process::Future<std::tuple<
-          process::Future<Result<containerizer::Termination>>,
-          process::Future<Option<int>>>>& future);
-
-  process::Future<Nothing> _update(
-      const ContainerID& containerId,
-      const Resources& resources);
-
-  process::Future<Nothing> __update(
-      const ContainerID& containerId,
-      const process::Future<Option<int>>& future);
-
-  process::Future<ResourceStatistics> _usage(
-      const ContainerID& containerId);
-
-  process::Future<ResourceStatistics> __usage(
-      const ContainerID& containerId,
-      const process::Future<std::tuple<
-          process::Future<Result<ResourceStatistics>>,
-          process::Future<Option<int>>>>& future);
-
-  void _destroy(const ContainerID& containerId);
-
-  void __destroy(
-      const ContainerID& containerId,
-      const process::Future<Option<int>>& future);
-
-  process::Future<hashset<ContainerID>> _containers(
-      const process::Future<std::tuple<
-          process::Future<Result<containerizer::Containers>>,
-          process::Future<Option<int>>>>& future);
-
-  // Abort a possibly pending "wait" in the external containerizer
-  // process.
-  void unwait(const ContainerID& containerId);
-
-  // Call back for when the containerizer has terminated all processes
-  // in the container.
-  void cleanup(const ContainerID& containerId);
-
-  // Invoke the external containerizer with the given command.
-  Try<process::Subprocess> invoke(
-      const std::string& command,
-      const Option<Sandbox>& sandbox = None(),
-      const Option<std::map<std::string, std::string>>& environment = None());
-
-  // Invoke the external containerizer with the given command and
-  // a protobuf message to be piped into its stdin.
-  // There can not be an Option<google::protobuf::Message> due to the
-  // pure virtual members of that class, hence this override is
-  // needed.
-  Try<process::Subprocess> invoke(
-      const std::string& command,
-      const google::protobuf::Message& message,
-      const Option<Sandbox>& sandbox = None(),
-      const Option<std::map<std::string, std::string>>& environment = None());
-};
-
-
-} // namespace slave {
-} // namespace internal {
-} // namespace mesos {
-
-#endif // __EXTERNAL_CONTAINERIZER_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/src/slave/flags.cpp
----------------------------------------------------------------------
diff --git a/src/slave/flags.cpp b/src/slave/flags.cpp
index b8ecc98..c07d6e5 100644
--- a/src/slave/flags.cpp
+++ b/src/slave/flags.cpp
@@ -94,9 +94,9 @@ mesos::internal::slave::Flags::Flags()
       "`cgroups/cpu,cgroups/mem`, or network/port_mapping\n"
       "(configure with flag: `--with-network-isolator` to enable),\n"
       "or `gpu/nvidia` for nvidia specific gpu isolation,\n"
-      "or `external`, or load an alternate isolator module using\n"
-      "the `--modules` flag. Note that this flag is only relevant\n"
-      "for the Mesos Containerizer.",
+      "or load an alternate isolator module using the `--modules`\n"
+      "flag. Note that this flag is only relevant for the Mesos\n"
+      "Containerizer.",
       "posix/cpu,posix/mem");
 
   add(&Flags::launcher,
@@ -500,25 +500,15 @@ mesos::internal::slave::Flags::Flags()
       "  ]\n"
       "}");
 
-  add(&Flags::containerizer_path,
-      "containerizer_path",
-      "The path to the external containerizer executable used when\n"
-      "external isolation is activated (`--isolation=external`).");
-
   add(&Flags::containerizers,
       "containerizers",
       "Comma-separated list of containerizer implementations\n"
       "to compose in order to provide containerization.\n"
-      "Available options are `mesos`, `external`, and\n"
-      "`docker` (on Linux). The order the containerizers\n"
-      "are specified is the order they are tried.\n",
+      "Available options are `mesos` and `docker` (on Linux).\n"
+      "The order the containerizers are specified is the order\n"
+      "they are tried.\n",
       "mesos");
 
-  add(&Flags::default_container_image,
-      "default_container_image",
-      "The default container image to use if not specified by a task,\n"
-      "when using external containerizer.");
-
   // Docker containerizer flags.
   add(&Flags::docker,
       "docker",

http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/src/slave/flags.hpp
----------------------------------------------------------------------
diff --git a/src/slave/flags.hpp b/src/slave/flags.hpp
index 58fba4a..ef2394c 100644
--- a/src/slave/flags.hpp
+++ b/src/slave/flags.hpp
@@ -103,9 +103,7 @@ public:
   Option<Firewall> firewall_rules;
   Option<Path> credential;
   Option<ACLs> acls;
-  Option<std::string> containerizer_path;
   std::string containerizers;
-  Option<std::string> default_container_image;
   std::string docker;
   Option<std::string> docker_mesos_image;
   Duration docker_remove_delay;

http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/src/tests/containerizer/external_containerizer_test.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/external_containerizer_test.cpp b/src/tests/containerizer/external_containerizer_test.cpp
deleted file mode 100644
index 226eac5..0000000
--- a/src/tests/containerizer/external_containerizer_test.cpp
+++ /dev/null
@@ -1,267 +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 <unistd.h>
-
-#include <string>
-#include <vector>
-#include <map>
-
-#include <gmock/gmock.h>
-
-#include <mesos/resources.hpp>
-
-#include <process/future.hpp>
-#include <process/owned.hpp>
-
-#include <stout/os.hpp>
-#include <stout/path.hpp>
-
-#include "master/master.hpp"
-
-#include "slave/flags.hpp"
-#include "slave/slave.hpp"
-
-#include "slave/containerizer/containerizer.hpp"
-#include "slave/containerizer/external_containerizer.hpp"
-
-#include "tests/mesos.hpp"
-#include "tests/flags.hpp"
-
-using namespace process;
-
-using mesos::internal::master::Master;
-using mesos::internal::slave::Containerizer;
-using mesos::internal::slave::Slave;
-
-using mesos::master::detector::MasterDetector;
-
-using std::string;
-using std::vector;
-
-using testing::_;
-using testing::DoAll;
-using testing::Return;
-using testing::Invoke;
-
-namespace mesos {
-namespace internal {
-namespace tests {
-
-// The external containerizer tests currently rely on a Python script
-// which needs the Mesos Python egg being built.
-// TODO(tillt): Consider providing tests that do not rely on Python.
-#ifdef MESOS_HAS_PYTHON
-
-// TODO(tillt): Update and enhance the ExternalContainerizer tests,
-// possibly following some of the patterns used within the
-// IsolatorTests or even entirely reusing the Containerizer tests.
-class ExternalContainerizerTest : public MesosTest {};
-
-
-class MockExternalContainerizer : public slave::ExternalContainerizer
-{
-public:
-  MOCK_METHOD8(
-      launch,
-      process::Future<bool>(
-          const ContainerID&,
-          const TaskInfo&,
-          const ExecutorInfo&,
-          const string&,
-          const Option<string>&,
-          const SlaveID&,
-          const process::PID<slave::Slave>&,
-          bool checkpoint));
-
-  MockExternalContainerizer(const slave::Flags& flags)
-    : ExternalContainerizer(flags)
-  {
-    // Set up defaults for mocked methods.
-    // NOTE: See TestContainerizer::setup for why we use
-    // 'EXPECT_CALL' and 'WillRepeatedly' here instead of
-    // 'ON_CALL' and 'WillByDefault'.
-    EXPECT_CALL(*this, launch(_, _, _, _, _, _, _, _))
-      .WillRepeatedly(Invoke(this, &MockExternalContainerizer::_launch));
-  }
-
-  process::Future<bool> _launch(
-      const ContainerID& containerId,
-      const TaskInfo& taskInfo,
-      const ExecutorInfo& executorInfo,
-      const string& directory,
-      const Option<string>& user,
-      const SlaveID& slaveId,
-      const PID<Slave>& slavePid,
-      bool checkpoint)
-  {
-    return slave::ExternalContainerizer::launch(
-        containerId,
-        taskInfo,
-        executorInfo,
-        directory,
-        user,
-        slaveId,
-        slavePid,
-        checkpoint);
-  }
-};
-
-
-// This test has been temporarily disabled due to MESOS-1257.
-TEST_F(ExternalContainerizerTest, DISABLED_Launch)
-{
-  Try<Owned<cluster::Master>> master = this->StartMaster();
-  ASSERT_SOME(master);
-
-  Flags testFlags;
-
-  slave::Flags flags = this->CreateSlaveFlags();
-
-  flags.isolation = "external";
-  flags.containerizer_path =
-    testFlags.build_dir + "/src/examples/python/test-containerizer";
-
-  MockExternalContainerizer containerizer(flags);
-
-  Owned<MasterDetector> detector = master.get()->createDetector();
-
-  Try<Owned<cluster::Slave>> slave =
-    this->StartSlave(detector.get(), &containerizer, 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);
-  AWAIT_READY(offers);
-
-  EXPECT_NE(0u, offers.get().size());
-
-  TaskInfo task;
-  task.set_name("isolator_test");
-  task.mutable_task_id()->set_value("1");
-  task.mutable_slave_id()->CopyFrom(offers.get()[0].slave_id());
-  task.mutable_resources()->CopyFrom(offers.get()[0].resources());
-
-  Resources resources(offers.get()[0].resources());
-  Option<Bytes> mem = resources.mem();
-  ASSERT_SOME(mem);
-  Option<double> cpus = resources.cpus();
-  ASSERT_SOME(cpus);
-
-  const string& file = path::join(flags.work_dir, "ready");
-
-  // This task induces user/system load in a child process by
-  // running top in a child process for ten seconds.
-  task.mutable_command()->set_value(
-#ifdef __APPLE__
-      // Use logging mode with 30,000 samples with no interval.
-      "top -l 30000 -s 0 2>&1 > /dev/null & "
-#else
-      // Batch mode, with 30,000 samples with no interval.
-      "top -b -d 0 -n 30000 2>&1 > /dev/null & "
-#endif
-      "touch " + file +  "; " // Signals that the top command is running.
-      "sleep 60");
-
-  Future<TaskStatus> status;
-  EXPECT_CALL(sched, statusUpdate(&driver, _))
-    .WillOnce(FutureArg<1>(&status))
-    .WillRepeatedly(Return()); // Ignore rest for now.
-
-  Future<ContainerID> containerId;
-  EXPECT_CALL(containerizer, launch(_, _, _, _, _, _, _, _))
-    .WillOnce(DoAll(FutureArg<0>(&containerId),
-                    Invoke(&containerizer,
-                           &MockExternalContainerizer::_launch)));
-
-  driver.launchTasks(offers.get()[0].id(), {task});
-
-  AWAIT_READY(containerId);
-
-  AWAIT_READY(status);
-
-  EXPECT_EQ(TASK_RUNNING, status.get().state());
-
-  // Wait for the task to begin inducing cpu time.
-  while (!os::exists(file));
-
-  ExecutorID executorId;
-  executorId.set_value(task.task_id().value());
-
-  // We'll wait up to 10 seconds for the child process to induce
-  // 1/8 of a second of user and system cpu time in total.
-  // TODO(bmahler): Also induce rss memory consumption, by re-using
-  // the balloon framework.
-  ResourceStatistics statistics;
-  Duration waited = Duration::zero();
-  do {
-    Future<ResourceStatistics> usage = containerizer.usage(containerId.get());
-    AWAIT_READY(usage);
-
-    statistics = usage.get();
-
-    // If we meet our usage expectations, we're done!
-    // NOTE: We are currently getting dummy-data from the test-
-    // containerizer python script matching these expectations.
-    // TODO(tillt): Consider working with real data.
-    if (statistics.cpus_user_time_secs() >= 0.120 &&
-        statistics.cpus_system_time_secs() >= 0.05 &&
-        statistics.mem_rss_bytes() >= 1024u) {
-      break;
-    }
-
-    os::sleep(Milliseconds(100));
-    waited += Milliseconds(100);
-  } while (waited < Seconds(10));
-
-  EXPECT_GE(statistics.cpus_user_time_secs(), 0.120);
-  EXPECT_GE(statistics.cpus_system_time_secs(), 0.05);
-  EXPECT_EQ(statistics.cpus_limit(), cpus.get());
-  EXPECT_GE(statistics.mem_rss_bytes(), 1024u);
-  EXPECT_EQ(statistics.mem_limit_bytes(), mem.get().bytes());
-
-  EXPECT_CALL(sched, statusUpdate(&driver, _))
-    .WillOnce(FutureArg<1>(&status));
-
-  driver.killTask(task.task_id());
-
-  AWAIT_READY(status);
-
-  EXPECT_EQ(TASK_KILLED, status.get().state());
-
-  driver.stop();
-  driver.join();
-}
-
-#endif // MESOS_HAS_PYTHON
-
-} // namespace tests {
-} // namespace internal {
-} // namespace mesos {


[2/2] mesos git commit: Removed the external containerizer.

Posted by ji...@apache.org.
Removed the external containerizer.

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


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

Branch: refs/heads/master
Commit: f6a0e72c5d142d247dfe5d5c5b6cfb1c3aa1b8cd
Parents: 751a481
Author: Gilbert Song <so...@gmail.com>
Authored: Tue Aug 9 13:13:32 2016 -0700
Committer: Jie Yu <yu...@gmail.com>
Committed: Tue Aug 9 13:13:32 2016 -0700

----------------------------------------------------------------------
 configure.ac                                    |    2 -
 docs/committers.md                              |    4 -
 docs/configuration.md                           |   28 +-
 docs/containerizer-internals.md                 |    1 -
 docs/external-containerizer.md                  |  501 -------
 docs/fetcher.md                                 |    4 +-
 docs/home.md                                    |    1 -
 docs/sandbox.md                                 |    1 -
 include/mesos/containerizer/containerizer.proto |   62 -
 src/CMakeLists.txt                              |    1 -
 src/Makefile.am                                 |    5 -
 src/examples/python/test-containerizer.in       |   37 -
 src/examples/python/test_containerizer.py       |  361 ------
 src/slave/containerizer/containerizer.cpp       |   42 +-
 .../containerizer/external_containerizer.cpp    | 1217 ------------------
 .../containerizer/external_containerizer.hpp    |  299 -----
 src/slave/flags.cpp                             |   22 +-
 src/slave/flags.hpp                             |    2 -
 .../external_containerizer_test.cpp             |  267 ----
 19 files changed, 13 insertions(+), 2844 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/configure.ac
----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index f82c631..57482d3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1656,8 +1656,6 @@ There are two possible workarounds for this issue:
                   [chmod +x src/examples/python/test-executor])
   AC_CONFIG_FILES([src/examples/python/test-framework],
                   [chmod +x src/examples/python/test-framework])
-  AC_CONFIG_FILES([src/examples/python/test-containerizer],
-                  [chmod +x src/examples/python/test-containerizer])
   AC_CONFIG_FILES([src/python/setup.py])
   AC_CONFIG_FILES([src/python/cli/setup.py])
   AC_CONFIG_FILES([src/python/interface/setup.py])

http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/docs/committers.md
----------------------------------------------------------------------
diff --git a/docs/committers.md b/docs/committers.md
index a4cf837..9c2cf6b 100644
--- a/docs/committers.md
+++ b/docs/committers.md
@@ -371,10 +371,6 @@ committers to learn about areas of the code that they are unfamiliar with.
         <td>Docker Containerizer</td>
         <td>Tim Chen, Benjamin Hindman</td>
       </tr>
-      <tr>
-        <td>External Containerizer</td>
-        <td>Till Toenshoff, Benjamin Hindman</td>
-      </tr>
     </tbody>
   </thead>
 </table>

http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/docs/configuration.md
----------------------------------------------------------------------
diff --git a/docs/configuration.md b/docs/configuration.md
index d6a7eb0..2090672 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -1146,21 +1146,12 @@ in the sandbox directory.
 </tr>
 <tr>
   <td>
-    --containerizer_path=VALUE
-  </td>
-  <td>
-The path to the external containerizer executable used when
-external isolation is activated (<code>--isolation=external</code>).
-  </td>
-</tr>
-<tr>
-  <td>
     --containerizers=VALUE
   </td>
   <td>
 Comma-separated list of containerizer implementations
 to compose in order to provide containerization.
-Available options are <code>mesos</code>, <code>external</code>, and
+Available options are <code>mesos</code> and
 <code>docker</code> (on Linux). The order the containerizers
 are specified is the order they are tried.
 (default: mesos)
@@ -1183,15 +1174,6 @@ Example:
 </tr>
 <tr>
   <td>
-    --default_container_image=VALUE
-  </td>
-  <td>
-The default container image to use if not specified by a task,
-when using external containerizer.
-  </td>
-</tr>
-<tr>
-  <td>
     --default_container_info=VALUE
   </td>
   <td>
@@ -1521,10 +1503,10 @@ Strategy for provisioning container rootfs from images, e.g., <code>aufs</code>,
 Isolation mechanisms to use, e.g., <code>posix/cpu,posix/mem</code>, or
 <code>cgroups/cpu,cgroups/mem</code>, or network/port_mapping
 (configure with flag: <code>--with-network-isolator</code> to enable),
-or `gpu/nvidia` for nvidia specific gpu isolation,
-or <code>external</code>, or load an alternate isolator module using
-the <code>--modules</code> flag. Note that this flag is only relevant
-for the Mesos Containerizer. (default: posix/cpu,posix/mem)
+or `gpu/nvidia` for nvidia specific gpu isolation, or load an alternate
+isolator module using the <code>--modules</code> flag. Note that this
+flag is only relevant for the Mesos Containerizer.
+(default: posix/cpu,posix/mem)
   </td>
 </tr>
 <tr>

http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/docs/containerizer-internals.md
----------------------------------------------------------------------
diff --git a/docs/containerizer-internals.md b/docs/containerizer-internals.md
index 1839c72..97776a6 100644
--- a/docs/containerizer-internals.md
+++ b/docs/containerizer-internals.md
@@ -33,7 +33,6 @@ Mesos currently supports the following containerizers:
 * Composing
 * [Docker](docker-containerizer.md)
 * [Mesos](containerizer.md)
-* [External](external-containerizer.md) (deprecated)
 
 #### Composing Containerizer
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/docs/external-containerizer.md
----------------------------------------------------------------------
diff --git a/docs/external-containerizer.md b/docs/external-containerizer.md
deleted file mode 100644
index eece9e7..0000000
--- a/docs/external-containerizer.md
+++ /dev/null
@@ -1,501 +0,0 @@
----
-title: Apache Mesos - External Containerizer
-layout: documentation
----
-
-# External Containerizer
-
-**NOTE:**  The external containerizer is deprecated. See
-[MESOS-3370](https://issues.apache.org/jira/browse/MESOS-3370) for details.
-
-* EC = external containerizer. A part of the mesos agent that provides
-an API for containerizing via external plugin executables.
-* ECP = external containerizer program. An external plugin executable
-implementing the actual containerizing by interfacing with a
-containerizing system (e.g. Docker).
-
-# Containerizing
-
-
-# General Overview
-
-EC invokes ECP as a shell process, passing the command as a parameter
-to the ECP executable. Additional data is exhanged via stdin and
-stdout.
-
-The ECP is expected to return a zero exit code for all commands it was
-able to process. A non-zero status code signals an error. Below you
-will find an overview of the commands that have to be implemented by
-an ECP, as well as their invocation scheme.
-
-The ECP is expected to be using stderr for state info and displaying
-additional debug information. That information is getting logged to
-a file, see [Enviroment: **Sandbox**](#sandbox).
-
-
-### Call and communication scheme
-
-Interface describing the functions an ECP has to implement via
-command calls. Many invocations on the ECP will also pass a
-protobuf message along via stdin. Some invocations on the ECP also
-expect to deliver a result protobuf message back via stdout.
-All protobuf messages are prefixed by their original length -
-this is sometimes referred to as "Record-IO"-format. See
-[Record-IO De/Serializing Example](#record-io-deserializing-example).
-
-**COMMAND < INPUT-PROTO > RESULT-PROTO**
-
-* `launch < containerizer::Launch`
-* `update < containerizer::Update`
-* `usage < containerizer::Usage > mesos::ResourceStatistics`
-* `wait < containerizer::Wait > containerizer::Termination`
-* `destroy < containerizer::Destroy`
-* `containers > containerizer::Containers`
-* `recover`
-
-
-# Command Ordering
-
-## Make no assumptions
-Commands may pretty much come in any order. There is only one
-exception to this rule; when launching a task, the EC will make sure
-that the ECP first receives a `launch` on that specific container, all
-other commands are queued until `launch` returns from the ECP.
-
-
-# Use Cases
-
-## Task Launching EC / ECP Overview
-
-* EC invokes `launch` on the ECP.
- * Along with that call, the ECP will receive a containerizer::Launch
- protobuf message via stdin.
- * ECP now makes sure the executor gets started.
-**Note** that `launch` is not supposed to block. It should return
-immediately after triggering the executor/command - that could be done
-via fork-exec within the ECP.
-* EC invokes `wait` on the ECP.
- * Along with that call, the ECP will receive a containerizer::Wait
- protobuf message via stdin.
- * ECP now blocks until the launched command is reaped - that could be
- implemented via waitpid within the ECP.
- * Once the command is reaped, the ECP should deliver a
- containerizer::Termination protobuf message via stdout, back to the
- EC.
-
-
-## Container Lifecycle Sequence Diagrams
-
-
-### Container Launching
-
-A container is in a staging state and now gets started and observed
-until it gets into a final state.
-
-![Container Launching Scheme](images/ec_launch_seqdiag.png?raw=true)
-
-### Container Running
-
-A container has gotten launched at some point and now is considered
-being in a non terminal state by the agent. The following commands
-will get triggered multiple times at the ECP over the lifetime of a
-container. Their order however is not determined.
-
-![Container Running Scheme](images/ec_lifecycle_seqdiag.png?raw=true)
-
-### Resource Limitation
-
-While a container is active, a resource limitation was identified
-(e.g. out of memory) by the ECP isolation mechanism of choice.
-
-![Resource Limitation Scheme](images/ec_kill_seqdiag.png?raw=true)
-
-<a name="agent-recovery-overview"></a>
-## Agent Recovery Overview
-
-* Agent recovers via check pointed state.
-* EC invokes `recover` on the ECP - there is no protobuf message sent
-or expected as a result from this command.
- * The ECP may try to recover internal states via its own failover
-mechanisms, if needed.
-* After `recover` returns, the EC will invoke `containers` on the ECP.
- * The ECP should return Containers which is a list of currently
- active containers.
-**Note** these containers are known to the ECP but might in fact
-partially be unknown to the agent (e.g. agent failed after launch but
-before or within wait) - those containers are considered to be
-orphans.
-* The EC now compares the list of agent known containers to those
-listed within `Containers`. For each orphan it identifies, the agent
-will invoke a `wait` followed by a `destroy` on the ECP for those
-containers.
-* Agent will now call `wait` on the ECP (via EC) for all recovered
-containers. This does once again put `wait` into the position of the
-ultimate command reaper.
-
-
-## Agent Recovery Sequence Diagram
-
-### Recovery
-
-While containers are active, the agent fails over.
-
-![Recovery Scheme](images/ec_recover_seqdiag.png?raw=true)
-
-### Orphan Destruction
-
-Containers identified by the ECP as being active but not agent state
-recoverable are getting terminated.
-
-![Orphan Destruction Scheme](images/ec_orphan_seqdiag.png?raw=true)
-
-
-# Command Details
-
-## launch
-### Start the containerized executor
-
-Hands over all information the ECP needs for launching a task
-via an executor.
-This call should not wait for the executor/command to return. The
-actual reaping of the containerized command is done via the `wait`
-call.
-
-    launch < containerizer::Launch
-
-This call receives the containerizer::Launch protobuf via stdin.
-
-    /**
-     * Encodes the launch command sent to the external containerizer
-     * program.
-     */
-    message Launch {
-      required ContainerID container_id = 1;
-      optional TaskInfo task_info = 2;
-      optional ExecutorInfo executor_info = 3;
-      optional string directory = 4;
-      optional string user = 5;
-      optional SlaveID agent_id = 6;
-      optional string agent_pid = 7;
-      optional bool checkpoint = 8;
-    }
-
-This call does not return any data via stdout.
-
-## wait
-### Gets information on the containerized executor's Termination
-
-Is expected to reap the executor/command. This call should block
-until the executor/command has terminated.
-
-    wait < containerizer::Wait > containerizer::Termination
-
-This call receives the containerizer::Wait protobuf via stdin.
-
-    /**
-     * Encodes the wait command sent to the external containerizer
-     * program.
-     */
-    message Wait {
-      required ContainerID container_id = 1;
-    }
-
-This call is expected to return containerizer::Termination via stdout.
-
-    /**
-     * Information about a container termination, returned by the
-     * containerizer to the agent.
-     */
-    message Termination {
-      // A container may be killed if it exceeds its resources; this will
-      // be indicated by killed=true and described by the message string.
-      required bool killed = 1;
-      required string message = 2;
-
-      // Exit status of the process.
-      optional int32 status = 3;
-    }
-
-The Termination attribute `killed` is to be set only when the
-containerizer or the underlying isolation had to enforce a limitation
-by killing the task (e.g. task exceeded suggested memory limit).
-
-## update
-### Updates the container's resource limits
-
-Is sending (new) resource constraints for the given container.
-Resource constraints onto a container may vary over the lifetime of
-the containerized task.
-
-    update < containerizer::Update
-
-This call receives the containerizer::Update protobuf via stdin.
-
-    /**
-     * Encodes the update command sent to the external containerizer
-     * program.
-     */
-    message Update {
-      required ContainerID container_id = 1;
-      repeated Resource resources = 2;
-    }
-
-This call does not return any data via stdout.
-
-## usage
-### Gathers resource usage statistics for a containerized task
-Is used for polling the current resource uses for the given container.
-
-    usage < containerizer::Usage > mesos::ResourceStatistics
-
-This call received the containerizer::Usage protobuf via stdin.
-
-    /**
-     * Encodes the usage command sent to the external containerizer
-     * program.
-     */
-    message Usage {
-      required ContainerID container_id = 1;
-    }
-
-This call is expected to return mesos::ResourceStatistics via stdout.
-
-    /*
-     * A snapshot of resource usage statistics.
-     */
-    message ResourceStatistics {
-      required double timestamp = 1; // Snapshot time, in seconds since the Epoch.
-
-      // CPU Usage Information:
-      // Total CPU time spent in user mode, and kernel mode.
-      optional double cpus_user_time_secs = 2;
-      optional double cpus_system_time_secs = 3;
-
-      // Number of CPUs allocated.
-      optional double cpus_limit = 4;
-
-      // cpu.stat on process throttling (for contention issues).
-      optional uint32 cpus_nr_periods = 7;
-      optional uint32 cpus_nr_throttled = 8;
-      optional double cpus_throttled_time_secs = 9;
-
-      // Memory Usage Information:
-      optional uint64 mem_rss_bytes = 5; // Resident Set Size.
-
-      // Amount of memory resources allocated.
-      optional uint64 mem_limit_bytes = 6;
-
-      // Broken out memory usage information (files, anonymous, and mmaped files)
-      optional uint64 mem_file_bytes = 10;
-      optional uint64 mem_anon_bytes = 11;
-      optional uint64 mem_mapped_file_bytes = 12;
-    }
-
-## destroy
-### Terminates the containerized executor
-
-Is used in rare situations, like for graceful agent shutdown
-but also in agent fail over scenarios - see Agent Recovery for more.
-
-    destroy < containerizer::Destroy
-
-This call receives the containerizer::Destroy protobuf via stdin.
-
-    /**
-     * Encodes the destroy command sent to the external containerizer
-     * program.
-     */
-    message Destroy {
-      required ContainerID container_id = 1;
-    }
-
-This call does not return any data via stdout.
-
-## containers
-### Gets all active container-id's
-
-Returns all container identifiers known to be currently active.
-
-    containers > containerizer::Containers
-
-This call does not receive any additional data via stdin.
-
-This call is expected to pass containerizer::Containers back via
-stdout.
-
-    /**
-     * Information on all active containers returned by the containerizer
-     * to the agent.
-     */
-    message Containers {
-      repeated ContainerID containers = 1;
-    }
-
-
-## recover
-### Internal ECP state recovery
-
-Allows the ECP to do a state recovery on its own. If the ECP
-uses state check-pointing e.g. via file system, then this call would
-be a good moment to de-serialize that state information. Make sure you
-also see [Agent Recovery Overview](#agent-recovery-overview) for more.
-
-    recover
-
-This call does not receive any additional data via stdin.
-No returned data via stdout.
-
-
-
-### Protobuf Message Definitions
-
-For possibly more up-to-date versions of the above mentioned protobufs
-as well as protobuf messages referenced by them, please check:
-
-* containerizer::XXX are defined within
-  include/mesos/containerizer/containerizer.proto.
-
-* mesos::XXX are defined within include/mesos/mesos.proto.
-
-
-
-# Environment
-
-<a name="sandbox"></a>
-## **Sandbox**
-
-A sandbox environment is formed by `cd` into the work-directory of the
-executor as well as a stderr redirect into the executor's "stderr"
-log-file.
-**Note** not **all** invocations have a complete sandbox environment.
-
-
-## Addional Environment Variables
-
-Additionally, there are a few new environment variables set when
-invoking the ECP.
-
-
-* MESOS_LIBEXEC_DIRECTORY = path to mesos-executor, mesos-usage, ...
-This information is always present.
-
-* MESOS_WORK_DIRECTORY = agent work directory. This should be used for
-distinguishing agent instances.
-This information is always present.
-
-**Note** that this is specifically helpful for being able to tie a set
-of containers to a specific agent instance, thus allowing proper
-recovery when needed.
-
-* MESOS_DEFAULT_CONTAINER_IMAGE = default image as provided via agent
-flags (default_container_image). This variable is provided only in
-calls to `launch`.
-
-
-
-# Debugging
-
-<a name="enhanced-verbosity-logging"></a>
-## Enhanced Verbosity Logging
-
-For receiving an increased level of status information from the EC
-use the GLOG verbosity level. Prefix your mesos startup call by
-setting the level to a value higher than or equal to two.
-
-`GLOG_v=2 ./bin/mesos-agent --master=[...]`
-
-
-## ECP stderr Logging
-
-All output to stderr of your ECP will get logged to the executor's
-'stderr' log file.
-The specific location can be extracted from the [Enhanced Verbosity
-Logging](#enhanced-verbosity-logging) of the EC.
-
-Example Log Output:
-
-    I0603 02:12:34.165662 174215168 external_containerizer.cpp:1083] Invoking external containerizer for method 'launch'
-    I0603 02:12:34.165675 174215168 external_containerizer.cpp:1100] calling: [/Users/till/Development/mesos-till/build/src/test-containerizer launch]
-    I0603 02:12:34.165678 175824896 agent.cpp:497] Successfully attached file '/tmp/ExternalContainerizerTest_Launch_lP22ci/agents/20140603-021232-16777343-51377-7591-0/frameworks/20140603-021232-16777343-51377-7591-0000/executors/1/runs/558e0a69-70da-4d71-b4c4-c2820b1d6345'
-    I0603 02:12:34.165686 174215168 external_containerizer.cpp:1101] directory: /tmp/ExternalContainerizerTest_Launch_lP22ci/agents/20140603-021232-16777343-51377-7591-0/frameworks/20140603-021232-16777343-51377-7591-0000/executors/1/runs/558e0a69-70da-4d71-b4c4-c2820b1d6345
-
-The stderr output of the ECP for this call is found within the stderr file located in the directory displayed in the last quoted line.
-
-    cat /tmp/ExternalContainerizerTest_Launch_lP22ci/agents/20140603-021232-16777343-51377-7591-0/frameworks/20140603-021232-16777343-51377-7591-0000/executors/1/runs/558e0a69-70da-4d71-b4c4-c2820b1d6345/stderr
-
-
-# Appendix
-
-## Record-IO Proto Example: Launch
-
-This is what a properly record-io formatted protobuf looks like.
-
-**name:    offset**
-
-* length: 00 - 03 = record length in byte
-
-* payload: 04 - (length + 4) = protobuf payload
-
-Example length: 00000240h = 576 byte total protobuf size
-
-Example Hexdump:
-
-    00000000:  4002 0000 0a26 0a24 3433 3532 3533 6162 2d64 3234 362d 3437  :@....&.$435253ab-d246-47
-    00000018:  6265 2d61 3335 302d 3335 3432 3034 3635 6438 3638 1a81 020a  :be-a350-35420465d868....
-    00000030:  030a 0131 2a16 0a04 6370 7573 1000 1a09 0900 0000 0000 0000  :...1*...cpus............
-    00000048:  4032 012a 2a15 0a03 6d65 6d10 001a 0909 0000 0000 0000 9040  :@2.**...mem............@
-    00000060:  3201 2a2a 160a 0464 6973 6b10 001a 0909 0000 0000 0000 9040  :2.**...disk............@
-    00000078:  3201 2a2a 180a 0570 6f72 7473 1001 220a 0a08 0898 f201 1080  :2.**...ports..".........
-    00000090:  fa01 3201 2a3a 2a1a 2865 6368 6f20 274e 6f20 7375 6368 2066  :..2.*:*.(echo 'No such f
-    000000a8:  696c 6520 6f72 2064 6972 6563 746f 7279 273b 2065 7869 7420  :ile or directory'; exit
-    000000c0:  3142 2b0a 2932 3031 3430 3532 362d 3031 3530 3036 2d31 3637  :1B+.)20140526-015006-167
-    000000d8:  3737 3334 332d 3535 3430 332d 3632 3536 372d 3030 3030 4a3d  :77343-55403-62567-0000J=
-    000000f0:  436f 6d6d 616e 6420 4578 6563 7574 6f72 2028 5461 736b 3a20  :Command Executor (Task:
-    00000108:  3129 2028 436f 6d6d 616e 643a 2073 6820 2d63 2027 7768 696c  :1) (Command: sh -c 'whil
-    00000120:  6520 7472 7565 203b 2e2e 2e27 2952 0131 22c5 012f 746d 702f  :e true ;...')R.1"../tmp/
-    00000138:  4578 7465 726e 616c 436f 6e74 6169 6e65 7269 7a65 7254 6573  :ExternalContainerizerTes
-    00000150:  745f 4c61 756e 6368 5f6c 5855 6839 662f 736c 6176 6573 2f32  :t_Launch_lXUh9f/agents/2
-    00000168:  3031 3430 3532 362d 3031 3530 3036 2d31 3637 3737 3334 332d  :0140526-015006-16777343-
-    00000180:  3535 3430 332d 3632 3536 372d 302f 6672 616d 6577 6f72 6b73  :55403-62567-0/frameworks
-    00000198:  2f32 3031 3430 3532 362d 3031 3530 3036 2d31 3637 3737 3334  :/20140526-015006-1677734
-    000001b0:  332d 3535 3430 332d 3632 3536 372d 3030 3030 2f65 7865 6375  :3-55403-62567-0000/execu
-    000001c8:  746f 7273 2f31 2f72 756e 732f 3433 3532 3533 6162 2d64 3234  :tors/1/runs/435253ab-d24
-    000001e0:  362d 3437 6265 2d61 3335 302d 3335 3432 3034 3635 6438 3638  :6-47be-a350-35420465d868
-    000001f8:  2a04 7469 6c6c 3228 0a26 3230 3134 3035 3236 2d30 3135 3030  :*.till2(.&20140526-01500
-    00000210:  362d 3136 3737 3733 3433 2d35 3534 3033 2d36 3235 3637 2d30  :6-16777343-55403-62567-0
-    00000228:  3a18 736c 6176 6528 3129 4031 3237 2e30 2e30 2e31 3a35 3534  ::.slave(1)@127.0.0.1:554
-    00000240:  3033 4000
-
-<a name="record-io-deserializing-example"></a>
-## Record-IO De/Serializing Example
-How to send and receive such record-io formatted message
-using Python
-
-*taken from src/examples/python/test_containerizer.py*
-
-    # Read a data chunk prefixed by its total size from stdin.
-    def receive():
-        # Read size (uint32 => 4 bytes).
-        size = struct.unpack('I', sys.stdin.read(4))
-        if size[0] <= 0:
-            print >> sys.stderr, "Expected protobuf size over stdin. " \
-                             "Received 0 bytes."
-            return ""
-
-        # Read payload.
-        data = sys.stdin.read(size[0])
-        if len(data) != size[0]:
-            print >> sys.stderr, "Expected %d bytes protobuf over stdin. " \
-                             "Received %d bytes." % (size[0], len(data))
-            return ""
-
-        return data
-
-    # Write a protobuf message prefixed by its total size (aka recordio)
-    # to stdout.
-    def send(data):
-        # Write size (uint32 => 4 bytes).
-        sys.stdout.write(struct.pack('I', len(data)))
-
-        # Write payload.
-        sys.stdout.write(data)

http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/docs/fetcher.md
----------------------------------------------------------------------
diff --git a/docs/fetcher.md b/docs/fetcher.md
index 6889845..d4ffd52 100644
--- a/docs/fetcher.md
+++ b/docs/fetcher.md
@@ -34,9 +34,7 @@ The Mesos fetcher mechanism comprises of these two parts:
 
 1. The agent-internal Fetcher Process (in terms of libprocess) that controls and
 coordinates all fetch actions. Every agent instance has exactly one internal
-fetcher instance that is used by every kind of containerizer (except the
-external containerizer variant, which is responsible for its own approach to
-fetching).
+fetcher instance that is used by every kind of containerizer.
 
 2. The external program `mesos-fetcher` that is invoked by the former. It
 performs all network and disk operations except file deletions and file size

http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/docs/home.md
----------------------------------------------------------------------
diff --git a/docs/home.md b/docs/home.md
index 80451f4..c8aeaef 100644
--- a/docs/home.md
+++ b/docs/home.md
@@ -22,7 +22,6 @@ layout: documentation
     * [Docker Volume Support](docker-volume.md)
     * [CNI support](cni.md)
   * [Docker Containerizer](docker-containerizer.md) for launching a Docker image as a Task, or as an Executor.
-  * [External Containerizer](external-containerizer.md) for custom containerization implementations (deprecated).
 * [Roles](roles.md)
 * [Weights](weights.md)
 * [Authentication](authentication.md)

http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/docs/sandbox.md
----------------------------------------------------------------------
diff --git a/docs/sandbox.md b/docs/sandbox.md
index db973e3..af0036d 100644
--- a/docs/sandbox.md
+++ b/docs/sandbox.md
@@ -150,7 +150,6 @@ executor and isolators:
 * Docker containerizer - As of Docker `1.9.1`, the Docker containerizer
   does not enforce nor support a disk quota.  See the
   [Docker issue](https://github.com/docker/docker/issues/3804).
-* [External containerizer](external-containerizer.md).
 
 ## Sandbox lifecycle
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/include/mesos/containerizer/containerizer.proto
----------------------------------------------------------------------
diff --git a/include/mesos/containerizer/containerizer.proto b/include/mesos/containerizer/containerizer.proto
index 4d8e827..7c35ef3 100644
--- a/include/mesos/containerizer/containerizer.proto
+++ b/include/mesos/containerizer/containerizer.proto
@@ -23,59 +23,6 @@ option java_outer_classname = "Protos";
 
 
 /**
- * Encodes the launch command sent to the external containerizer
- * program.
- */
-message Launch {
-  required ContainerID container_id = 1;
-  optional TaskInfo task_info = 2;
-  optional ExecutorInfo executor_info = 3;
-  optional string directory = 4;
-  optional string user = 5;
-  optional SlaveID slave_id = 6;
-  optional string slave_pid = 7;
-  optional bool checkpoint = 8;
-}
-
-
-/**
- * Encodes the update command sent to the external containerizer
- * program.
- */
-message Update {
-  required ContainerID container_id = 1;
-  repeated Resource resources = 2;
-}
-
-
-/**
- * Encodes the wait command sent to the external containerizer
- * program.
- */
-message Wait {
-  required ContainerID container_id = 1;
-}
-
-
-/**
- * Encodes the destroy command sent to the external containerizer
- * program.
- */
-message Destroy {
-  required ContainerID container_id = 1;
-}
-
-
-/**
- * Encodes the usage command sent to the external containerizer
- * program.
- */
-message Usage {
-  required ContainerID container_id = 1;
-}
-
-
-/**
  * Information about a container termination, returned by the
  * containerizer to the slave.
  */
@@ -89,12 +36,3 @@ message Termination {
   repeated TaskStatus.Reason reasons = 5;
   optional string message = 2;
 }
-
-
-/**
- * Information on all active containers returned by the containerizer
- * to the slave.
- */
-message Containers {
-  repeated ContainerID containers = 1;
-}

http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/src/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 1286ee0..6088c26 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -315,7 +315,6 @@ set(AGENT_SRC
   slave/containerizer/mesos/launch.cpp
   slave/containerizer/fetcher.cpp
   slave/containerizer/containerizer.cpp
-  slave/containerizer/external_containerizer.cpp
   slave/containerizer/composing.cpp
   slave/containerizer/mesos/containerizer.cpp
   slave/container_logger.cpp

http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index 39e3199..cf76a69 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -843,7 +843,6 @@ libmesos_no_3rdparty_la_SOURCES +=					\
   slave/containerizer/composing.cpp					\
   slave/containerizer/containerizer.cpp					\
   slave/containerizer/docker.cpp					\
-  slave/containerizer/external_containerizer.cpp			\
   slave/containerizer/fetcher.cpp					\
   slave/containerizer/mesos/containerizer.cpp				\
   slave/containerizer/mesos/isolator.cpp				\
@@ -967,7 +966,6 @@ libmesos_no_3rdparty_la_SOURCES +=					\
   slave/containerizer/composing.hpp					\
   slave/containerizer/containerizer.hpp					\
   slave/containerizer/docker.hpp					\
-  slave/containerizer/external_containerizer.hpp			\
   slave/containerizer/fetcher.hpp					\
   slave/containerizer/mesos/constants.hpp				\
   slave/containerizer/mesos/containerizer.hpp				\
@@ -2156,7 +2154,6 @@ mesos_tests_SOURCES =						\
   tests/containerizer/docker_containerizer_tests.cpp		\
   tests/containerizer/docker_spec_tests.cpp			\
   tests/containerizer/docker_tests.cpp				\
-  tests/containerizer/external_containerizer_test.cpp		\
   tests/containerizer/isolator_tests.cpp			\
   tests/containerizer/launcher.cpp				\
   tests/containerizer/memory_test_helper.cpp			\
@@ -2248,8 +2245,6 @@ if HAS_PYTHON
 mesos_tests_DEPENDENCIES += $(MESOS_EGG)
 
 EXAMPLESCRIPTSPYTHON =						\
-  examples/python/test_containerizer.py				\
-  examples/python/test-containerizer				\
   examples/python/test_executor.py				\
   examples/python/test-executor					\
   examples/python/test_framework.py				\

http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/src/examples/python/test-containerizer.in
----------------------------------------------------------------------
diff --git a/src/examples/python/test-containerizer.in b/src/examples/python/test-containerizer.in
deleted file mode 100644
index 5066b45..0000000
--- a/src/examples/python/test-containerizer.in
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/usr/bin/env bash
-
-# This script uses MESOS_SOURCE_DIR and MESOS_BUILD_DIR which come
-# from configuration substitutions.
-MESOS_SOURCE_DIR=@abs_top_srcdir@
-MESOS_BUILD_DIR=@abs_top_builddir@
-
-# Use colors for errors.
-. ${MESOS_SOURCE_DIR}/support/colors.sh
-
-# Force the use of the Python interpreter configured during building.
-test ! -z "${PYTHON}" && \
-  echo "${RED}Ignoring PYTHON environment variable (using @PYTHON@)${NORMAL}"
-
-PYTHON=@PYTHON@
-
-SETUPTOOLS=`echo ${MESOS_BUILD_DIR}/3rdparty/setuptools-*/`
-
-# Just warn in the case when build with --disable-bundled.
-test ! -e ${SETUPTOOLS} && \
-  echo "${RED}Failed to find ${SETUPTOOLS}${NORMAL}"
-
-PROTOBUF=`echo ${MESOS_BUILD_DIR}/3rdparty/protobuf-*/python/`
-
-test ! -e ${PROTOBUF} && \
-  echo "${RED}Failed to find ${PROTOBUF}${NORMAL}"
-
-MESOS_EGGS=$(find ${MESOS_BUILD_DIR}/src/python/dist -name "*.egg" | tr "\\n" ":")
-
-SCRIPT=${MESOS_SOURCE_DIR}/src/examples/python/test_containerizer.py
-
-test ! -e ${SCRIPT} && \
-  echo "${RED}Failed to find ${SCRIPT}${NORMAL}" && \
-  exit 1
-
-PYTHONPATH="${SETUPTOOLS}:${PROTOBUF}:${MESOS_EGGS}" \
-  exec ${PYTHON} ${SCRIPT} "${@}"

http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/src/examples/python/test_containerizer.py
----------------------------------------------------------------------
diff --git a/src/examples/python/test_containerizer.py b/src/examples/python/test_containerizer.py
deleted file mode 100644
index 8e154b0..0000000
--- a/src/examples/python/test_containerizer.py
+++ /dev/null
@@ -1,361 +0,0 @@
-#!/usr/bin/env python
-
-# 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.
-
-# The scheme an external containerizer has to adhere to is;
-#
-# COMMAND < INPUT-PROTO > RESULT-PROTO
-#
-# launch < Launch
-# update < Update
-# usage < Usage > ResourceStatistics
-# wait < Wait > Termination
-# destroy < Destroy
-# containers > Containers
-# recover
-#
-# 'wait' is expected to block until the task command/executor has
-# terminated.
-
-import fcntl
-import multiprocessing
-import os
-import signal
-import subprocess
-import sys
-import struct
-import time
-import google
-
-from mesos.interface import containerizer_pb2
-from mesos.interface import mesos_pb2
-
-# Render a string describing how to use this script.
-def use(argv0, methods):
-    out = "Usage: %s <command>\n" % argv0
-    out += "Valid commands: " + ', '.join(methods)
-
-    return out
-
-
-# Read a data chunk prefixed by its total size from stdin.
-def receive():
-    # Read size (uint32 => 4 bytes).
-    size = struct.unpack('I', sys.stdin.read(4))
-    if size[0] <= 0:
-        print >> sys.stderr, "Expected protobuf size over stdin. " \
-                             "Received 0 bytes."
-        return ""
-
-    # Read payload.
-    data = sys.stdin.read(size[0])
-    if len(data) != size[0]:
-        print >> sys.stderr, "Expected %d bytes protobuf over stdin. " \
-                             "Received %d bytes." % (size[0], len(data))
-        return ""
-
-    return data
-
-
-# Write a protobuf message prefixed by its total size (aka recordio)
-# to stdout.
-def send(data):
-    # Write size (uint32 => 4 bytes).
-    sys.stdout.write(struct.pack('I', len(data)))
-
-    # Write payload.
-    sys.stdout.write(data)
-
-
-# Start a containerized executor. Expects to receive an Launch
-# protobuf via stdin.
-def launch():
-    try:
-        data = receive()
-        if len(data) == 0:
-            return 1
-
-        launch = containerizer_pb2.Launch()
-        launch.ParseFromString(data)
-
-        if launch.task_info.HasField("executor"):
-            command = ["sh",
-                       "-c",
-                       launch.task_info.executor.command.value]
-        else:
-            print >> sys.stderr, "No executor passed; using mesos-executor!"
-            executor = os.path.join(os.environ['MESOS_LIBEXEC_DIRECTORY'],
-                                    "mesos-executor")
-            command = ["sh",
-                       "-c",
-                       executor]
-            print >> sys.stderr, "command " + str(command)
-
-        lock_dir = os.path.join("/tmp/mesos-test-containerizer",
-                                launch.container_id.value)
-        subprocess.check_call(["mkdir", "-p", lock_dir])
-
-        # Fork a child process for allowing a blocking wait.
-        pid = os.fork()
-        if pid == 0:
-            # We are in the child.
-            proc = subprocess.Popen(command, env=os.environ.copy())
-
-            # Wait and serialize the process status when done.
-            lock = os.path.join(lock_dir, "wait")
-            with open(lock, "w+") as lk:
-                fcntl.flock(lk, fcntl.LOCK_EX)
-
-                status = proc.wait()
-
-                lk.write(str(status) + "\n")
-
-            sys.exit(status)
-        else:
-            # We are in the parent.
-
-            # Serialize the subprocess pid.
-            lock = os.path.join(lock_dir, "pid")
-            with open(lock, "w+") as lk:
-                fcntl.flock(lk, fcntl.LOCK_EX)
-
-                lk.write(str(pid) + "\n")
-
-    except google.protobuf.message.DecodeError:
-        print >> sys.stderr, "Could not deserialise Launch protobuf"
-        return 1
-
-    except OSError as e:
-        print >> sys.stderr, e.strerror
-        return 1
-
-    except ValueError:
-        print >> sys.stderr, "Value is invalid"
-        return 1
-
-    return 0
-
-
-# Update the container's resources.
-# Expects to receive a Update protobuf via stdin.
-def update():
-    try:
-        data = receive()
-        if len(data) == 0:
-            return 1
-
-        update = containerizer_pb2.Update()
-        update.ParseFromString(data)
-
-        print >> sys.stderr, "Received "                \
-                           + str(len(update.resources)) \
-                           + " resource elements."
-
-    except google.protobuf.message.DecodeError:
-        print >> sys.stderr, "Could not deserialise Update protobuf."
-        return 1
-
-    except OSError as e:
-        print >> sys.stderr, e.strerror
-        return 1
-
-    except ValueError:
-        print >> sys.stderr, "Value is invalid"
-        return 1
-
-    return 0
-
-
-# Gather resource usage statistics for the containerized executor.
-# Delivers an ResourceStatistics protobut via stdout when
-# successful.
-def usage():
-    try:
-        data = receive()
-        if len(data) == 0:
-            return 1
-        usage = containerizer_pb2.Usage()
-        usage.ParseFromString(data)
-
-        statistics = mesos_pb2.ResourceStatistics()
-
-        statistics.timestamp = time.time()
-
-        # Return hardcoded dummy statistics.
-        # TODO(tillt): Make use of mesos-usage here for capturing real
-        # statistics.
-        statistics.mem_rss_bytes = 1073741824
-        statistics.mem_limit_bytes = 1073741824
-        statistics.cpus_limit = 2
-        statistics.cpus_user_time_secs = 0.12
-        statistics.cpus_system_time_secs = 0.5
-
-        send(statistics.SerializeToString())
-
-    except google.protobuf.message.DecodeError:
-        print >> sys.stderr, "Could not deserialise Usage protobuf."
-        return 1
-
-    except google.protobuf.message.EncodeError:
-        print >> sys.stderr, "Could not serialise ResourceStatistics protobuf."
-        return 1
-
-    except OSError as e:
-        print >> sys.stderr, e.strerror
-        return 1
-
-    return 0
-
-
-# Terminate the containerized executor.
-def destroy():
-    try:
-        data = receive()
-        if len(data) == 0:
-            return 1
-        destroy = containerizer_pb2.Destroy()
-        destroy.ParseFromString(data)
-
-        lock_dir = os.path.join("/tmp/mesos-test-containerizer",
-                                destroy.container_id.value)
-        lock = os.path.join(lock_dir, "pid")
-
-        # Obtain our shared lock once it becomes available, read
-        # the pid and kill that process.
-        with open(lock, "r") as lk:
-            fcntl.flock(lk, fcntl.LOCK_SH)
-
-            pid = int(lk.read())
-
-            os.kill(pid, signal.SIGKILL)
-
-    except google.protobuf.message.DecodeError:
-        print >> sys.stderr, "Could not deserialise Destroy protobuf."
-        return 1
-
-    except OSError as e:
-        print >> sys.stderr, e.strerror
-        return 1
-
-    return 0
-
-
-# Recover all containerized executors states.
-def recover():
-
-    # This currently does not try to recover any internal state and
-    # therefore is to be regarded as being not complete.
-    # A complete implementation would attempt to recover all active
-    # containers by deserializing all previously checkpointed
-    # ContainerIDs.
-
-    return 0
-
-
-# Get the containerized executor's Termination.
-# Delivers a Termination protobuf filled with the information
-# gathered from launch's wait via stdout.
-def wait():
-    try:
-        data = receive()
-        if len(data) == 0:
-            return 1
-        wait = containerizer_pb2.Wait()
-        wait.ParseFromString(data)
-
-        lock_dir = os.path.join("/tmp/mesos-test-containerizer",
-                                wait.container_id.value)
-        lock = os.path.join(lock_dir, "wait")
-
-        # Obtain our shared lock once it becomes available and read
-        # the status code.
-        with open(lock, "r") as lk:
-            fcntl.flock(lk, fcntl.LOCK_SH)
-            status = int(lk.read())
-
-        # Deliver the termination protobuf back to the slave.
-        termination = containerizer_pb2.Termination()
-        termination.killed = false
-        termination.status = status
-        termination.message = ""
-
-        send(termination.SerializeToString())
-
-    except google.protobuf.message.DecodeError:
-        print >> sys.stderr, "Could not deserialise Termination protobuf."
-        return 1
-
-    except google.protobuf.message.EncodeError:
-        print >> sys.stderr, "Could not serialise Termination protobuf."
-        return 1
-
-    except OSError as e:
-        print >> sys.stderr, e.strerror
-        return 1
-
-    return 0
-
-
-def containers():
-    try:
-        containers = containerizer_pb2.Containers()
-
-        # This currently does not fill in any active containers and
-        # therefore is to be regarded as being not complete.
-        # A complete implementation would fill the containers message
-        # with all active ContainerIDs.
-
-        send(containers.SerializeToString())
-
-    except google.protobuf.message.EncodeError:
-        print >> sys.stderr, "Could not serialise Containers protobuf."
-        return 1
-
-    except OSError as e:
-        print >> sys.stderr, e.strerror
-        return 1
-
-    return 0
-
-
-if __name__ == "__main__":
-    methods = { "launch":       launch,
-                "update":       update,
-                "destroy":      destroy,
-                "containers":   containers,
-                "recover":      recover,
-                "usage":        usage,
-                "wait":         wait }
-
-    if sys.argv[1:2] == ["--help"] or sys.argv[1:2] == ["-h"]:
-        print use(sys.argv[0], methods.keys())
-        sys.exit(0)
-
-    if len(sys.argv) < 2:
-        print >> sys.stderr, "Please pass a command"
-        print >> sys.stderr, use(sys.argv[0], methods.keys())
-        sys.exit(1)
-
-    command = sys.argv[1]
-    if command not in methods:
-        print >> sys.stderr, "Invalid command passed"
-        print >> sys.stderr, use(sys.argv[0], methods.keys())
-        sys.exit(2)
-
-    method = methods.get(command)
-
-    sys.exit(method())

http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/src/slave/containerizer/containerizer.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/containerizer.cpp b/src/slave/containerizer/containerizer.cpp
index ba3b3f6..2d59caf 100644
--- a/src/slave/containerizer/containerizer.cpp
+++ b/src/slave/containerizer/containerizer.cpp
@@ -37,7 +37,6 @@
 #include "slave/containerizer/composing.hpp"
 #include "slave/containerizer/containerizer.hpp"
 #include "slave/containerizer/docker.hpp"
-#include "slave/containerizer/external_containerizer.hpp"
 
 #include "slave/containerizer/mesos/containerizer.hpp"
 #include "slave/containerizer/mesos/launcher.hpp"
@@ -59,8 +58,7 @@ namespace internal {
 namespace slave {
 
 // TODO(idownes): Move this to the Containerizer interface to complete
-// the delegation of containerization, i.e., external containerizers should be
-// able to report the resources they can isolate.
+// the delegation of containerization.
 Try<Resources> Containerizer::resources(const Flags& flags)
 {
   Try<Resources> parsed = Resources::parse(
@@ -189,28 +187,6 @@ Try<Containerizer*> Containerizer::create(
     bool local,
     Fetcher* fetcher)
 {
-  if (flags.isolation == "external") {
-    LOG(WARNING) << "The 'external' isolation flag is deprecated, "
-                 << "please update your flags to"
-                 << " '--containerizers=external'.";
-
-    if (flags.container_logger.isSome()) {
-      return Error(
-          "The external containerizer does not support custom container "
-          "logger modules.  The '--isolation=external' flag cannot be "
-          " set along with '--container_logger=...'");
-    }
-
-    Try<ExternalContainerizer*> containerizer =
-      ExternalContainerizer::create(flags);
-    if (containerizer.isError()) {
-      return Error("Could not create ExternalContainerizer: " +
-                   containerizer.error());
-    }
-
-    return containerizer.get();
-  }
-
   // Get the set of containerizer types.
   const vector<string> _types = strings::split(flags.containerizers, ",");
   const set<string> containerizerTypes(_types.begin(), _types.end());
@@ -295,22 +271,6 @@ Try<Containerizer*> Containerizer::create(
       } else {
         containerizers.push_back(containerizer.get());
       }
-    } else if (type == "external") {
-      if (flags.container_logger.isSome()) {
-        return Error(
-            "The external containerizer does not support custom container "
-            "logger modules.  The '--containerizers=external' flag cannot be "
-            "set along with '--container_logger=...'");
-      }
-
-      Try<ExternalContainerizer*> containerizer =
-        ExternalContainerizer::create(flags);
-      if (containerizer.isError()) {
-        return Error("Could not create ExternalContainerizer: " +
-                     containerizer.error());
-      } else {
-        containerizers.push_back(containerizer.get());
-      }
     } else {
       return Error("Unknown or unsupported containerizer: " + type);
     }

http://git-wip-us.apache.org/repos/asf/mesos/blob/f6a0e72c/src/slave/containerizer/external_containerizer.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/external_containerizer.cpp b/src/slave/containerizer/external_containerizer.cpp
deleted file mode 100644
index 9ee137a..0000000
--- a/src/slave/containerizer/external_containerizer.cpp
+++ /dev/null
@@ -1,1217 +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 <errno.h>
-
-#ifndef __WINDOWS__
-#include <poll.h>
-#endif // __WINDOWS__
-
-#include <signal.h>
-#include <stdio.h>
-
-#include <list>
-#include <tuple>
-
-#include <mesos/type_utils.hpp>
-
-#include <process/async.hpp>
-#include <process/collect.hpp>
-#include <process/defer.hpp>
-#include <process/delay.hpp>
-#include <process/id.hpp>
-#include <process/io.hpp>
-#include <process/reap.hpp>
-
-#include <stout/check.hpp>
-#include <stout/foreach.hpp>
-#include <stout/lambda.hpp>
-#include <stout/nothing.hpp>
-#include <stout/option.hpp>
-#include <stout/os.hpp>
-#include <stout/strings.hpp>
-#include <stout/uuid.hpp>
-
-#include <stout/os/killtree.hpp>
-
-#include "common/status_utils.hpp"
-
-#include "slave/paths.hpp"
-
-#include "slave/containerizer/external_containerizer.hpp"
-
-using lambda::bind;
-
-using std::list;
-using std::map;
-using std::set;
-using std::string;
-using std::tuple;
-using std::vector;
-
-using namespace process;
-
-namespace mesos {
-namespace internal {
-namespace slave {
-
-using state::ExecutorState;
-using state::FrameworkState;
-using state::RunState;
-using state::SlaveState;
-
-Try<ExternalContainerizer*> ExternalContainerizer::create(const Flags& flags)
-{
-  return new ExternalContainerizer(flags);
-}
-
-
-// Validate the invocation result.
-static Option<Error> validate(
-    const Future<Option<int>>& future)
-{
-  if (!future.isReady()) {
-    return Error("Status not ready");
-  }
-
-  Option<int> status = future.get();
-  if (status.isNone()) {
-    return Error("External containerizer has no status available");
-  }
-
-  // The status is a waitpid-result which has to be checked for SIGNAL
-  // based termination before masking out the exit-code.
-  if (!WIFEXITED(status.get()) || WEXITSTATUS(status.get()) != 0) {
-    return Error("Externel containerizer " + WSTRINGIFY(status.get()));
-  }
-
-  return None();
-}
-
-
-// Validate the invocation results and extract a piped protobuf
-// message.
-template <typename T>
-static Try<T> result(
-    const Future<tuple<Future<Result<T>>, Future<Option<int>>>>& future)
-{
-  if (!future.isReady()) {
-    return Error("Could not receive any result");
-  }
-
-  Option<Error> error = validate(std::get<1>(future.get()));
-  if (error.isSome()) {
-    return error.get();
-  }
-
-  Future<Result<T>> result = std::get<0>(future.get());
-  if (result.isFailed()) {
-    return Error("Could not receive any result: " + result.failure());
-  }
-
-  if (result.get().isError()) {
-    return Error("Could not receive any result: " + result.get().error());
-  }
-
-  if (result.get().isNone()) {
-    return Error("Could not receive any result");
-  }
-
-  return result.get().get();
-}
-
-
-ExternalContainerizer::ExternalContainerizer(const Flags& flags)
-  : process(new ExternalContainerizerProcess(flags))
-{
-  spawn(process.get());
-}
-
-
-ExternalContainerizer::~ExternalContainerizer()
-{
-  terminate(process.get());
-  process::wait(process.get());
-}
-
-
-Future<Nothing> ExternalContainerizer::recover(
-    const Option<state::SlaveState>& state)
-{
-  return dispatch(process.get(),
-                  &ExternalContainerizerProcess::recover,
-                  state);
-}
-
-
-Future<bool> ExternalContainerizer::launch(
-    const ContainerID& containerId,
-    const ExecutorInfo& executorInfo,
-    const string& directory,
-    const Option<string>& user,
-    const SlaveID& slaveId,
-    const PID<Slave>& slavePid,
-    bool checkpoint)
-{
-    return dispatch(process.get(),
-                    &ExternalContainerizerProcess::launch,
-                    containerId,
-                    None(),
-                    executorInfo,
-                    directory,
-                    user,
-                    slaveId,
-                    slavePid,
-                    checkpoint);
-}
-
-
-Future<bool> ExternalContainerizer::launch(
-    const ContainerID& containerId,
-    const TaskInfo& taskInfo,
-    const ExecutorInfo& executorInfo,
-    const string& directory,
-    const Option<string>& user,
-    const SlaveID& slaveId,
-    const PID<Slave>& slavePid,
-    bool checkpoint)
-{
-    return dispatch(process.get(),
-                    &ExternalContainerizerProcess::launch,
-                    containerId,
-                    taskInfo,
-                    executorInfo,
-                    directory,
-                    user,
-                    slaveId,
-                    slavePid,
-                    checkpoint);
-}
-
-
-Future<Nothing> ExternalContainerizer::update(
-    const ContainerID& containerId,
-    const Resources& resources)
-{
-    return dispatch(process.get(),
-                    &ExternalContainerizerProcess::update,
-                    containerId,
-                    resources);
-}
-
-
-Future<ResourceStatistics> ExternalContainerizer::usage(
-    const ContainerID& containerId)
-{
-  return dispatch(process.get(),
-                  &ExternalContainerizerProcess::usage,
-                  containerId);
-}
-
-
-Future<containerizer::Termination> ExternalContainerizer::wait(
-    const ContainerID& containerId)
-{
-  return dispatch(process.get(),
-                  &ExternalContainerizerProcess::wait,
-                  containerId);
-}
-
-
-void ExternalContainerizer::destroy(const ContainerID& containerId)
-{
-  dispatch(process.get(),
-           &ExternalContainerizerProcess::destroy,
-           containerId);
-}
-
-
-Future<hashset<ContainerID>> ExternalContainerizer::containers()
-{
-  return dispatch(process.get(),
-                  &ExternalContainerizerProcess::containers);
-}
-
-
-ExternalContainerizerProcess::ExternalContainerizerProcess(
-    const Flags& _flags) : flags(_flags) {}
-
-
-Future<Nothing> ExternalContainerizerProcess::recover(
-    const Option<state::SlaveState>& state)
-{
-  LOG(INFO) << "Recovering containerizer";
-
-  // Ask the external containerizer to recover its internal state.
-  Try<Subprocess> invoked = invoke("recover");
-
-  if (invoked.isError()) {
-    return Failure("Recover failed: " + invoked.error());
-  }
-
-  return invoked.get().status()
-    .then(defer(
-        PID<ExternalContainerizerProcess>(this),
-        &ExternalContainerizerProcess::_recover,
-        state,
-        lambda::_1));
-}
-
-
-Future<Nothing> ExternalContainerizerProcess::_recover(
-    const Option<state::SlaveState>& state,
-    const Future<Option<int>>& future)
-{
-  VLOG(1) << "Recover validation callback triggered";
-
-  Option<Error> error = validate(future);
-
-  if (error.isSome()) {
-    return Failure("Recover failed: " + error.get().message);
-  }
-
-  // Gather the active containers from the external containerizer.
-  return containers()
-    .then(defer(
-        PID<ExternalContainerizerProcess>(this),
-        &ExternalContainerizerProcess::__recover,
-        state,
-        lambda::_1));
-}
-
-
-Future<Nothing> ExternalContainerizerProcess::__recover(
-    const Option<state::SlaveState>& state,
-    const hashset<ContainerID>& containers)
-{
-  VLOG(1) << "Recover continuation triggered";
-
-  // An orphaned container is known to the external containerizer but
-  // not to the slave, thus not recoverable but pending.
-  hashset<ContainerID> orphaned = containers;
-
-  if (state.isSome()) {
-    foreachvalue (const FrameworkState& framework, state.get().frameworks) {
-      foreachvalue (const ExecutorState& executor, framework.executors) {
-        if (executor.info.isNone()) {
-          LOG(WARNING) << "Skipping recovery of executor '" << executor.id
-                       << "' of framework " << framework.id
-                       << " because its info could not be recovered";
-          continue;
-        }
-
-        if (executor.latest.isNone()) {
-          LOG(WARNING) << "Skipping recovery of executor '" << executor.id
-                       << "' of framework " << framework.id
-                       << " because its latest run could not be recovered";
-          continue;
-        }
-
-        // We are only interested in the latest run of the executor!
-        const ContainerID& containerId = executor.latest.get();
-        Option<RunState> run = executor.runs.get(containerId);
-        CHECK_SOME(run);
-
-        if (run.get().completed) {
-          VLOG(1) << "Skipping recovery of executor '" << executor.id
-                  << "' of framework " << framework.id
-                  << " because its latest run "
-                  << containerId << " is completed";
-          continue;
-        }
-
-        // Containers the external containerizer does not have
-        // information on, should be skipped as their state is not
-        // recoverable.
-        if (!containers.contains(containerId)) {
-          LOG(WARNING) << "Skipping recovery of executor '" << executor.id
-                       << "' of framework " << framework.id
-                       << " because the external containerizer has not "
-                       << " identified " << containerId << " as active";
-          continue;
-        }
-
-        LOG(INFO) << "Recovering container '" << containerId
-                  << "' for executor '" << executor.id
-                  << "' of framework " << framework.id;
-
-        Option<string> user = None();
-
-        // NOTE: `chown` has no meaningful interpretation on Windows. This is
-        // safe to `#ifdef` out because we don't compile the user flag on
-        // Windows, so this should always be `None`.
-#ifndef __WINDOWS__
-        if (flags.switch_user) {
-          // The command (either in form of task or executor command)
-          // can define a specific user to run as. If present, this
-          // precedes the framework user value.
-          if (executor.info.isSome() &&
-              executor.info.get().command().has_user()) {
-            user = executor.info.get().command().user();
-          } else if (framework.info.isSome()) {
-            user = framework.info.get().user();
-          }
-        }
-#endif // __WINDOWS__
-
-        // Re-create the sandbox for this container.
-        const string directory = paths::createExecutorDirectory(
-            flags.work_dir,
-            state.get().id,
-            framework.id,
-            executor.id,
-            containerId,
-            user);
-
-        Sandbox sandbox(directory, user);
-
-        // Collect this container as being active.
-        actives.put(containerId, Owned<Container>(new Container(sandbox)));
-
-        // Assume that this container had been launched, if this proves
-        // to be wrong, the containerizer::Termination delivered by the
-        // subsequent wait invocation will tell us.
-        actives[containerId]->launched.set(Nothing());
-
-        // Remove this container from the orphan collection as it is not
-        // orphaned.
-        orphaned.erase(containerId);
-      }
-    }
-  }
-
-  // Done when we got no orphans to take care of.
-  if (orphaned.empty()) {
-    VLOG(1) << "Recovery done";
-    return Nothing();
-  }
-
-  list<Future<containerizer::Termination>> futures;
-
-  // Enforce a 'destroy' on all orphaned containers.
-  foreach (const ContainerID& containerId, orphaned) {
-    LOG(INFO) << "Destroying container '" << containerId << "' as it "
-              << "is in an orphaned state.";
-    // For being able to wait on an orphan, we need to create an
-    // internal Container state - we just can not have a sandbox for
-    // it.
-    actives.put(containerId, Owned<Container>(new Container(None())));
-    actives[containerId]->launched.set(Nothing());
-
-    // Wrap the orphan destruction by a wait so we know when it is
-    // finally gone.
-    futures.push_back(_wait(containerId));
-
-    destroy(containerId);
-  }
-
-  VLOG(1) << "Awaiting all orphans to get destructed";
-
-  // Orphan destruction needs to complete before we satisfy the
-  // returned future.
-  return collect(futures)
-    .then(defer(
-        PID<ExternalContainerizerProcess>(this),
-        &ExternalContainerizerProcess::___recover));
-}
-
-
-Future<Nothing> ExternalContainerizerProcess::___recover()
-{
-  VLOG(1) << "Recovery done";
-  return Nothing();
-}
-
-
-Future<bool> ExternalContainerizerProcess::launch(
-    const ContainerID& containerId,
-    const Option<TaskInfo>& taskInfo,
-    const ExecutorInfo& executor,
-    const string& directory,
-    const Option<string>& user,
-    const SlaveID& slaveId,
-    const PID<Slave>& slavePid,
-    bool checkpoint)
-{
-  LOG(INFO) << "Launching container '" << containerId << "'";
-
-  if (actives.contains(containerId)) {
-    return Failure("Cannot start already running container '" +
-                   containerId.value() + "'");
-  }
-
-  map<string, string> environment = executorEnvironment(
-      executor,
-      directory,
-      slaveId,
-      slavePid,
-      checkpoint,
-      flags);
-
-  // TODO(tillt): Consider moving this into
-  // Containerizer::executorEnvironment.
-  if (!flags.hadoop_home.empty()) {
-    environment["HADOOP_HOME"] = flags.hadoop_home;
-  }
-
-  if (flags.default_container_image.isSome()) {
-    environment["MESOS_DEFAULT_CONTAINER_IMAGE"] =
-      flags.default_container_image.get();
-  }
-
-  containerizer::Launch launch;
-  launch.mutable_container_id()->CopyFrom(containerId);
-  if (taskInfo.isSome()) {
-    launch.mutable_task_info()->CopyFrom(taskInfo.get());
-  }
-  launch.mutable_executor_info()->CopyFrom(executor);
-  launch.set_directory(directory);
-  if (user.isSome()) {
-    launch.set_user(user.get());
-  }
-  launch.mutable_slave_id()->CopyFrom(slaveId);
-  launch.set_slave_pid(slavePid);
-  launch.set_checkpoint(checkpoint);
-
-  Sandbox sandbox(directory, user);
-
-  Try<Subprocess> invoked = invoke(
-      "launch",
-      launch,
-      sandbox,
-      environment);
-
-  if (invoked.isError()) {
-    return Failure("Launch of container '" + containerId.value() +
-                   "' failed: " + invoked.error());
-  }
-
-  // Checkpoint the executor's pid if requested.
-  // NOTE: Containerizer(s) currently rely on their state being
-  // persisted in the slave. However, that responsibility should have
-  // been delegated to the containerizer.
-  // To work around the mandatory forked pid recovery, we need to
-  // checkpoint one. See MESOS-1328 and MESOS-923.
-  // TODO(tillt): Remove this entirely as soon as MESOS-923 is fixed.
-  if (checkpoint) {
-    const string& path = slave::paths::getForkedPidPath(
-        slave::paths::getMetaRootDir(flags.work_dir),
-        slaveId,
-        executor.framework_id(),
-        executor.executor_id(),
-        containerId);
-
-    LOG(INFO) << "Checkpointing executor's forked pid " << invoked.get().pid()
-              << " to '" << path <<  "'";
-
-    Try<Nothing> checkpointed =
-      slave::state::checkpoint(path, stringify(invoked.get().pid()));
-
-    if (checkpointed.isError()) {
-      LOG(ERROR) << "Failed to checkpoint executor's forked pid to '"
-                 << path << "': " << checkpointed.error();
-
-      return Failure("Could not checkpoint executor's pid");
-    }
-  }
-
-  // Record the container launch intend.
-  actives.put(containerId, Owned<Container>(new Container(sandbox)));
-
-  return invoked.get().status()
-    .then(defer(
-        PID<ExternalContainerizerProcess>(this),
-        &ExternalContainerizerProcess::_launch,
-        containerId,
-        lambda::_1))
-    .onAny(defer(
-        PID<ExternalContainerizerProcess>(this),
-        &ExternalContainerizerProcess::__launch,
-        containerId,
-        lambda::_1));
-}
-
-
-Future<bool> ExternalContainerizerProcess::_launch(
-    const ContainerID& containerId,
-    const Future<Option<int>>& future)
-{
-  VLOG(1) << "Launch validation callback triggered on container '"
-          << containerId << "'";
-
-  Option<Error> error = validate(future);
-  if (error.isSome()) {
-    return Failure("Could not launch container '" +
-                   containerId.value() + "': " + error.get().message);
-  }
-
-  VLOG(1) << "Launch finishing up for container '" << containerId << "'";
-
-  // Launch is done, we can now process all other commands that might
-  // have gotten chained up.
-  actives[containerId]->launched.set(Nothing());
-
-  return true;
-}
-
-
-void ExternalContainerizerProcess::__launch(
-    const ContainerID& containerId,
-    const Future<bool>& future)
-{
-  VLOG(1) << "Launch confirmation callback triggered on container '"
-          << containerId << "'";
-
-  // We need to cleanup whenever this callback was invoked due to a
-  // failure or discarded future.
-  if (!future.isReady()) {
-    cleanup(containerId);
-  }
-}
-
-
-Future<containerizer::Termination> ExternalContainerizerProcess::wait(
-    const ContainerID& containerId)
-{
-  VLOG(1) << "Wait triggered on container '" << containerId << "'";
-
-  if (!actives.contains(containerId)) {
-    return Failure("Container '" + containerId.value() + "' not running");
-  }
-
-  // Defer wait until launch is done.
-  return actives[containerId]->launched.future()
-    .then(defer(
-        PID<ExternalContainerizerProcess>(this),
-        &ExternalContainerizerProcess::_wait,
-        containerId));
-}
-
-
-Future<containerizer::Termination> ExternalContainerizerProcess::_wait(
-    const ContainerID& containerId)
-{
-  VLOG(1) << "Wait continuation triggered on container '"
-          << containerId << "'";
-
-  if (!actives.contains(containerId)) {
-    return Failure("Container '" + containerId.value() + "' not running");
-  }
-
-  // We must not run multiple 'wait' invocations concurrently on the
-  // same container.
-  if (actives[containerId]->pid.isSome()) {
-    VLOG(2) << "Already waiting for " << containerId;
-    return actives[containerId]->termination.future();
-  }
-
-  containerizer::Wait wait;
-  wait.mutable_container_id()->CopyFrom(containerId);
-
-  Try<Subprocess> invoked = invoke(
-      "wait",
-      wait,
-      actives[containerId]->sandbox);
-
-  if (invoked.isError()) {
-    // 'wait' has failed, we need to tear down everything now.
-    unwait(containerId);
-    return Failure("Wait on container '" + containerId.value() +
-                   "' failed: " + invoked.error());
-  }
-
-  actives[containerId]->pid = invoked.get().pid();
-
-  // Invoke the protobuf::read asynchronously.
-  // TODO(tillt): Consider moving protobuf::read into libprocess and
-  // making it work fully asynchronously.
-#ifndef __WINDOWS__
-  Result<containerizer::Termination>(*read)(int, bool, bool) =
-#else
-  Result<containerizer::Termination>(*read)(HANDLE, bool, bool) =
-#endif // __WINDOWS__
-    &::protobuf::read<containerizer::Termination>;
-
-  Future<Result<containerizer::Termination>> future = async(
-      read, invoked.get().out().get(), false, false);
-
-  // Await both, a protobuf Message from the subprocess as well as
-  // its exit.
-  await(future, invoked.get().status())
-    .onAny(defer(
-        PID<ExternalContainerizerProcess>(this),
-        &ExternalContainerizerProcess::__wait,
-        containerId,
-        lambda::_1));
-
-  return actives[containerId]->termination.future();
-}
-
-
-void ExternalContainerizerProcess::__wait(
-    const ContainerID& containerId,
-    const Future<tuple<
-        Future<Result<containerizer::Termination>>,
-        Future<Option<int>>>>& future)
-{
-  VLOG(1) << "Wait callback triggered on container '" << containerId << "'";
-
-  if (!actives.contains(containerId)) {
-    LOG(ERROR) << "Container '" << containerId << "' not running";
-    return;
-  }
-
-  // When 'wait' was terminated by 'destroy', it is getting SIGKILLed
-  // (see unwait). We need to test for that specific case as otherwise
-  // the result validation below will return an error due to a non 0
-  // exit status.
-  if (actives[containerId]->destroying && future.isReady()) {
-    Future<Option<int>> statusFuture = std::get<1>(future.get());
-    if (statusFuture.isReady()) {
-      Option<int> status = statusFuture.get();
-      if (status.isSome()) {
-        VLOG(2) << "Wait got destroyed on '" << containerId << "'";
-        containerizer::Termination termination;
-        termination.set_status(status.get());
-        actives[containerId]->termination.set(termination);
-        cleanup(containerId);
-        return;
-      }
-    }
-  }
-
-  Try<containerizer::Termination> termination =
-    result<containerizer::Termination>(future);
-
-  if (termination.isError()) {
-    VLOG(2) << "Wait termination failed on '" << containerId << "'";
-    // 'wait' has failed, we need to tear down everything now.
-    actives[containerId]->termination.fail(termination.error());
-    unwait(containerId);
-  } else {
-    VLOG(2) << "Wait Termination: " << termination.get().DebugString();
-    // Set the promise to alert others waiting on this container.
-    actives[containerId]->termination.set(termination.get());
-  }
-
-  // The container has been waited on, we can safely cleanup now.
-  cleanup(containerId);
-}
-
-
-Future<Nothing> ExternalContainerizerProcess::update(
-    const ContainerID& containerId,
-    const Resources& resources)
-{
-  VLOG(1) << "Update triggered on container '" << containerId << "'";
-
-  if (!actives.contains(containerId)) {
-    return Failure("Container '" + containerId.value() + "'' not running");
-  }
-
-  // Defer update until launch is done.
-  return actives[containerId]->launched.future()
-    .then(defer(
-        PID<ExternalContainerizerProcess>(this),
-        &ExternalContainerizerProcess::_update,
-        containerId,
-        resources));
-}
-
-
-Future<Nothing> ExternalContainerizerProcess::_update(
-    const ContainerID& containerId,
-    const Resources& resources)
-{
-  VLOG(1) << "Update continuation triggered on container '"
-          << containerId << "'";
-
-  if (!actives.contains(containerId)) {
-    return Failure("Container '" + containerId.value() + "'' not running");
-  }
-
-  actives[containerId]->resources = resources;
-
-  containerizer::Update update;
-  update.mutable_container_id()->CopyFrom(containerId);
-  update.mutable_resources()->CopyFrom(resources);
-
-  Try<Subprocess> invoked = invoke(
-      "update",
-      update,
-      actives[containerId]->sandbox);
-
-  if (invoked.isError()) {
-    return Failure("Update of container '" + containerId.value() +
-                   "' failed: " + invoked.error());
-  }
-
-  return invoked.get().status()
-    .then(defer(
-        PID<ExternalContainerizerProcess>(this),
-        &ExternalContainerizerProcess::__update,
-        containerId,
-        lambda::_1));
-}
-
-
-Future<Nothing> ExternalContainerizerProcess::__update(
-    const ContainerID& containerId,
-    const Future<Option<int>>& future)
-{
-  VLOG(1) << "Update callback triggered on container '" << containerId << "'";
-
-  if (!actives.contains(containerId)) {
-    return Failure("Container '" + containerId.value() + "' not running");
-  }
-
-  Option<Error> error = validate(future);
-  if (error.isSome()) {
-    return Failure(error.get());
-  }
-
-  return Nothing();
-}
-
-
-Future<ResourceStatistics> ExternalContainerizerProcess::usage(
-    const ContainerID& containerId)
-{
-  VLOG(1) << "Usage triggered on container '" << containerId << "'";
-
-  if (!actives.contains(containerId)) {
-    return Failure("Container '" + containerId.value() + "'' not running");
-  }
-
-  // Defer usage until launch is done.
-  return actives[containerId]->launched.future()
-    .then(defer(
-        PID<ExternalContainerizerProcess>(this),
-        &ExternalContainerizerProcess::_usage,
-        containerId));
-}
-
-
-Future<ResourceStatistics> ExternalContainerizerProcess::_usage(
-    const ContainerID& containerId)
-{
-  VLOG(1) << "Usage continuation on container '" << containerId << "'";
-
-  if (!actives.contains(containerId)) {
-    return Failure("Container '" + containerId.value() + "'' not running");
-  }
-
-  containerizer::Usage usage;
-  usage.mutable_container_id()->CopyFrom(containerId);
-
-  Try<Subprocess> invoked = invoke(
-      "usage",
-      usage,
-      actives[containerId]->sandbox);
-
-  if (invoked.isError()) {
-    // 'usage' has failed but we keep the container alive for now.
-    return Failure("Usage on container '" + containerId.value() +
-                   "' failed: " + invoked.error());
-  }
-
-#ifndef __WINDOWS__
-  Result<ResourceStatistics>(*read)(int, bool, bool) =
-#else
-  Result<ResourceStatistics>(*read)(HANDLE, bool, bool) =
-#endif // __WINDOWS__
-    &::protobuf::read<ResourceStatistics>;
-
-  Future<Result<ResourceStatistics>> future = async(
-      read, invoked.get().out().get(), false, false);
-
-  // Await both, a protobuf Message from the subprocess as well as
-  // its exit.
-  return await(future, invoked.get().status())
-    .then(defer(
-        PID<ExternalContainerizerProcess>(this),
-        &ExternalContainerizerProcess::__usage,
-        containerId,
-        lambda::_1));
-}
-
-
-Future<ResourceStatistics> ExternalContainerizerProcess::__usage(
-    const ContainerID& containerId,
-    const Future<tuple<
-        Future<Result<ResourceStatistics>>,
-        Future<Option<int>>>>& future)
-{
-  VLOG(1) << "Usage callback triggered on container '" << containerId << "'";
-
-  if (!actives.contains(containerId)) {
-    return Failure("Container '" + containerId.value() + "' not running");
-  }
-
-  Try<ResourceStatistics> statistics = result<ResourceStatistics>(future);
-
-  if (statistics.isError()) {
-    return Failure(statistics.error());
-  }
-
-  VLOG(2) << "Container '" << containerId << "' "
-          << "total mem usage "
-          << statistics.get().mem_rss_bytes() << " "
-          << "total CPU user usage "
-          << statistics.get().cpus_user_time_secs() << " "
-          << "total CPU system usage "
-          << statistics.get().cpus_system_time_secs();
-
-  return statistics.get();
-}
-
-
-void ExternalContainerizerProcess::destroy(const ContainerID& containerId)
-{
-  VLOG(1) << "Destroy triggered on container '" << containerId << "'";
-
-  if (!actives.contains(containerId)) {
-    LOG(ERROR) << "Container '" << containerId << "' not running";
-    return;
-  }
-
-  // Defer destroy until launch is done.
-  actives[containerId]->launched.future()
-    .onAny(defer(
-        PID<ExternalContainerizerProcess>(this),
-        &ExternalContainerizerProcess::_destroy,
-        containerId));
-}
-
-
-void ExternalContainerizerProcess::_destroy(const ContainerID& containerId)
-{
-  VLOG(1) << "Destroy continuation on container '" << containerId << "'";
-
-  if (!actives.contains(containerId)) {
-    LOG(ERROR) << "Container '" << containerId << "' not running";
-    return;
-  }
-
-  if (actives[containerId]->destroying) {
-    LOG(WARNING) << "Container '" << containerId
-                 << "' is already being destroyed";
-    return;
-  }
-  actives[containerId]->destroying = true;
-
-  containerizer::Destroy destroy;
-  destroy.mutable_container_id()->CopyFrom(containerId);
-
-  Try<Subprocess> invoked = invoke(
-      "destroy",
-      destroy,
-      actives[containerId]->sandbox);
-
-  if (invoked.isError()) {
-    LOG(ERROR) << "Destroy of container '" << containerId
-               << "' failed: " << invoked.error();
-    unwait(containerId);
-    return;
-  }
-
-  invoked.get().status()
-    .onAny(defer(
-        PID<ExternalContainerizerProcess>(this),
-        &ExternalContainerizerProcess::__destroy,
-        containerId,
-        lambda::_1));
-}
-
-
-void ExternalContainerizerProcess::__destroy(
-    const ContainerID& containerId,
-    const Future<Option<int>>& future)
-{
-  VLOG(1) << "Destroy callback triggered on container '" << containerId << "'";
-
-  if (!actives.contains(containerId)) {
-    LOG(ERROR) << "Container '" << containerId << "' not running ";
-    return;
-  }
-
-  Option<Error> error = validate(future);
-  if (error.isSome()) {
-    LOG(ERROR) << "Destroy of container '" << containerId
-               << "' failed: " << error.get().message;
-  }
-
-  // Additionally to the optional external destroy-command, we need to
-  // terminate the external containerizer's "wait" process.
-  unwait(containerId);
-}
-
-
-Future<hashset<ContainerID>> ExternalContainerizerProcess::containers()
-{
-  VLOG(1) << "Containers triggered";
-
-  Try<Subprocess> invoked = invoke("containers");
-
-  if (invoked.isError()) {
-    return Failure("Containers failed: " + invoked.error());
-  }
-
-#ifndef __WINDOWS__
-  Result<containerizer::Containers>(*read)(int, bool, bool) =
-#else
-  Result<containerizer::Containers>(*read)(HANDLE, bool, bool) =
-#endif // __WINDOWS__
-    &::protobuf::read<containerizer::Containers>;
-
-  Future<Result<containerizer::Containers>> future = async(
-      read, invoked.get().out().get(), false, false);
-
-  // Await both, a protobuf Message from the subprocess as well as
-  // its exit.
-  return await(future, invoked.get().status())
-    .then(defer(
-        PID<ExternalContainerizerProcess>(this),
-        &ExternalContainerizerProcess::_containers,
-        lambda::_1));
-}
-
-
-Future<hashset<ContainerID>> ExternalContainerizerProcess::_containers(
-    const Future<tuple<
-        Future<Result<containerizer::Containers>>,
-        Future<Option<int>>>>& future)
-{
-  VLOG(1) << "Containers callback triggered";
-
-  Try<containerizer::Containers> containers =
-    result<containerizer::Containers>(future);
-
-  if (containers.isError()) {
-    return Failure(containers.error());
-  }
-
-  hashset<ContainerID> result;
-  foreach (const ContainerID& containerId, containers.get().containers()) {
-    result.insert(containerId);
-  }
-
-  return result;
-}
-
-
-void ExternalContainerizerProcess::cleanup(const ContainerID& containerId)
-{
-  VLOG(1) << "Callback performing final cleanup of running state";
-
-  if (actives.contains(containerId)) {
-    actives.erase(containerId);
-  } else {
-    LOG(WARNING) << "Container '" << containerId << "' not running anymore";
-  }
-}
-
-
-void ExternalContainerizerProcess::unwait(const ContainerID& containerId)
-{
-  if (!actives.contains(containerId)) {
-    LOG(WARNING) << "Container '" << containerId << "' not running";
-    return;
-  }
-
-  Option<pid_t> pid = actives[containerId]->pid;
-
-  // Containers that are being waited on have the "wait" command's
-  // pid assigned.
-  if (pid.isNone()) {
-    // If we reached this, launch most likely failed due to some error
-    // on the external containerizer's side (e.g. returned non zero on
-    // launch).
-    LOG(WARNING) << "Container '" << containerId << "' not being waited on";
-    cleanup(containerId);
-    return;
-  }
-
-  // Terminate the containerizer.
-  VLOG(2) << "About to send a SIGKILL to containerizer pid: " << pid.get();
-
-  // TODO(tillt): Add graceful termination as soon as we have an
-  // accepted way to do that in place.
-  Try<list<os::ProcessTree>> trees =
-    os::killtree(pid.get(), SIGKILL, true, true);
-
-  if (trees.isError()) {
-    LOG(WARNING) << "Failed to kill the process tree rooted at pid "
-                 << pid.get() << ": " << trees.error();
-    cleanup(containerId);
-    return;
-  }
-
-  LOG(INFO) << "Killed the following process tree/s:\n"
-            << stringify(trees.get());
-
-  // The cleanup function will get invoked via __wait which triggers
-  // once the external containerizer's "wait" subprocess gets
-  // terminated.
-}
-
-
-Try<Subprocess> ExternalContainerizerProcess::invoke(
-    const string& command,
-    const Option<Sandbox>& sandbox,
-    const Option<map<string, string>>& commandEnvironment)
-{
-  CHECK_SOME(flags.containerizer_path) << "containerizer_path not set";
-
-  VLOG(1) << "Invoking external containerizer for method '" << command << "'";
-
-  // Prepare a default environment.
-  map<string, string> environment = os::environment();
-
-  environment["MESOS_LIBEXEC_DIRECTORY"] = flags.launcher_dir;
-  environment["MESOS_WORK_DIRECTORY"] = flags.work_dir;
-
-  // Update default environment with command specific one.
-  if (commandEnvironment.isSome()) {
-    environment.insert(
-        commandEnvironment.get().begin(),
-        commandEnvironment.get().end());
-  }
-
-  // Construct the command to execute.
-  string execute = flags.containerizer_path.get() + " " + command;
-
-  VLOG(2) << "calling: [" << execute << "]";
-  VLOG_IF(2, sandbox.isSome()) << "directory: " << sandbox.get().directory;
-  VLOG_IF(2, sandbox.isSome() &&
-      sandbox.get().user.isSome()) << "user: " << sandbox.get().user.get();
-
-  // NOTE: `chown` has no meaningful interpretation on Windows. This is safe to
-  // `#ifdef` out because we don't compile the user flag on Windows, so this
-  // should always be `None`.
-#ifndef __WINDOWS__
-  // Re/establish the sandbox conditions for the containerizer.
-  if (sandbox.isSome() && sandbox.get().user.isSome()) {
-    Try<Nothing> chown = os::chown(
-        sandbox.get().user.get(),
-        sandbox.get().directory);
-    if (chown.isError()) {
-      return Error("Failed to chown work directory: " + chown.error());
-    }
-  }
-#endif // __WINDOWS__
-
-  // Fork exec of external process. Run a chdir and a setsid within
-  // the child-context.
-  // TODO(tillt): Consider having the kernel notify us when our parent
-  // process dies e.g. by invoking prctl(PR_SET_PDEATHSIG, ..) on linux.
-  Try<Subprocess> external = process::subprocess(
-      execute,
-      Subprocess::PIPE(),
-      Subprocess::PIPE(),
-      Subprocess::PIPE(),
-      process::SETSID,
-      environment,
-      None(),
-      Subprocess::Hook::None(),
-      sandbox.isSome() ? Option<string>::some(sandbox->directory) : None());
-
-  if (external.isError()) {
-    return Error("Failed to execute external containerizer: " +
-                 external.error());
-  }
-
-  // Set stderr into non-blocking mode.
-  Try<Nothing> nonblock = os::nonblock(external.get().err().get());
-  if (nonblock.isError()) {
-    return Error("Failed to accept nonblock: " + nonblock.error());
-  }
-
-  // We are not setting stdin or stdout into non-blocking mode as
-  // protobuf::read / write do currently not support it.
-
-  // Redirect output (stderr) from the external containerizer to log
-  // file in the executor work directory, chown'ing it if a user is
-  // specified. When no sandbox is given, redirect to /dev/null to
-  // prevent blocking on the subprocess side.
-  // TODO(tillt): Consider switching to atomic close-on-exec instead.
-  Try<int> err = os::open(
-      sandbox.isSome() ? path::join(sandbox.get().directory, "stderr")
-                       : "/dev/null",
-      O_WRONLY | O_CREAT | O_APPEND | O_NONBLOCK | O_CLOEXEC,
-      S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
-  if (err.isError()) {
-    return Error(
-        "Failed to redirect stderr: Failed to open: " +
-        err.error());
-  }
-
-  // NOTE: `chown` has no meaningful interpretation on Windows. This is safe to
-  // `#ifdef` out because we don't compile the user flag on Windows, so this
-  // should always be `None`.
-#ifndef __WINDOWS__
-  if (sandbox.isSome() && sandbox.get().user.isSome()) {
-    Try<Nothing> chown = os::chown(
-        sandbox.get().user.get(),
-        path::join(sandbox.get().directory, "stderr"));
-    if (chown.isError()) {
-      os::close(err.get());
-      return Error(
-          "Failed to redirect stderr: Failed to chown: " +
-          chown.error());
-    }
-  }
-#endif // __WINDOWS__
-
-  // TODO(tillt): Consider adding an overload to io::redirect
-  // that accepts a file path as 'to' for further reducing code.
-  io::redirect(external.get().err().get(), err.get());
-
-  // Redirect does 'dup' the file descriptor, hence we can close the
-  // original now.
-  os::close(err.get());
-
-  VLOG(2) << "Subprocess pid: " << external.get().pid() << ", "
-          << "output pipe: " << external.get().out().get();
-
-  return external;
-}
-
-
-Try<Subprocess> ExternalContainerizerProcess::invoke(
-    const string& command,
-    const google::protobuf::Message& message,
-    const Option<Sandbox>& sandbox,
-    const Option<map<string, string>>& commandEnvironment)
-{
-  Try<Subprocess> external = invoke(command, sandbox, commandEnvironment);
-  if (external.isError()) {
-    return external;
-  }
-
-  // Transmit protobuf data via stdout towards the external
-  // containerizer. Each message is prefixed by its total size.
-  Try<Nothing> write = ::protobuf::write(external.get().in().get(), message);
-  if (write.isError()) {
-    return Error("Failed to write protobuf to pipe: " + write.error());
-  }
-
-  return external;
-}
-
-} // namespace slave {
-} // namespace internal {
-} // namespace mesos {