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:05 UTC

[13/17] mesos git commit: Rename Docker reference store to metadata manager.

Rename Docker reference store to metadata manager.


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

Branch: refs/heads/master
Commit: 2cc0dfb20643188570bcc61af6e66c83297ade8d
Parents: f392f75
Author: Timothy Chen <tn...@gmail.com>
Authored: Tue Sep 8 22:19:05 2015 -0700
Committer: Timothy Chen <tn...@apache.org>
Committed: Fri Sep 25 09:02:05 2015 -0700

----------------------------------------------------------------------
 src/Makefile.am                                 |   4 +-
 src/slave/containerizer/provisioners/docker.cpp |  27 +-
 src/slave/containerizer/provisioners/docker.hpp |  76 ++++--
 .../provisioners/docker/local_store.cpp         |  65 ++---
 .../provisioners/docker/local_store.hpp         |  12 +-
 .../provisioners/docker/metadata_manager.cpp    | 254 +++++++++++++++++++
 .../provisioners/docker/metadata_manager.hpp    | 104 ++++++++
 .../provisioners/docker/reference_store.cpp     | 249 ------------------
 .../provisioners/docker/reference_store.hpp     | 102 --------
 .../containerizer/provisioners/docker/store.hpp |   4 +-
 .../containerizer/provisioner_docker_tests.cpp  |  27 +-
 11 files changed, 492 insertions(+), 432 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/2cc0dfb2/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index cd8b2ca..a2a64dc 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -531,8 +531,8 @@ libmesos_no_3rdparty_la_SOURCES =					\
 	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/reference_store.cpp	\
 	slave/containerizer/provisioner/docker/registry_client.cpp	\
 	slave/containerizer/provisioner/docker/token_manager.cpp	\
 	slave/resource_estimators/noop.cpp				\
@@ -831,8 +831,8 @@ 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/paths.hpp		\
-	slave/containerizer/provisioner/docker/reference_store.hpp	\
 	slave/containerizer/provisioner/docker/registry_client.hpp	\
 	slave/containerizer/provisioner/docker/store.hpp		\
 	slave/containerizer/provisioner/docker/token_manager.hpp	\

http://git-wip-us.apache.org/repos/asf/mesos/blob/2cc0dfb2/src/slave/containerizer/provisioners/docker.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioners/docker.cpp b/src/slave/containerizer/provisioners/docker.cpp
index b1f737f..b5c5a7b 100644
--- a/src/slave/containerizer/provisioners/docker.cpp
+++ b/src/slave/containerizer/provisioners/docker.cpp
@@ -97,9 +97,10 @@ private:
 };
 
 
-ImageName::ImageName(const std::string& name)
+Try<ImageName> ImageName::create(const std::string& name)
 {
-  registry = None();
+  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("/"));
@@ -107,12 +108,14 @@ ImageName::ImageName(const std::string& name)
 
   std::size_t found = components.back().find_last_of(':');
   if (found == std::string::npos) {
-    repo = components.back();
-    tag = "latest";
+    imageName.repository = components.back();
+    imageName.tag = "latest";
   } else {
-    repo = components.back().substr(0, found);
-    tag = components.back().substr(found + 1);
+    imageName.repository = components.back().substr(0, found);
+    imageName.tag = components.back().substr(found + 1);
   }
+
+  return imageName;
 }
 
 Try<Owned<Provisioner>> DockerProvisioner::create(
@@ -191,7 +194,7 @@ Try<Owned<DockerProvisionerProcess>> DockerProvisionerProcess::create(
     }
   }
 
-  hashmap<string, Owned<Backend>> backends = Backend::create(flags);
+  const hashmap<string, Owned<Backend>> backends = Backend::create(flags);
   if (backends.empty()) {
     return Error("No usable Docker provisioner backend created");
   }
