Repository: mesos
Updated Branches:
  refs/heads/master c2c9bddd9 -> f0f6a276d


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/f0f6a276
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/f0f6a276
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/f0f6a276

Branch: refs/heads/master
Commit: f0f6a276da0935e0041710dc2df61e32b6e8e270
Parents: c2c9bdd
Author: Ian Downes <[email protected]>
Authored: Thu Jul 16 16:54:02 2015 -0700
Committer: Timothy Chen <[email protected]>
Committed: Thu Jul 16 17:32:14 2015 -0700

----------------------------------------------------------------------
 include/mesos/slave/isolator.hpp                |  17 +-
 src/Makefile.am                                 |   2 +
 src/slave/containerizer/mesos/containerizer.cpp | 175 ++++++++++++++++++-
 src/slave/containerizer/mesos/containerizer.hpp |  37 +++-
 src/slave/containerizer/provisioner.cpp         |  43 +++++
 src/slave/containerizer/provisioner.hpp         |  77 ++++++++
 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               |  32 +++-
 13 files changed, 419 insertions(+), 22 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/f0f6a276/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/f0f6a276/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index e2cbd15..d8fdfab 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -520,6 +520,7 @@ if OS_LINUX
   libmesos_no_3rdparty_la_SOURCES += 
slave/containerizer/isolators/namespaces/pid.cpp
   libmesos_no_3rdparty_la_SOURCES += 
slave/containerizer/isolators/filesystem/shared.cpp
   libmesos_no_3rdparty_la_SOURCES += slave/containerizer/linux_launcher.cpp
+  libmesos_no_3rdparty_la_SOURCES += slave/containerizer/provisioner.cpp
 else
   EXTRA_DIST += linux/cgroups.cpp
   EXTRA_DIST += linux/fs.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/f0f6a276/src/slave/containerizer/mesos/containerizer.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/containerizer.cpp 
b/src/slave/containerizer/mesos/containerizer.cpp
index 47d1461..54787ac 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,16 @@ 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 +400,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 +421,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 +454,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) {
@@ -538,6 +565,7 @@ Future<bool> MesosContainerizerProcess::launch(
   Container* container = new Container();
   container->directory = directory;
   container->state = PREPARING;
+  container->executorInfo = executorInfo;
   containers_[containerId] = Owned<Container>(container);
 
   // Prepare volumes for the container.
@@ -556,7 +584,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 +604,70 @@ 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(), [=](const string& rootfs) -> Future<Nothing> {
+      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's rootfs path '" << rootfs
+                  << "' to '" << path << "'";
+
+        Try<Nothing> checkpointed = slave::state::checkpoint(path, rootfs);
+        if (checkpointed.isError()) {
+          LOG(ERROR) << "Failed to checkpoint container's rootfs path to '"
+                     << path << "': " << checkpointed.error();
+
+          return Failure("Could not checkpoint container's rootfs");
+        }
+      }
+
+      return Nothing();
+    }));
+}
+
+
 Future<bool> MesosContainerizerProcess::launch(
     const ContainerID& containerId,
     const TaskInfo& taskInfo,
@@ -694,7 +792,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 +817,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];
@@ -1143,6 +1248,58 @@ void MesosContainerizerProcess::____destroy(
     }
   }
 
