Repository: mesos
Updated Branches:
  refs/heads/master a7de6513c -> 65e1712bc


Add support for container image provisioners.

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


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

Branch: refs/heads/master
Commit: 65e1712bc84f10d1c6b693998013ec248b12dd4e
Parents: a7de651
Author: Ian Downes <[email protected]>
Authored: Thu Jul 16 16:54:02 2015 -0700
Committer: Jie Yu <[email protected]>
Committed: Tue Jul 21 11:17:35 2015 -0700

----------------------------------------------------------------------
 include/mesos/slave/isolator.hpp                |  17 +-
 src/Makefile.am                                 |   2 +
 src/slave/containerizer/mesos/containerizer.cpp | 196 +++++++++++++++++--
 src/slave/containerizer/mesos/containerizer.hpp |  42 +++-
 src/slave/containerizer/provisioner.cpp         |  36 ++++
 src/slave/containerizer/provisioner.hpp         |  76 +++++++
 src/slave/flags.cpp                             |   5 +
 src/slave/flags.hpp                             |   1 +
 src/slave/paths.cpp                             |  19 ++
 src/slave/paths.hpp                             |   8 +
 src/slave/state.cpp                             |  24 +++
 src/slave/state.hpp                             |   1 +
 src/tests/containerizer_tests.cpp               |  28 ++-
 13 files changed, 430 insertions(+), 25 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/65e1712b/include/mesos/slave/isolator.hpp
----------------------------------------------------------------------
diff --git a/include/mesos/slave/isolator.hpp b/include/mesos/slave/isolator.hpp
index ef2205d..85e38f5 100644
--- a/include/mesos/slave/isolator.hpp
+++ b/include/mesos/slave/isolator.hpp
@@ -67,12 +67,23 @@ struct Limitation
 // any dependency on RunState and in turn on internal protobufs.
 struct ExecutorRunState
 {
-  ExecutorRunState(ContainerID id_, pid_t pid_, std::string directory_)
-    : id(id_), pid(pid_), directory(directory_) {}
-
+  ExecutorRunState(
+      const ExecutorInfo& executorInfo_,
+      const ContainerID& id_,
+      pid_t pid_,
+      const std::string& directory_,
+      const Option<std::string>& rootfs_)
+    : executorInfo(executorInfo_),
+      id(id_),
+      pid(pid_),
+      directory(directory_),
+      rootfs(rootfs_) {}
+
+  ExecutorInfo executorInfo;
   ContainerID id;        // Container id of the last executor run.
   pid_t pid;             // Executor pid.
   std::string directory; // Executor work directory.
+  Option<std::string> rootfs; // Optional container rootfs.
 };
 
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/65e1712b/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index e2cbd15..d99f940 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -407,6 +407,7 @@ libmesos_no_3rdparty_la_SOURCES =                           
        \
        slave/containerizer/launcher.cpp                                \
        slave/containerizer/mesos/containerizer.cpp                     \
        slave/containerizer/mesos/launch.cpp                            \
+       slave/containerizer/provisioner.cpp                             \
        slave/resource_estimators/noop.cpp                              \
        usage/usage.cpp                                                 \
        watcher/whitelist_watcher.cpp                                   \
@@ -640,6 +641,7 @@ libmesos_no_3rdparty_la_SOURCES +=                          
        \
        slave/containerizer/isolators/cgroups/perf_event.hpp            \
        slave/containerizer/isolators/namespaces/pid.hpp                \
        slave/containerizer/isolators/filesystem/shared.hpp             \
+       slave/containerizer/provisioner.hpp                             \
        slave/resource_estimators/noop.hpp                              \
        tests/cluster.hpp                                               \
        tests/containerizer.hpp                                         \

http://git-wip-us.apache.org/repos/asf/mesos/blob/65e1712b/src/slave/containerizer/mesos/containerizer.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/containerizer.cpp 
b/src/slave/containerizer/mesos/containerizer.cpp
index 47d1461..d3a596d 100644
--- a/src/slave/containerizer/mesos/containerizer.cpp
+++ b/src/slave/containerizer/mesos/containerizer.cpp
@@ -178,8 +178,20 @@ Try<MesosContainerizer*> MesosContainerizer::create(
     return Error("Failed to create launcher: " + launcher.error());
   }
 