@@ -345,7 +348,13 @@ Future<string> DockerProvisionerProcess::provision(
 
   infos[containerId]->rootfses[flags.docker_backend].put(rootfsId, rootfs);
 
-  return store->get(image.docker().name())
+  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));
 }
 
@@ -361,7 +370,7 @@ Future<string> DockerProvisionerProcess::_provision(
             << " to '" << rootfs << "'";
 
   vector<string> layerPaths;
-  foreach (const string& layerId, image.layers) {
+  foreach (const string& layerId, image.layerIds) {
     layerPaths.push_back(
         paths::getImageLayerRootfsPath(flags.docker_store_dir, layerId));
   }

http://git-wip-us.apache.org/repos/asf/mesos/blob/2cc0dfb2/src/slave/containerizer/provisioners/docker.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioners/docker.hpp b/src/slave/containerizer/provisioners/docker.hpp
index d3ada9d..cd7911c 100644
--- a/src/slave/containerizer/provisioners/docker.hpp
+++ b/src/slave/containerizer/provisioners/docker.hpp
@@ -52,50 +52,83 @@ namespace docker {
 // Forward declaration.
 class Store;
 
+/**
+ * Represents Docker Image Name, which composes of a repository and a
+ * tag.
+ */
 struct ImageName
 {
-  std::string repo;
-  std::string tag;
-  Option<std::string> registry;
-
-  ImageName(const std::string& name);
+  static Try<ImageName> create(const std::string& name);
 
   ImageName(
-      const std::string& repo,
-      const std::string& tag,
-      const Option<std::string>& registry = None())
-    : repo(repo), tag(tag), registry(registry) {}
+      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)
 {
-  if (image.registry.isSome()) {
-    return stream << image.registry.get()
-                  << "/" << image.repo << ":" << image.tag;
-  }
-  return stream << image.repo << ":" << image.tag;
+  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 std::string& imageName,
-      const std::list<std::string>& layers)
-  : imageName(imageName), layers(layers) {}
+      const ImageName& _imageName,
+      const std::list<std::string>& _layerIds)
+  : imageName(_imageName), layerIds(_layerIds) {}
 
-  std::string imageName;
-  std::list<std::string> layers;
+  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:
@@ -117,8 +150,9 @@ public:
 
 private:
   explicit DockerProvisioner(process::Owned<DockerProvisionerProcess> _process);
-  DockerProvisioner(const DockerProvisioner&); // Not copyable.
-  DockerProvisioner& operator=(const DockerProvisioner&); // Not assignable.
+
+  DockerProvisioner& operator=(const DockerProvisioner&) = delete; // Not assignable.
+  DockerProvisioner(const DockerProvisioner&) = delete; // Not copyable.
 
   process::Owned<DockerProvisionerProcess> process;
 };

http://git-wip-us.apache.org/repos/asf/mesos/blob/2cc0dfb2/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
index 80b5b06..ec0420e 100644
--- a/src/slave/containerizer/provisioners/docker/local_store.cpp
+++ b/src/slave/containerizer/provisioners/docker/local_store.cpp
@@ -35,8 +35,9 @@
 
 #include "slave/containerizer/fetcher.hpp"
 
-#include "slave/containerizer/provisioners/docker/store.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"
 
@@ -60,22 +61,22 @@ public:
       const Flags& flags,
       Fetcher* fetcher);
 
-  process::Future<DockerImage> get(const std::string& name);
+  process::Future<DockerImage> get(const ImageName& name);
 
   process::Future<Nothing> recover();
 
 private:
   LocalStoreProcess(
       const Flags& _flags,
-      Owned<ReferenceStore> _refStore)
-    : flags(_flags), refStore(_refStore) {}
+      Owned<MetadataManager> _metadataManager)
+    : flags(_flags), metadataManager(_metadataManager) {}
 
   process::Future<Nothing> untarImage(
       const std::string& tarPath,
       const std::string& staging);
 
   process::Future<DockerImage> putImage(
-      const std::string& name,
+      const ImageName& name,
       const std::string& staging);
 
   Result<std::string> getParentId(
@@ -84,7 +85,7 @@ private:
 
   process::Future<Nothing> putLayers(
       const std::string& staging,
-      const std::list<std::string>& layers);
+      const std::list<std::string>& layerIds);
 
   process::Future<Nothing> putLayer(
       const std::string& staging,
@@ -95,7 +96,7 @@ private:
       const std::string& id);
 
   const Flags flags;
