You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by tn...@apache.org on 2015/09/25 18:33:03 UTC

[11/17] mesos git commit: Restructure Docker provisioner and protos.

Restructure Docker provisioner and protos.


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

Branch: refs/heads/master
Commit: 4dedbf4aec2183af94ee083d2f6fb22057f9dfc8
Parents: 2cc0dfb
Author: Timothy Chen <tn...@gmail.com>
Authored: Fri Sep 11 00:19:42 2015 -0700
Committer: Timothy Chen <tn...@apache.org>
Committed: Fri Sep 25 09:02:05 2015 -0700

----------------------------------------------------------------------
 src/Makefile.am                                 |   9 +-
 src/messages/docker_provisioner.hpp             |  24 -
 src/messages/docker_provisioner.proto           |  35 --
 src/slave/containerizer/provisioner.cpp         |   1 +
 .../provisioner/docker/local_store.cpp          | 460 ++++++++++++++++++
 .../provisioner/docker/local_store.hpp          |  64 +++
 .../provisioner/docker/message.hpp              |  71 +++
 .../provisioner/docker/message.proto            |  43 ++
 .../provisioner/docker/metadata_manager.cpp     | 251 ++++++++++
 .../provisioner/docker/metadata_manager.hpp     | 106 +++++
 .../containerizer/provisioner/docker/paths.cpp  | 104 ++++
 .../containerizer/provisioner/docker/paths.hpp  |  86 ++++
 .../containerizer/provisioner/docker/store.cpp  |  47 ++
 .../containerizer/provisioner/docker/store.hpp  |  58 +++
 .../containerizer/provisioner/provisioner.hpp   |   2 +-
 src/slave/containerizer/provisioners/docker.cpp | 425 -----------------
 src/slave/containerizer/provisioners/docker.hpp | 166 -------
 .../provisioners/docker/local_store.cpp         | 471 -------------------
 .../provisioners/docker/local_store.hpp         |  66 ---
 .../provisioners/docker/metadata_manager.cpp    | 254 ----------
 .../provisioners/docker/metadata_manager.hpp    | 104 ----
 .../containerizer/provisioners/docker/paths.cpp | 104 ----
 .../containerizer/provisioners/docker/paths.hpp |  86 ----
 .../containerizer/provisioners/docker/store.hpp |  76 ---
 src/slave/flags.cpp                             |  12 +-
 src/slave/flags.hpp                             |   7 +-
 .../containerizer/provisioner_docker_tests.cpp  |  59 ++-
 27 files changed, 1336 insertions(+), 1855 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index a2a64dc..818d62d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -250,8 +250,9 @@ BUILT_SOURCES += $(STATE_PROTOS)
 CLEANFILES += $(STATE_PROTOS)
 
 DOCKER_PROVISIONER_PROTOS =                                             \
-  messages/docker_provisioner.pb.cc                                     \
-  messages/docker_provisioner.pb.h
+  slave/containerizer/provisioner/docker/message.pb.cc			\
+  slave/containerizer/provisioner/docker/message.pb.h
+
 
 BUILT_SOURCES += $(DOCKER_PROVISIONER_PROTOS)
 CLEANFILES += $(DOCKER_PROVISIONER_PROTOS)
@@ -529,11 +530,11 @@ libmesos_no_3rdparty_la_SOURCES =					\
 	slave/containerizer/provisioner/appc/store.cpp			\
 	slave/containerizer/provisioner/backend.cpp			\
 	slave/containerizer/provisioner/backends/copy.cpp		\
-	slave/containerizer/provisioner/docker.cpp			\
 	slave/containerizer/provisioner/docker/local_store.cpp		\
 	slave/containerizer/provisioner/docker/metadata_manager.cpp	\
 	slave/containerizer/provisioner/docker/paths.cpp		\
 	slave/containerizer/provisioner/docker/registry_client.cpp	\
+	slave/containerizer/provisioner/docker/store.cpp		\
 	slave/containerizer/provisioner/docker/token_manager.cpp	\
 	slave/resource_estimators/noop.cpp				\
 	usage/usage.cpp							\
@@ -831,7 +832,7 @@ libmesos_no_3rdparty_la_SOURCES +=					\
 	slave/containerizer/provisioner/backends/copy.hpp		\
 	slave/containerizer/provisioner/docker.hpp			\
 	slave/containerizer/provisioner/docker/local_store.hpp		\
-	slave/containerizer/provisioners/docker/metadata_manager.hpp    \
+	slave/containerizer/provisioner/docker/metadata_manager.hpp	\
 	slave/containerizer/provisioner/docker/paths.hpp		\
 	slave/containerizer/provisioner/docker/registry_client.hpp	\
 	slave/containerizer/provisioner/docker/store.hpp		\

