Refactor store implementations to pullers.

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

Branch: refs/heads/master
Commit: 9fb62ceccfd5eda3027560ef0d816e5d2caa937d
Parents: 4dedbf4
Author: Timothy Chen <[email protected]>
Authored: Sun Sep 20 23:18:53 2015 -0700
Committer: Timothy Chen <[email protected]>
Committed: Fri Sep 25 09:02:05 2015 -0700

----------------------------------------------------------------------
 src/Makefile.am                                 |  13 +-
 src/slave/containerizer/provisioner.cpp         |  82 ----
 .../provisioner/docker/local_puller.cpp         | 349 ++++++++++++++
 .../provisioner/docker/local_puller.hpp         |  66 +++
 .../provisioner/docker/local_store.cpp          | 460 -------------------
 .../provisioner/docker/local_store.hpp          |  64 ---
 .../provisioner/docker/message.hpp              |   6 +-
 .../provisioner/docker/message.proto            |   7 +-
 .../provisioner/docker/metadata_manager.cpp     | 101 ++--
 .../provisioner/docker/metadata_manager.hpp     |  32 +-
 .../containerizer/provisioner/docker/paths.cpp  |  43 +-
 .../containerizer/provisioner/docker/paths.hpp  |  44 +-
 .../containerizer/provisioner/docker/puller.cpp |  46 ++
 .../containerizer/provisioner/docker/puller.hpp |  68 +++
 .../containerizer/provisioner/docker/store.cpp  | 247 +++++++++-
 .../containerizer/provisioner/docker/store.hpp  |  36 +-
 src/slave/containerizer/provisioner/store.cpp   |   3 +
 src/slave/flags.cpp                             |  20 +-
 src/slave/flags.hpp                             |   5 +-
 .../containerizer/provisioner_docker_tests.cpp  |  74 +--
 20 files changed, 971 insertions(+), 795 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index 818d62d..8aa4566 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -253,7 +253,6 @@ DOCKER_PROVISIONER_PROTOS =                                 
            \
   slave/containerizer/provisioner/docker/message.pb.cc                 \
   slave/containerizer/provisioner/docker/message.pb.h
 
-
 BUILT_SOURCES += $(DOCKER_PROVISIONER_PROTOS)
 CLEANFILES += $(DOCKER_PROVISIONER_PROTOS)
 
@@ -488,7 +487,6 @@ libmesos_no_3rdparty_la_SOURCES =                           
        \
        master/validation.cpp                                           \
        master/allocator/allocator.cpp                                  \
        master/allocator/sorter/drf/sorter.cpp                          \
-       messages/docker_provisioner.proto                               \
        messages/flags.proto                                            \
        messages/messages.cpp                                           \
        messages/messages.proto                                         \
@@ -530,9 +528,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/local_store.cpp          \
+       slave/containerizer/provisioner/docker/local_puller.cpp         \
+       slave/containerizer/provisioner/docker/message.proto            \
        slave/containerizer/provisioner/docker/metadata_manager.cpp     \
        slave/containerizer/provisioner/docker/paths.cpp                \
+       slave/containerizer/provisioner/docker/puller.cpp               \
        slave/containerizer/provisioner/docker/registry_client.cpp      \
        slave/containerizer/provisioner/docker/store.cpp                \
        slave/containerizer/provisioner/docker/token_manager.cpp        \
@@ -800,7 +800,6 @@ libmesos_no_3rdparty_la_SOURCES +=                          
        \
        master/allocator/mesos/hierarchical.hpp                         \
        master/allocator/sorter/drf/sorter.hpp                          \
        master/allocator/sorter/sorter.hpp                              \
-       messages/docker_provisioner.hpp                                 \
        messages/flags.hpp                                              \
        messages/messages.hpp                                           \
        module/manager.hpp                                              \
@@ -830,10 +829,11 @@ libmesos_no_3rdparty_la_SOURCES +=                        
                \
        slave/containerizer/provisioner/backend.hpp                     \
        slave/containerizer/provisioner/backends/bind.hpp               \
        slave/containerizer/provisioner/backends/copy.hpp               \
-       slave/containerizer/provisioner/docker.hpp                      \
-       slave/containerizer/provisioner/docker/local_store.hpp          \
+       slave/containerizer/provisioner/docker/local_puller.hpp         \
+       slave/containerizer/provisioner/docker/message.hpp              \
        slave/containerizer/provisioner/docker/metadata_manager.hpp     \
        slave/containerizer/provisioner/docker/paths.hpp                \
+       slave/containerizer/provisioner/docker/puller.hpp               \
        slave/containerizer/provisioner/docker/registry_client.hpp      \
        slave/containerizer/provisioner/docker/store.hpp                \
        slave/containerizer/provisioner/docker/token_manager.hpp        \
@@ -1776,7 +1776,6 @@ mesos_tests_DEPENDENCIES = # Initialized to allow += 
below.
 if OS_LINUX
   mesos_tests_SOURCES += tests/containerizer/cgroups_isolator_tests.cpp
   mesos_tests_SOURCES += tests/containerizer/cgroups_tests.cpp
-  mesos_tests_SOURCES += tests/containerizer/docker_provisioner_tests.cpp
   mesos_tests_SOURCES += tests/containerizer/filesystem_isolator_tests.cpp
   mesos_tests_SOURCES += tests/containerizer/fs_tests.cpp
   mesos_tests_SOURCES += tests/containerizer/launch_tests.cpp