+  Try<hashmap<ContainerInfo::Image::Type, Owned<Provisioner>>> provisioners =
+    Provisioner::create(flags, fetcher);
+
+  if (provisioners.isError()) {
+    return Error("Failed to create provisioner(s): " + provisioners.error());
+  }
+
   return new MesosContainerizer(
-      flags_, local, fetcher, Owned<Launcher>(launcher.get()), isolators);
+      flags_,
+      local,
+      fetcher,
+      Owned<Launcher>(launcher.get()),
+      isolators,
+      provisioners.get());
 }
 
 
@@ -188,13 +200,15 @@ MesosContainerizer::MesosContainerizer(
     bool local,
     Fetcher* fetcher,
     const Owned<Launcher>& launcher,
-    const vector<Owned<Isolator>>& isolators)
+    const vector<Owned<Isolator>>& isolators,
+    const hashmap<ContainerInfo::Image::Type, Owned<Provisioner>>& 
provisioners)
   : process(new MesosContainerizerProcess(
       flags,
       local,
       fetcher,
       launcher,
-      isolators))
+      isolators,
+      provisioners))
 {
   spawn(process.get());
 }
@@ -385,9 +399,11 @@ Future<Nothing> MesosContainerizerProcess::recover(
         CHECK(os::exists(directory));
 
         ExecutorRunState executorRunState(
+            executorInfo,
             run.get().id.get(),
             run.get().forkedPid.get(),
-            directory);
+            directory,
+            run.get().rootfs);
 
         recoverable.push_back(executorRunState);
       }
@@ -404,13 +420,19 @@ Future<Nothing> MesosContainerizerProcess::_recover(
     const list<ExecutorRunState>& recoverable,
     const hashset<ContainerID>& orphans)
 {
-  // Then recover the isolators.
   list<Future<Nothing>> futures;
+
+  // Then recover the provisioners.
+  foreachvalue (const Owned<Provisioner>& provisioner, provisioners) {
+    futures.push_back(provisioner->recover(recoverable, orphans));
+  }
+
+  // Then recover the isolators.
   foreach (const Owned<Isolator>& isolator, isolators) {
     futures.push_back(isolator->recover(recoverable, orphans));
   }
 
-  // If all isolators recover then continue.
+  // If all provisioners and isolators recover then continue.
   return collect(futures)
     .then(defer(self(), &Self::__recover, recoverable, orphans));
 }