http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/messages/docker_provisioner.hpp
----------------------------------------------------------------------
diff --git a/src/messages/docker_provisioner.hpp b/src/messages/docker_provisioner.hpp
deleted file mode 100644
index 684bd46..0000000
--- a/src/messages/docker_provisioner.hpp
+++ /dev/null
@@ -1,24 +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 __MESSAGES_DOCKER_PROVISIONER_HPP__
-#define __MESSAGES_DOCKER_PROVISIONER_HPP__
-
-#include "messages/docker_provisioner.pb.h"
-
-#endif // __MESSAGES_DOCKER_PROVISIONER_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/messages/docker_provisioner.proto
----------------------------------------------------------------------
diff --git a/src/messages/docker_provisioner.proto b/src/messages/docker_provisioner.proto
deleted file mode 100644
index 9de6707..0000000
--- a/src/messages/docker_provisioner.proto
+++ /dev/null
@@ -1,35 +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.
- */
-
-import "mesos/mesos.proto";
-
-package mesos.internal;
-
-/**
- * A Docker Image name and the layer ids of the layers that comprise the image.
- * The layerIds are ordered, with the root layer id (no parent layer id) first
- * and the leaf layer id last.
- */
-message DockerProvisionerImages {
-  message Image {
-    required string name = 1;
-    repeated string layer_ids = 2;
-  }
-
-  repeated Image images = 1;
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioner.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner.cpp b/src/slave/containerizer/provisioner.cpp
index 1ff3f10..6a0faac 100644
--- a/src/slave/containerizer/provisioner.cpp
+++ b/src/slave/containerizer/provisioner.cpp
@@ -23,6 +23,7 @@
 #include "slave/containerizer/provisioner.hpp"
 
 #include "slave/containerizer/provisioners/appc/provisioner.hpp"
+
 #include "slave/containerizer/provisioners/docker/provisioner.hpp"
 
 using namespace process;

http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioner/docker/local_store.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/docker/local_store.cpp b/src/slave/containerizer/provisioner/docker/local_store.cpp
new file mode 100644
index 0000000..6a73dbb
--- /dev/null
+++ b/src/slave/containerizer/provisioner/docker/local_store.cpp
@@ -0,0 +1,460 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <list>
+#include <vector>
+
+#include <glog/logging.h>
+
+#include <stout/json.hpp>
+#include <stout/os.hpp>
+#include <stout/result.hpp>
+
+#include <process/collect.hpp>
+#include <process/defer.hpp>
+#include <process/dispatch.hpp>
+
+#include "common/status_utils.hpp"
+
+#include "slave/containerizer/provisioner/docker/local_store.hpp"
+#include "slave/containerizer/provisioner/docker/metadata_manager.hpp"
+#include "slave/containerizer/provisioner/docker/paths.hpp"
+#include "slave/containerizer/provisioner/docker/store.hpp"
+
+#include "slave/flags.hpp"
+
+using namespace process;
+
+using std::list;
+using std::string;
+using std::vector;
+
+namespace mesos {
+namespace internal {
+namespace slave {
+namespace docker {
+
+class LocalStoreProcess : public process::Process<LocalStoreProcess>
+{
+public:
+  LocalStoreProcess(
+      const Flags& _flags,
+      Owned<MetadataManager> _metadataManager)
+    : flags(_flags), metadataManager(_metadataManager) {}
+
+  ~LocalStoreProcess() {}
+
+  static Try<process::Owned<LocalStoreProcess>> create(const Flags& flags);
+
+  process::Future<vector<string>> get(const Image& image);
+
+  process::Future<Nothing> recover();
+
+private:
+  process::Future<DockerImage> _get(
+      const DockerImage::Name& name,
+      const Option<DockerImage>& image);
+
+  process::Future<Nothing> untarImage(
+      const std::string& tarPath,
+      const std::string& staging);
+
+  process::Future<DockerImage> putImage(
+      const DockerImage::Name& name,
+      const std::string& staging);
+
+  Result<std::string> getParentId(
+      const std::string& staging,
+      const std::string& layerId);
+
+  process::Future<Nothing> putLayers(
+      const std::string& staging,
+      const std::list<std::string>& layerIds);
+
+  process::Future<Nothing> putLayer(
+      const std::string& staging,
+      const std::string& id);
+
+  process::Future<Nothing> moveLayer(
+      const std::string& staging,
+      const std::string& id);
+
+  const Flags flags;
+  process::Owned<MetadataManager> metadataManager;
+};
+
+
+Try<Owned<slave::Store>> LocalStore::create(const Flags& flags)
+{
+  if (!os::exists(flags.docker_store_dir)) {
+    Try<Nothing> mkdir = os::mkdir(flags.docker_store_dir);
+    if (mkdir.isError()) {
+      return Error("Failed to create Docker store directory: " + mkdir.error());
+    }
+  }
+
+  if (!os::exists(paths::getStagingDir(flags.docker_store_dir))) {
+    Try<Nothing> mkdir =
+      os::mkdir(paths::getStagingDir(flags.docker_store_dir));
+    if (mkdir.isError()) {
+      return Error("Failed to create Docker store staging directory: " +
+                   mkdir.error());
+    }
+  }
+
+  Try<Owned<MetadataManager>> metadataManager = MetadataManager::create(flags);
+  if (metadataManager.isError()) {
+    return Error(metadataManager.error());
+  }
+
+  Owned<LocalStoreProcess> process(
+      new LocalStoreProcess(flags, metadataManager.get()));
+
+  return Owned<slave::Store>(new LocalStore(process));
+}
+
+
+LocalStore::LocalStore(Owned<LocalStoreProcess> _process) : process(_process)
+{
+  process::spawn(CHECK_NOTNULL(process.get()));
+}
+
+
+LocalStore::~LocalStore()
+{
+  process::terminate(process.get());
+  process::wait(process.get());
+}
+
+
+Future<vector<string>> LocalStore::get(const Image& image)
+{
+  return dispatch(process.get(), &LocalStoreProcess::get, image);
+}
+
+
+Future<Nothing> LocalStore::recover()
+{
+  return dispatch(process.get(), &LocalStoreProcess::recover);
+}
+
+
+Future<vector<string>> LocalStoreProcess::get(const Image& image)
+{
+  CHECK_EQ(image.type(), Image::DOCKER);
+
+  Try<DockerImage::Name> dockerName = parseName(image.docker().name());
+  if (dockerName.isError()) {
+    return Failure("Unable to parse docker image name: " + dockerName.error());
+  }
+
+  return metadataManager->get(dockerName.get())
+    .then(defer(self(), &Self::_get, dockerName.get(), lambda::_1))
+    .then([](const DockerImage& dockerImage) {
+      vector<string> layers;
+      foreach (const string& layer, dockerImage.layer_ids()) {
+        layers.push_back(layer);
+      }
+
+      return layers;
+    });
+}
+
+
+Future<DockerImage> LocalStoreProcess::_get(
+    const DockerImage::Name& name,
+    const Option<DockerImage>& image)
+{
+  if (image.isSome()) {
+    return image.get();
+  }
+
+  string tarPath = paths::getLocalImageTarPath(
+      flags.docker_store_discovery_local_dir,
+      stringify(name));
+
+  if (!os::exists(tarPath)) {
+    VLOG(1) << "Unable to find image in local store with path: " << tarPath;
+    return Failure("No Docker image tar archive found");
+  }
+
+  // Create a temporary staging directory.
+  Try<string> staging =
+    os::mkdtemp(paths::getTempStaging(flags.docker_store_dir));
+  if (staging.isError()) {
+    return Failure("Failed to create a staging directory");
+  }
+
+  return untarImage(tarPath, staging.get())
+    .then(defer(self(), &Self::putImage, name, staging.get()));
+}
+
+
+Future<Nothing> LocalStoreProcess::recover()
+{
+  return metadataManager->recover();
+}
+
+
+Future<Nothing> LocalStoreProcess::untarImage(
+    const string& tarPath,
+    const string& staging)
+{
+  VLOG(1) << "Untarring image at: " << tarPath;
+
+  // Untar store_discovery_local_dir/name.tar into staging/.
+  vector<string> argv = {
+    "tar",
+    "-C",
+    staging,
+    "-x",
+    "-f",
+    tarPath
+  };
+
+  Try<Subprocess> s = subprocess(
+      "tar",
+      argv,
+      Subprocess::PATH("/dev/null"),
+      Subprocess::PATH("/dev/null"),
+      Subprocess::PATH("/dev/null"));
+
+  if (s.isError()) {
+    return Failure("Failed to create tar subprocess: " + s.error());
+  }
+
+  return s.get().status()
+    .then([=](const Option<int>& status) -> Future<Nothing> {
+      if (status.isNone()) {
+        return Failure("Failed to reap status for tar subprocess in " +
+                        tarPath);
+      }
+      if (!WIFEXITED(status.get()) || WEXITSTATUS(status.get()) != 0) {
+          return Failure("Untar image failed with exit code: " +
+                          WSTRINGIFY(status.get()));
+      }
+
+      return Nothing();
+    });
+}
+
+
+Future<DockerImage> LocalStoreProcess::putImage(
+    const DockerImage::Name& name,
+    const string& staging)
+{
+  Try<string> value = os::read(paths::getLocalImageRepositoriesPath(staging));
+  if (value.isError()) {
+    return Failure("Failed to read repository JSON: " + value.error());
+  }
+
+  Try<JSON::Object> json = JSON::parse<JSON::Object>(value.get());
+  if (json.isError()) {
+    return Failure("Failed to parse JSON: " + json.error());
+  }
+
+  Result<JSON::Object> repositoryValue =
+    json.get().find<JSON::Object>(name.repository());
+  if (repositoryValue.isError()) {
+    return Failure("Failed to find repository: " + repositoryValue.error());
+  } else if (repositoryValue.isNone()) {
+    return Failure("Repository '" + name.repository() + "' is not found");
+  }
+
+  JSON::Object repositoryJson = repositoryValue.get();
+
+  // We don't use JSON find here because a tag might contain a '.'.
+  std::map<string, JSON::Value>::const_iterator entry =
+    repositoryJson.values.find(name.tag());
+  if (entry == repositoryJson.values.end()) {
+    return Failure("Tag '" + name.tag() + "' is not found");
+  } else if (!entry->second.is<JSON::String>()) {
+    return Failure("Tag JSON value expected to be JSON::String");
+  }
+
+  string layerId = entry->second.as<JSON::String>().value;
+
+  Try<string> manifest =
+    os::read(paths::getLocalImageLayerManifestPath(staging, layerId));
+  if (manifest.isError()) {
+    return Failure("Failed to read manifest: " + manifest.error());
+  }
+
+  Try<JSON::Object> manifestJson = JSON::parse<JSON::Object>(manifest.get());
+  if (manifestJson.isError()) {
+    return Failure("Failed to parse manifest: " + manifestJson.error());
+  }
+
+  list<string> layerIds;
+  layerIds.push_back(layerId);
+  Result<string> parentId = getParentId(staging, layerId);
+  while(parentId.isSome()) {
+    layerIds.push_front(parentId.get());
+    parentId = getParentId(staging, parentId.get());
+  }
+  if (parentId.isError()) {
+    return Failure("Failed to obtain parent layer id: " + parentId.error());
+  }
+
+  return putLayers(staging, layerIds)
+    .then([=]() -> Future<DockerImage> {
+      return metadataManager->put(name, layerIds);
+    });
+}
+
+
+Result<string> LocalStoreProcess::getParentId(
+    const string& staging,
+    const string& layerId)
+{
+  Try<string> manifest =
+    os::read(paths::getLocalImageLayerManifestPath(staging, layerId));
+  if (manifest.isError()) {
+    return Error("Failed to read manifest: " + manifest.error());
+  }
+
+  Try<JSON::Object> json = JSON::parse<JSON::Object>(manifest.get());
+  if (json.isError()) {
+    return Error("Failed to parse manifest: " + json.error());
+  }
+
+  Result<JSON::String> parentId = json.get().find<JSON::String>("parent");
+  if (parentId.isNone() || (parentId.isSome() && parentId.get() == "")) {
+    return None();
+  } else if (parentId.isError()) {
+    return Error("Failed to read parent of layer: " + parentId.error());
+  }
+  return parentId.get().value;
+}
+
+
+Future<Nothing> LocalStoreProcess::putLayers(
+    const string& staging,
+    const list<string>& layerIds)
+{
+  list<Future<Nothing>> futures{ Nothing() };
+  foreach (const string& layer, layerIds) {
+    futures.push_back(
+        futures.back().then(
+          defer(self(), &Self::putLayer, staging, layer)));
+  }
+
+  return collect(futures)
+    .then([]() -> Future<Nothing> { return Nothing(); });
+}
+
+
+Future<Nothing> LocalStoreProcess::putLayer(
+    const string& staging,
+    const string& id)
+{
+  // We untar the layer from source into a staging directory, then
+  // move the layer into store. We do this instead of untarring
+  // directly to store to make sure we don't end up with partially
+  // untarred layer rootfs.
+
+  // Check if image layer rootfs is already in store.
+  if (os::exists(paths::getImageLayerRootfsPath(flags.docker_store_dir, id))) {
+    VLOG(1) << "Image layer '" << id << "' rootfs already in store. "
+            << "Skipping put.";
+    return Nothing();
+  }
+
+  const string imageLayerPath =
+    paths::getImageLayerPath(flags.docker_store_dir, id);
+  if (!os::exists(imageLayerPath)) {
+    Try<Nothing> mkdir = os::mkdir(imageLayerPath);
+    if (mkdir.isError()) {
+      return Failure("Failed to create Image layer directory '" +
+                     imageLayerPath + "': " + mkdir.error());
+    }
+  }
+
+  // Image layer has been untarred but is not present in the store directory.
+  string localRootfsPath = paths::getLocalImageLayerRootfsPath(staging, id);
+  if (os::exists(localRootfsPath)) {
+    LOG(WARNING) << "Image layer '" << id << "' rootfs present at but not in "
+                 << "store directory '" << localRootfsPath << "'. Removing "
+                 << "staged rootfs and untarring layer again.";
+    Try<Nothing> rmdir = os::rmdir(localRootfsPath);
+    if (rmdir.isError()) {
+      return Failure("Failed to remove incomplete staged rootfs for layer '" +
+                     id + "': " + rmdir.error());
+    }
+  }
+
+  Try<Nothing> mkdir = os::mkdir(localRootfsPath);
+  if (mkdir.isError()) {
+    return Failure("Failed to create rootfs path '" + localRootfsPath + "': " +
+                   mkdir.error());
+  }
+  // Untar staging/id/layer.tar into staging/id/rootfs.
+  vector<string> argv = {
+    "tar",
+    "-C",
+    localRootfsPath,
+    "-x",
+    "-f",
+    paths::getLocalImageLayerTarPath(staging, id)
+  };
+
+  Try<Subprocess> s = subprocess(
+      "tar",
+      argv,
+      Subprocess::PATH("/dev/null"),
+      Subprocess::PATH("/dev/null"),
+      Subprocess::PATH("/dev/null"));
+  if (s.isError()) {
+    return Failure("Failed to create tar subprocess: " + s.error());
+  }
+
+  return s.get().status()
+    .then([=](const Option<int>& status) -> Future<Nothing> {
+      if (status.isNone()) {
+        return Failure("Failed to reap subprocess to untar image");
+      } else if (!WIFEXITED(status.get()) || WEXITSTATUS(status.get()) != 0) {
+        return Failure("Untar failed with exit code: " +
+                        WSTRINGIFY(status.get()));
+      }
+
+      return moveLayer(staging, id);
+    });
+}
+
+
+Future<Nothing> LocalStoreProcess::moveLayer(
+    const string& staging,
+    const string& id)
+{
+  Try<Nothing> status = os::rename(
+      paths::getLocalImageLayerRootfsPath(staging, id),
+      paths::getImageLayerRootfsPath(flags.docker_store_dir, id));
+
+  if (status.isError()) {
+    return Failure("Failed to move layer to store directory: "
+                   + status.error());
+  }
+
+  return Nothing();
+}
+
+
+} // namespace docker {
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioner/docker/local_store.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/docker/local_store.hpp b/src/slave/containerizer/provisioner/docker/local_store.hpp
new file mode 100644
index 0000000..5f6152b
--- /dev/null
+++ b/src/slave/containerizer/provisioner/docker/local_store.hpp
@@ -0,0 +1,64 @@
+/**
+ * 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_DOCKER_LOCAL_STORE_HPP__
+#define __MESOS_DOCKER_LOCAL_STORE_HPP__
+
+#include "slave/containerizer/provisioner/store.hpp"
+
+namespace mesos {
+namespace internal {
+namespace slave {
+namespace docker {
+
+// Forward declaration.
+class LocalStoreProcess;
+
+
+/**
+ * LocalStore assumes Docker images are stored in a local directory
+ * (configured with flags.docker_discovery_local_dir), with all the
+ * images saved as tar with the name as the image name with tag (e.g:
+ * ubuntu:14.04.tar).
+ */
+class LocalStore : public slave::Store
+{
+public:
+  static Try<process::Owned<Store>> create(const Flags& flags);
+
+  virtual ~LocalStore();
+
+  virtual process::Future<Nothing> recover();
+
+  virtual process::Future<std::vector<std::string>> get(const Image& image);
+
+private:
+  explicit LocalStore(process::Owned<LocalStoreProcess> _process);
+
+  LocalStore& operator=(const LocalStore&) = delete; // Not assignable.
+  LocalStore(const LocalStore&) = delete; // Not copyable.
+
+  process::Owned<LocalStoreProcess> process;
+};
+
+} // namespace docker {
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
+
+#endif // __MESOS_DOCKER_LOCAL_STORE_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioner/docker/message.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/docker/message.hpp b/src/slave/containerizer/provisioner/docker/message.hpp
new file mode 100644
index 0000000..c1596df
--- /dev/null
+++ b/src/slave/containerizer/provisioner/docker/message.hpp
@@ -0,0 +1,71 @@
+/**
+ * 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 __MESSAGES_DOCKER_PROVISIONER_HPP__
+#define __MESSAGES_DOCKER_PROVISIONER_HPP__
+
+#include <stout/strings.hpp>
+
+// ONLY USEFUL AFTER RUNNING PROTOC.
+#include "slave/containerizer/provisioner/docker/message.pb.h"
+
+namespace mesos {
+namespace internal {
+namespace slave {
+namespace docker {
+
+inline DockerImage::Name parseName(const std::string& value)
+{
+  DockerImage::Name imageName;
+  Option<std::string> registry = None();
+  std::vector<std::string> components = strings::split(value, "/");
+  if (components.size() > 2) {
+    imageName.set_registry(value.substr(0, value.find_last_of("/")));
+  }
+
+  std::size_t found = components.back().find_last_of(':');
+  if (found == std::string::npos) {
+    imageName.set_repository(components.back());
+    imageName.set_tag("latest");
+  } else {
+    imageName.set_repository(components.back().substr(0, found));
+    imageName.set_tag(components.back().substr(found + 1));
+  }
+
+  return imageName;
+}
+
+
+inline std::ostream& operator<<(
+    std::ostream& stream,
+    const DockerImage::Name& name)
+{
+  if (name.has_registry()) {
+    return stream << name.registry() << "/" << name.repository() << ":"
+                  << name.tag();
+  }
+
+  return stream << name.repository() << ":" << name.tag();
+}
+
+} // namespace docker {
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
+
+#endif // __MESSAGES_DOCKER_PROVISIONER_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioner/docker/message.proto
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/docker/message.proto b/src/slave/containerizer/provisioner/docker/message.proto
new file mode 100644
index 0000000..d771968
--- /dev/null
+++ b/src/slave/containerizer/provisioner/docker/message.proto
@@ -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.
+ */
+
+import "mesos/mesos.proto";
+
+package mesos.internal.slave.docker;
+
+/**
+ * A Docker Image name and the layer ids of the layers that comprise the image.
+ * The layerIds are ordered, with the root layer id (no parent layer id) first
+ * and the leaf layer id last.
+ */
+message DockerImage {
+  message Name {
+    optional string registry = 1;
+    required string repository = 2;
+    required string tag = 3;
+  }
+
+  required Name name = 1;
+
+  // The order of the layers represents the dependency between layers.
+  repeated string layer_ids = 2;
+}
+
+message DockerImages {
+  repeated DockerImage images = 1;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioner/docker/metadata_manager.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/docker/metadata_manager.cpp b/src/slave/containerizer/provisioner/docker/metadata_manager.cpp
new file mode 100644
index 0000000..197931c
--- /dev/null
+++ b/src/slave/containerizer/provisioner/docker/metadata_manager.cpp
@@ -0,0 +1,251 @@
+/**
+ * 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 <vector>
+
+#include <glog/logging.h>
+
+#include <stout/foreach.hpp>
+#include <stout/hashset.hpp>
+#include <stout/os.hpp>
+#include <stout/protobuf.hpp>
+
+#include <process/defer.hpp>
+#include <process/dispatch.hpp>
+#include <process/owned.hpp>
+
+#include "common/status_utils.hpp"
+
+#include "slave/containerizer/provisioner/docker/paths.hpp"
+#include "slave/containerizer/provisioner/docker/message.hpp"
+#include "slave/containerizer/provisioner/docker/metadata_manager.hpp"
+
+#include "slave/state.hpp"
+
+using namespace process;
+
+using std::list;
+using std::string;
+using std::vector;
+
+namespace mesos {
+namespace internal {
+namespace slave {
+namespace docker {
+
+
+class MetadataManagerProcess : public process::Process<MetadataManagerProcess>
+{
+public:
+  ~MetadataManagerProcess() {}
+
+  static Try<process::Owned<MetadataManagerProcess>> create(
+      const Flags& flags);
+
+  Future<DockerImage> put(
+      const DockerImage::Name& name,
+      const std::list<std::string>& layerIds);
+
+  Future<Option<DockerImage>> get(const DockerImage::Name& name);
+
+  Future<Nothing> recover();
+
+  // TODO(chenlily): Implement removal of unreferenced images.
+
+private:
+  MetadataManagerProcess(const Flags& flags);
+
+  // Write out metadata manager state to persistent store.
+  Try<Nothing> persist();
+
+  const Flags flags;
+
+  // This is a lookup table for images that are stored in memory. It is keyed
+  // by the name of the DockerImage.
+  // For example, "ubuntu:14.04" -> ubuntu14:04 DockerImage.
+  hashmap<std::string, DockerImage> storedImages;
+};
+
+
+Try<Owned<MetadataManager>> MetadataManager::create(const Flags& flags)
+{
+  Try<Owned<MetadataManagerProcess>> process =
+    MetadataManagerProcess::create(flags);
+  if (process.isError()) {
+    return Error("Failed to create Metadata Manager: " + process.error());
+  }
+  return Owned<MetadataManager>(new MetadataManager(process.get()));
+}
+
+
+MetadataManager::MetadataManager(Owned<MetadataManagerProcess> process)
+  : process(process)
+{
+  process::spawn(CHECK_NOTNULL(process.get()));
+}
+
+
+MetadataManager::~MetadataManager()
+{
+  process::terminate(process.get());
+  process::wait(process.get());
+}
+
+
+Future<Nothing> MetadataManager::recover()
+{
+  return process::dispatch(process.get(), &MetadataManagerProcess::recover);
+}
+
+
+Future<DockerImage> MetadataManager::put(
+    const DockerImage::Name& name,
+    const list<string>& layerIds)
+{
+  return dispatch(
+      process.get(), &MetadataManagerProcess::put, name, layerIds);
+}
+
+
+Future<Option<DockerImage>> MetadataManager::get(const DockerImage::Name& name)
+{
+  return dispatch(process.get(), &MetadataManagerProcess::get, name);
+}
+
+
+MetadataManagerProcess::MetadataManagerProcess(const Flags& flags)
+  : flags(flags) {}
+
+
+Try<Owned<MetadataManagerProcess>> MetadataManagerProcess::create(
+    const Flags& flags)
+{
+  Owned<MetadataManagerProcess> metadataManager =
+    Owned<MetadataManagerProcess>(new MetadataManagerProcess(flags));
+
+  return metadataManager;
+}
+
+
+Future<DockerImage> MetadataManagerProcess::put(
+    const DockerImage::Name& name,
+    const list<string>& layerIds)
+{
+  const string imageName = stringify(name);
+
+  DockerImage dockerImage;
+  dockerImage.mutable_name()->CopyFrom(name);
+  foreach (const string& layerId, layerIds) {
+    dockerImage.add_layer_ids(layerId);
+  }
+
+  storedImages[imageName] = dockerImage;
+
+  Try<Nothing> status = persist();
+  if (status.isError()) {
+    return Failure("Failed to save state of Docker images" + status.error());
+  }
+
+  return dockerImage;
+}
+
+
+Future<Option<DockerImage>> MetadataManagerProcess::get(
+    const DockerImage::Name& name)
+{
+  const string imageName = stringify(name);
+
+  if (!storedImages.contains(imageName)) {
+    return None();
+  }
+
+  return storedImages[imageName];
+}
+
+
+Try<Nothing> MetadataManagerProcess::persist()
+{
+  DockerImages images;
+
+  foreachvalue (const DockerImage& image, storedImages) {
+    images.add_images()->CopyFrom(image);
+  }
+
+  Try<Nothing> status = mesos::internal::slave::state::checkpoint(
+      paths::getStoredImagesPath(flags.docker_store_dir), images);
+  if (status.isError()) {
+    return Error("Failed to perform checkpoint: " + status.error());
+  }
+
+  return Nothing();
+}
+
+
+Future<Nothing> MetadataManagerProcess::recover()
+{
+  string storedImagesPath = paths::getStoredImagesPath(flags.docker_store_dir);
+
+  storedImages.clear();
+  if (!os::exists(storedImagesPath)) {
+    LOG(INFO) << "No images to load from disk. Docker provisioner image "
+              << "storage path: " << storedImagesPath << " does not exist.";
+    return Nothing();
+  }
+
+  Result<DockerImages> images =
+    ::protobuf::read<DockerImages>(storedImagesPath);
+  if (images.isError()) {
+    return Failure("Failed to read protobuf for Docker provisioner image: " +
+                   images.error());
+  }
+
+  foreach (const DockerImage image, images.get().images()) {
+    vector<string> missingLayerIds;
+    foreach (const string layerId, image.layer_ids()) {
+      const string rootfsPath =
+        paths::getImageLayerRootfsPath(flags.docker_store_dir, layerId);
+
+      if (!os::exists(rootfsPath)) {
+        missingLayerIds.push_back(layerId);
+      }
+    }
+
+    if (!missingLayerIds.empty()) {
+      LOG(WARNING) << "Skipped loading image: " << stringify(image.name())
+                   << " due to missing layers: " << stringify(missingLayerIds);
+      continue;
+    }
+
+    const string imageName = stringify(image.name());
+    if (storedImages.contains(imageName)) {
+      LOG(WARNING) << "Found duplicate image in recovery for image name '" << imageName
+                   << "'";
+    } else {
+      storedImages[imageName] = image;
+    }
+  }
+
+  LOG(INFO) << "Loaded " << storedImages.size() << " Docker images.";
+
+  return Nothing();
+}
+
+} // namespace docker {
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioner/docker/metadata_manager.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/docker/metadata_manager.hpp b/src/slave/containerizer/provisioner/docker/metadata_manager.hpp
new file mode 100644
index 0000000..647a478
--- /dev/null
+++ b/src/slave/containerizer/provisioner/docker/metadata_manager.hpp
@@ -0,0 +1,106 @@
+/**
+ * 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_DOCKER_METADATA_MANAGER_HPP__
+#define __MESOS_DOCKER_METADATA_MANAGER_HPP__
+
+#include <list>
+#include <string>
+
+#include <stout/hashmap.hpp>
+#include <stout/json.hpp>
+#include <stout/option.hpp>
+#include <stout/protobuf.hpp>
+#include <stout/try.hpp>
+
+#include <process/future.hpp>
+#include <process/owned.hpp>
+#include <process/process.hpp>
+
+#include "slave/containerizer/provisioner/provisioner.hpp"
+
+#include "slave/containerizer/provisioner/docker/message.hpp"
+
+#include "slave/flags.hpp"
+
+namespace mesos {
+namespace internal {
+namespace slave {
+namespace docker {
+
+// Forward Declaration.
+class MetadataManagerProcess;
+
+/**
+ * The MetadataManager tracks the Docker images cached by the
+ * provisioner that are stored on disk. It keeps track of the layers
+ * that Docker images are composed of and recovers DockerImage objects
+ * upon initialization by checking for dependent layers stored on disk.
+ * Currently, image layers are stored indefinitely, with no garbage
+ * collection of unreferenced image layers.
+ */
+class MetadataManager
+{
+public:
+  static Try<process::Owned<MetadataManager>> create(const Flags& flags);
+
+   ~MetadataManager();
+
+  /**
+   * Create a Image, put it in metadata manager and persist the reference
+   * store state to disk.
+   *
+   * @param name     the name of the Docker image to place in the reference
+   *                 store.
+   * @param layerIds the list of layer ids that comprise the Docker image in
+   *                 order where the root layer's id (no parent layer) is first
+   *                 and the leaf layer's id is last.
+   */
+  process::Future<DockerImage> put(
+      const DockerImage::Name& name,
+      const std::list<std::string>& layerIds);
+
+  /**
+   * Retrieve DockerImage based on image name if it is among the DockerImages
+   * stored in memory.
+   *
+   * @param name  the name of the Docker image to retrieve
+   */
+  process::Future<Option<DockerImage>> get(const DockerImage::Name& name);
+
+  /**
+   * Recover all stored DockerImage and its layer references.
+   */
+  process::Future<Nothing> recover();
+
+private:
+  explicit MetadataManager(process::Owned<MetadataManagerProcess> process);
+
+  MetadataManager(const MetadataManager&); // Not copyable.
+  MetadataManager& operator=(const MetadataManager&); // Not assignable.
+
+  process::Owned<MetadataManagerProcess> process;
+};
+
+
+} // namespace docker {
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
+
+#endif // __MESOS_DOCKER_METADATA_MANAGER_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioner/docker/paths.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/docker/paths.cpp b/src/slave/containerizer/provisioner/docker/paths.cpp
new file mode 100644
index 0000000..81a2176
--- /dev/null
+++ b/src/slave/containerizer/provisioner/docker/paths.cpp
@@ -0,0 +1,104 @@
+/**
+ * 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/docker/paths.hpp"
+
+#include <stout/path.hpp>
+
+using std::string;
+
+namespace mesos {
+namespace internal {
+namespace slave {
+namespace docker {
+namespace paths {
+
+string getStagingDir(const string& storeDir)
+{
+  return path::join(storeDir, "staging");
+}
+
+string getTempStaging(const string& storeDir)
+{
+  return path::join(getStagingDir(storeDir), "XXXXXX");
+}
+
+string getLocalImageTarPath(
+    const string& discoveryDir,
+    const string& name)
+{
+  return path::join(discoveryDir, name + ".tar");
+}
+
+string getLocalImageRepositoriesPath(const string& staging)
+{
+  return path::join(staging, "repositories");
+}
+
+std::string getLocalImageLayerPath(
+    const string& staging,
+    const string& layerId)
+{
+  return path::join(staging, layerId);
+}
+
+string getLocalImageLayerManifestPath(
+    const string& staging,
+    const string& layerId)
+{
+  return path::join(getLocalImageLayerPath(staging, layerId), "json");
+}
+
+string getLocalImageLayerTarPath(
+  const string& staging,
+  const string& layerId)
+{
+  return path::join(getLocalImageLayerPath(staging, layerId), "layer.tar");
+}
+
+string getLocalImageLayerRootfsPath(
+    const string& staging,
+    const string& layerId)
+{
+  return path::join(getLocalImageLayerPath(staging, layerId), "rootfs");
+}
+
+string getImageLayerPath(
+    const string& storeDir,
+    const string& layerId)
+{
+  return path::join(storeDir, "layers", layerId);
+}
+
+string getImageLayerRootfsPath(
+    const string& storeDir,
+    const string& layerId)
+{
+  return path::join(getImageLayerPath(storeDir, layerId), "rootfs");
+}
+
+string getStoredImagesPath(const string& storeDir)
+{
+  return path::join(storeDir, "storedImages");
+}
+
+} // namespace paths {
+} // namespace docker {
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioner/docker/paths.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/docker/paths.hpp b/src/slave/containerizer/provisioner/docker/paths.hpp
new file mode 100644
index 0000000..02f129f
--- /dev/null
+++ b/src/slave/containerizer/provisioner/docker/paths.hpp
@@ -0,0 +1,86 @@
+/**
+ * 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_DOCKER_PATHS_HPP__
+#define __MESOS_DOCKER_PATHS_HPP__
+
+#include <list>
+#include <string>
+
+#include <mesos/mesos.hpp>
+
+namespace mesos {
+namespace internal {
+namespace slave {
+namespace docker {
+namespace paths {
+
+/**
+ * The Docker store file system layout is as follows:
+ * Image store dir ('--docker_store_dir' slave flag)
+ *    |--staging (contains temp directories for downloads and extract)
+ *    |--layers
+ *       |--<layer_id>
+ *           |--rootfs
+ *    |--rootfses
+ *    |--storedImages (file holding on cached images)
+ */
+
+std::string getStagingDir(const std::string& storeDir);
+
+std::string getTempStaging(const std::string& storeDir);
+
+std::string getLocalImageTarPath(
+    const std::string& discoveryDir,
+    const std::string& name);
+
+std::string getLocalImageRepositoriesPath(const std::string& staging);
+
+std::string getLocalImageLayerPath(
+    const std::string& staging,
+    const std::string& layerId);
+
+std::string getLocalImageLayerManifestPath(
+    const std::string& staging,
+    const std::string& layerId);
+
+std::string getLocalImageLayerTarPath(
+  const std::string& staging,
+  const std::string& layerId);
+
+std::string getLocalImageLayerRootfsPath(
+  const std::string& staging,
+  const std::string& layerId);
+
+std::string getImageLayerPath(
+    const std::string& storeDir,
+    const std::string& layerId);
+
+std::string getImageLayerRootfsPath(
+    const std::string& storeDir,
+    const std::string& layerId);
+
+std::string getStoredImagesPath(const std::string& storeDir);
+
+} // namespace paths {
+} // namespace docker {
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
+
+#endif // __MESOS_DOCKER_PATHS_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioner/docker/store.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/docker/store.cpp b/src/slave/containerizer/provisioner/docker/store.cpp
new file mode 100644
index 0000000..51f4d6b
--- /dev/null
+++ b/src/slave/containerizer/provisioner/docker/store.cpp
@@ -0,0 +1,47 @@
+/**
+ * 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/docker/local_store.hpp"
+#include "slave/containerizer/provisioner/docker/store.hpp"
+
+using process::Owned;
+
+using std::string;
+
+namespace mesos {
+namespace internal {
+namespace slave {
+namespace docker {
+
+Try<Owned<slave::Store>> Store::create(const Flags& flags)
+{
+  hashmap<string, Try<Owned<slave::Store>>(*)(const Flags&)> creators{
+    {"local", &LocalStore::create}
+  };
+
+  if (!creators.contains(flags.docker_store_discovery)) {
+    return Error("Unknown Docker store: " + flags.docker_store_discovery);
+  }
+
+  return creators[flags.docker_store_discovery](flags);
+}
+
+} // namespace docker {
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioner/docker/store.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/docker/store.hpp b/src/slave/containerizer/provisioner/docker/store.hpp
new file mode 100644
index 0000000..ae06706
--- /dev/null
+++ b/src/slave/containerizer/provisioner/docker/store.hpp
@@ -0,0 +1,58 @@
+/**
+ * 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_DOCKER_STORE_HPP__
+#define __MESOS_DOCKER_STORE_HPP__
+
+#include <string>
+
+#include <stout/hashmap.hpp>
+#include <stout/nothing.hpp>
+#include <stout/option.hpp>
+#include <stout/try.hpp>
+
+#include <process/future.hpp>
+
+#include "slave/containerizer/fetcher.hpp"
+
+#include "slave/containerizer/provisioner/store.hpp"
+
+#include "slave/containerizer/provisioner/docker/message.hpp"
+
+#include "slave/flags.hpp"
+
+namespace mesos {
+namespace internal {
+namespace slave {
+namespace docker {
+
+// Store fetches the Docker images and stores them on disk.
+// TODO(tnachen): Make this store the only docker store
+// implementation, and move local and remote to different pullers.
+class Store
+{
+public:
+  static Try<process::Owned<slave::Store>> create(const Flags& flags);
+};
+
+} // namespace docker {
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
+
+#endif // __MESOS_DOCKER_STORE_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioner/provisioner.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/provisioner.hpp b/src/slave/containerizer/provisioner/provisioner.hpp
index d42822b..912fc5a 100644
--- a/src/slave/containerizer/provisioner/provisioner.hpp
+++ b/src/slave/containerizer/provisioner/provisioner.hpp
@@ -67,7 +67,7 @@ public:
   // image and return the absolute path to the root filesystem.
   virtual process::Future<std::string> provision(
       const ContainerID& containerId,
-      const Image& image) = 0;
+      const Image& image);
 
   // Destroy a previously provisioned root filesystem. Assumes that
   // all references (e.g., mounts, open files) to the provisioned

http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioners/docker.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioners/docker.cpp b/src/slave/containerizer/provisioners/docker.cpp
deleted file mode 100644
index b5c5a7b..0000000
--- a/src/slave/containerizer/provisioners/docker.cpp
+++ /dev/null
@@ -1,425 +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 "slave/containerizer/provisioners/docker.hpp"
-
-#include <glog/logging.h>
-
-#include <stout/json.hpp>
-#include <stout/nothing.hpp>
-#include <stout/os.hpp>
-
-#include <process/collect.hpp>
-#include <process/defer.hpp>
-#include <process/dispatch.hpp>
-#include <process/owned.hpp>
-#include <process/sequence.hpp>
-
-#include "slave/containerizer/provisioners/backend.hpp"
-#include "slave/containerizer/provisioners/paths.hpp"
-
-#include "slave/containerizer/provisioners/docker/paths.hpp"
-#include "slave/containerizer/provisioners/docker/store.hpp"
-
-#include "slave/paths.hpp"
-
-using namespace process;
-using namespace mesos::internal::slave;
-
-using std::list;
-using std::string;
-using std::vector;
-
-using mesos::slave::ContainerState;
-
-namespace mesos {
-namespace internal {
-namespace slave {
-namespace docker {
-
-class DockerProvisionerProcess :
-  public process::Process<DockerProvisionerProcess>
-{
-public:
-  static Try<process::Owned<DockerProvisionerProcess>> create(
-      const Flags& flags,
-      Fetcher* fetcher);
-
-  process::Future<Nothing> recover(
-      const std::list<mesos::slave::ContainerState>& states,
-      const hashset<ContainerID>& orphans);
-
-  process::Future<std::string> provision(
-      const ContainerID& containerId,
-      const Image& image);
-
-  process::Future<bool> destroy(const ContainerID& containerId);
-
-private:
-  DockerProvisionerProcess(
-      const string& _rootDir,
-      const Flags& _flags,
-      const process::Owned<Store>& _store,
-      const hashmap<std::string, process::Owned<Backend>>& _backends);
-
-  process::Future<std::string> _provision(
-      const DockerImage& image,
-      const ContainerID& containerId,
-      const string& rootfs);
-
-  const string& rootDir;
-  const Flags flags;
-  process::Owned<Store> store;
-  hashmap<string, process::Owned<Backend>> backends;
-
-  struct Info
-  {
-    // Mappings: backend -> rootfsId -> rootfsPath.
-    hashmap<string, hashmap<string, string>> rootfses;
-  };
-
-  hashmap<ContainerID, Owned<Info>> infos;
-};
-
-
-Try<ImageName> ImageName::create(const std::string& name)
-{
-  ImageName imageName;
-  Option<string> registry = None();
-  std::vector<std::string> components = strings::split(name, "/");
-  if (components.size() > 2) {
-    registry = name.substr(0, name.find_last_of("/"));
-  }
-
-  std::size_t found = components.back().find_last_of(':');
-  if (found == std::string::npos) {
-    imageName.repository = components.back();
-    imageName.tag = "latest";
-  } else {
-    imageName.repository = components.back().substr(0, found);
-    imageName.tag = components.back().substr(found + 1);
-  }
-
-  return imageName;
-}
-
-Try<Owned<Provisioner>> DockerProvisioner::create(
-    const Flags& flags,
-    Fetcher* fetcher)
-{
-  Try<Owned<DockerProvisionerProcess>> create =
-    DockerProvisionerProcess::create(flags, fetcher);
-  if (create.isError()) {
-    return Error(create.error());
-  }
-
-  return Owned<Provisioner>(new DockerProvisioner(create.get()));
-}
-
-
-DockerProvisioner::DockerProvisioner(Owned<DockerProvisionerProcess> _process)
-  : process(_process)
-{
-  process::spawn(CHECK_NOTNULL(process.get()));
-}
-
-
-DockerProvisioner::~DockerProvisioner()
-{
-  process::terminate(process.get());
-  process::wait(process.get());
-}
-
-
-Future<Nothing> DockerProvisioner::recover(
-    const list<ContainerState>& states,
-    const hashset<ContainerID>& orphans)
-{
-  return dispatch(
-      process.get(),
-      &DockerProvisionerProcess::recover,
-      states,
-      orphans);
-}
-
-
-Future<string> DockerProvisioner::provision(
-    const ContainerID& containerId,
-    const Image& image)
-{
-  return dispatch(
-      process.get(),
-      &DockerProvisionerProcess::provision,
-      containerId,
-      image);
-}
-
-
-Future<bool> DockerProvisioner::destroy(const ContainerID& containerId)
-{
-  return dispatch(
-      process.get(),
-      &DockerProvisionerProcess::destroy,
-      containerId);
-}
-
-
-Try<Owned<DockerProvisionerProcess>> DockerProvisionerProcess::create(
-    const Flags& flags,
-    Fetcher* fetcher)
-{
-  string rootDir =
-    slave::paths::getProvisionerDir(flags.work_dir, Image::DOCKER);
-
-  if (!os::exists(rootDir)) {
-    Try<Nothing> mkdir = os::mkdir(rootDir);
-    if (mkdir.isError()) {
-      return Error("Failed to create Docker provisioner root directory '" +
-                   rootDir + "': " + mkdir.error());
-    }
-  }
-
-  const hashmap<string, Owned<Backend>> backends = Backend::create(flags);
-  if (backends.empty()) {
-    return Error("No usable Docker provisioner backend created");
-  }
-
-  if (!backends.contains(flags.docker_backend)) {
-    return Error("The specified Docker provisioner backend '" +
-                 flags.docker_backend + "'is unsupported");
-  }
-
-  Try<Owned<Store>> store = Store::create(flags, fetcher);
-  if (store.isError()) {
-    return Error("Failed to create image store: " + store.error());
-  }
-
-  return Owned<DockerProvisionerProcess>(
-      new DockerProvisionerProcess(
-          rootDir,
-          flags,
-          store.get(),
-          backends));
-}
-
-
-DockerProvisionerProcess::DockerProvisionerProcess(
-    const string& _rootDir,
-    const Flags& _flags,
-    const Owned<Store>& _store,
-    const hashmap<string, Owned<Backend>>& _backends)
-  : rootDir(_rootDir),
-    flags(_flags),
-    store(_store),
-    backends(_backends) {}
-
-
-Future<Nothing> DockerProvisionerProcess::recover(
-    const list<ContainerState>& states,
-    const hashset<ContainerID>& orphans)
-{
-  // TODO(tnachen): Consider merging this with
-  // AppcProvisionerProcess::recover.
-
-  // Register living containers, including the ones that do not
-  // provision Docker images.
-  hashset<ContainerID> alive;
-
-  foreach (const ContainerState& state, states) {
-    if (state.executor_info().has_container() &&
-        state.executor_info().container().type() == ContainerInfo::MESOS) {
-      alive.insert(state.container_id());
-    }
-  }
-
-  // List provisioned containers; recover living ones; destroy unknown orphans.
-  // Note that known orphan containers are recovered as well and they will
-  // be destroyed by the containerizer using the normal cleanup path. See
-  // MESOS-2367 for details.
-  Try<hashmap<ContainerID, string>> containers =
-    provisioners::paths::listContainers(rootDir);
-
-  if (containers.isError()) {
-    return Failure("Failed to list the containers managed by Docker "
-                   "provisioner: " + containers.error());
-  }
-
-  // If no container has been launched the 'containers' directory will be empty.
-  foreachkey (const ContainerID& containerId, containers.get()) {
-    if (alive.contains(containerId) || orphans.contains(containerId)) {
-      Owned<Info> info = Owned<Info>(new Info());
-
-      Try<hashmap<string, hashmap<string, string>>> rootfses =
-        provisioners::paths::listContainerRootfses(rootDir, containerId);
-
-      if (rootfses.isError()) {
-        return Failure("Unable to list rootfses belonged to container '" +
-                       containerId.value() + "': " + rootfses.error());
-      }
-
-      foreachkey (const string& backend, rootfses.get()) {
-        if (!backends.contains(backend)) {
-          return Failure("Found rootfses managed by an unrecognized backend: " +
-                         backend);
-        }
-
-        info->rootfses.put(backend, rootfses.get()[backend]);
-      }
-
-      VLOG(1) << "Recovered container " << containerId;
-      infos.put(containerId, info);
-
-      continue;
-    }
-
-    // Destroy (unknown) orphan container's rootfses.
-    Try<hashmap<string, hashmap<string, string>>> rootfses =
-      provisioners::paths::listContainerRootfses(rootDir, containerId);
-
-    if (rootfses.isError()) {
-      return Failure("Unable to find rootfses for container '" +
-                     containerId.value() + "': " + rootfses.error());
-    }
-
-    foreachkey (const string& backend, rootfses.get()) {
-      if (!backends.contains(backend)) {
-        return Failure("Found rootfses managed by an unrecognized backend: " +
-                       backend);
-      }
-
-      foreachvalue (const string& rootfs, rootfses.get()[backend]) {
-        VLOG(1) << "Destroying orphan rootfs " << rootfs;
-
-        // Not waiting for the destruction and we don't care about
-        // the return value.
-        backends.get(backend).get()->destroy(rootfs)
-          .onFailed([rootfs](const std::string& error) {
-            LOG(WARNING) << "Failed to destroy orphan rootfs '" << rootfs
-                         << "': "<< error;
-          });
-      }
-    }
-  }
-
-  LOG(INFO) << "Recovered Docker provisioner rootfses";
-
-  return store->recover()
-    .then([]() -> Future<Nothing> {
-      LOG(INFO) << "Recovered Docker image store";
-      return Nothing();
-    });
-}
-
-
-Future<string> DockerProvisionerProcess::provision(
-    const ContainerID& containerId,
-    const Image& image)
-{
-  if (image.type() != Image::DOCKER) {
-    return Failure("Unsupported container image type");
-  }
-
-  if (!image.has_docker()) {
-    return Failure("Missing Docker image info");
-  }
-
-  string rootfsId = UUID::random().toString();
-  string rootfs = provisioners::paths::getContainerRootfsDir(
-      rootDir, containerId, flags.docker_backend, rootfsId);
-
-  if (!infos.contains(containerId)) {
-    infos.put(containerId, Owned<Info>(new Info()));
-  }
-
-  infos[containerId]->rootfses[flags.docker_backend].put(rootfsId, rootfs);
-
-  Try<ImageName> imageName = ImageName::create(image.docker().name());
-  if (imageName.isError()) {
-    return Failure("Unable to able to parse Docker image name '" +
-                   image.docker().name() + "': " + imageName.error());
-  }
-
-  return store->get(imageName.get())
-    .then(defer(self(), &Self::_provision, lambda::_1, containerId, rootfs));
-}
-
-
-Future<string> DockerProvisionerProcess::_provision(
-    const DockerImage& image,
-    const ContainerID& containerId,
-    const string& rootfs)
-{
-  CHECK(backends.contains(flags.docker_backend));
-
-  LOG(INFO) << "Provisioning rootfs for container '" << containerId << "'"
-            << " to '" << rootfs << "'";
-
-  vector<string> layerPaths;
-  foreach (const string& layerId, image.layerIds) {
-    layerPaths.push_back(
-        paths::getImageLayerRootfsPath(flags.docker_store_dir, layerId));
-  }
-
-  return backends[flags.docker_backend]->provision(layerPaths, rootfs)
-    .then([rootfs]() -> Future<string> { return rootfs; });
-}
-
-
-Future<bool> DockerProvisionerProcess::destroy(
-    const ContainerID& containerId)
-{
-  // TODO(tnachen): Consider merging this with
-  // AppcProvisionerProcess::destroy.
-  if (!infos.contains(containerId)) {
-    LOG(INFO) << "Ignoring destroy request for unknown container: "
-              << containerId;
-
-    return false;
-  }
-
-  // Unregister the container first. If destroy() fails, we can rely on
-  // recover() to retry it later.
-  Owned<Info> info = infos[containerId];
-  infos.erase(containerId);
-
-  list<Future<bool>> futures;
-  foreachkey (const string& backend, info->rootfses) {
-    foreachvalue (const string& rootfs, info->rootfses[backend]) {
-      if (!backends.contains(backend)) {
-        return Failure("Cannot destroy rootfs '" + rootfs +
-                       "' provisioned by an unknown backend '" + backend + "'");
-      }
-
-      LOG(INFO) << "Destroying container rootfs for container '"
-                << containerId << "' at '" << rootfs << "'";
-
-      futures.push_back(
-          backends.get(backend).get()->destroy(rootfs));
-    }
-  }
-
-  return collect(futures)
-    .then([=](const list<bool>& results) -> Future<bool> {
-      return true;
-    });
-}
-
-} // namespace docker {
-} // namespace slave {
-} // namespace internal {
-} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioners/docker.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioners/docker.hpp b/src/slave/containerizer/provisioners/docker.hpp
deleted file mode 100644
index cd7911c..0000000
--- a/src/slave/containerizer/provisioners/docker.hpp
+++ /dev/null
@@ -1,166 +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 __MESOS_DOCKER_HPP__
-#define __MESOS_DOCKER_HPP__
-
-#include <list>
-#include <ostream>
-#include <sstream>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <stout/hashmap.hpp>
-#include <stout/json.hpp>
-#include <stout/nothing.hpp>
-#include <stout/option.hpp>
-#include <stout/strings.hpp>
-#include <stout/try.hpp>
-
-#include <process/future.hpp>
-#include <process/owned.hpp>
-#include <process/process.hpp>
-#include <process/shared.hpp>
-
-#include <mesos/resources.hpp>
-
-#include "slave/containerizer/provisioner.hpp"
-
-#include "slave/flags.hpp"
-
-namespace mesos {
-namespace internal {
-namespace slave {
-namespace docker {
-
-// Forward declaration.
-class Store;
-
-/**
- * Represents Docker Image Name, which composes of a repository and a
- * tag.
- */
-struct ImageName
-{
-  static Try<ImageName> create(const std::string& name);
-
-  ImageName(
-      const std::string& _repository,
-      const std::string& _tag,
-      const Option<std::string>& _registry = None())
-    : repository(_repository),
-      tag(_tag),
-      registry(_registry) {}
-
-  ImageName() {}
-
-  /**
-   * The string representation of this image.
-   */
-  std::string name() const
-  {
-    if (registry.isSome()) {
-      return registry.get() +  "/" + repository + ":" + tag;
-    }
-
-    return repository + ":" + tag;
-  }
-
-  /**
-   * Repository of this image (e.g, ubuntu).
-   */
-  std::string repository;
-
-  /**
-   * Tag of this image (e.g: 14.04).
-   */
-  std::string tag;
-
-  /**
-   * Custom registry that the image points to.
-   */
-  Option<std::string> registry;
-};
-
-
-inline std::ostream& operator<<(std::ostream& stream, const ImageName& image)
-{
-  return stream << image.name();
-}
-
-
-/**
- * Represents a Docker Image that holds its name and all of its layers
- * sorted by its dependency.
- */
-struct DockerImage
-{
-  DockerImage() {}
-
-  DockerImage(
-      const ImageName& _imageName,
-      const std::list<std::string>& _layerIds)
-  : imageName(_imageName), layerIds(_layerIds) {}
-
-  ImageName imageName;
-  std::list<std::string> layerIds;
-};
-
-// Forward declaration.
-class DockerProvisionerProcess;
-
-/**
- * Docker Provisioner is responsible to provision rootfs for
- * containers with Docker images.
- */
-class DockerProvisioner : public mesos::internal::slave::Provisioner
-{
-public:
-  static Try<process::Owned<mesos::internal::slave::Provisioner>> create(
-      const Flags& flags,
-      Fetcher* fetcher);
-
-  virtual ~DockerProvisioner();
-
-  virtual process::Future<Nothing> recover(
-      const std::list<mesos::slave::ContainerState>& states,
-      const hashset<ContainerID>& orphans);
-
-  virtual process::Future<std::string> provision(
-      const ContainerID& containerId,
-      const Image& image);
-
-  virtual process::Future<bool> destroy(const ContainerID& containerId);
-
-private:
-  explicit DockerProvisioner(process::Owned<DockerProvisionerProcess> _process);
-
-  DockerProvisioner& operator=(const DockerProvisioner&) = delete; // Not assignable.
-  DockerProvisioner(const DockerProvisioner&) = delete; // Not copyable.
-
-  process::Owned<DockerProvisionerProcess> process;
-};
-
-
-} // namespace docker {
-} // namespace slave {
-} // namespace internal {
-} // namespace mesos {
-
-#endif // __MESOS_DOCKER_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioners/docker/local_store.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioners/docker/local_store.cpp b/src/slave/containerizer/provisioners/docker/local_store.cpp
deleted file mode 100644
index ec0420e..0000000
--- a/src/slave/containerizer/provisioners/docker/local_store.cpp
+++ /dev/null
@@ -1,471 +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 "slave/containerizer/provisioners/docker/local_store.hpp"
-
-#include <list>
-#include <vector>
-
-#include <glog/logging.h>
-
-#include <stout/json.hpp>
-#include <stout/os.hpp>
-#include <stout/result.hpp>
-
-#include <process/collect.hpp>
-#include <process/defer.hpp>
-#include <process/dispatch.hpp>
-
-#include "common/status_utils.hpp"
-
-#include "slave/containerizer/fetcher.hpp"
-
-#include "slave/containerizer/provisioners/docker/metadata_manager.hpp"
-#include "slave/containerizer/provisioners/docker/paths.hpp"
-#include "slave/containerizer/provisioners/docker/store.hpp"
-
-#include "slave/flags.hpp"
-
-using namespace process;
-
-using std::list;
-using std::string;
-using std::vector;
-
-namespace mesos {
-namespace internal {
-namespace slave {
-namespace docker {
-
-class LocalStoreProcess : public process::Process<LocalStoreProcess>
-{
-public:
-  ~LocalStoreProcess() {}
-
-  static Try<process::Owned<LocalStoreProcess>> create(
-      const Flags& flags,
-      Fetcher* fetcher);
-
-  process::Future<DockerImage> get(const ImageName& name);
-
-  process::Future<Nothing> recover();
-
-private:
-  LocalStoreProcess(
-      const Flags& _flags,
-      Owned<MetadataManager> _metadataManager)
-    : flags(_flags), metadataManager(_metadataManager) {}
-
-  process::Future<Nothing> untarImage(
-      const std::string& tarPath,
-      const std::string& staging);
-
-  process::Future<DockerImage> putImage(
-      const ImageName& name,
-      const std::string& staging);
-
-  Result<std::string> getParentId(
-      const std::string& staging,
-      const std::string& layerId);
-
-  process::Future<Nothing> putLayers(
-      const std::string& staging,
-      const std::list<std::string>& layerIds);
-
-  process::Future<Nothing> putLayer(
-      const std::string& staging,
-      const std::string& id);
-
-  process::Future<Nothing> moveLayer(
-      const std::string& staging,
-      const std::string& id);
-
-  const Flags flags;
-  process::Owned<MetadataManager> metadataManager;
-};
-
-
-Try<Owned<Store>> Store::create(
-    const Flags& flags,
-    Fetcher* fetcher)
-{
-  hashmap<string, Try<Owned<Store>>(*)(const Flags&, Fetcher*)> creators{
-    {"local", &LocalStore::create}
-  };
-
-  if (!creators.contains(flags.docker_store)) {
-    return Error("Unknown Docker store: " + flags.docker_store);
-  }
-
-  return creators[flags.docker_store](flags, fetcher);
-}
-
-
-Try<Owned<Store>> LocalStore::create(
-    const Flags& flags,
-    Fetcher* fetcher)
-{
-  Try<Owned<LocalStoreProcess>> process =
-    LocalStoreProcess::create(flags, fetcher);
-  if (process.isError()) {
-    return Error(process.error());
-  }
-
-  return Owned<Store>(new LocalStore(process.get()));
-}
-
-
-LocalStore::LocalStore(Owned<LocalStoreProcess> _process) : process(_process)
-{
-  process::spawn(CHECK_NOTNULL(process.get()));
-}
-
-
-LocalStore::~LocalStore()
-{
-  process::terminate(process.get());
-  process::wait(process.get());
-}
-
-
-Future<DockerImage> LocalStore::get(const ImageName& name)
-{
-  return dispatch(process.get(), &LocalStoreProcess::get, name);
-}
-
-
-Future<Nothing> LocalStore::recover()
-{
-  return dispatch(process.get(), &LocalStoreProcess::recover);
-}
-
-
-Try<Owned<LocalStoreProcess>> LocalStoreProcess::create(
-    const Flags& flags,
-    Fetcher* fetcher)
-{
-  if (!os::exists(flags.docker_store_dir)) {
-    Try<Nothing> mkdir = os::mkdir(flags.docker_store_dir);
-    if (mkdir.isError()) {
-      return Error("Failed to create Docker store directory: " + mkdir.error());
-    }
-  }
-
-  if (!os::exists(paths::getStagingDir(flags.docker_store_dir))) {
-    Try<Nothing> mkdir =
-      os::mkdir(paths::getStagingDir(flags.docker_store_dir));
-    if (mkdir.isError()) {
-      return Error("Failed to create Docker store staging directory: " +
-                   mkdir.error());
-    }
-  }
-
-  Try<Owned<MetadataManager>> metadataManager = MetadataManager::create(flags);
-  if (metadataManager.isError()) {
-    return Error(metadataManager.error());
-  }
-
-  return Owned<LocalStoreProcess>(
-      new LocalStoreProcess(flags, metadataManager.get()));
-}
-
-
-Future<DockerImage> LocalStoreProcess::get(const ImageName& name)
-{
-  return metadataManager->get(name)
-    .then(defer(self(),
-                [this, name](
-                    const Option<DockerImage>& image) -> Future<DockerImage> {
-      if (image.isSome()) {
-        return image.get();
-      }
-
-      string tarPath = paths::getLocalImageTarPath(
-          flags.docker_discovery_local_dir,
-          name.name());
-
-      if (!os::exists(tarPath)) {
-        VLOG(1) << "Unable to find image in local store with path: " << tarPath;
-        return Failure("No Docker image tar archive found");
-      }
-
-      // Create a temporary staging directory.
-      Try<string> staging =
-        os::mkdtemp(paths::getTempStaging(flags.docker_store_dir));
-      if (staging.isError()) {
-        return Failure("Failed to create a staging directory");
-      }
-
-      return untarImage(tarPath, staging.get())
-        .then(defer(self(), &Self::putImage, name, staging.get()));
-    }));
-}
-
-
-Future<Nothing> LocalStoreProcess::recover()
-{
-  return metadataManager->recover();
-}
-
-Future<Nothing> LocalStoreProcess::untarImage(
-    const string& tarPath,
-    const string& staging)
-{
-  VLOG(1) << "Untarring image at: " << tarPath;
-
-  // Untar discovery_local_dir/name.tar into staging/.
-  vector<string> argv = {
-    "tar",
-    "-C",
-    staging,
-    "-x",
-    "-f",
-    tarPath
-  };
-
-  Try<Subprocess> s = subprocess(
-      "tar",
-      argv,
-      Subprocess::PATH("/dev/null"),
-      Subprocess::PATH("/dev/null"),
-      Subprocess::PATH("/dev/null"));
-
-  if (s.isError()) {
-    return Failure("Failed to create tar subprocess: " + s.error());
-  }
-
-  return s.get().status()
-    .then([=](const Option<int>& status) -> Future<Nothing> {
-      if (status.isNone()) {
-        return Failure("Failed to reap status for tar subprocess in " +
-                        tarPath);
-      }
-      if (!WIFEXITED(status.get()) || WEXITSTATUS(status.get()) != 0) {
-          return Failure("Untar image failed with exit code: " +
-                          WSTRINGIFY(status.get()));
-      }
-
-      return Nothing();
-    });
-}
-
-
-Future<DockerImage> LocalStoreProcess::putImage(
-    const ImageName& name,
-    const string& staging)
-{
-  Try<string> value = os::read(paths::getLocalImageRepositoriesPath(staging));
-  if (value.isError()) {
-    return Failure("Failed to read repository JSON: " + value.error());
-  }
-
-  Try<JSON::Object> json = JSON::parse<JSON::Object>(value.get());
-  if (json.isError()) {
-    return Failure("Failed to parse JSON: " + json.error());
-  }
-
-  Result<JSON::Object> repositoryValue =
-    json.get().find<JSON::Object>(name.repository);
-  if (repositoryValue.isError()) {
-    return Failure("Failed to find repository: " + repositoryValue.error());
-  } else if (repositoryValue.isNone()) {
-    return Failure("Repository '" + name.repository + "' is not found");
-  }
-
-  JSON::Object repositoryJson = repositoryValue.get();
-
-  // We don't use JSON find here because a tag might contain a '.'.
-  std::map<string, JSON::Value>::const_iterator entry =
-    repositoryJson.values.find(name.tag);
-  if (entry == repositoryJson.values.end()) {
-    return Failure("Tag '" + name.tag + "' is not found");
-  } else if (!entry->second.is<JSON::String>()) {
-    return Failure("Tag JSON value expected to be JSON::String");
-  }
-
-  string layerId = entry->second.as<JSON::String>().value;
-
-  Try<string> manifest =
-    os::read(paths::getLocalImageLayerManifestPath(staging, layerId));
-  if (manifest.isError()) {
-    return Failure("Failed to read manifest: " + manifest.error());
-  }
-
-  Try<JSON::Object> manifestJson = JSON::parse<JSON::Object>(manifest.get());
-  if (manifestJson.isError()) {
-    return Failure("Failed to parse manifest: " + manifestJson.error());
-  }
-
-  list<string> layerIds;
-  layerIds.push_back(layerId);
-  Result<string> parentId = getParentId(staging, layerId);
-  while(parentId.isSome()) {
-    layerIds.push_front(parentId.get());
-    parentId = getParentId(staging, parentId.get());
-  }
-  if (parentId.isError()) {
-    return Failure("Failed to obtain parent layer id: " + parentId.error());
-  }
-
-  return putLayers(staging, layerIds)
-    .then([=]() -> Future<DockerImage> {
-      return metadataManager->put(name, layerIds);
-    });
-}
-
-
-Result<string> LocalStoreProcess::getParentId(
-    const string& staging,
-    const string& layerId)
-{
-  Try<string> manifest =
-    os::read(paths::getLocalImageLayerManifestPath(staging, layerId));
-  if (manifest.isError()) {
-    return Error("Failed to read manifest: " + manifest.error());
-  }
-
-  Try<JSON::Object> json = JSON::parse<JSON::Object>(manifest.get());
-  if (json.isError()) {
-    return Error("Failed to parse manifest: " + json.error());
-  }
-
-  Result<JSON::String> parentId = json.get().find<JSON::String>("parent");
-  if (parentId.isNone() || (parentId.isSome() && parentId.get() == "")) {
-    return None();
-  } else if (parentId.isError()) {
-    return Error("Failed to read parent of layer: " + parentId.error());
-  }
-  return parentId.get().value;
-}
-
-
-Future<Nothing> LocalStoreProcess::putLayers(
-    const string& staging,
-    const list<string>& layerIds)
-{
-  list<Future<Nothing>> futures{ Nothing() };
-  foreach (const string& layer, layerIds) {
-    futures.push_back(
-        futures.back().then(
-          defer(self(), &Self::putLayer, staging, layer)));
-  }
-
-  return collect(futures)
-    .then([]() -> Future<Nothing> { return Nothing(); });
-}
-
-
-Future<Nothing> LocalStoreProcess::putLayer(
-    const string& staging,
-    const string& id)
-{
-  // We untar the layer from source into a staging directory, then
-  // move the layer into store. We do this instead of untarring
-  // directly to store to make sure we don't end up with partially
-  // untarred layer rootfs.
-
-  // Check if image layer rootfs is already in store.
-  if (os::exists(paths::getImageLayerRootfsPath(flags.docker_store_dir, id))) {
-    VLOG(1) << "Image layer '" << id << "' rootfs already in store. "
-            << "Skipping put.";
-    return Nothing();
-  }
-
-  const string imageLayerPath =
-    paths::getImageLayerPath(flags.docker_store_dir, id);
-  if (!os::exists(imageLayerPath)) {
-    Try<Nothing> mkdir = os::mkdir(imageLayerPath);
-    if (mkdir.isError()) {
-      return Failure("Failed to create Image layer directory '" +
-                     imageLayerPath + "': " + mkdir.error());
-    }
-  }
-
-  // Image layer has been untarred but is not present in the store directory.
-  string localRootfsPath = paths::getLocalImageLayerRootfsPath(staging, id);
-  if (os::exists(localRootfsPath)) {
-    LOG(WARNING) << "Image layer '" << id << "' rootfs present at but not in "
-                 << "store directory '" << localRootfsPath << "'. Removing "
-                 << "staged rootfs and untarring layer again.";
-    Try<Nothing> rmdir = os::rmdir(localRootfsPath);
-    if (rmdir.isError()) {
-      return Failure("Failed to remove incomplete staged rootfs for layer '" +
-                     id + "': " + rmdir.error());
-    }
-  }
-
-  Try<Nothing> mkdir = os::mkdir(localRootfsPath);
-  if (mkdir.isError()) {
-    return Failure("Failed to create rootfs path '" + localRootfsPath + "': " +
-                   mkdir.error());
-  }
-  // Untar staging/id/layer.tar into staging/id/rootfs.
-  vector<string> argv = {
-    "tar",
-    "-C",
-    localRootfsPath,
-    "-x",
-    "-f",
-    paths::getLocalImageLayerTarPath(staging, id)
-  };
-
-  Try<Subprocess> s = subprocess(
-      "tar",
-      argv,
-      Subprocess::PATH("/dev/null"),
-      Subprocess::PATH("/dev/null"),
-      Subprocess::PATH("/dev/null"));
-  if (s.isError()) {
-    return Failure("Failed to create tar subprocess: " + s.error());
-  }
-
-  return s.get().status()
-    .then([=](const Option<int>& status) -> Future<Nothing> {
-      if (status.isNone()) {
-        return Failure("Failed to reap subprocess to untar image");
-      } else if (!WIFEXITED(status.get()) || WEXITSTATUS(status.get()) != 0) {
-        return Failure("Untar failed with exit code: " +
-                        WSTRINGIFY(status.get()));
-      }
-
-      return moveLayer(staging, id);
-    });
-}
-
-
-Future<Nothing> LocalStoreProcess::moveLayer(
-    const string& staging,
-    const string& id)
-{
-  Try<Nothing> status = os::rename(
-      paths::getLocalImageLayerRootfsPath(staging, id),
-      paths::getImageLayerRootfsPath(flags.docker_store_dir, id));
-
-  if (status.isError()) {
-    return Failure("Failed to move layer to store directory: "
-                   + status.error());
-  }
-
-  return Nothing();
-}
-
-
-} // namespace docker {
-} // namespace slave {
-} // namespace internal {
-} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioners/docker/local_store.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioners/docker/local_store.hpp b/src/slave/containerizer/provisioners/docker/local_store.hpp
deleted file mode 100644
index 64a6fc0..0000000
--- a/src/slave/containerizer/provisioners/docker/local_store.hpp
+++ /dev/null
@@ -1,66 +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 __MESOS_DOCKER_LOCAL_STORE_HPP__
-#define __MESOS_DOCKER_LOCAL_STORE_HPP__
-
-#include "slave/containerizer/provisioners/docker/store.hpp"
-
-namespace mesos {
-namespace internal {
-namespace slave {
-namespace docker {
-
-// Forward declaration.
-class LocalStoreProcess;
-
-
-/**
- * LocalStore assumes Docker images are stored in a local directory
- * (configured with flags.docker_discovery_local_dir), with all the
- * images saved as tar with the name as the image name with tag (e.g:
- * ubuntu:14.04.tar).
- */
-class LocalStore : public Store
-{
-public:
-  static Try<process::Owned<Store>> create(
-      const Flags& flags,
-      Fetcher* fetcher);
-
-  virtual ~LocalStore();
-
-  virtual process::Future<DockerImage> get(const ImageName& name);
-
-  virtual process::Future<Nothing> recover();
-
-private:
-  explicit LocalStore(process::Owned<LocalStoreProcess> _process);
-
-  LocalStore& operator=(const LocalStore&) = delete; // Not assignable.
-  LocalStore(const LocalStore&) = delete; // Not copyable.
-
-  process::Owned<LocalStoreProcess> process;
-};
-
-} // namespace docker {
-} // namespace slave {
-} // namespace internal {
-} // namespace mesos {
-
-#endif // __MESOS_DOCKER_LOCAL_STORE_HPP__