http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/src/slave/containerizer/provisioner.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner.cpp 
b/src/slave/containerizer/provisioner.cpp
deleted file mode 100644
index 6a0faac..0000000
--- a/src/slave/containerizer/provisioner.cpp
+++ /dev/null
@@ -1,82 +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 <stout/hashset.hpp>
-#include <stout/stringify.hpp>
-#include <stout/strings.hpp>
-
-#include "slave/containerizer/provisioner.hpp"
-
-#include "slave/containerizer/provisioners/appc/provisioner.hpp"
-
-#include "slave/containerizer/provisioners/docker/provisioner.hpp"
-
-using namespace process;
-
-using std::string;
-
-namespace mesos {
-namespace internal {
-namespace slave {
-
-Try<hashmap<Image::Type, Owned<Provisioner>>> Provisioner::create(
-    const Flags& flags,
-    Fetcher* fetcher)
-{
-  if (flags.provisioners.isNone()) {
-    return hashmap<Image::Type, Owned<Provisioner>>();
-  }
-
-  hashmap<Image::Type,
-          Try<Owned<Provisioner>>(*)(const Flags&, Fetcher*)> creators;
-
-  // Register all supported creators.
-  creators.put(Image::APPC, &appc::AppcProvisioner::create);
-  creators.put(Image::DOCKER, &docker::DockerProvisioner::create);
-
-  hashmap<Image::Type, Owned<Provisioner>> provisioners;
-
-  // NOTE: Change in '--provisioners' flag may result in leaked rootfs
-  // files on the disk but it's at least safe because files managed by
-  // different provisioners are totally separated.
-  foreach (const string& type,
-           strings::tokenize(flags.provisioners.get(), ",")) {
-     Image::Type imageType;
-     if (!Image::Type_Parse(strings::upper(type), &imageType)) {
-       return Error("Unknown provisioner '" + type + "'");
-     }
-
-     if (!creators.contains(imageType)) {
-       return Error("Unsupported provisioner '" + type + "'");
-     }
-
-     Try<Owned<Provisioner>> provisioner = creators[imageType](flags, fetcher);
-     if (provisioner.isError()) {
-       return Error("Failed to create '" + stringify(imageType) +
-                    "' provisioner: " + provisioner.error());
-     }
-
-     provisioners[imageType] = provisioner.get();
-  }
-
-  return provisioners;
-}
-
-} // namespace slave {
-} // namespace internal {
-} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/src/slave/containerizer/provisioner/docker/local_puller.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/docker/local_puller.cpp 
b/src/slave/containerizer/provisioner/docker/local_puller.cpp
new file mode 100644
index 0000000..4a0b7d1
--- /dev/null
+++ b/src/slave/containerizer/provisioner/docker/local_puller.cpp
@@ -0,0 +1,349 @@
+/**
+ * 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 <process/subprocess.hpp>
+
+#include "common/status_utils.hpp"
+
+#include "slave/containerizer/provisioner/docker/local_puller.hpp"
+#include "slave/containerizer/provisioner/docker/paths.hpp"
+#include "slave/containerizer/provisioner/docker/store.hpp"
+
+using namespace process;
+
+using std::list;
+using std::pair;
+using std::string;
+using std::vector;
+
+namespace mesos {
+namespace internal {
+namespace slave {
+namespace docker {
+
+class LocalPullerProcess : public process::Process<LocalPullerProcess>
+{
+public:
+  LocalPullerProcess(const Flags& _flags) : flags(_flags) {}
+
+  ~LocalPullerProcess() {}
+
+  process::Future<list<pair<string, string>>> pull(
+      const Image::Name& name,
+      const string& directory);
+
+private:
+  process::Future<Nothing> untarImage(
+      const std::string& tarPath,
+      const std::string& directory);
+
+  process::Future<list<pair<string, string>>> putImage(
+      const Image::Name& name,
+      const std::string& directory);
+
+  process::Future<list<pair<string, string>>> putLayers(
+      const std::string& directory,
+      const std::vector<std::string>& layerIds);
+
+  process::Future<pair<string, string>> putLayer(
+      const std::string& directory,
+      const std::string& layerId);
+
+  const Flags flags;
+};
+
+
+LocalPuller::LocalPuller(const Flags& flags)
+{
+  process = Owned<LocalPullerProcess>(new LocalPullerProcess(flags));
+  process::spawn(process.get());
+}
+
+
+LocalPuller::~LocalPuller()
+{
+  process::terminate(process.get());
+  process::wait(process.get());
+}
+
+
+Future<list<pair<string, string>>> LocalPuller::pull(
+    const Image::Name& name,
+    const string& directory)
+{
+  return dispatch(process.get(), &LocalPullerProcess::pull, name, directory);
+}
+
+
+Future<list<pair<string, string>>> LocalPullerProcess::pull(
+    const Image::Name& name,
+    const string& directory)
+{
+  string tarPath = paths::getImageArchiveTarPath(
+      flags.docker_local_archives_dir,
+      stringify(name));
+
+  if (!os::exists(tarPath)) {
+    return Failure("Failed to find archive for image '" + stringify(name) +
+                   "' at '" + tarPath + "'");
+  }
+
+  return untarImage(tarPath, directory)
+    .then(defer(self(), &Self::putImage, name, directory));
+}
+
+
+Future<Nothing> LocalPullerProcess::untarImage(
+    const string& tarPath,
+    const string& directory)
+{
+  VLOG(1) << "Untarring image from '" << directory
+          << "' to '" << tarPath << "'";
+
+  // Untar store_discovery_local_dir/name.tar into directory/.
+  // TODO(tnachen): Terminate tar process when slave exits.
+  vector<string> argv = {
+    "tar",
+    "-C",
+    directory,
+    "-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([tarPath](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();
+    });
+}
+
+
+static Result<string> getParentId(
+    const string& directory,
+    const string& layerId)
+{
+  Try<string> manifest =
+    os::read(paths::getImageArchiveLayerManifestPath(directory, 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<list<pair<string, string>>> LocalPullerProcess::putImage(
+    const Image::Name& name,
+    const string& directory)
+{
+  Try<string> value =
+    os::read(paths::getImageArchiveRepositoriesPath(directory));
+  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::getImageArchiveLayerManifestPath(directory, 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());
+  }
+
+  vector<string> layerIds;
+  layerIds.push_back(layerId);
+  Result<string> parentId = getParentId(directory, layerId);
+  while (parentId.isSome()) {
+    layerIds.insert(layerIds.begin(), parentId.get());
+    parentId = getParentId(directory, parentId.get());
+  }
+
+  if (parentId.isError()) {
+    return Failure("Failed to find parent layer id of layer '" + layerId +
+                   "': " + parentId.error());
+  }
+
+  return putLayers(directory, layerIds);
+}
+
+
+Future<list<pair<string, string>>> LocalPullerProcess::putLayers(
+    const string& directory,
+    const vector<string>& layerIds)
+{
+  list<Future<pair<string, string>>> futures;
+  foreach (const string& layerId, layerIds) {
+    futures.push_back(putLayer(directory, layerId));
+  }
+
+  return collect(futures);
+}
+
+
+Future<pair<string, string>> LocalPullerProcess::putLayer(
+    const string& directory,
+    const string& layerId)
+{
+  // We untar the layer from source into a 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.
+
+  string localRootfsPath =
+    paths::getImageArchiveLayerRootfsPath(directory, layerId);
+
+  // Image layer has been untarred but is not present in the store directory.
+  if (os::exists(localRootfsPath)) {
+    LOG(WARNING) << "Image layer '" << layerId << "' 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 '" +
+                     layerId + "': " + rmdir.error());
+    }
+  }
+
+  Try<Nothing> mkdir = os::mkdir(localRootfsPath);
+  if (mkdir.isError()) {
+    return Failure("Failed to create rootfs path '" + localRootfsPath +
+                   "': " + mkdir.error());
+  }
+
+  // Untar directory/id/layer.tar into directory/id/rootfs.
+  // The tar file will be removed when the staging directory is
+  // removed.
+  vector<string> argv = {
+    "tar",
+    "-C",
+    localRootfsPath,
+    "-x",
+    "-f",
+    paths::getImageArchiveLayerTarPath(directory, layerId)
+  };
+
+  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([directory, layerId](
+        const Option<int>& status) -> Future<pair<string, string>> {
+      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()));
+      }
+
+      const string rootfsPath =
+        paths::getImageArchiveLayerRootfsPath(directory, layerId);
+
+      if (!os::exists(rootfsPath)) {
+        return Failure("Failed to find the rootfs path after extracting layer"
+                       " '" + layerId + "'");
+      }
+
+      return pair<string, string>(layerId, rootfsPath);
+    });
+}
+
+} // namespace docker {
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/src/slave/containerizer/provisioner/docker/local_puller.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/docker/local_puller.hpp 
b/src/slave/containerizer/provisioner/docker/local_puller.hpp
new file mode 100644
index 0000000..4574e8a
--- /dev/null
+++ b/src/slave/containerizer/provisioner/docker/local_puller.hpp
@@ -0,0 +1,66 @@
+/**
+ * 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 __PROVISIONER_DOCKER_LOCAL_PULLER_HPP__
+#define __PROVISIONER_DOCKER_LOCAL_PULLER_HPP__
+
+#include "slave/containerizer/provisioner/store.hpp"
+
+#include "slave/containerizer/provisioner/docker/message.hpp"
+#include "slave/containerizer/provisioner/docker/puller.hpp"
+
+#include "slave/flags.hpp"
+
+namespace mesos {
+namespace internal {
+namespace slave {
+namespace docker {
+
+// Forward declaration.
+class LocalPullerProcess;
+
+
+/**
+ * LocalPuller assumes Docker images are stored in a local directory
+ * (configured with flags.docker_local_archives_dir), with all the
+ * images saved as tars with file names in the form of <repo>:<tag>.tar.
+ */
+class LocalPuller : public Puller
+{
+public:
+  explicit LocalPuller(const Flags& flags);
+
+  ~LocalPuller();
+
+  process::Future<std::list<std::pair<std::string, std::string>>> pull(
+      const Image::Name& name,
+      const std::string& directory);
+
+private:
+  LocalPuller& operator=(const LocalPuller&) = delete; // Not assignable.
+  LocalPuller(const LocalPuller&) = delete; // Not copyable.
+
+  process::Owned<LocalPullerProcess> process;
+};
+
+} // namespace docker {
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
+
+#endif // __PROVISIONER_DOCKER_LOCAL_PULLER_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/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
deleted file mode 100644
index 6a73dbb..0000000
--- a/src/slave/containerizer/provisioner/docker/local_store.cpp
+++ /dev/null
@@ -1,460 +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 <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/9fb62cec/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
deleted file mode 100644
index 5f6152b..0000000
--- a/src/slave/containerizer/provisioner/docker/local_store.hpp
+++ /dev/null
@@ -1,64 +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/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/9fb62cec/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
index c1596df..6368bf4 100644
--- a/src/slave/containerizer/provisioner/docker/message.hpp
+++ b/src/slave/containerizer/provisioner/docker/message.hpp
@@ -29,9 +29,9 @@ namespace internal {
 namespace slave {
 namespace docker {
 
-inline DockerImage::Name parseName(const std::string& value)
+inline Image::Name parseName(const std::string& value)
 {
-  DockerImage::Name imageName;
+  Image::Name imageName;
   Option<std::string> registry = None();
   std::vector<std::string> components = strings::split(value, "/");
   if (components.size() > 2) {
@@ -53,7 +53,7 @@ inline DockerImage::Name parseName(const std::string& value)
 
 inline std::ostream& operator<<(
     std::ostream& stream,
-    const DockerImage::Name& name)
+    const Image::Name& name)
 {
   if (name.has_registry()) {
     return stream << name.registry() << "/" << name.repository() << ":"

http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/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
index d771968..bbac2e6 100644
--- a/src/slave/containerizer/provisioner/docker/message.proto
+++ b/src/slave/containerizer/provisioner/docker/message.proto
@@ -25,7 +25,7 @@ package mesos.internal.slave.docker;
  * The layerIds are ordered, with the root layer id (no parent layer id) first
  * and the leaf layer id last.
  */
-message DockerImage {
+message Image {
   message Name {
     optional string registry = 1;
     required string repository = 2;
@@ -38,6 +38,7 @@ message DockerImage {
   repeated string layer_ids = 2;
 }
 
-message DockerImages {
-  repeated DockerImage images = 1;
+
+message Images {
+  repeated Image images = 1;
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/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
index 197931c..2b2de52 100644
--- a/src/slave/containerizer/provisioner/docker/metadata_manager.cpp
+++ b/src/slave/containerizer/provisioner/docker/metadata_manager.cpp
@@ -16,6 +16,8 @@
  * limitations under the License.
  */
 
+#include "slave/containerizer/provisioner/docker/metadata_manager.hpp"
+
 #include <vector>
 
 #include <glog/logging.h>
@@ -33,7 +35,6 @@
 
 #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"
 
@@ -48,48 +49,41 @@ namespace internal {
 namespace slave {
 namespace docker {
 
-
 class MetadataManagerProcess : public process::Process<MetadataManagerProcess>
 {
 public:
-  ~MetadataManagerProcess() {}
+  MetadataManagerProcess(const Flags& _flags) : flags(_flags) {}
 
-  static Try<process::Owned<MetadataManagerProcess>> create(
-      const Flags& flags);
+  ~MetadataManagerProcess() {}
 
-  Future<DockerImage> put(
-      const DockerImage::Name& name,
-      const std::list<std::string>& layerIds);
+  Future<Nothing> recover();
 
-  Future<Option<DockerImage>> get(const DockerImage::Name& name);
+  Future<Image> put(
+      const Image::Name& name,
+      const std::vector<std::string>& layerIds);
 
-  Future<Nothing> recover();
+  Future<Option<Image>> get(const Image::Name& name);
 
   // 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;
+  // by the name of the Image.
+  // For example, "ubuntu:14.04" -> ubuntu14:04 Image.
+  hashmap<std::string, Image> 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()));
+  Owned<MetadataManagerProcess> process(new MetadataManagerProcess(flags));
+
+  return Owned<MetadataManager>(new MetadataManager(process));
 }
 
 
@@ -113,42 +107,31 @@ Future<Nothing> MetadataManager::recover()
 }
 
 
-Future<DockerImage> MetadataManager::put(
-    const DockerImage::Name& name,
-    const list<string>& layerIds)
+Future<Image> MetadataManager::put(
+    const Image::Name& name,
+    const vector<string>& layerIds)
 {
   return dispatch(
-      process.get(), &MetadataManagerProcess::put, name, layerIds);
+      process.get(),
+      &MetadataManagerProcess::put,
+      name,
+      layerIds);
 }
 
 
-Future<Option<DockerImage>> MetadataManager::get(const DockerImage::Name& name)
+Future<Option<Image>> MetadataManager::get(const Image::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)
+Future<Image> MetadataManagerProcess::put(
+    const Image::Name& name,
+    const vector<string>& layerIds)
 {
   const string imageName = stringify(name);
 
-  DockerImage dockerImage;
+  Image dockerImage;
   dockerImage.mutable_name()->CopyFrom(name);
   foreach (const string& layerId, layerIds) {
     dockerImage.add_layer_ids(layerId);
@@ -158,15 +141,15 @@ Future<DockerImage> MetadataManagerProcess::put(
 
   Try<Nothing> status = persist();
   if (status.isError()) {
-    return Failure("Failed to save state of Docker images" + status.error());
+    return Failure("Failed to save state of Docker images: " + status.error());
   }
 
   return dockerImage;
 }
 
 
-Future<Option<DockerImage>> MetadataManagerProcess::get(
-    const DockerImage::Name& name)
+Future<Option<Image>> MetadataManagerProcess::get(
+    const Image::Name& name)
 {
   const string imageName = stringify(name);
 
@@ -180,13 +163,13 @@ Future<Option<DockerImage>> MetadataManagerProcess::get(
 
 Try<Nothing> MetadataManagerProcess::persist()
 {
-  DockerImages images;
+  Images images;
 
-  foreachvalue (const DockerImage& image, storedImages) {
+  foreachvalue (const Image& image, storedImages) {
     images.add_images()->CopyFrom(image);
   }
 
-  Try<Nothing> status = mesos::internal::slave::state::checkpoint(
+  Try<Nothing> status = state::checkpoint(
       paths::getStoredImagesPath(flags.docker_store_dir), images);
   if (status.isError()) {
     return Error("Failed to perform checkpoint: " + status.error());
@@ -200,21 +183,19 @@ 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.";
+              << "storage path '" << storedImagesPath << "' does not exist";
     return Nothing();
   }
 
-  Result<DockerImages> images =
-    ::protobuf::read<DockerImages>(storedImagesPath);
+  Result<Images> images = ::protobuf::read<Images>(storedImagesPath);
   if (images.isError()) {
     return Failure("Failed to read protobuf for Docker provisioner image: " +
                    images.error());
   }
 
-  foreach (const DockerImage image, images.get().images()) {
+  foreach (const Image image, images.get().images()) {
     vector<string> missingLayerIds;
     foreach (const string layerId, image.layer_ids()) {
       const string rootfsPath =
@@ -226,21 +207,21 @@ Future<Nothing> MetadataManagerProcess::recover()
     }
 
     if (!missingLayerIds.empty()) {
-      LOG(WARNING) << "Skipped loading image: " << stringify(image.name())
-                   << " due to missing layers: " << stringify(missingLayerIds);
+      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
-                   << "'";
+      LOG(WARNING) << "Found duplicate image in recovery for image name '"
+                   << imageName << "'";
     } else {
       storedImages[imageName] = image;
     }
   }
 
-  LOG(INFO) << "Loaded " << storedImages.size() << " Docker images.";
+  LOG(INFO) << "Loaded " << storedImages.size() << " Docker images";
 
   return Nothing();
 }

http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/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
index 647a478..885080d 100644
--- a/src/slave/containerizer/provisioner/docker/metadata_manager.hpp
+++ b/src/slave/containerizer/provisioner/docker/metadata_manager.hpp
@@ -16,8 +16,8 @@
  * limitations under the License.
  */
 
-#ifndef __MESOS_DOCKER_METADATA_MANAGER_HPP__
-#define __MESOS_DOCKER_METADATA_MANAGER_HPP__
+#ifndef __PROVISIONER_DOCKER_METADATA_MANAGER_HPP__
+#define __PROVISIONER_DOCKER_METADATA_MANAGER_HPP__
 
 #include <list>
 #include <string>
@@ -49,7 +49,7 @@ 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
+ * that Docker images are composed of and recovers Image 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.
@@ -59,10 +59,15 @@ class MetadataManager
 public:
   static Try<process::Owned<MetadataManager>> create(const Flags& flags);
 
-   ~MetadataManager();
+  ~MetadataManager();
 
   /**
-   * Create a Image, put it in metadata manager and persist the reference
+   * Recover all stored Image and its layer references.
+   */
+  process::Future<Nothing> recover();
+
+  /**
+   * Create an 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
@@ -71,22 +76,17 @@ public:
    *                 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);
+  process::Future<Image> put(
+      const Image::Name& name,
+      const std::vector<std::string>& layerIds);
 
   /**
-   * Retrieve DockerImage based on image name if it is among the DockerImages
+   * Retrieve Image based on image name if it is among the Images
    * 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();
+  process::Future<Option<Image>> get(const Image::Name& name);
 
 private:
   explicit MetadataManager(process::Owned<MetadataManagerProcess> process);
@@ -103,4 +103,4 @@ private:
 } // namespace internal {
 } // namespace mesos {
 
-#endif // __MESOS_DOCKER_METADATA_MANAGER_HPP__
+#endif // __PROVISIONER_DOCKER_METADATA_MANAGER_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/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
index 81a2176..5733fb7 100644
--- a/src/slave/containerizer/provisioner/docker/paths.cpp
+++ b/src/slave/containerizer/provisioner/docker/paths.cpp
@@ -33,51 +33,60 @@ string getStagingDir(const string& storeDir)
   return path::join(storeDir, "staging");
 }
 
-string getTempStaging(const string& storeDir)
+
+string getStagingTempDir(const string& storeDir)
 {
   return path::join(getStagingDir(storeDir), "XXXXXX");
 }
 
-string getLocalImageTarPath(
+
+string getImageArchiveTarPath(
     const string& discoveryDir,
     const string& name)
 {
   return path::join(discoveryDir, name + ".tar");
 }
 
-string getLocalImageRepositoriesPath(const string& staging)
+
+string getImageArchiveRepositoriesPath(const string& archivePath)
 {
-  return path::join(staging, "repositories");
+  return path::join(archivePath, "repositories");
 }
 
-std::string getLocalImageLayerPath(
-    const string& staging,
+
+std::string getImageArchiveLayerPath(
+    const string& archivePath,
     const string& layerId)
 {
-  return path::join(staging, layerId);
+  return path::join(archivePath, layerId);
 }
 
-string getLocalImageLayerManifestPath(
-    const string& staging,
+
+string getImageArchiveLayerManifestPath(
+    const string& archivePath,
     const string& layerId)
 {
-  return path::join(getLocalImageLayerPath(staging, layerId), "json");
+  return path::join(getImageArchiveLayerPath(archivePath, layerId), "json");
 }
 
-string getLocalImageLayerTarPath(
-  const string& staging,
+
+string getImageArchiveLayerTarPath(
+  const string& archivePath,
   const string& layerId)
 {
-  return path::join(getLocalImageLayerPath(staging, layerId), "layer.tar");
+  return path::join(
+      getImageArchiveLayerPath(archivePath, layerId), "layer.tar");
 }
 
-string getLocalImageLayerRootfsPath(
-    const string& staging,
+
+string getImageArchiveLayerRootfsPath(
+    const string& archivePath,
     const string& layerId)
 {
-  return path::join(getLocalImageLayerPath(staging, layerId), "rootfs");
+  return path::join(getImageArchiveLayerPath(archivePath, layerId), "rootfs");
 }
 
+
 string getImageLayerPath(
     const string& storeDir,
     const string& layerId)
@@ -85,6 +94,7 @@ string getImageLayerPath(
   return path::join(storeDir, "layers", layerId);
 }
 
+
 string getImageLayerRootfsPath(
     const string& storeDir,
     const string& layerId)
@@ -92,6 +102,7 @@ string getImageLayerRootfsPath(
   return path::join(getImageLayerPath(storeDir, layerId), "rootfs");
 }
 
+
 string getStoredImagesPath(const string& storeDir)
 {
   return path::join(storeDir, "storedImages");

http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/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
index 02f129f..18beb2e 100644
--- a/src/slave/containerizer/provisioner/docker/paths.hpp
+++ b/src/slave/containerizer/provisioner/docker/paths.hpp
@@ -16,8 +16,8 @@
  * limitations under the License.
  */
 
-#ifndef __MESOS_DOCKER_PATHS_HPP__
-#define __MESOS_DOCKER_PATHS_HPP__
+#ifndef __PROVISIONER_DOCKER_PATHS_HPP__
+#define __PROVISIONER_DOCKER_PATHS_HPP__
 
 #include <list>
 #include <string>
@@ -33,48 +33,60 @@ 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)
+ *    |--staging
+ *       |-- <temp_dir_archive>
+ *           |-- <layer_id>
+ *               |-- rootfs
  *    |--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(
+std::string getStagingTempDir(const std::string& storeDir);
+
+
+std::string getImageArchiveTarPath(
     const std::string& discoveryDir,
     const std::string& name);
 
-std::string getLocalImageRepositoriesPath(const std::string& staging);
 
-std::string getLocalImageLayerPath(
-    const std::string& staging,
+std::string getImageArchiveRepositoriesPath(const std::string& archivePath);
+
+
+std::string getImageArchiveLayerPath(
+    const std::string& archivePath,
     const std::string& layerId);
 
-std::string getLocalImageLayerManifestPath(
-    const std::string& staging,
+
+std::string getImageArchiveLayerManifestPath(
+    const std::string& archivePath,
     const std::string& layerId);
 
-std::string getLocalImageLayerTarPath(
-  const std::string& staging,
+
+std::string getImageArchiveLayerTarPath(
+  const std::string& archivePath,
   const std::string& layerId);
 
-std::string getLocalImageLayerRootfsPath(
-  const std::string& staging,
+
+std::string getImageArchiveLayerRootfsPath(
+  const std::string& archivePath,
   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 {
@@ -83,4 +95,4 @@ std::string getStoredImagesPath(const std::string& storeDir);
 } // namespace internal {
 } // namespace mesos {
 
-#endif // __MESOS_DOCKER_PATHS_HPP__
+#endif // __PROVISIONER_DOCKER_PATHS_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/src/slave/containerizer/provisioner/docker/puller.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/docker/puller.cpp 
b/src/slave/containerizer/provisioner/docker/puller.cpp
new file mode 100644
index 0000000..cb05324
--- /dev/null
+++ b/src/slave/containerizer/provisioner/docker/puller.cpp
@@ -0,0 +1,46 @@
+/**
+ * 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/puller.hpp"
+
+#include "slave/containerizer/provisioner/docker/local_puller.hpp"
+
+using std::string;
+
+using process::Owned;
+
+namespace mesos {
+namespace internal {
+namespace slave {
+namespace docker {
+
+Try<Owned<Puller>> Puller::create(const Flags& flags)
+{
+  const string puller = flags.docker_puller;
+
+  if (puller == "local") {
+    return Owned<Puller>(new LocalPuller(flags));
+  }
+
+  return Error("Unknown or unsupported docker puller: " + puller);
+}
+
+} // namespace docker {
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/src/slave/containerizer/provisioner/docker/puller.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/docker/puller.hpp 
b/src/slave/containerizer/provisioner/docker/puller.hpp
new file mode 100644
index 0000000..105b4e7
--- /dev/null
+++ b/src/slave/containerizer/provisioner/docker/puller.hpp
@@ -0,0 +1,68 @@
+/**
+ * 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 __PROVISIONER_DOCKER_PULLER_HPP__
+#define __PROVISIONER_DOCKER_PULLER_HPP__
+
+#include <list>
+#include <utility>
+
+#include <stout/try.hpp>
+
+#include <process/future.hpp>
+#include <process/owned.hpp>
+
+#include "slave/containerizer/provisioner/docker/message.hpp"
+
+#include "slave/flags.hpp"
+
+namespace mesos {
+namespace internal {
+namespace slave {
+namespace docker {
+
+class Puller
+{
+public:
+  static Try<process::Owned<Puller>> create(const Flags& flags);
+
+  virtual ~Puller() {}
+
+  /**
+   * Pull a Docker image layers into the specified directory, and
+   * return the list of layer ids in that image in the right
+   * dependency order, and also return the directory where
+   * the puller puts its changeset.
+   *
+   * @param name The name of the image.
+   * @param directory The target directory to store the layers.
+   * @return list of layers maped to its local directory ordered by its
+   *         dependency.
+   */
+  virtual process::Future<std::list<std::pair<std::string, std::string>>> pull(
+      const docker::Image::Name& name,
+      const std::string& directory) = 0;
+};
+
+} // namespace docker {
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
+
+
+#endif // __PROVISIONER_DOCKER_PULLER_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/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
index 51f4d6b..cbb6768 100644
--- a/src/slave/containerizer/provisioner/docker/store.cpp
+++ b/src/slave/containerizer/provisioner/docker/store.cpp
@@ -16,29 +16,260 @@
  * limitations under the License.
  */
 
-#include "slave/containerizer/provisioner/docker/local_store.hpp"
 #include "slave/containerizer/provisioner/docker/store.hpp"
 
-using process::Owned;
+#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 <process/subprocess.hpp>
+
+#include "common/status_utils.hpp"
+
+#include "slave/containerizer/provisioner/docker/metadata_manager.hpp"
+#include "slave/containerizer/provisioner/docker/paths.hpp"
+#include "slave/containerizer/provisioner/docker/puller.hpp"
+
+#include "slave/flags.hpp"
+
+using namespace process;
+
+using std::list;
+using std::pair;
 using std::string;
+using std::vector;
 
 namespace mesos {
 namespace internal {
 namespace slave {
 namespace docker {
 
+class StoreProcess : public Process<StoreProcess>
+{
+public:
+  StoreProcess(
+      const Flags& _flags,
+      const Owned<MetadataManager>& _metadataManager,
+      const Owned<Puller>& _puller)
+    : flags(_flags), metadataManager(_metadataManager), puller(_puller) {}
+
+  ~StoreProcess() {}
+
+  Future<Nothing> recover();
+
+  Future<vector<string>> get(const mesos::Image& image);
+
+private:
+  Future<Image> _get(
+      const Image::Name& name,
+      const Option<Image>& image);
+
+  Future<vector<string>> __get(const Image& image);
+
+  Future<vector<string>> moveLayers(
+      const std::string& staging,
+      const std::list<pair<string, string>>& layerPaths);
+
+  Future<Image> storeImage(
+      const Image::Name& name,
+      const std::vector<std::string>& layerIds);
+
+  Future<Nothing> moveLayer(const pair<string, string>& layerPath);
+
+  const Flags flags;
+  Owned<MetadataManager> metadataManager;
+  Owned<Puller> puller;
+};
+
+
 Try<Owned<slave::Store>> Store::create(const Flags& flags)
 {
-  hashmap<string, Try<Owned<slave::Store>>(*)(const Flags&)> creators{
-    {"local", &LocalStore::create}
-  };
+  Try<Owned<Puller>> puller = Puller::create(flags);
+  if (puller.isError()) {
+    return Error("Failed to create Docker puller: " + puller.error());
+  }
+
+  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<StoreProcess> process(
+      new StoreProcess(flags, metadataManager.get(), puller.get()));
+
+  return Owned<slave::Store>(new Store(process));
+}
+
+
+Store::Store(const Owned<StoreProcess>& _process) : process(_process)
+{
+  process::spawn(CHECK_NOTNULL(process.get()));
+}
+
+
+Store::~Store()
+{
+  process::terminate(process.get());
+  process::wait(process.get());
+}
+
+
+Future<Nothing> Store::recover()
+{
+  return dispatch(process.get(), &StoreProcess::recover);
+}
+
+
+Future<vector<string>> Store::get(const mesos::Image& image)
+{
+  return dispatch(process.get(), &StoreProcess::get, image);
+}
+
+
+Future<vector<string>> StoreProcess::get(const mesos::Image& image)
+{
+  if (image.type() != mesos::Image::DOCKER) {
+    return Failure("Docker provisioner store only supports Docker images");
+  }
+
+  Try<Image::Name> imageName = parseName(image.docker().name());
+  if (imageName.isError()) {
+    return Failure("Unable to parse docker image name: " + imageName.error());
+  }
+
+  return metadataManager->get(imageName.get())
+    .then(defer(self(), &Self::_get, imageName.get(), lambda::_1))
+    .then(defer(self(), &Self::__get, lambda::_1));
+}
+
+
+Future<Image> StoreProcess::_get(
+    const Image::Name& name,
+    const Option<Image>& image)
+{
+  if (image.isSome()) {
+    return image.get();
+  }
+
+  Try<string> staging =
+    os::mkdtemp(paths::getStagingTempDir(flags.docker_store_dir));
+  if (staging.isError()) {
+    return Failure("Failed to create a staging directory");
+  }
+
+  return puller->pull(name, staging.get())
+    .then(defer(self(), &Self::moveLayers, staging.get(), lambda::_1))
+    .then(defer(self(), &Self::storeImage, name, lambda::_1))
+    .onAny([staging]() {
+      Try<Nothing> rmdir = os::rmdir(staging.get());
+      if (rmdir.isError()) {
+        LOG(WARNING) << "Failed to remove staging directory: " << 
rmdir.error();
+      }
+    });
+}
+
+
+Future<vector<string>> StoreProcess::__get(const Image& image)
+{
+  vector<string> layerDirectories;
+  foreach (const string& layer, image.layer_ids()) {
+    layerDirectories.push_back(
+        paths::getImageLayerRootfsPath(
+            flags.docker_store_dir, layer));
+  }
+
+  return layerDirectories;
+}
+
+
+Future<Nothing> StoreProcess::recover()
+{
+  return metadataManager->recover();
+}
+
+
+Future<vector<string>> StoreProcess::moveLayers(
+    const string& staging,
+    const list<pair<string, string>>& layerPaths)
+{
+  list<Future<Nothing>> futures;
+  foreach (const auto& layerPath, layerPaths) {
+    futures.push_back(moveLayer(layerPath));
+  }
+
+  return collect(futures)
+    .then([layerPaths]() {
+        vector<string> layerIds;
+        foreach (const auto& layerPath, layerPaths) {
+          layerIds.push_back(layerPath.first);
+        }
+
+        return layerIds;
+    });
+}
+
+
+Future<Image> StoreProcess::storeImage(
+    const Image::Name& name,
+    const vector<string>& layerIds)
+{
+  return metadataManager->put(name, layerIds);
+}
+
+
+Future<Nothing> StoreProcess::moveLayer(const pair<string, string>& layerPath)
+{
+  if (!os::exists(layerPath.second)) {
+    return Failure("Unable to find layer '" + layerPath.first + "' in '" +
+                   layerPath.second + "'");
+  }
+
+  const string imageLayerPath =
+    paths::getImageLayerPath(flags.docker_store_dir, layerPath.first);
+
+  if (!os::exists(imageLayerPath)) {
+    Try<Nothing> mkdir = os::mkdir(imageLayerPath);
+    if (mkdir.isError()) {
+      return Failure("Failed to create layer path in store for id '" +
+                     layerPath.first + "': " + mkdir.error());
+    }
+  }
+
+  Try<Nothing> status = os::rename(
+      layerPath.second,
+      paths::getImageLayerRootfsPath(
+          flags.docker_store_dir, layerPath.first));
 
-  if (!creators.contains(flags.docker_store_discovery)) {
-    return Error("Unknown Docker store: " + flags.docker_store_discovery);
+  if (status.isError()) {
+    return Failure("Failed to move layer '" + layerPath.first +
+                   "' to store directory: " + status.error());
   }
 
-  return creators[flags.docker_store_discovery](flags);
+  return Nothing();
 }
 
 } // namespace docker {

http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/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
index ae06706..b5e6c87 100644
--- a/src/slave/containerizer/provisioner/docker/store.hpp
+++ b/src/slave/containerizer/provisioner/docker/store.hpp
@@ -16,24 +16,17 @@
  * limitations under the License.
  */
 
-#ifndef __MESOS_DOCKER_STORE_HPP__
-#define __MESOS_DOCKER_STORE_HPP__
+#ifndef __PROVISIONER_DOCKER_STORE_HPP__
+#define __PROVISIONER_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 {
@@ -41,13 +34,30 @@ namespace internal {
 namespace slave {
 namespace docker {
 
+// Forward Declarations.
+class Puller;
+class StoreProcess;
+
+
 // 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
+class Store : public slave::Store
 {
 public:
   static Try<process::Owned<slave::Store>> create(const Flags& flags);
+
+  ~Store();
+
+  process::Future<Nothing> recover();
+
+  process::Future<std::vector<std::string>> get(const mesos::Image& image);
+
+private:
+  explicit Store(const process::Owned<StoreProcess>& _process);
+
+  Store& operator=(const Store&) = delete; // Not assignable.
+  Store(const Store&) = delete; // Not copyable.
+
+  process::Owned<StoreProcess> process;
 };
 
 } // namespace docker {
@@ -55,4 +65,4 @@ public:
 } // namespace internal {
 } // namespace mesos {
 
-#endif // __MESOS_DOCKER_STORE_HPP__
+#endif // __PROVISIONER_DOCKER_STORE_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/src/slave/containerizer/provisioner/store.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/store.cpp 
b/src/slave/containerizer/provisioner/store.cpp
index 35d1199..a8bc302 100644
--- a/src/slave/containerizer/provisioner/store.cpp
+++ b/src/slave/containerizer/provisioner/store.cpp
@@ -28,6 +28,8 @@
 
 #include "slave/containerizer/provisioner/appc/store.hpp"
 
+#include "slave/containerizer/provisioner/docker/store.hpp"
+
 using namespace process;
 
 using std::string;
@@ -44,6 +46,7 @@ Try<hashmap<Image::Type, Owned<Store>>> Store::create(const 
Flags& flags)
 
   hashmap<Image::Type, Try<Owned<Store>>(*)(const Flags&)> creators;
   creators.put(Image::APPC, &appc::Store::create);
+  creators.put(Image::DOCKER, &docker::Store::create);
 
   hashmap<Image::Type, Owned<Store>> stores;
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/src/slave/flags.cpp
----------------------------------------------------------------------
diff --git a/src/slave/flags.cpp b/src/slave/flags.cpp
index bf26b98..029aa1e 100644
--- a/src/slave/flags.cpp
+++ b/src/slave/flags.cpp
@@ -93,21 +93,21 @@ mesos::internal::slave::Flags::Flags()
       "Directory the appc provisioner will store images in.",
       "/tmp/mesos/store/appc");
 
+  add(&Flags::docker_local_archives_dir,
+      "docker_local_archives_dir",
+      "Directory for docker local puller to look in for image archives",
+      "/tmp/mesos/images/docker");
+
+  add(&Flags::docker_puller,
+      "docker_puller",
+      "Strategy for docker puller to fetch images",
+      "local");
+
   add(&Flags::docker_store_dir,
       "docker_store_dir",
       "Directory the docker provisioner will store images in",
       "/tmp/mesos/store/docker");
 
-  add(&Flags::docker_store_discovery,
-      "docker_store_discovery",
-      "Strategy for docker store to fetch images",
-      "local");
-
-  add(&Flags::docker_store_discovery_local_dir,
-      "docker_store_discovery_local_dir",
-      "Directory for docker provisioner to look in for local images",
-      "/tmp/mesos/images/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/9fb62cec/src/slave/flags.hpp
----------------------------------------------------------------------
diff --git a/src/slave/flags.hpp b/src/slave/flags.hpp
index 1ce123c..f76f0f6 100644
--- a/src/slave/flags.hpp
+++ b/src/slave/flags.hpp
@@ -53,10 +53,9 @@ public:
   std::string image_provisioner_backend;
   std::string appc_store_dir;
 
-  std::string docker_provisioner_backend;
+  std::string docker_local_archives_dir;
+  std::string docker_puller;
   std::string docker_store_dir;
-  std::string docker_store_discovery;
-  std::string docker_store_discovery_local_dir;
 
   std::string default_role;
   Option<std::string> attributes;

http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/src/tests/containerizer/provisioner_docker_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/provisioner_docker_tests.cpp 
b/src/tests/containerizer/provisioner_docker_tests.cpp
index 3a817c0..d895eb9 100644
--- a/src/tests/containerizer/provisioner_docker_tests.cpp
+++ b/src/tests/containerizer/provisioner_docker_tests.cpp
@@ -38,6 +38,7 @@
 #include <process/ssl/gtest.hpp>
 
 #include "slave/containerizer/provisioner/docker/metadata_manager.hpp"
+#include "slave/containerizer/provisioner/docker/paths.hpp"
 #include "slave/containerizer/provisioner/docker/registry_client.hpp"
 #include "slave/containerizer/provisioner/docker/store.hpp"
 #include "slave/containerizer/provisioner/docker/token_manager.hpp"
@@ -50,8 +51,6 @@ using std::map;
 using std::string;
 using std::vector;
 
-using namespace mesos::internal::slave::docker::registry;
-
 using process::Clock;
 using process::Future;
 using process::Owned;
@@ -61,6 +60,8 @@ using process::network::Socket;
 using namespace process;
 using namespace mesos::internal::slave;
 using namespace mesos::internal::slave::docker;
+using namespace mesos::internal::slave::docker::paths;
+using namespace mesos::internal::slave::docker::registry;
 
 using ManifestResponse = RegistryClient::ManifestResponse;
 
@@ -68,7 +69,6 @@ namespace mesos {
 namespace internal {
 namespace tests {
 
-
 /**
  * Provides token operations and defaults.
  */
@@ -695,9 +695,11 @@ TEST_F(RegistryClientTest, BadRequest)
   ASSERT_TRUE(strings::contains(resultFuture.failure(), "Error2"));
 }
 
+
 #endif // USE_SSL_SOCKET
 
-class DockerProvisionerLocalStoreTest : public TemporaryDirectoryTest
+
+class ProvisionerDockerLocalStoreTest : public TemporaryDirectoryTest
 {
 public:
   void verifyLocalDockerImage(
@@ -707,19 +709,21 @@ public:
     string layersPath = path::join(flags.docker_store_dir, "layers");
 
     // Verify contents of the image in store directory.
-    EXPECT_TRUE(os::exists(path::join(layersPath, "123", "rootfs")));
-    EXPECT_TRUE(os::exists(path::join(layersPath, "456", "rootfs")));
+    string layerPath1 = getImageLayerRootfsPath(flags.docker_store_dir, "123");
+    string layerPath2 = getImageLayerRootfsPath(flags.docker_store_dir, "456");
+    EXPECT_TRUE(os::exists(layerPath1));
+    EXPECT_TRUE(os::exists(layerPath2));
     EXPECT_SOME_EQ(
         "foo 123",
-        os::read(path::join(layersPath, "123", "rootfs" , "temp")));
+        os::read(path::join(layerPath1 , "temp")));
     EXPECT_SOME_EQ(
         "bar 456",
-        os::read(path::join(layersPath, "456", "rootfs", "temp")));
+        os::read(path::join(layerPath2, "temp")));
 
     // Verify the Docker Image provided.
     vector<string> expectedLayers;
-    expectedLayers.push_back("123");
-    expectedLayers.push_back("456");
+    expectedLayers.push_back(layerPath1);
+    expectedLayers.push_back(layerPath2);
     EXPECT_EQ(expectedLayers, layers);
   }
 
@@ -734,19 +738,19 @@ protected:
     ASSERT_SOME(os::mkdir(image));
 
     JSON::Value repositories = JSON::parse(
-      "{"
-      "  \"abc\": {"
-      "    \"latest\": \"456\""
-      "  }"
-      "}").get();
+        "{"
+        "  \"abc\": {"
+        "    \"latest\": \"456\""
+        "  }"
+        "}").get();
     ASSERT_SOME(
         os::write(path::join(image, "repositories"), stringify(repositories)));
 
     ASSERT_SOME(os::mkdir(path::join(image, "123")));
     JSON::Value manifest123 = JSON::parse(
-      "{"
-      "  \"parent\": \"\""
-      "}").get();
+        "{"
+        "  \"parent\": \"\""
+        "}").get();
     ASSERT_SOME(os::write(
         path::join(image, "123", "json"), stringify(manifest123)));
     ASSERT_SOME(os::mkdir(path::join(image, "123", "layer")));
@@ -762,9 +766,9 @@ protected:
 
     ASSERT_SOME(os::mkdir(path::join(image, "456")));
     JSON::Value manifest456 = JSON::parse(
-      "{"
-      "  \"parent\": \"123\""
-      "}").get();
+        "{"
+        "  \"parent\": \"123\""
+        "}").get();
     ASSERT_SOME(
         os::write(path::join(image, "456", "json"), stringify(manifest456)));
     ASSERT_SOME(os::mkdir(path::join(image, "456", "layer")));
@@ -783,10 +787,11 @@ protected:
   }
 };
 
+
 // This test verifies that a locally stored Docker image in the form of a
 // tar achive created from a 'docker save' command can be unpacked and
 // stored in the proper locations accessible to the Docker provisioner.
-TEST_F(DockerProvisionerLocalStoreTest, LocalStoreTestWithTar)
+TEST_F(ProvisionerDockerLocalStoreTest, LocalStoreTestWithTar)
 {
   string imageDir = path::join(os::getcwd(), "images");
   string image = path::join(imageDir, "abc:latest");
@@ -794,12 +799,11 @@ TEST_F(DockerProvisionerLocalStoreTest, 
LocalStoreTestWithTar)
   ASSERT_SOME(os::mkdir(image));
 
   slave::Flags flags;
-  flags.docker_store_discovery = "local";
+  flags.docker_puller = "local";
   flags.docker_store_dir = path::join(os::getcwd(), "store");
-  flags.docker_store_discovery_local_dir = imageDir;
+  flags.docker_local_archives_dir = imageDir;
 
-  Try<Owned<slave::Store>> store =
-    mesos::internal::slave::docker::Store::create(flags);
+  Try<Owned<slave::Store>> store = slave::docker::Store::create(flags);
   ASSERT_SOME(store);
 
   string sandbox = path::join(os::getcwd(), "sandbox");
@@ -815,17 +819,17 @@ TEST_F(DockerProvisionerLocalStoreTest, 
LocalStoreTestWithTar)
   verifyLocalDockerImage(flags, layers.get());
 }
 
-// This tests the ability of the reference store to recover the images it has
+
+// This tests the ability of the metadata manger to recover the images it has
 // already stored on disk when it is initialized.
-TEST_F(DockerProvisionerLocalStoreTest, MetadataManagerInitialization)
+TEST_F(ProvisionerDockerLocalStoreTest, MetadataManagerInitialization)
 {
   slave::Flags flags;
-  flags.docker_store_discovery = "local";
+  flags.docker_puller = "local";
   flags.docker_store_dir = path::join(os::getcwd(), "store");
-  flags.docker_store_discovery_local_dir = path::join(os::getcwd(), "images");
+  flags.docker_local_archives_dir = path::join(os::getcwd(), "images");
 
-  Try<Owned<slave::Store>> store =
-    mesos::internal::slave::docker::Store::create(flags);
+  Try<Owned<slave::Store>> store = slave::docker::Store::create(flags);
   ASSERT_SOME(store);
 
   string sandbox = path::join(os::getcwd(), "sandbox");
@@ -838,11 +842,13 @@ TEST_F(DockerProvisionerLocalStoreTest, 
MetadataManagerInitialization)
   Future<vector<string>> layers = store.get()->get(image);
   AWAIT_READY(layers);
 
-  // Store is deleted and recreated. Reference Store is initialized upon
+  // Store is deleted and recreated. Metadata Manager is initialized upon
   // creation of the store.
   store.get().reset();
-  store = mesos::internal::slave::docker::Store::create(flags);
+  store = slave::docker::Store::create(flags);
   ASSERT_SOME(store);
+  Future<Nothing> recover = store.get()->recover();
+  AWAIT_READY(recover);
 
   layers = store.get()->get(image);
   AWAIT_READY(layers);

Reply via email to