+  if (container->rootfs.isNone()) {
+    // No rootfs to clean up so continue.
+    return _____destroy(containerId, status, message, killed, Nothing());
+  }
+
+  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";
+    return _____destroy(containerId, status, message, killed, Nothing());
+  }
+
+  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>& cleanup)
+{
+  CHECK(containers_.contains(containerId));
+
+  Container* container = containers_[containerId].get();
+
+  if (!cleanup.isReady()) {
+    container->promise.fail(
+        "Failed to clean up the root filesystem when destroying container: " +
+        (cleanup.isFailed() ? cleanup.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/f0f6a276/src/slave/containerizer/mesos/containerizer.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/containerizer.hpp 
b/src/slave/containerizer/mesos/containerizer.hpp
index 3ac2387..f8eb0a9 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,13 @@ 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<std::list<Option<CommandInfo>>> prepare(
       const ContainerID& containerId,
       const ExecutorInfo& executorInfo,
@@ -219,7 +233,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 +242,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 +277,9 @@ 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 +318,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/f0f6a276/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..1d304a6
--- /dev/null
+++ b/src/slave/containerizer/provisioner.cpp
@@ -0,0 +1,43 @@
+/**
+ * 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 <stout/hashmap.hpp>
+#include <stout/strings.hpp>
+
+#include "slave/containerizer/provisioner.hpp"
+
+#include "slave/containerizer/provisioners/appc.hpp"
+
+using namespace process;
+
+using std::string;
+
+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/f0f6a276/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..eef935f
--- /dev/null
+++ b/src/slave/containerizer/provisioner.hpp
@@ -0,0 +1,77 @@
+/**
+ * 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 <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"
+
+// For ExecutorRunState.
+#include "mesos/slave/isolator.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 containers from the run states and the orphan containers
+  // (known to the launcher but not known to the slave) detected by
+  // the launcher.
+  // Recover also is 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/f0f6a276/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/f0f6a276/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/f0f6a276/src/slave/paths.cpp
----------------------------------------------------------------------
diff --git a/src/slave/paths.cpp b/src/slave/paths.cpp
index 0741616..5fe567d 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";
@@ -230,6 +231,24 @@ string getExecutorLatestRunPath(
 }
 
 
+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);
+}
+
+
 string getLibprocessPidPath(
     const string& rootDir,
     const SlaveID& slaveId,

http://git-wip-us.apache.org/repos/asf/mesos/blob/f0f6a276/src/slave/paths.hpp
----------------------------------------------------------------------
diff --git a/src/slave/paths.hpp b/src/slave/paths.hpp
index 00476f1..7e1e315 100644
--- a/src/slave/paths.hpp
+++ b/src/slave/paths.hpp
@@ -188,6 +188,14 @@ std::string getExecutorLatestRunPath(
     const ExecutorID& executorId);
 
 
+std::string getContainerRootfsPath(
+    const std::string& rootDir,
+    const SlaveID& slaveId,
+    const FrameworkID& frameworkId,
+    const ExecutorID& executorId,
+    const ContainerID& containerId);
+
+
 std::string getLibprocessPidPath(
     const std::string& rootDir,
     const SlaveID& slaveId,

http://git-wip-us.apache.org/repos/asf/mesos/blob/f0f6a276/src/slave/state.cpp
----------------------------------------------------------------------
diff --git a/src/slave/state.cpp b/src/slave/state.cpp
index f8a9514..a2b8c5a 100644
--- a/src/slave/state.cpp
+++ b/src/slave/state.cpp
@@ -458,6 +458,30 @@ Try<RunState> RunState::recover(
     state.errors += task.get().errors;
   }
 
+  // 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();
+  }
+
   // Read the forked pid.
   path = paths::getForkedPidPath(
       rootDir, slaveId, frameworkId, executorId, containerId);

http://git-wip-us.apache.org/repos/asf/mesos/blob/f0f6a276/src/slave/state.hpp
----------------------------------------------------------------------
diff --git a/src/slave/state.hpp b/src/slave/state.hpp
index bb0eee7..c549b18 100644
--- a/src/slave/state.hpp
+++ b/src/slave/state.hpp
@@ -206,6 +206,7 @@ struct RunState
 
   Option<ContainerID> id;
   hashmap<TaskID, TaskState> tasks;
+  Option<std::string> rootfs;
   Option<pid_t> forkedPid;
   Option<process::UPID> libprocessPid;
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/f0f6a276/src/tests/containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer_tests.cpp 
b/src/tests/containerizer_tests.cpp
index 0cdb2d2..cae717e 100644
--- a/src/tests/containerizer_tests.cpp
+++ b/src/tests/containerizer_tests.cpp
@@ -94,12 +94,15 @@ public:
       return Error(launcher.error());
     }
 
+    hashmap<ContainerInfo::Image::Type, Owned<Provisioner>> provisioners;
+
     return new MesosContainerizer(
         flags,
         false,
         fetcher,
         Owned<Launcher>(launcher.get()),
-        isolators);
+        isolators,
+        provisioners);
   }
 
 
@@ -335,8 +338,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
@@ -440,12 +451,15 @@ TEST_F(MesosContainerizerDestroyTest, 
DestroyWhileFetching)
 
   Fetcher fetcher;
 
+  hashmap<ContainerInfo::Image::Type, Owned<Provisioner>> provisioners;
+
   MockMesosContainerizerProcess* process = new MockMesosContainerizerProcess(
       flags,
       true,
       &fetcher,
       Owned<Launcher>(launcher.get()),
-      isolators);
+      isolators,
+      provisioners);
 
   Future<Nothing> exec;
   Promise<bool> promise;
@@ -509,12 +523,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 +601,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