@@ -431,11 +453,15 @@ Future<Nothing> MesosContainerizerProcess::__recover(
 
     container->directory = run.directory;
 
+    container->rootfs = run.rootfs;
+
     // We only checkpoint the containerizer pid after the container
     // successfully launched, therefore we can assume checkpointed
     // containers should be running after recover.
     container->state = RUNNING;
 
+    container->executorInfo = run.executorInfo;
+
     containers_[containerId] = Owned<Container>(container);
 
     foreach (const Owned<Isolator>& isolator, isolators) {
@@ -536,6 +562,7 @@ Future<bool> MesosContainerizerProcess::launch(
             << "' of framework '" << executorInfo.framework_id() << "'";
 
   Container* container = new Container();
+  container->executorInfo = executorInfo;
   container->directory = directory;
   container->state = PREPARING;
   containers_[containerId] = Owned<Container>(container);
@@ -556,7 +583,13 @@ Future<bool> MesosContainerizerProcess::launch(
   // container resources.
   container->resources = executorInfo.resources();
 
-  return prepare(containerId, executorInfo, directory, user)
+  return provision(containerId, executorInfo, slaveId, directory, checkpoint)
+    .then(defer(self(),
+                &Self::prepare,
+                containerId,
+                executorInfo,
+                directory,
+                user))
     .then(defer(self(),
                 &Self::_launch,
                 containerId,
@@ -570,6 +603,83 @@ Future<bool> MesosContainerizerProcess::launch(
 }
 
 
+Future<Nothing> MesosContainerizerProcess::provision(
+    const ContainerID& containerId,
+    const ExecutorInfo& executorInfo,
+    const SlaveID& slaveId,
+    const string& directory,
+    bool checkpoint)
+{
+  if (!executorInfo.has_container()) {
+    // TODO(idownes): Consider refusing to run a container if the
+    // containerizer is configured with an image provisioner but the
+    // executor doesn't specify an image.
+    return Nothing();
+  }
+
+  if (executorInfo.container().type() != ContainerInfo::MESOS) {
+    return Failure("Mesos containerizer can only provision Mesos containers");
+  }
+
+  if (!executorInfo.container().mesos().has_image()) {
+    // No image to provision.
+    return Nothing();
+  }
+
+  ContainerInfo::Image image = executorInfo.container().mesos().image();
+
+  if (!provisioners.contains(image.type())) {
+    return Failure("ExecutorInfo specifies container image type '" +
+                    stringify(image.type()) +
+                    "' but no suitable provisioner available");
+  }
+
+  return provisioners[image.type()]->provision(containerId, image)
+    .then(defer(self(),
+                &Self::_provision,
+                containerId,
+                executorInfo,
+                slaveId,
+                checkpoint,
+                lambda::_1));
+}
+
+
+Future<Nothing> MesosContainerizerProcess::_provision(
+    const ContainerID& containerId,
+    const ExecutorInfo& executorInfo,
+    const SlaveID& slaveId,
+    bool checkpoint,
+    const string& rootfs)
+{
+  if (!containers_.contains(containerId)) {
+    return Failure("Container is already destroyed");
+  }
+
+  containers_[containerId]->rootfs = rootfs;
+
+  if (checkpoint) {
+    const string path = slave::paths::getContainerRootfsPath(
+        slave::paths::getMetaRootDir(flags.work_dir),
+        slaveId,
+        executorInfo.framework_id(),
+        executorInfo.executor_id(),
+        containerId);
+
+    LOG(INFO) << "Checkpointing container " << containerId << " rootfs path '"
+              << rootfs << "' to '" << path << "'";
+
+    Try<Nothing> checkpointed = slave::state::checkpoint(path, rootfs);
+    if (checkpointed.isError()) {
+      return Failure("Failed to checkpoint container rootfs path to '" +
+                     path + "': " + checkpointed.error());
+    }
+  }
+
+  return Nothing();
+}
+
+
 Future<bool> MesosContainerizerProcess::launch(
     const ContainerID& containerId,
     const TaskInfo& taskInfo,
@@ -694,7 +804,9 @@ Future<bool> MesosContainerizerProcess::_launch(
   // Prepare environment variables for the executor.
   map<string, string> environment = executorEnvironment(
       executorInfo,
-      directory,
+      containers_[containerId]->rootfs.isSome()
+        ? flags.sandbox_directory
+        : directory,
       slaveId,
       slavePid,
       checkpoint,
@@ -717,7 +829,12 @@ Future<bool> MesosContainerizerProcess::_launch(
   MesosContainerizerLaunch::Flags launchFlags;
 
   launchFlags.command = JSON::Protobuf(executorInfo.command());
-  launchFlags.directory = directory;
+
+  launchFlags.directory = containers_[containerId]->rootfs.isSome()
+    ? flags.sandbox_directory
+    : directory;
+
+  launchFlags.rootfs = containers_[containerId]->rootfs;
   launchFlags.user = user;
   launchFlags.pipe_read = pipes[0];
   launchFlags.pipe_write = pipes[1];
@@ -1131,9 +1248,9 @@ void MesosContainerizerProcess::____destroy(
   foreach (const Future<Nothing>& cleanup, cleanups.get()) {
     if (!cleanup.isReady()) {
       container->promise.fail(
-        "Failed to clean up an isolator when destroying container '" +
-        stringify(containerId) + "' :" +
-        (cleanup.isFailed() ? cleanup.failure() : "discarded future"));
+          "Failed to clean up an isolator when destroying container '" +
+          stringify(containerId) + "' :" +
+          (cleanup.isFailed() ? cleanup.failure() : "discarded future"));
 
       containers_.erase(containerId);
 
@@ -1143,6 +1260,61 @@ void MesosContainerizerProcess::____destroy(
     }
   }
 
+  if (container->rootfs.isNone()) {
+    // No rootfs to clean up so continue.
+    _____destroy(containerId, status, message, killed, Nothing());
+    return;
+  }
+
+  ContainerInfo::Image::Type type =
+    container->executorInfo.container().mesos().image().type();
+
+  if (!provisioners.contains(type)) {
+    // We should have a provisioner to handle cleaning up the rootfs
+    // but continue clean up even if we don't.
+    LOG(WARNING) << "No provisioner to clean up rootfs for container "
+                 << containerId << " (type '" << type << "'), continuing";
+
+    _____destroy(containerId, status, message, killed, Nothing());
+    return;
+  }
+
+  provisioners[type]->destroy(containerId)
+    .onAny(defer(self(),
+                 &Self::_____destroy,
+                 containerId,
+                 status,
+                 message,
+                 killed,
+                 lambda::_1));
+
+  return;
+}
+
+
+void MesosContainerizerProcess::_____destroy(
+    const ContainerID& containerId,
+    const Future<Option<int>>& status,
+    Option<string> message,
+    bool killed,
+    const Future<Nothing>& future)
+{
+  CHECK(containers_.contains(containerId));
+
+  Container* container = containers_[containerId].get();
+
+  if (!future.isReady()) {
+    container->promise.fail(
+        "Failed to clean up the root filesystem when destroying container: " +
+        (future.isFailed() ? future.failure() : "discarded future"));
+
+    containers_.erase(containerId);
+
+    ++metrics.container_destroy_errors;
+
+    return;
+  }
+
   // If any isolator limited the container then we mark it to killed.
   // Note: We may not see a limitation in time for it to be
   // registered. This could occur if the limitation (e.g., an OOM)

http://git-wip-us.apache.org/repos/asf/mesos/blob/65e1712b/src/slave/containerizer/mesos/containerizer.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/containerizer.hpp 
b/src/slave/containerizer/mesos/containerizer.hpp
index 3ac2387..a8c71d6 100644
--- a/src/slave/containerizer/mesos/containerizer.hpp
+++ b/src/slave/containerizer/mesos/containerizer.hpp
@@ -33,6 +33,7 @@
 
 #include "slave/containerizer/containerizer.hpp"
 #include "slave/containerizer/launcher.hpp"
+#include "slave/containerizer/provisioner.hpp"
 
 namespace mesos {
 namespace internal {
@@ -56,7 +57,10 @@ public:
       bool local,
       Fetcher* fetcher,
       const process::Owned<Launcher>& launcher,
-      const std::vector<process::Owned<mesos::slave::Isolator>>& isolators);
+      const std::vector<process::Owned<mesos::slave::Isolator>>& isolators,
+      const hashmap<ContainerInfo::Image::Type,
+                    process::Owned<Provisioner>>& provisioners);
+
 
   // Used for testing.
   MesosContainerizer(const process::Owned<MesosContainerizerProcess>& 
_process);
@@ -113,12 +117,15 @@ public:
       bool _local,
       Fetcher* _fetcher,
       const process::Owned<Launcher>& _launcher,
-      const std::vector<process::Owned<mesos::slave::Isolator>>& _isolators)
+      const std::vector<process::Owned<mesos::slave::Isolator>>& _isolators,
+      const hashmap<ContainerInfo::Image::Type,
+                    process::Owned<Provisioner>>& _provisioners)
     : flags(_flags),
       local(_local),
       fetcher(_fetcher),
       launcher(_launcher),
-      isolators(_isolators) {}
+      isolators(_isolators),
+      provisioners(_provisioners) {}
 
   virtual ~MesosContainerizerProcess() {}
 
@@ -176,6 +183,20 @@ private:
       const std::list<mesos::slave::ExecutorRunState>& recovered,
       const hashset<ContainerID>& orphans);
 
+  process::Future<Nothing> provision(
+      const ContainerID& containerId,
+      const ExecutorInfo& executorInfo,
+      const SlaveID& slaveId,
+      const std::string& directory,
+      bool checkpoint);
+
+  process::Future<Nothing> _provision(
+      const ContainerID& containerId,
+      const ExecutorInfo& executorInfo,
+      const SlaveID& slaveId,
+      bool checkpoint,
+      const std::string& rootfs);
+
   process::Future<std::list<Option<CommandInfo>>> prepare(
       const ContainerID& containerId,
       const ExecutorInfo& executorInfo,
@@ -219,7 +240,7 @@ private:
       const Option<std::string>& message,
       bool killed);
 
-  // Continues (and completes) '__destroy()' once all isolators have completed
+  // Continues '__destroy()' once all isolators have completed
   // cleanup.
   void ____destroy(
       const ContainerID& containerId,
@@ -228,6 +249,15 @@ private:
       Option<std::string> message,
       bool killed);
 
+  // Continues (and completes) '__destroy()' once any root filessystem
+  // has been cleaned up.
+  void _____destroy(
+      const ContainerID& containerId,
+      const process::Future<Option<int>>& status,
+      Option<std::string> message,
+      bool killed,
+      const process::Future<Nothing>& cleanup);
+
   // Call back for when an isolator limits a container and impacts the
   // processes. This will trigger container destruction.
   void limited(
@@ -254,6 +284,7 @@ private:
   Fetcher* fetcher;
   const process::Owned<Launcher> launcher;
   const std::vector<process::Owned<mesos::slave::Isolator>> isolators;
+  hashmap<ContainerInfo::Image::Type, process::Owned<Provisioner>> 
provisioners;
 
   enum State
   {
@@ -292,6 +323,9 @@ private:
     // ResourceStatistics limits in usage().
     Resources resources;
 
+    // We keep track of the ExecutorInfo so we have the optional ContainerInfo.
+    ExecutorInfo executorInfo;
+
     // The executor's working directory on the host.
     std::string directory;
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/65e1712b/src/slave/containerizer/provisioner.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner.cpp 
b/src/slave/containerizer/provisioner.cpp
new file mode 100644
index 0000000..df52e36
--- /dev/null
+++ b/src/slave/containerizer/provisioner.cpp
@@ -0,0 +1,36 @@
+/**
+ * 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 "slave/containerizer/provisioner.hpp"
+
+using namespace process;
+
+namespace mesos {
+namespace internal {
+namespace slave {
+
+Try<hashmap<ContainerInfo::Image::Type, Owned<Provisioner>>>
+  Provisioner::create(const Flags& flags, Fetcher* fetcher)
+{
+  // TODO(tnachen): Load provisioners when one of them is available.
+  return hashmap<ContainerInfo::Image::Type, Owned<Provisioner>>();
+}
+
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/65e1712b/src/slave/containerizer/provisioner.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner.hpp 
b/src/slave/containerizer/provisioner.hpp
new file mode 100644
index 0000000..f7fb068
--- /dev/null
+++ b/src/slave/containerizer/provisioner.hpp
@@ -0,0 +1,76 @@
+/**
+ * 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 __MESOS_PROVISIONER_HPP__
+#define __MESOS_PROVISIONER_HPP__
+
+#include <list>
+
+#include <mesos/resources.hpp>
+
+#include <mesos/slave/isolator.hpp> // For ExecutorRunState.
+
+#include <stout/hashmap.hpp>
+#include <stout/nothing.hpp>
+#include <stout/try.hpp>
+
+#include <process/future.hpp>
+#include <process/owned.hpp>
+
+#include "slave/flags.hpp"
+
+#include "slave/containerizer/fetcher.hpp"
+
+namespace mesos {
+namespace internal {
+namespace slave {
+
+class Provisioner
+{
+public:
+  virtual ~Provisioner() {}
+
+  static Try<hashmap<ContainerInfo::Image::Type, process::Owned<Provisioner>>>
+    create(const Flags& flags, Fetcher* fetcher);
+
+  // Recover root filesystems for containers from the run states and
+  // the orphan containers (known to the launcher but not known to the
+  // slave) detected by the launcher. This function is also
+  // responsible for cleaning up any intermediate artifacts (e.g.
+  // directories) to not leak anything.
+  virtual process::Future<Nothing> recover(
+      const std::list<mesos::slave::ExecutorRunState>& states,
+      const hashset<ContainerID>& orphans) = 0;
+
+  // Provision a root filesystem for the container using the specified
+  // image and return the absolute path to the root filesystem.
+  virtual process::Future<std::string> provision(
+      const ContainerID& containerId,
+      const ContainerInfo::Image& image) = 0;
+
+  // Destroy a previously provisioned root filesystem. Assumes that
+  // all references (e.g., mounts, open files) to the provisioned
+  // filesystem have been removed.
+  virtual process::Future<Nothing> destroy(const ContainerID& containerId) = 0;
+};
+
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
+
+#endif // __MESOS_PROVISIONER_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/65e1712b/src/slave/flags.cpp
----------------------------------------------------------------------
diff --git a/src/slave/flags.cpp b/src/slave/flags.cpp
index f569363..82b6cf4 100644
--- a/src/slave/flags.cpp
+++ b/src/slave/flags.cpp
@@ -59,6 +59,11 @@ mesos::internal::slave::Flags::Flags()
       "for the Mesos Containerizer.",
       "posix/cpu,posix/mem");
 
+  add(&Flags::provisioners,
+      "provisioners",
+      "Comma separated list of image rootfs provisioners,\n"
+      "e.g., appc,docker");
+
   add(&Flags::default_role,
       "default_role",
       "Any resources in the --resources flag that\n"

http://git-wip-us.apache.org/repos/asf/mesos/blob/65e1712b/src/slave/flags.hpp
----------------------------------------------------------------------
diff --git a/src/slave/flags.hpp b/src/slave/flags.hpp
index bbc1b9e..881d494 100644
--- a/src/slave/flags.hpp
+++ b/src/slave/flags.hpp
@@ -46,6 +46,7 @@ public:
   Option<std::string> hostname;
   Option<std::string> resources;
   std::string isolation;
+  Option<std::string> provisioners;
   std::string default_role;
   Option<std::string> attributes;
   Bytes fetcher_cache_size;

http://git-wip-us.apache.org/repos/asf/mesos/blob/65e1712b/src/slave/paths.cpp
----------------------------------------------------------------------
diff --git a/src/slave/paths.cpp b/src/slave/paths.cpp
index 0741616..404c214 100644
--- a/src/slave/paths.cpp
+++ b/src/slave/paths.cpp
@@ -51,6 +51,7 @@ const char LIBPROCESS_PID_FILE[] = "libprocess.pid";
 const char EXECUTOR_INFO_FILE[] = "executor.info";
 const char EXECUTOR_SENTINEL_FILE[] = "executor.sentinel";
 const char FORKED_PID_FILE[] = "forked.pid";
+const char CONTAINER_ROOTFS_FILE[] = "rootfs";
 const char TASK_INFO_FILE[] = "task.info";
 const char TASK_UPDATES_FILE[] = "task.updates";
 const char RESOURCES_INFO_FILE[] = "resources.info";
@@ -268,6 +269,24 @@ string getForkedPidPath(
 }
 
 
+string getContainerRootfsPath(
+    const string& rootDir,
+    const SlaveID& slaveId,
+    const FrameworkID& frameworkId,
+    const ExecutorID& executorId,
+    const ContainerID& containerId)
+{
+  return path::join(
+      getExecutorRunPath(
+          rootDir,
+          slaveId,
+          frameworkId,
+          executorId,
+          containerId),
+      CONTAINER_ROOTFS_FILE);
+}
+
+
 Try<list<string>> getTaskPaths(
     const string& rootDir,
     const SlaveID& slaveId,

http://git-wip-us.apache.org/repos/asf/mesos/blob/65e1712b/src/slave/paths.hpp
----------------------------------------------------------------------
diff --git a/src/slave/paths.hpp b/src/slave/paths.hpp
index 00476f1..c7f85f1 100644
--- a/src/slave/paths.hpp
+++ b/src/slave/paths.hpp
@@ -204,6 +204,14 @@ std::string getForkedPidPath(
     const ContainerID& containerId);
 
 
+std::string getContainerRootfsPath(
+    const std::string& rootDir,
+    const SlaveID& slaveId,
+    const FrameworkID& frameworkId,
+    const ExecutorID& executorId,
+    const ContainerID& containerId);
+
+
 Try<std::list<std::string>> getTaskPaths(
     const std::string& rootDir,
     const SlaveID& slaveId,

http://git-wip-us.apache.org/repos/asf/mesos/blob/65e1712b/src/slave/state.cpp
----------------------------------------------------------------------
diff --git a/src/slave/state.cpp b/src/slave/state.cpp
index f8a9514..b9f2d8a 100644
--- a/src/slave/state.cpp
+++ b/src/slave/state.cpp
@@ -534,6 +534,30 @@ Try<RunState> RunState::recover(
 
   state.libprocessPid = process::UPID(pid.get());
 
+  // Read the location of the container's root filesystem, if used.
+  path = paths::getContainerRootfsPath(
+      rootDir, slaveId, frameworkId, executorId, containerId);
+
+  // Because a container root filesystem is optional we'll recover the
+  // path if present but continue to recover normally if it's not.
+  if (os::exists(path)) {
+    Try<string> rootfs = os::read(path);
+    if (rootfs.isError()) {
+      message = "Failed to recover container rootfs from '" + path +
+                "': " + rootfs.error();
+
+      if (strict) {
+        return Error(message);
+      } else {
+        LOG(WARNING) << message;
+        state.errors++;
+        return state;
+      }
+    }
+
+    state.rootfs = rootfs.get();
+  }
+
   return state;
 }
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/65e1712b/src/slave/state.hpp
----------------------------------------------------------------------
diff --git a/src/slave/state.hpp b/src/slave/state.hpp
index bb0eee7..4e00468 100644
--- a/src/slave/state.hpp
+++ b/src/slave/state.hpp
@@ -208,6 +208,7 @@ struct RunState
   hashmap<TaskID, TaskState> tasks;
   Option<pid_t> forkedPid;
   Option<process::UPID> libprocessPid;
+  Option<std::string> rootfs;
 
   // Executor terminated and all its updates acknowledged.
   bool completed;

http://git-wip-us.apache.org/repos/asf/mesos/blob/65e1712b/src/tests/containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer_tests.cpp 
b/src/tests/containerizer_tests.cpp
index 0cdb2d2..29114e7 100644
--- a/src/tests/containerizer_tests.cpp
+++ b/src/tests/containerizer_tests.cpp
@@ -99,7 +99,8 @@ public:
         false,
         fetcher,
         Owned<Launcher>(launcher.get()),
-        isolators);
+        isolators,
+        hashmap<ContainerInfo::Image::Type, Owned<Provisioner>>());
   }
 
 
@@ -335,8 +336,16 @@ public:
       bool local,
       Fetcher* fetcher,
       const process::Owned<Launcher>& launcher,
-      const vector<process::Owned<Isolator>>& isolators)
-    : MesosContainerizerProcess(flags, local, fetcher, launcher, isolators)
+      const vector<process::Owned<Isolator>>& isolators,
+      const hashmap<ContainerInfo::Image::Type,
+                    Owned<Provisioner>>& provisioners)
+    : MesosContainerizerProcess(
+        flags,
+        local,
+        fetcher,
+        launcher,
+        isolators,
+        provisioners)
   {
     // NOTE: See TestContainerizer::setup for why we use
     // 'EXPECT_CALL' and 'WillRepeatedly' here instead of
@@ -445,7 +454,8 @@ TEST_F(MesosContainerizerDestroyTest, DestroyWhileFetching)
       true,
       &fetcher,
       Owned<Launcher>(launcher.get()),
-      isolators);
+      isolators,
+      hashmap<ContainerInfo::Image::Type, Owned<Provisioner>>());
 
   Future<Nothing> exec;
   Promise<bool> promise;
@@ -509,12 +519,15 @@ TEST_F(MesosContainerizerDestroyTest, 
DestroyWhilePreparing)
 
   Fetcher fetcher;
 
+  hashmap<ContainerInfo::Image::Type, Owned<Provisioner>> provisioners;
+
   MockMesosContainerizerProcess* process = new MockMesosContainerizerProcess(
       flags,
       true,
       &fetcher,
       Owned<Launcher>(launcher.get()),
-      isolators);
+      isolators,
+      provisioners);
 
   MesosContainerizer 
containerizer((Owned<MesosContainerizerProcess>(process)));
 
@@ -584,12 +597,15 @@ TEST_F(MesosContainerizerDestroyTest, 
LauncherDestroyFailure)
   vector<process::Owned<Isolator>> isolators;
   Fetcher fetcher;
 
+  hashmap<ContainerInfo::Image::Type, Owned<Provisioner>> provisioners;
+
   MesosContainerizerProcess* process = new MesosContainerizerProcess(
       flags,
       true,
       &fetcher,
       Owned<Launcher>(launcher),
-      isolators);
+      isolators,
+      provisioners);
 
   MesosContainerizer 
containerizer((Owned<MesosContainerizerProcess>(process)));
 

Reply via email to