-  process::Owned<ReferenceStore> refStore;
+  process::Owned<MetadataManager> metadataManager;
 };
 
 
@@ -142,7 +143,7 @@ LocalStore::~LocalStore()
 }
 
 
-Future<DockerImage> LocalStore::get(const string& name)
+Future<DockerImage> LocalStore::get(const ImageName& name)
 {
   return dispatch(process.get(), &LocalStoreProcess::get, name);
 }
@@ -174,18 +175,19 @@ Try<Owned<LocalStoreProcess>> LocalStoreProcess::create(
     }
   }
 
-  Try<Owned<ReferenceStore>> refStore = ReferenceStore::create(flags);
-  if (refStore.isError()) {
-    return Error(refStore.error());
+  Try<Owned<MetadataManager>> metadataManager = MetadataManager::create(flags);
+  if (metadataManager.isError()) {
+    return Error(metadataManager.error());
   }
 
-  return Owned<LocalStoreProcess>(new LocalStoreProcess(flags, refStore.get()));
+  return Owned<LocalStoreProcess>(
+      new LocalStoreProcess(flags, metadataManager.get()));
 }
 
 
-Future<DockerImage> LocalStoreProcess::get(const string& name)
+Future<DockerImage> LocalStoreProcess::get(const ImageName& name)
 {
-  return refStore->get(name)
+  return metadataManager->get(name)
     .then(defer(self(),
                 [this, name](
                     const Option<DockerImage>& image) -> Future<DockerImage> {
@@ -193,9 +195,12 @@ Future<DockerImage> LocalStoreProcess::get(const string& name)
         return image.get();
       }
 
-      string tarPath =
-        paths::getLocalImageTarPath(flags.docker_discovery_local_dir, name);
+      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");
       }
 
@@ -214,7 +219,7 @@ Future<DockerImage> LocalStoreProcess::get(const string& name)
 
 Future<Nothing> LocalStoreProcess::recover()
 {
-  return refStore->recover();
+  return metadataManager->recover();
 }
 
 Future<Nothing> LocalStoreProcess::untarImage(
@@ -261,11 +266,9 @@ Future<Nothing> LocalStoreProcess::untarImage(
 
 
 Future<DockerImage> LocalStoreProcess::putImage(
-    const std::string& name,
+    const ImageName& name,
     const string& staging)
 {
-  ImageName imageName(name);
-
   Try<string> value = os::read(paths::getLocalImageRepositoriesPath(staging));
   if (value.isError()) {
     return Failure("Failed to read repository JSON: " + value.error());
@@ -277,20 +280,20 @@ Future<DockerImage> LocalStoreProcess::putImage(
   }
 
   Result<JSON::Object> repositoryValue =
-    json.get().find<JSON::Object>(imageName.repo);
+    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 '" + imageName.repo + "' is not found");
+    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(imageName.tag);
+    repositoryJson.values.find(name.tag);
   if (entry == repositoryJson.values.end()) {
-    return Failure("Tag '" + imageName.tag + "' is not found");
+    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");
   }
@@ -308,20 +311,20 @@ Future<DockerImage> LocalStoreProcess::putImage(
     return Failure("Failed to parse manifest: " + manifestJson.error());
   }
 
-  list<string> layers;
-  layers.push_back(layerId);
+  list<string> layerIds;
+  layerIds.push_back(layerId);
   Result<string> parentId = getParentId(staging, layerId);
   while(parentId.isSome()) {
-    layers.push_front(parentId.get());
+    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, layers)
+  return putLayers(staging, layerIds)
     .then([=]() -> Future<DockerImage> {
-      return refStore->put(name, layers);
+      return metadataManager->put(name, layerIds);
     });
 }
 
@@ -353,10 +356,10 @@ Result<string> LocalStoreProcess::getParentId(
 
 Future<Nothing> LocalStoreProcess::putLayers(
     const string& staging,
-    const list<string>& layers)
+    const list<string>& layerIds)
 {
   list<Future<Nothing>> futures{ Nothing() };
-  foreach (const string& layer, layers) {
+  foreach (const string& layer, layerIds) {
     futures.push_back(
         futures.back().then(
           defer(self(), &Self::putLayer, staging, layer)));

http://git-wip-us.apache.org/repos/asf/mesos/blob/2cc0dfb2/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
index b650b5e..64a6fc0 100644
--- a/src/slave/containerizer/provisioners/docker/local_store.hpp
+++ b/src/slave/containerizer/provisioners/docker/local_store.hpp
@@ -28,7 +28,6 @@ namespace docker {
 
 // Forward declaration.
 class LocalStoreProcess;
-class ReferenceStore;
 
 
 /**
@@ -40,22 +39,21 @@ class ReferenceStore;
 class LocalStore : public Store
 {
 public:
-  virtual ~LocalStore();
-
   static Try<process::Owned<Store>> create(
       const Flags& flags,
       Fetcher* fetcher);
 
-  virtual process::Future<DockerImage> get(const std::string& name);
+  virtual ~LocalStore();
+
+  virtual process::Future<DockerImage> get(const ImageName& name);
 
   virtual process::Future<Nothing> recover();
 
 private:
   explicit LocalStore(process::Owned<LocalStoreProcess> _process);
 
-  LocalStore(const LocalStore&); // Not copyable.
-
-  LocalStore& operator=(const LocalStore&); // Not assignable.
+  LocalStore& operator=(const LocalStore&) = delete; // Not assignable.
+  LocalStore(const LocalStore&) = delete; // Not copyable.
 
   process::Owned<LocalStoreProcess> process;
 };

http://git-wip-us.apache.org/repos/asf/mesos/blob/2cc0dfb2/src/slave/containerizer/provisioners/docker/metadata_manager.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioners/docker/metadata_manager.cpp b/src/slave/containerizer/provisioners/docker/metadata_manager.cpp
new file mode 100644
index 0000000..55eb382
--- /dev/null
+++ b/src/slave/containerizer/provisioners/docker/metadata_manager.cpp
@@ -0,0 +1,254 @@
+/**
+ * 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 "messages/docker_provisioner.hpp"
+
+#include "slave/containerizer/provisioners/docker/paths.hpp"
+#include "slave/containerizer/provisioners/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 ImageName& name,
+      const std::list<std::string>& layerIds);
+
+  Future<Option<DockerImage>> get(const ImageName& 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 ImageName& name,
+    const list<string>& layerIds)
+{
+  return dispatch(
+      process.get(), &MetadataManagerProcess::put, name, layerIds);
+}
+
+
+Future<Option<DockerImage>> MetadataManager::get(const ImageName& 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 ImageName& name,
+    const list<string>& layerIds)
+{
+  storedImages[name.name()] = DockerImage(name, layerIds);
+
+  Try<Nothing> status = persist();
+  if (status.isError()) {
+    return Failure("Failed to save state of Docker images" + status.error());
+  }
+
+  return storedImages[name.name()];
+}
+
+
+Future<Option<DockerImage>> MetadataManagerProcess::get(const ImageName& name)
+{
+  if (!storedImages.contains(name.name())) {
+    return None();
+  }
+
+  return storedImages[name.name()];
+}
+
+
+Try<Nothing> MetadataManagerProcess::persist()
+{
+  DockerProvisionerImages images;
+
+  foreachpair(
+      const string& name, const DockerImage& dockerImage, storedImages) {
+    DockerProvisionerImages::Image* image = images.add_images();
+
+    image->set_name(name);
+
+    foreach (const string& layerId, dockerImage.layerIds) {
+      image->add_layer_ids(layerId);
+    }
+  }
+
+  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<DockerProvisionerImages> images =
+    ::protobuf::read<DockerProvisionerImages>(storedImagesPath);
+  if (images.isError()) {
+    return Failure("Failed to read protobuf for Docker provisioner image: " +
+                   images.error());
+  }
+
+  for (int i = 0; i < images.get().images_size(); i++) {
+    string name = images.get().images(i).name();
+
+    list<string> layerIds;
+    vector<string> missingLayerIds;
+    for (int j = 0; j < images.get().images(i).layer_ids_size(); j++) {
+      string layerId = images.get().images(i).layer_ids(j);
+
+      layerIds.push_back(layerId);
+
+      if (!os::exists(
+              paths::getImageLayerRootfsPath(flags.docker_store_dir, layerId))) {
+        missingLayerIds.push_back(layerId);
+      }
+    }
+
+    if (!missingLayerIds.empty()) {
+      foreach (const string& layerId, missingLayerIds) {
+        LOG(WARNING) << "Image layer: " << layerId << " required for Docker "
+                     << "image: " << name << " is not on disk.";
+      }
+      LOG(WARNING) << "Skipped loading image: " << name
+                   << " due to missing layers.";
+      continue;
+    }
+
+    Try<ImageName> imageName = ImageName::create(name);
+    if (imageName.isError()) {
+      return Failure("Unable to parse Docker image name: " + imageName.error());
+    }
+    storedImages[imageName.get().name()] = DockerImage(imageName.get(), layerIds);
+  }
+
+  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/2cc0dfb2/src/slave/containerizer/provisioners/docker/metadata_manager.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioners/docker/metadata_manager.hpp b/src/slave/containerizer/provisioners/docker/metadata_manager.hpp
new file mode 100644
index 0000000..9db3f47
--- /dev/null
+++ b/src/slave/containerizer/provisioners/docker/metadata_manager.hpp
@@ -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.
+ */
+
+#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/provisioners/docker.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 DockerImage, 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 ImageName& 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 ImageName& 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/2cc0dfb2/src/slave/containerizer/provisioners/docker/reference_store.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioners/docker/reference_store.cpp b/src/slave/containerizer/provisioners/docker/reference_store.cpp
deleted file mode 100644
index 4b72319..0000000
--- a/src/slave/containerizer/provisioners/docker/reference_store.cpp
+++ /dev/null
@@ -1,249 +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 <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 "messages/docker_provisioner.hpp"
-
-#include "slave/containerizer/provisioners/docker/paths.hpp"
-#include "slave/containerizer/provisioners/docker/reference_store.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 ReferenceStoreProcess : public process::Process<ReferenceStoreProcess>
-{
-public:
-  ~ReferenceStoreProcess() {}
-
-  static Try<process::Owned<ReferenceStoreProcess>> create(const Flags& flags);
-
-  Future<DockerImage> put(
-      const std::string& name,
-      const std::list<std::string>& layers);
-
-  Future<Option<DockerImage>> get(const std::string& name);
-
-  Future<Nothing> recover();
-
-  // TODO(chenlily): Implement removal of unreferenced images.
-
-private:
-  ReferenceStoreProcess(const Flags& flags);
-
-  // Write out reference store 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<ReferenceStore>> ReferenceStore::create(const Flags& flags)
-{
-  Try<Owned<ReferenceStoreProcess>> process =
-    ReferenceStoreProcess::create(flags);
-  if (process.isError()) {
-    return Error("Failed to create reference store: " + process.error());
-  }
-  return Owned<ReferenceStore>(new ReferenceStore(process.get()));
-}
-
-
-ReferenceStore::ReferenceStore(Owned<ReferenceStoreProcess> process)
-  : process(process)
-{
-  process::spawn(CHECK_NOTNULL(process.get()));
-}
-
-
-ReferenceStore::~ReferenceStore()
-{
-  process::terminate(process.get());
-  process::wait(process.get());
-}
-
-
-Future<Nothing> ReferenceStore::recover()
-{
-  return process::dispatch(process.get(), &ReferenceStoreProcess::recover);
-}
-
-
-Future<DockerImage> ReferenceStore::put(
-    const string& name,
-    const list<string>& layers)
-{
-  return dispatch(
-      process.get(), &ReferenceStoreProcess::put, name, layers);
-}
-
-
-Future<Option<DockerImage>> ReferenceStore::get(const string& name)
-{
-  return dispatch(process.get(), &ReferenceStoreProcess::get, name);
-}
-
-
-ReferenceStoreProcess::ReferenceStoreProcess(const Flags& flags)
-  : flags(flags) {}
-
-
-Try<Owned<ReferenceStoreProcess>> ReferenceStoreProcess::create(
-    const Flags& flags)
-{
-  Owned<ReferenceStoreProcess> referenceStore =
-    Owned<ReferenceStoreProcess>(new ReferenceStoreProcess(flags));
-
-  return referenceStore;
-}
-
-
-Future<DockerImage> ReferenceStoreProcess::put(
-    const string& name,
-    const list<string>& layers)
-{
-  storedImages[name] = DockerImage(name, layers);
-
-  Try<Nothing> status = persist();
-  if (status.isError()) {
-    return Failure("Failed to save state of Docker images" + status.error());
-  }
-
-  return storedImages[name];
-}
-
-
-Future<Option<DockerImage>> ReferenceStoreProcess::get(const string& name)
-{
-  if (!storedImages.contains(name)) {
-    return None();
-  }
-
-  return storedImages[name];
-}
-
-
-Try<Nothing> ReferenceStoreProcess::persist()
-{
-  DockerProvisionerImages images;
-
-  foreachpair(
-      const string& name, const DockerImage& dockerImage, storedImages) {
-    DockerProvisionerImages::Image* image = images.add_images();
-
-    image->set_name(name);
-
-    foreach (const string& layer, dockerImage.layers) {
-      image->add_layer_ids(layer);
-    }
-  }
-
-  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> ReferenceStoreProcess::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<DockerProvisionerImages> images =
-    ::protobuf::read<DockerProvisionerImages>(storedImagesPath);
-  if (images.isError()) {
-    return Failure("Failed to read protobuf for Docker provisioner image: " +
-                   images.error());
-  }
-
-  for (int i = 0; i < images.get().images_size(); i++) {
-    string imageName = images.get().images(i).name();
-
-    list<string> layers;
-    vector<string> missingLayers;
-    for (int j = 0; j < images.get().images(i).layer_ids_size(); j++) {
-      string layerId = images.get().images(i).layer_ids(j);
-
-      layers.push_back(layerId);
-
-      if (!os::exists(
-              paths::getImageLayerRootfsPath(flags.docker_store_dir, layerId))) {
-        missingLayers.push_back(layerId);
-      }
-    }
-
-    if (!missingLayers.empty()) {
-      foreach (const string& layer, missingLayers) {
-        LOG(WARNING) << "Image layer: " << layer << " required for Docker "
-                     << "image: " << imageName << " is not on disk.";
-      }
-      LOG(WARNING) << "Skipped loading image: " << imageName
-                   << " due to missing layers.";
-      continue;
-    }
-
-    storedImages[imageName] = DockerImage(imageName, layers);
-  }
-
-  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/2cc0dfb2/src/slave/containerizer/provisioners/docker/reference_store.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioners/docker/reference_store.hpp b/src/slave/containerizer/provisioners/docker/reference_store.hpp
deleted file mode 100644
index be652ae..0000000
--- a/src/slave/containerizer/provisioners/docker/reference_store.hpp
+++ /dev/null
@@ -1,102 +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_REFERENCE_STORE_HPP__
-#define __MESOS_DOCKER_REFERENCE_STORE_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/provisioners/docker.hpp"
-#include "slave/flags.hpp"
-
-namespace mesos {
-namespace internal {
-namespace slave {
-namespace docker {
-
-// Forward Declaration.
-class ReferenceStoreProcess;
-
-/**
- * The Reference Store is a way to track the Docker images used by the
- * provisioner that are stored in 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 ReferenceStore
-{
-public:
-  ~ReferenceStore();
-
-  static Try<process::Owned<ReferenceStore>> create(const Flags& flags);
-
-  /**
-   * Create a DockerImage, put it in reference store and persist the reference
-   * store state to disk.
-   *
-   * @param name   the name of the Docker image to place in the reference store.
-   * @param layers 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 std::string& name,
-      const std::list<std::string>& layers);
-
-  /**
-   * 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 std::string& name);
-
-  /**
-   * Recover all stored DockerImage and its layer references.
-   */
-  process::Future<Nothing> recover();
-
-private:
-  explicit ReferenceStore(process::Owned<ReferenceStoreProcess> process);
-
-  ReferenceStore(const ReferenceStore&); // Not copyable.
-  ReferenceStore& operator=(const ReferenceStore&); // Not assignable.
-
-  process::Owned<ReferenceStoreProcess> process;
-};
-
-
-} // namespace docker {
-} // namespace slave {
-} // namespace internal {
-} // namespace mesos {
-
-#endif // __MESOS_DOCKER_REFERENCE_STORE_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/2cc0dfb2/src/slave/containerizer/provisioners/docker/store.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioners/docker/store.hpp b/src/slave/containerizer/provisioners/docker/store.hpp
index a9201d5..03958bf 100644
--- a/src/slave/containerizer/provisioners/docker/store.hpp
+++ b/src/slave/containerizer/provisioners/docker/store.hpp
@@ -30,7 +30,7 @@
 
 #include "slave/containerizer/fetcher.hpp"
 #include "slave/containerizer/provisioners/docker.hpp"
-#include "slave/containerizer/provisioners/docker/reference_store.hpp"
+
 #include "slave/flags.hpp"
 
 namespace mesos {
@@ -55,7 +55,7 @@ public:
    *
    * @return The DockerImage that holds the Docker layers.
    */
-  virtual process::Future<DockerImage> get(const std::string& name) = 0;
+  virtual process::Future<DockerImage> get(const ImageName& name) = 0;
 
   /**
    * Recover all stored images

http://git-wip-us.apache.org/repos/asf/mesos/blob/2cc0dfb2/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 e1a311b..dd4f813 100644
--- a/src/tests/containerizer/provisioner_docker_tests.cpp
+++ b/src/tests/containerizer/provisioner_docker_tests.cpp
@@ -37,12 +37,15 @@
 
 #include <process/ssl/gtest.hpp>
 
+#include "slave/containerizer/provisioner/docker/metadata_manager.hpp"
 #include "slave/containerizer/provisioner/docker/registry_client.hpp"
+#include "slave/containerizer/provisioner/docker/store.hpp"
 #include "slave/containerizer/provisioner/docker/token_manager.hpp"
 
 #include "tests/mesos.hpp"
 #include "tests/utils.hpp"
 
+using std::list;
 using std::map;
 using std::string;
 using std::vector;
@@ -714,11 +717,12 @@ public:
         os::read(path::join(layersPath, "456", "rootfs", "temp")));
 
     // Verify the Docker Image provided.
-    EXPECT_EQ(dockerImage.imageName, "abc");
+    EXPECT_EQ(dockerImage.imageName.repository, "abc");
+    EXPECT_EQ(dockerImage.imageName.tag, "latest");
     list<string> expectedLayers;
     expectedLayers.push_back("123");
     expectedLayers.push_back("456");
-    EXPECT_EQ(dockerImage.layers, expectedLayers);
+    EXPECT_EQ(dockerImage.layerIds, expectedLayers);
   }
 
 protected:
@@ -727,7 +731,7 @@ protected:
     TemporaryDirectoryTest::SetUp();
 
     string imageDir = path::join(os::getcwd(), "images");
-    string image = path::join(imageDir, "abc");
+    string image = path::join(imageDir, "abc:latest");
     ASSERT_SOME(os::mkdir(imageDir));
     ASSERT_SOME(os::mkdir(image));
 
@@ -775,7 +779,7 @@ protected:
     ASSERT_SOME(os::rmdir(path::join(image, "456", "layer")));
 
     ASSERT_SOME(os::chdir(image));
-    ASSERT_SOME(os::tar(".", "../abc.tar"));
+    ASSERT_SOME(os::tar(".", "../abc:latest.tar"));
     ASSERT_SOME(os::chdir(cwd));
     ASSERT_SOME(os::rmdir(image));
   }
@@ -787,7 +791,7 @@ protected:
 TEST_F(DockerProvisionerLocalStoreTest, LocalStoreTestWithTar)
 {
   string imageDir = path::join(os::getcwd(), "images");
-  string image = path::join(imageDir, "abc");
+  string image = path::join(imageDir, "abc:latest");
   ASSERT_SOME(os::mkdir(imageDir));
   ASSERT_SOME(os::mkdir(image));
 
@@ -804,7 +808,10 @@ TEST_F(DockerProvisionerLocalStoreTest, LocalStoreTestWithTar)
 
   string sandbox = path::join(os::getcwd(), "sandbox");
   ASSERT_SOME(os::mkdir(sandbox));
-  Future<DockerImage> dockerImage = store.get()->get("abc");
+  Try<ImageName> imageName = ImageName::create("abc");
+  ASSERT_SOME(imageName);
+
+  Future<DockerImage> dockerImage = store.get()->get(imageName.get());
   AWAIT_READY(dockerImage);
 
   verifyLocalDockerImage(flags, dockerImage.get());
@@ -812,7 +819,7 @@ TEST_F(DockerProvisionerLocalStoreTest, LocalStoreTestWithTar)
 
 // This tests the ability of the reference store to recover the images it has
 // already stored on disk when it is initialized.
-TEST_F(DockerProvisionerLocalStoreTest, ReferenceStoreInitialization)
+TEST_F(DockerProvisionerLocalStoreTest, MetadataManagerInitialization)
 {
   slave::Flags flags;
   flags.docker_store = "local";
@@ -827,7 +834,9 @@ TEST_F(DockerProvisionerLocalStoreTest, ReferenceStoreInitialization)
 
   string sandbox = path::join(os::getcwd(), "sandbox");
   ASSERT_SOME(os::mkdir(sandbox));
-  Future<DockerImage> dockerImage = store.get()->get("abc");
+  Try<ImageName> imageName = ImageName::create("abc");
+  ASSERT_SOME(imageName);
+  Future<DockerImage> dockerImage = store.get()->get(imageName.get());
   AWAIT_READY(dockerImage);
 
   // Store is deleted and recreated. Reference Store is initialized upon
@@ -836,7 +845,7 @@ TEST_F(DockerProvisionerLocalStoreTest, ReferenceStoreInitialization)
   store = Store::create(flags, &fetcher);
   ASSERT_SOME(store);
 
-  dockerImage = store.get()->get("abc");
+  dockerImage = store.get()->get(imageName.get());
   AWAIT_READY(dockerImage);
   verifyLocalDockerImage(flags, dockerImage.get());
 }