You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by vi...@apache.org on 2014/02/12 02:31:14 UTC

[08/11] git commit: Containerizer (part 1)

Containerizer (part 1)

The proposed Containerizer interface is to replace the existing
Isolator.

One ContainerizerProcess has been written:
MesosContainerizerProcess - implements containerizeration internally
using a Launcher and one or more Isolators (following review)

The intent is to also support a generic ExternalContainerizerProcess
that can delegate containerizeration by making external calls. Other
Containerizers could interface with specific external containerization
techniques such as Docker or LXC.

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


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

Branch: refs/heads/master
Commit: f90fe7641ea8f7066a6a1171a24ddaa8dc30e789
Parents: c578fe5
Author: Ian Downes <ia...@gmail.com>
Authored: Tue Feb 11 16:24:59 2014 -0800
Committer: Vinod Kone <vi...@twitter.com>
Committed: Tue Feb 11 16:59:50 2014 -0800

----------------------------------------------------------------------
 include/mesos/mesos.proto                       |   25 +-
 src/Makefile.am                                 |   26 +-
 src/common/type_utils.hpp                       |   40 +
 src/launcher/fetcher.cpp                        |  242 +++
 src/launcher/launcher.cpp                       |  489 ------
 src/launcher/launcher.hpp                       |  125 --
 src/launcher/main.cpp                           |  100 --
 src/local/local.cpp                             |   23 +-
 src/slave/cgroups_isolator.cpp                  | 1412 ------------------
 src/slave/cgroups_isolator.hpp                  |  320 ----
 src/slave/containerizer/containerizer.cpp       |  269 ++++
 src/slave/containerizer/containerizer.hpp       |  137 ++
 src/slave/containerizer/mesos_containerizer.cpp |  907 +++++++++++
 src/slave/containerizer/mesos_containerizer.hpp |  208 +++
 src/slave/flags.hpp                             |   15 +-
 src/slave/http.cpp                              |    2 +-
 src/slave/isolator.cpp                          |   53 -
 src/slave/isolator.hpp                          |  110 --
 src/slave/main.cpp                              |   20 +-
 src/slave/monitor.cpp                           |  132 +-
 src/slave/monitor.hpp                           |   54 +-
 src/slave/paths.hpp                             |   32 +-
 src/slave/process_isolator.cpp                  |  516 -------
 src/slave/process_isolator.hpp                  |  117 --
 src/slave/slave.cpp                             |  487 +++---
 src/slave/slave.hpp                             |   32 +-
 src/slave/state.cpp                             |   45 +-
 src/slave/state.hpp                             |   10 +-
 src/slave/status_update_manager.cpp             |   33 +-
 src/slave/status_update_manager.hpp             |    8 +-
 30 files changed, 2243 insertions(+), 3746 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/f90fe764/include/mesos/mesos.proto
----------------------------------------------------------------------
diff --git a/include/mesos/mesos.proto b/include/mesos/mesos.proto
index 7079e03..69a4a60 100644
--- a/include/mesos/mesos.proto
+++ b/include/mesos/mesos.proto
@@ -83,6 +83,17 @@ message ExecutorID {
 
 
 /**
+ * A slave generated ID to distinguish a container. The ID must be unique
+ * between any active or completed containers on the slave. In particular,
+ * containers for different runs of the same (framework, executor) pair must be
+ * unique.
+ */
+message ContainerID {
+  required string value = 1;
+}
+
+
+/**
  * Describes a framework. If the user field is set to an empty string
  * Mesos will automagically set it to the current user. Note that the
  * ID is only available after a framework has registered, however, it
@@ -109,13 +120,13 @@ message FrameworkInfo {
 
 
 /**
- * Describes a command, executed via: '/bin/sh -c value'. Any uri's
- * specified are fetched before executing the command.
- * If executable field for an uri is set, executable file permission 
- * is set on the downloaded file. Also, if the downloaded file has ".tgz" 
- * extension it is extracted into the executor's working directory.
- * In addition, any environment variables are set before executing 
- * the command (so they can be used to "parameterize" your command).
+ * Describes a command, executed via: '/bin/sh -c value'. Any URIs specified
+ * are fetched before executing the command.  If the executable field for an
+ * uri is set, executable file permission is set on the downloaded file.
+ * Otherwise, if the downloaded file has a recognized archive extension
+ * (currently [compressed] tar and zip) it is extracted into the executor's
+ * working directory.  In addition, any environment variables are set before
+ * executing the command (so they can be used to "parameterize" your command).
  */
 message CommandInfo {
   message URI {

http://git-wip-us.apache.org/repos/asf/mesos/blob/f90fe764/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index c307068..9d39666 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -176,10 +176,9 @@ libmesos_no_3rdparty_la_SOURCES =					\
 	slave/state.cpp							\
 	slave/slave.cpp							\
 	slave/http.cpp							\
-	slave/isolator.cpp						\
-	slave/process_isolator.cpp					\
+	slave/containerizer/containerizer.cpp				\
+	slave/containerizer/mesos_containerizer.cpp			\
 	slave/status_update_manager.cpp					\
-	launcher/launcher.cpp						\
 	exec/exec.cpp							\
 	common/lock.cpp							\
 	common/date_utils.cpp						\
@@ -204,11 +203,9 @@ pkginclude_HEADERS = $(top_srcdir)/include/mesos/executor.hpp	\
 nodist_pkginclude_HEADERS = ../include/mesos/mesos.hpp mesos.pb.h
 
 if OS_LINUX
-  libmesos_no_3rdparty_la_SOURCES += slave/cgroups_isolator.cpp
   libmesos_no_3rdparty_la_SOURCES += linux/cgroups.cpp
   libmesos_no_3rdparty_la_SOURCES += linux/fs.cpp
 else
-  EXTRA_DIST += slave/cgroups_isolator.cpp
   EXTRA_DIST += linux/cgroups.cpp
   EXTRA_DIST += linux/fs.cpp
 endif
@@ -220,7 +217,7 @@ libmesos_no_3rdparty_la_SOURCES += common/attributes.hpp		\
 	common/type_utils.hpp common/thread.hpp				\
 	examples/utils.hpp files/files.hpp				\
 	hdfs/hdfs.hpp							\
-	launcher/launcher.hpp linux/cgroups.hpp				\
+	linux/cgroups.hpp						\
 	linux/fs.hpp local/flags.hpp local/local.hpp			\
 	logging/flags.hpp logging/logging.hpp				\
 	master/allocator.hpp						\
@@ -232,17 +229,16 @@ libmesos_no_3rdparty_la_SOURCES += common/attributes.hpp		\
 	master/registrar.hpp						\
 	master/master.hpp master/sorter.hpp				\
 	messages/messages.hpp slave/constants.hpp			\
+	slave/containerizer/containerizer.hpp				\
+	slave/containerizer/mesos_containerizer.hpp			\
 	slave/flags.hpp slave/gc.hpp slave/monitor.hpp			\
-	slave/isolator.hpp						\
-	slave/cgroups_isolator.hpp					\
 	slave/paths.hpp slave/state.hpp					\
 	slave/status_update_manager.hpp					\
-	slave/process_isolator.hpp					\
 	slave/slave.hpp							\
 	tests/environment.hpp tests/script.hpp				\
 	tests/zookeeper.hpp tests/flags.hpp tests/utils.hpp		\
 	tests/cluster.hpp						\
-	tests/isolator.hpp						\
+	tests/containerizer.hpp						\
 	tests/mesos.hpp							\
 	tests/zookeeper_test_server.hpp zookeeper/authentication.hpp	\
 	zookeeper/contender.hpp						\
@@ -391,10 +387,10 @@ mesos_local_SOURCES = local/main.cpp
 mesos_local_CPPFLAGS = $(MESOS_CPPFLAGS)
 mesos_local_LDADD = libmesos.la
 
-pkglibexec_PROGRAMS += mesos-launcher
-mesos_launcher_SOURCES = launcher/main.cpp
-mesos_launcher_CPPFLAGS = $(MESOS_CPPFLAGS)
-mesos_launcher_LDADD = libmesos.la
+pkglibexec_PROGRAMS += mesos-fetcher
+mesos_fetcher_SOURCES = launcher/fetcher.cpp
+mesos_fetcher_CPPFLAGS = $(MESOS_CPPFLAGS)
+mesos_fetcher_LDADD = libmesos.la
 
 pkglibexec_PROGRAMS += mesos-executor
 mesos_executor_SOURCES = launcher/executor.cpp
@@ -838,6 +834,7 @@ mesos_tests_SOURCES =				\
   tests/allocator_tests.cpp			\
   tests/attributes_tests.cpp			\
   tests/authentication_tests.cpp		\
+  tests/containerizer.cpp			\
   tests/environment.cpp				\
   tests/examples_tests.cpp			\
   tests/exception_tests.cpp			\
@@ -878,7 +875,6 @@ mesos_tests_LDADD = ../$(LIBPROCESS)/3rdparty/libgmock.la libmesos.la
 mesos_tests_DEPENDENCIES = # Initialized to allow += below.
 
 if OS_LINUX
-  mesos_tests_SOURCES += tests/cgroups_isolator_tests.cpp
   mesos_tests_SOURCES += tests/cgroups_tests.cpp
   mesos_tests_SOURCES += tests/fs_tests.cpp
 endif

http://git-wip-us.apache.org/repos/asf/mesos/blob/f90fe764/src/common/type_utils.hpp
----------------------------------------------------------------------
diff --git a/src/common/type_utils.hpp b/src/common/type_utils.hpp
index b8fc573..784a808 100644
--- a/src/common/type_utils.hpp
+++ b/src/common/type_utils.hpp
@@ -72,6 +72,14 @@ inline std::ostream& operator << (
 }
 
 
+inline std::ostream& operator << (
+    std::ostream& stream,
+    const ContainerID& containerId)
+{
+  return stream << containerId.value();
+}
+
+
 inline std::ostream& operator << (std::ostream& stream, const TaskState& state)
 {
   return stream << TaskState_descriptor()->FindValueByNumber(state)->name();
@@ -150,6 +158,18 @@ inline bool operator == (const ExecutorID& left, const ExecutorID& right)
 }
 
 
+inline bool operator == (const ContainerID& left, const ContainerID& right)
+{
+  return left.value() == right.value();
+}
+
+
+inline bool operator != (const ContainerID& left, const ContainerID& right)
+{
+  return left.value() != right.value();
+}
+
+
 inline bool operator == (const FrameworkID& left, const std::string& right)
 {
   return left.value() == right;
@@ -180,6 +200,12 @@ inline bool operator == (const ExecutorID& left, const std::string& right)
 }
 
 
+inline bool operator == (const ContainerID& left, const std::string& right)
+{
+  return left.value() == right;
+}
+
+
 inline bool operator < (const FrameworkID& left, const FrameworkID& right)
 {
   return left.value() < right.value();
@@ -210,6 +236,12 @@ inline bool operator < (const ExecutorID& left, const ExecutorID& right)
 }
 
 
+inline bool operator < (const ContainerID& left, const ContainerID& right)
+{
+  return left.value() < right.value();
+}
+
+
 inline bool operator == (const Environment& left, const Environment& right)
 {
   if (left.variables().size() != right.variables().size()) {
@@ -355,6 +387,14 @@ inline std::size_t hash_value(const ExecutorID& executorId)
 }
 
 
+inline std::size_t hash_value(const ContainerID& containerId)
+{
+  size_t seed = 0;
+  boost::hash_combine(seed, containerId.value());
+  return seed;
+}
+
+
 namespace internal {
 
 inline bool operator == (const Task& left, const Task& right)

http://git-wip-us.apache.org/repos/asf/mesos/blob/f90fe764/src/launcher/fetcher.cpp
----------------------------------------------------------------------
diff --git a/src/launcher/fetcher.cpp b/src/launcher/fetcher.cpp
new file mode 100644
index 0000000..9c9f07d
--- /dev/null
+++ b/src/launcher/fetcher.cpp
@@ -0,0 +1,242 @@
+/**
+ * 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 <string>
+
+#include <mesos/mesos.hpp>
+
+#include <stout/net.hpp>
+#include <stout/option.hpp>
+#include <stout/os.hpp>
+#include <stout/strings.hpp>
+
+#include "hdfs/hdfs.hpp"
+
+using namespace mesos;
+
+using std::string;
+
+// Try to extract filename into directory. If filename is recognized as an
+// archive it will be extracted and true returned; if not recognized then false
+// will be returned. An Error is returned if the extraction command fails.
+Try<Nothing> extract(const string& filename, const string& directory)
+{
+  string command;
+  // Extract any .tgz, tar.gz, tar.bz2 or zip files.
+  if (strings::endsWith(filename, ".tgz") ||
+      strings::endsWith(filename, ".tar.gz") ||
+      strings::endsWith(filename, ".tbz2") ||
+      strings::endsWith(filename, ".tar.bz2") ||
+      strings::endsWith(filename, ".txz") ||
+      strings::endsWith(filename, ".tar.xz")) {
+    command = "tar -C '" + directory + "' xJf";
+  } else if (strings::endsWith(filename, ".zip")) {
+    command = "unzip -d '" + directory + "'";
+  } else {
+    return Error("Could not extract file with unrecognized extension");
+  }
+
+  command += " '" + filename + "'";
+  int status = os::system(command);
+  if (status != 0) {
+    return Error("Failed to extract: command " + command +
+                 " exited with status: " + stringify(status));
+  }
+
+  LOG(INFO) << "Extracted resource '" << filename
+            << "' into '" << directory << "'";
+
+  return Nothing();
+}
+
+
+// Fetch URI into directory.
+Try<string> fetch(
+    const string& uri,
+    const string& directory)
+{
+  LOG(INFO) << "Fetching URI '" << uri << "'";
+
+  // Some checks to make sure using the URI value in shell commands
+  // is safe. TODO(benh): These should be pushed into the scheduler
+  // driver and reported to the user.
+  if (uri.find_first_of('\\') != string::npos ||
+      uri.find_first_of('\'') != string::npos ||
+      uri.find_first_of('\0') != string::npos) {
+    LOG(ERROR) << "URI contains illegal characters, refusing to fetch";
+    return Error("Illegal characters in URI");
+  }
+
+  // Grab the resource from HDFS if its path begins with hdfs:// or
+  // hftp:
+  // TODO(matei): Enforce some size limits on files we get from HDFS
+  if (strings::startsWith(uri, "hdfs://") ||
+      strings::startsWith(uri, "hftp://")) {
+    Try<string> base = os::basename(uri);
+    if (base.isError()) {
+      LOG(ERROR) << "Invalid basename for URI: " << base.error();
+      return Error("Invalid basename for URI");
+    }
+    string path = path::join(directory, base.get());
+
+    HDFS hdfs;
+
+    LOG(INFO) << "Downloading resource from '" << uri
+              << "' to '" << path << "'";
+    Try<Nothing> result = hdfs.copyToLocal(uri, path);
+    if (result.isError()) {
+      LOG(ERROR) << "HDFS copyToLocal failed: " << result.error();
+      return Error(result.error());
+    }
+
+    return path;
+  } else if (strings::startsWith(uri, "http://") ||
+             strings::startsWith(uri, "https://") ||
+             strings::startsWith(uri, "ftp://") ||
+             strings::startsWith(uri, "ftps://")) {
+    string path = uri.substr(uri.find("://") + 3);
+    if (path.find("/") == string::npos ||
+        path.size() <= path.find("/") + 1) {
+      LOG(ERROR) << "Malformed URL (missing path)";
+      return Error("Malformed URI");
+    }
+
+    path =  path::join(directory, path.substr(path.find_last_of("/") + 1));
+    LOG(INFO) << "Downloading '" << uri << "' to '" << path << "'";
+    Try<int> code = net::download(uri, path);
+    if (code.isError()) {
+      LOG(ERROR) << "Error downloading resource: " << code.error().c_str();
+      return Error("Fetch of URI failed (" + code.error() + ")");
+    } else if (code.get() != 200) {
+      LOG(ERROR) << "Error downloading resource, received HTTP/FTP return code "
+                 << code.get();
+      return Error("HTTP/FTP error (" + stringify(code.get()) + ")");
+    }
+
+    return path;
+  } else { // Copy the local resource.
+    string local = uri;
+    if (local.find_first_of("/") != 0) {
+      // We got a non-Hadoop and non-absolute path.
+      if (os::hasenv("MESOS_FRAMEWORKS_HOME")) {
+        local = path::join(os::getenv("MESOS_FRAMEWORKS_HOME"), local);
+        LOG(INFO) << "Prepended environment variable "
+                  << "MESOS_FRAMEWORKS_HOME to relative path, "
+                  << "making it: '" << local << "'";
+      } else {
+        LOG(ERROR) << "A relative path was passed for the resource but the "
+                   << "environment variable MESOS_FRAMEWORKS_HOME is not set. "
+                   << "Please either specify this config option "
+                   << "or avoid using a relative path";
+        return Error("Could not resolve relative URI");
+      }
+    }
+
+    Try<string> base = os::basename(local);
+    if (base.isError()) {
+      LOG(ERROR) << base.error();
+      return Error("Fetch of URI failed");
+    }
+
+    // Copy the resource to the directory.
+    string path = path::join(directory, base.get());
+    std::ostringstream command;
+    command << "cp '" << local << "' '" << path << "'";
+    LOG(INFO) << "Copying resource from '" << local
+              << "' to '" << directory << "'";
+
+    int status = os::system(command.str());
+    if (status != 0) {
+      LOG(ERROR) << "Failed to copy '" << local
+                 << "' : Exit status " << status;
+      return Error("Local copy failed");
+    }
+
+    return path;
+  }
+}
+
+
+int main(int argc, char* argv[])
+{
+  GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+  CommandInfo commandInfo;
+  // Construct URIs from the encoded environment string.
+  const std::string& uris = os::getenv("MESOS_EXECUTOR_URIS");
+  foreach (const std::string& token, strings::tokenize(uris, " ")) {
+    // Delimiter between URI and execute permission.
+    size_t pos = token.rfind("+");
+    CHECK(pos != std::string::npos)
+      << "Invalid executor uri token in env " << token;
+
+    CommandInfo::URI uri;
+    uri.set_value(token.substr(0, pos));
+    uri.set_executable(token.substr(pos + 1) == "1");
+
+    commandInfo.add_uris()->MergeFrom(uri);
+  }
+
+  CHECK(os::hasenv("MESOS_WORK_DIRECTORY"))
+    << "Missing MESOS_WORK_DIRECTORY environment variable";
+  std::string directory = os::getenv("MESOS_WORK_DIRECTORY");
+
+  // We cannot use Some in the ternary expression because the compiler needs to
+  // be able to infer the type, thus the explicit Option<string>.
+  // TODO(idownes): Add an os::hasenv that returns an Option<string>.
+  Option<std::string> user = os::hasenv("MESOS_USER")
+    ? Option<std::string>(os::getenv("MESOS_USER")) // Explicit so it compiles.
+    : None();
+
+  // Fetch each URI to a local file, chmod, then chown if a user is provided.
+  foreach (const CommandInfo::URI& uri, commandInfo.uris()) {
+    // Fetch the URI to a local file.
+    Try<string> fetched = fetch(uri.value(), directory);
+    if (fetched.isError()) {
+      EXIT(1) << "Failed to fetch: " << uri.value();
+    }
+
+    // Chmod the fetched URI if it's executable, else assume it's an archive
+    // that should be extracted.
+    if (uri.executable()) {
+      bool chmodded = os::chmod(
+          fetched.get(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
+      if (!chmodded) {
+        EXIT(1) << "Failed to chmod: " << fetched.get();
+      }
+    } else {
+      //TODO(idownes): Consider removing the archive once extracted.
+      // Try to extract the file if it's recognized as an archive.
+      Try<Nothing> extracted = extract(fetched.get(), directory);
+      if (extracted.isError()) {
+        EXIT(1) << "Failed to extract "
+                << fetched.get() << ":" << extracted.error();
+      }
+    }
+
+    // Recursively chown the directory if a user is provided.
+    if (user.isSome()) {
+      Try<Nothing> chowned = os::chown(user.get(), directory);
+      if (chowned.isError()) {
+        EXIT(1) << "Failed to chown " << directory << ": " << chowned.error();
+      }
+    }
+  }
+
+  return 0;
+}

http://git-wip-us.apache.org/repos/asf/mesos/blob/f90fe764/src/launcher/launcher.cpp
----------------------------------------------------------------------
diff --git a/src/launcher/launcher.cpp b/src/launcher/launcher.cpp
deleted file mode 100644
index d5ab667..0000000
--- a/src/launcher/launcher.cpp
+++ /dev/null
@@ -1,489 +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 <dirent.h>
-#include <errno.h>
-#include <libgen.h>
-#include <pwd.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include <iostream>
-#include <map>
-#include <sstream>
-
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-
-#include <stout/fatal.hpp>
-#include <stout/foreach.hpp>
-#include <stout/net.hpp>
-#include <stout/nothing.hpp>
-#include <stout/os.hpp>
-#include <stout/path.hpp>
-
-#include "hdfs/hdfs.hpp"
-
-#include "launcher/launcher.hpp"
-
-#include "slave/flags.hpp"
-#include "slave/paths.hpp"
-#include "slave/state.hpp"
-
-using std::cerr;
-using std::cout;
-using std::endl;
-using std::map;
-using std::ostringstream;
-using std::string;
-
-namespace mesos {
-namespace internal {
-namespace launcher {
-
-ExecutorLauncher::ExecutorLauncher(
-    const SlaveID& _slaveId,
-    const FrameworkID& _frameworkId,
-    const ExecutorID& _executorId,
-    const UUID& _uuid,
-    const CommandInfo& _commandInfo,
-    const string& _user,
-    const string& _workDirectory,
-    const string& _slaveDirectory,
-    const string& _slavePid,
-    const string& _frameworksHome,
-    const string& _hadoopHome,
-    bool _redirectIO,
-    bool _shouldSwitchUser,
-    bool _checkpoint,
-    Duration _recoveryTimeout)
-  : slaveId(_slaveId),
-    frameworkId(_frameworkId),
-    executorId(_executorId),
-    uuid(_uuid),
-    commandInfo(_commandInfo),
-    user(_user),
-    workDirectory(_workDirectory),
-    slaveDirectory(_slaveDirectory),
-    slavePid(_slavePid),
-    frameworksHome(_frameworksHome),
-    hadoopHome(_hadoopHome),
-    redirectIO(_redirectIO),
-    shouldSwitchUser(_shouldSwitchUser),
-    checkpoint(_checkpoint),
-    recoveryTimeout(_recoveryTimeout) {}
-
-
-ExecutorLauncher::~ExecutorLauncher() {}
-
-
-// NOTE: We avoid fatalerror()s in this function because, we don't
-// want to kill the slave (in the case of cgroups isolator).
-int ExecutorLauncher::setup()
-{
-  // Checkpoint the forked pid, if necessary. The checkpointing must
-  // be done in the forked process (cgroups isolator) or execed
-  // launcher process (process isolator), because the slave process
-  // can die immediately after the isolator forks but before it would
-  // have a chance to write the pid to disk. That would result in an
-  // orphaned executor process unknown to the recovering slave.
-  if (checkpoint) {
-    const string& path = slave::paths::getForkedPidPath(
-        slave::paths::getMetaRootDir(slaveDirectory),
-        slaveId,
-        frameworkId,
-        executorId,
-        uuid);
-    cout << "Checkpointing executor's forked pid " << getpid()
-         << " to '" << path <<  "'" << endl;
-
-    Try<Nothing> checkpoint =
-      slave::state::checkpoint(path, stringify(getpid()));
-
-    if (checkpoint.isError()) {
-      cerr << "Failed to checkpoint executor's forked pid to '"
-           << path << "': " << checkpoint.error();
-      return -1;
-    }
-  }
-
-  const string& cwd = os::getcwd();
-
-  // TODO(benh): Do this in the slave?
-  if (shouldSwitchUser) {
-    Try<Nothing> chown = os::chown(user, workDirectory);
-
-    if (chown.isError()) {
-      cerr << "Failed to change ownership of the executor work directory "
-           << workDirectory << " to user " << user << ": " << chown.error()
-           << endl;
-      return -1;
-    }
-  }
-
-  // Enter working directory.
-  if (!os::chdir(workDirectory)) {
-    cerr << "Failed to chdir into executor work directory" << endl;
-    return -1;
-  }
-
-  // Redirect output to files in working dir if required.
-  // TODO(bmahler): It would be best if instead of closing stderr /
-  // stdout and redirecting, we instead always output to stderr /
-  // stdout. Also tee'ing their output into the work directory files
-  // when redirection is desired.
-  if (redirectIO) {
-    if (freopen("stdout", "w", stdout) == NULL) {
-      fatalerror("freopen failed");
-    }
-    if (freopen("stderr", "w", stderr) == NULL) {
-      fatalerror("freopen failed");
-    }
-  }
-
-  if (fetchExecutors() < 0) {
-    cerr << "Failed to fetch executors" << endl;
-    return -1;
-  }
-
-  // Go back to previous directory.
-  if (!os::chdir(cwd)) {
-    cerr << "Failed to chdir (back) into slave directory" << endl;
-    return -1;
-  }
-
-  return 0;
-}
-
-
-int ExecutorLauncher::launch()
-{
-  // Enter working directory.
-  if (os::chdir(workDirectory) < 0) {
-    fatalerror("Failed to chdir into the executor work directory");
-  }
-
-  if (shouldSwitchUser) {
-    switchUser();
-  }
-
-  setupEnvironment();
-
-  const string& command = commandInfo.value();
-
-  // Execute the command (via '/bin/sh -c command').
-  execl("/bin/sh", "sh", "-c", command.c_str(), (char*) NULL);
-
-  // If we get here, the execv call failed.
-  fatalerror("Could not execute '/bin/sh -c %s'", command.c_str());
-
-  return -1; // Silence end of non-void function warning.
-}
-
-
-int ExecutorLauncher::run()
-{
-  int ret = setup();
-  if (ret < 0) {
-    return ret;
-  }
-  return launch();
-}
-
-
-// Download the executor's files and optionally set executable permissions
-// if requested.
-int ExecutorLauncher::fetchExecutors()
-{
-  cout << "Fetching resources into '" << workDirectory << "'" << endl;
-
-  foreach(const CommandInfo::URI& uri, commandInfo.uris()) {
-    string resource = uri.value();
-    bool executable = uri.has_executable() && uri.executable();
-
-    cout << "Fetching resource '" << resource << "'" << endl;
-
-    // Some checks to make sure using the URI value in shell commands
-    // is safe. TODO(benh): These should be pushed into the scheduler
-    // driver and reported to the user.
-    if (resource.find_first_of('\\') != string::npos ||
-        resource.find_first_of('\'') != string::npos ||
-        resource.find_first_of('\0') != string::npos) {
-      cerr << "Illegal characters in URI" << endl;
-      return -1;
-    }
-
-    // Grab the resource from HDFS if its path begins with hdfs:// or
-    // htfp://. TODO(matei): Enforce some size limits on files we get
-    // from HDFS
-    if (resource.find("hdfs://") == 0 || resource.find("hftp://") == 0) {
-      HDFS hdfs(path::join(hadoopHome, "bin/hadoop"));
-
-      Try<std::string> basename = os::basename(resource);
-      if (basename.isError()) {
-        cerr << basename.error() << endl;
-        return -1;
-      }
-
-      string localFile = path::join(".", basename.get());
-
-      Try<Nothing> copy = hdfs.copyToLocal(resource, localFile);
-
-      if (copy.isError()) {
-        cerr << "Failed to copy from HDFS: " << copy.error() << endl;
-        return -1;
-      }
-
-      resource = localFile;
-    } else if (resource.find("http://") == 0
-               || resource.find("https://") == 0
-               || resource.find("ftp://") == 0
-               || resource.find("ftps://") == 0) {
-      string path = resource.substr(resource.find("://") + 3);
-      if (path.find("/") == string::npos) {
-        cerr << "Malformed URL (missing path)" << endl;
-        return -1;
-      }
-
-      if (path.size() <= path.find("/") + 1) {
-        cerr << "Malformed URL (missing path)" << endl;
-        return -1;
-      }
-
-      path =  path::join(".", path.substr(path.find_last_of("/") + 1));
-      cout << "Downloading '" << resource << "' to '" << path << "'" << endl;
-      Try<int> code = net::download(resource, path);
-      if (code.isError()) {
-        cerr << "Error downloading resource: " << code.error().c_str() << endl;
-        return -1;
-      } else if (code.get() != 200) {
-        cerr << "Error downloading resource, received HTTP/FTP return code "
-             << code.get() << endl;
-        return -1;
-      }
-      resource = path;
-    } else { // Copy the local resource.
-      if (resource.find_first_of("/") != 0) {
-        // We got a non-Hadoop and non-absolute path.
-        if (frameworksHome != "") {
-          resource = path::join(frameworksHome, resource);
-          cout << "Prepended configuration option frameworks_home to resource "
-               << "path, making it: '" << resource << "'" << endl;
-        } else {
-          cerr << "A relative path was passed for the resource, but "
-               << "the configuration option frameworks_home is not set. "
-               << "Please either specify this config option "
-               << "or avoid using a relative path" << endl;
-          return -1;
-        }
-      }
-
-      // Copy the resource to the current working directory.
-      ostringstream command;
-      command << "cp '" << resource << "' .";
-      cout << "Copying resource from '" << resource << "' to ." << endl;
-
-      int status = os::system(command.str());
-      if (status != 0) {
-        cerr << "Failed to copy '" << resource
-             << "' : Exit status " << status << endl;
-        return -1;
-      }
-
-      Try<std::string> base = os::basename(resource);
-      if (base.isError()) {
-        cerr << base.error() << endl;
-        return -1;
-      }
-
-      resource = path::join(".", base.get());
-    }
-
-    if (shouldSwitchUser) {
-      Try<Nothing> chown = os::chown(user, resource);
-
-      if (chown.isError()) {
-        cerr << "Failed to chown '" << resource << "' to user " << user << ": "
-             << chown.error() << endl;
-        return -1;
-      }
-    }
-
-    if (executable &&
-        !os::chmod(resource, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) {
-      cerr << "Failed to chmod '" << resource << "'" << endl;
-      return -1;
-    }
-
-    // Extract any .tgz, tar.gz, tar.bz2 or zip files.
-    if (strings::endsWith(resource, ".tgz") ||
-        strings::endsWith(resource, ".tar.gz")) {
-      string command = "tar xzf '" + resource + "'";
-      cout << "Extracting resource: " << command << endl;
-      int code = os::system(command);
-      if (code != 0) {
-        cerr << "Failed to extract resource: tar exit code " << code << endl;
-        return -1;
-      }
-    } else if (strings::endsWith(resource, ".tbz2") ||
-               strings::endsWith(resource, ".tar.bz2")) {
-      string command = "tar xjf '" + resource + "'";
-      cout << "Extracting resource: " << command << endl;
-      int code = os::system(command);
-      if (code != 0) {
-        cerr << "Failed to extract resource: tar exit code " << code << endl;
-        return -1;
-      }
-    } else if (strings::endsWith(resource, ".txz") ||
-               strings::endsWith(resource, ".tar.xz")) {
-      // If you want to use XZ on Mac OS, you can try the packages here:
-      // http://macpkg.sourceforge.net/
-      string command = "tar xJf '" + resource + "'";
-      cout << "Extracting resource: " << command << endl;
-      int code = os::system(command);
-      if (code != 0) {
-        cerr << "Failed to extract resource: tar exit code " << code << endl;
-        return -1;
-      }
-    } else if (strings::endsWith(resource, ".zip")) {
-      string command = "unzip '" + resource + "'";
-      cout << "Extracting resource: " << command << endl;
-      int code = os::system(command);
-      if (code != 0) {
-        cerr << "Failed to extract resource: unzip exit code " << code << endl;
-        return -1;
-      }
-    }
-  }
-
-  // Recursively chown the work directory, since extraction may have occurred.
-  if (shouldSwitchUser) {
-    Try<Nothing> chown = os::chown(user, ".");
-
-    if (chown.isError()) {
-      cerr << "Failed to recursively chown the work directory "
-           << workDirectory << " to user " << user << ": " << chown.error()
-           << endl;
-      return -1;
-    }
-  }
-
-  return 0;
-}
-
-
-void ExecutorLauncher::switchUser()
-{
-  if (!os::su(user)) {
-    fatal("Failed to switch to user %s for executor %s of framework %s",
-          user.c_str(), executorId.value().c_str(), frameworkId.value().c_str());
-  }
-}
-
-
-// Set up environment variables for launching a framework's executor.
-void ExecutorLauncher::setupEnvironment()
-{
-  foreachpair (const string& key, const string& value, getEnvironment()) {
-    os::setenv(key, value);
-  }
-}
-
-
-map<string, string> ExecutorLauncher::getEnvironment()
-{
-  map<string, string> env;
-
-  // Set LIBPROCESS_PORT so that we bind to a random free port (since
-  // this might have been set via --port option). We do this before
-  // the environment variables below in case it is included.
-  env["LIBPROCESS_PORT"] = "0";
-
-  // Also add MESOS_NATIVE_LIBRARY if it's not already present (and
-  // like above, we do this before the environment variables below in
-  // case the framework wants to override).
-  if (!os::hasenv("MESOS_NATIVE_LIBRARY")) {
-    string path =
-#ifdef __APPLE__
-      LIBDIR "/libmesos-" VERSION ".dylib";
-#else
-      LIBDIR "/libmesos-" VERSION ".so";
-#endif
-    if (os::exists(path)) {
-      env["MESOS_NATIVE_LIBRARY"] = path;
-    }
-  }
-
-  // Set up the environment as specified in the ExecutorInfo.
-  if (commandInfo.has_environment()) {
-    foreach (const Environment::Variable& variable,
-             commandInfo.environment().variables()) {
-      env[variable.name()] = variable.value();
-    }
-  }
-
-  // Set Mesos environment variables for slave ID, framework ID, etc.
-  env["MESOS_DIRECTORY"] = workDirectory;
-  env["MESOS_SLAVE_PID"] = slavePid;
-  env["MESOS_SLAVE_ID"] = slaveId.value();
-  env["MESOS_FRAMEWORK_ID"] = frameworkId.value();
-  env["MESOS_EXECUTOR_ID"] = executorId.value();
-  env["MESOS_EXECUTOR_UUID"] = uuid.toString();
-  env["MESOS_CHECKPOINT"] = checkpoint ? "1" : "0";
-
-  if (checkpoint) {
-    env["MESOS_RECOVERY_TIMEOUT"] = stringify(recoveryTimeout);
-  }
-
-  return env;
-}
-
-
-// Get Mesos environment variables that launcher/main.cpp will
-// pass as arguments to an ExecutorLauncher there.
-map<string, string> ExecutorLauncher::getLauncherEnvironment()
-{
-  map<string, string> env = getEnvironment();
-
-  string uris = "";
-  foreach (const CommandInfo::URI& uri, commandInfo.uris()) {
-   uris += uri.value() + "+" +
-           (uri.has_executable() && uri.executable() ? "1" : "0");
-   uris += " ";
-  }
-
-  // Remove extra space at the end.
-  if (uris.size() > 0) {
-    uris = strings::trim(uris);
-  }
-
-  env["MESOS_EXECUTOR_URIS"] = uris;
-  env["MESOS_COMMAND"] = commandInfo.value();
-  env["MESOS_USER"] = user;
-  env["MESOS_SLAVE_DIRECTORY"] = slaveDirectory;
-  env["MESOS_HADOOP_HOME"] = hadoopHome;
-  env["MESOS_REDIRECT_IO"] = redirectIO ? "1" : "0";
-  env["MESOS_SWITCH_USER"] = shouldSwitchUser ? "1" : "0";
-
-  return env;
-}
-
-} // namespace launcher {
-} // namespace internal {
-} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/f90fe764/src/launcher/launcher.hpp
----------------------------------------------------------------------
diff --git a/src/launcher/launcher.hpp b/src/launcher/launcher.hpp
deleted file mode 100644
index 104fe81..0000000
--- a/src/launcher/launcher.hpp
+++ /dev/null
@@ -1,125 +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 __LAUNCHER_HPP__
-#define __LAUNCHER_HPP__
-
-#include <map>
-#include <string>
-
-#include <mesos/mesos.hpp>
-
-#include <stout/duration.hpp>
-#include <stout/uuid.hpp>
-
-#include "slave/flags.hpp"
-
-namespace mesos {
-namespace internal {
-namespace launcher {
-
-// This class sets up the environment for an executor and then exec()'s it.
-// It can either be used after a fork() in the slave process, or run as a
-// standalone program (with the main function in launcher_main.cpp).
-//
-// The environment is initialized through for steps:
-// 1) A work directory for the framework is created by createWorkingDirectory().
-// 2) The executor is fetched off HDFS if necessary by fetchExecutor().
-// 3) Environment variables are set by setupEnvironment().
-// 4) We switch to the framework's user in switchUser().
-//
-// Isolators that wish to override the default behaviour can subclass
-// Launcher and override some of the methods to perform extra actions.
-class ExecutorLauncher {
-public:
-  ExecutorLauncher(
-      const SlaveID& slaveId,
-      const FrameworkID& frameworkId,
-      const ExecutorID& executorId,
-      const UUID& uuid,
-      const CommandInfo& commandInfo,
-      const std::string& user,
-      const std::string& workDirectory,
-      const std::string& slaveWorkDirectory,
-      const std::string& slavePid,
-      const std::string& frameworksHome,
-      const std::string& hadoopHome,
-      bool redirectIO,
-      bool shouldSwitchUser,
-      bool checkpoint,
-      Duration recoveryTimeout);
-
-  virtual ~ExecutorLauncher();
-
-  // Initialize the working directory and fetch the executor.
-  virtual int setup();
-
-  // Launches the downloaded executor.
-  virtual int launch();
-
-  // Convenience function that calls setup() and then launch().
-  virtual int run();
-
-  // Return a map of environment variables for exec'ing a
-  // launch_main.cpp (mesos-launcher binary) process. This is used
-  // by isolators that cannot exec the user's executor directly
-  // (e.g., due to potential deadlocks in forked process).
-  virtual std::map<std::string, std::string> getLauncherEnvironment();
-
-protected:
-  // Download the required files for the executor from the given set of URIs.
-  // Optionally, it will set the executable file permissions for the files.
-  // This method is expected to place files in the workDirectory.
-  virtual int fetchExecutors();
-
-  // Return a map of environment variables for launching a
-  // framework's executor.
-  virtual std::map<std::string, std::string> getEnvironment();
-
-  // Set up environment variables for launching a
-  // framework's executor.
-  virtual void setupEnvironment();
-
-  // Switch to a framework's user in preparation for exec()'ing its executor.
-  virtual void switchUser();
-
-protected:
-  const SlaveID slaveId;
-  const FrameworkID frameworkId;
-  const ExecutorID executorId;
-  const UUID uuid;
-  const CommandInfo commandInfo;
-  const std::string user;
-  const std::string workDirectory;
-  const std::string slaveDirectory;
-  const std::string slavePid;
-  const std::string frameworksHome;
-  const std::string hadoopHome;
-  const bool redirectIO;   // Whether to redirect stdout and stderr to files.
-  const bool shouldSwitchUser; // Whether to setuid to framework's user.
-  const bool checkpoint; // Whether the framework enabled checkpointing.
-
-  // Executor suicide timeout for slave recovery.
-  const Duration recoveryTimeout;
-};
-
-} // namespace launcher {
-} // namespace internal {
-} // namespace mesos {
-
-#endif // __LAUNCHER_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/f90fe764/src/launcher/main.cpp
----------------------------------------------------------------------
diff --git a/src/launcher/main.cpp b/src/launcher/main.cpp
deleted file mode 100644
index de64609..0000000
--- a/src/launcher/main.cpp
+++ /dev/null
@@ -1,100 +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 <string>
-
-#include <mesos/mesos.hpp>
-
-#include <stout/duration.hpp>
-#include <stout/strings.hpp>
-#include <stout/os.hpp>
-
-#include "launcher/launcher.hpp"
-
-using namespace mesos;
-using namespace mesos::internal; // For 'utils'.
-
-using std::string;
-
-
-int main(int argc, char** argv)
-{
-  GOOGLE_PROTOBUF_VERIFY_VERSION;
-
-  SlaveID slaveId;
-  slaveId.set_value(os::getenv("MESOS_SLAVE_ID"));
-
-  FrameworkID frameworkId;
-  frameworkId.set_value(os::getenv("MESOS_FRAMEWORK_ID"));
-
-  ExecutorID executorId;
-  executorId.set_value(os::getenv("MESOS_EXECUTOR_ID"));
-
-  CommandInfo commandInfo;
-  commandInfo.set_value(os::getenv("MESOS_COMMAND"));
-
-  // Construct URIs from the encoded environment string.
-  const std::string& uris = os::getenv("MESOS_EXECUTOR_URIS");
-  foreach (const std::string& token, strings::tokenize(uris, " ")) {
-    size_t pos = token.rfind("+"); // Delim between uri and exec permission.
-    CHECK(pos != std::string::npos) << "Invalid executor uri token in env "
-                                    << token;
-
-    CommandInfo::URI uri;
-    uri.set_value(token.substr(0, pos));
-    uri.set_executable(token.substr(pos + 1) == "1");
-
-    commandInfo.add_uris()->MergeFrom(uri);
-  }
-
-  bool checkpoint = os::getenv("MESOS_CHECKPOINT", false) == "1";
-
-  Duration recoveryTimeout = slave::RECOVERY_TIMEOUT;
-
-  // Get the recovery timeout if checkpointing is enabled.
-  if (checkpoint) {
-    string value = os::getenv("MESOS_RECOVERY_TIMEOUT", false);
-
-    if (!value.empty()) {
-      Try<Duration> _recoveryTimeout = Duration::parse(value);
-
-      CHECK_SOME(_recoveryTimeout)
-        << "Cannot parse MESOS_RECOVERY_TIMEOUT '" + value + "'";
-
-      recoveryTimeout = _recoveryTimeout.get();
-    }
-  }
-
-  return mesos::internal::launcher::ExecutorLauncher(
-      slaveId,
-      frameworkId,
-      executorId,
-      UUID::fromString(os::getenv("MESOS_EXECUTOR_UUID")),
-      commandInfo,
-      os::getenv("MESOS_USER"),
-      os::getenv("MESOS_DIRECTORY"),
-      os::getenv("MESOS_SLAVE_DIRECTORY"),
-      os::getenv("MESOS_SLAVE_PID"),
-      os::getenv("MESOS_FRAMEWORKS_HOME", false),
-      os::getenv("MESOS_HADOOP_HOME"),
-      os::getenv("MESOS_REDIRECT_IO") == "1",
-      os::getenv("MESOS_SWITCH_USER") == "1",
-      checkpoint,
-      recoveryTimeout)
-    .run();
-}

http://git-wip-us.apache.org/repos/asf/mesos/blob/f90fe764/src/local/local.cpp
----------------------------------------------------------------------
diff --git a/src/local/local.cpp b/src/local/local.cpp
index e650de9..5112391 100644
--- a/src/local/local.cpp
+++ b/src/local/local.cpp
@@ -40,7 +40,7 @@
 #include "master/master.hpp"
 #include "master/registrar.hpp"
 
-#include "slave/process_isolator.hpp"
+#include "slave/containerizer/containerizer.hpp"
 #include "slave/slave.hpp"
 
 #include "state/leveldb.hpp"
@@ -57,9 +57,8 @@ using mesos::internal::master::allocator::HierarchicalDRFAllocatorProcess;
 using mesos::internal::master::Master;
 using mesos::internal::master::Registrar;
 
+using mesos::internal::slave::Containerizer;
 using mesos::internal::slave::Slave;
-using mesos::internal::slave::Isolator;
-using mesos::internal::slave::ProcessIsolator;
 
 using process::PID;
 using process::UPID;
@@ -80,7 +79,7 @@ static state::Storage* storage = NULL;
 static state::protobuf::State* state = NULL;
 static Registrar* registrar = NULL;
 static Master* master = NULL;
-static map<Isolator*, Slave*> slaves;
+static map<Containerizer*, Slave*> slaves;
 static StandaloneMasterDetector* detector = NULL;
 static MasterContender* contender = NULL;
 static Files* files = NULL;
@@ -142,9 +141,6 @@ PID<Master> launch(const Flags& flags, Allocator* _allocator)
   vector<UPID> pids;
 
   for (int i = 0; i < flags.num_slaves; i++) {
-    // TODO(benh): Create a local isolator?
-    ProcessIsolator* isolator = new ProcessIsolator();
-
     slave::Flags flags;
     Try<Nothing> load = flags.load("MESOS_");
     if (load.isError()) {
@@ -152,13 +148,18 @@ PID<Master> launch(const Flags& flags, Allocator* _allocator)
               << "slave flags from the environment: " << load.error();
     }
 
+    Try<Containerizer*> containerizer = Containerizer::create(flags, true);
+    if (containerizer.isError()) {
+      EXIT(1) << "Failed to create a containerizer: " << containerizer.error();
+    }
+
     // Use a different work directory for each slave.
     flags.work_dir = path::join(flags.work_dir, stringify(i));
 
     // NOTE: At this point detector is already initialized by the
     // Master.
-    Slave* slave = new Slave(flags, true, detector, isolator, files);
-    slaves[isolator] = slave;
+    Slave* slave = new Slave(flags, detector, containerizer.get(), files);
+    slaves[containerizer.get()] = slave;
     pids.push_back(process::spawn(slave));
   }
 
@@ -182,10 +183,10 @@ void shutdown()
     // isolator, we can't delete the isolator until we have stopped
     // the slave.
 
-    foreachpair (Isolator* isolator, Slave* slave, slaves) {
+    foreachpair (Containerizer* containerizer, Slave* slave, slaves) {
       process::terminate(slave->self());
       process::wait(slave->self());
-      delete isolator;
+      delete containerizer;
       delete slave;
     }