You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by tn...@apache.org on 2015/09/25 18:33:03 UTC
[11/17] mesos git commit: Restructure Docker provisioner and protos.
Restructure Docker provisioner and protos.
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/4dedbf4a
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/4dedbf4a
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/4dedbf4a
Branch: refs/heads/master
Commit: 4dedbf4aec2183af94ee083d2f6fb22057f9dfc8
Parents: 2cc0dfb
Author: Timothy Chen <tn...@gmail.com>
Authored: Fri Sep 11 00:19:42 2015 -0700
Committer: Timothy Chen <tn...@apache.org>
Committed: Fri Sep 25 09:02:05 2015 -0700
----------------------------------------------------------------------
src/Makefile.am | 9 +-
src/messages/docker_provisioner.hpp | 24 -
src/messages/docker_provisioner.proto | 35 --
src/slave/containerizer/provisioner.cpp | 1 +
.../provisioner/docker/local_store.cpp | 460 ++++++++++++++++++
.../provisioner/docker/local_store.hpp | 64 +++
.../provisioner/docker/message.hpp | 71 +++
.../provisioner/docker/message.proto | 43 ++
.../provisioner/docker/metadata_manager.cpp | 251 ++++++++++
.../provisioner/docker/metadata_manager.hpp | 106 +++++
.../containerizer/provisioner/docker/paths.cpp | 104 ++++
.../containerizer/provisioner/docker/paths.hpp | 86 ++++
.../containerizer/provisioner/docker/store.cpp | 47 ++
.../containerizer/provisioner/docker/store.hpp | 58 +++
.../containerizer/provisioner/provisioner.hpp | 2 +-
src/slave/containerizer/provisioners/docker.cpp | 425 -----------------
src/slave/containerizer/provisioners/docker.hpp | 166 -------
.../provisioners/docker/local_store.cpp | 471 -------------------
.../provisioners/docker/local_store.hpp | 66 ---
.../provisioners/docker/metadata_manager.cpp | 254 ----------
.../provisioners/docker/metadata_manager.hpp | 104 ----
.../containerizer/provisioners/docker/paths.cpp | 104 ----
.../containerizer/provisioners/docker/paths.hpp | 86 ----
.../containerizer/provisioners/docker/store.hpp | 76 ---
src/slave/flags.cpp | 12 +-
src/slave/flags.hpp | 7 +-
.../containerizer/provisioner_docker_tests.cpp | 59 ++-
27 files changed, 1336 insertions(+), 1855 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index a2a64dc..818d62d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -250,8 +250,9 @@ BUILT_SOURCES += $(STATE_PROTOS)
CLEANFILES += $(STATE_PROTOS)
DOCKER_PROVISIONER_PROTOS = \
- messages/docker_provisioner.pb.cc \
- messages/docker_provisioner.pb.h
+ slave/containerizer/provisioner/docker/message.pb.cc \
+ slave/containerizer/provisioner/docker/message.pb.h
+
BUILT_SOURCES += $(DOCKER_PROVISIONER_PROTOS)
CLEANFILES += $(DOCKER_PROVISIONER_PROTOS)
@@ -529,11 +530,11 @@ libmesos_no_3rdparty_la_SOURCES = \
slave/containerizer/provisioner/appc/store.cpp \
slave/containerizer/provisioner/backend.cpp \
slave/containerizer/provisioner/backends/copy.cpp \
- slave/containerizer/provisioner/docker.cpp \
slave/containerizer/provisioner/docker/local_store.cpp \
slave/containerizer/provisioner/docker/metadata_manager.cpp \
slave/containerizer/provisioner/docker/paths.cpp \
slave/containerizer/provisioner/docker/registry_client.cpp \
+ slave/containerizer/provisioner/docker/store.cpp \
slave/containerizer/provisioner/docker/token_manager.cpp \
slave/resource_estimators/noop.cpp \
usage/usage.cpp \
@@ -831,7 +832,7 @@ libmesos_no_3rdparty_la_SOURCES += \
slave/containerizer/provisioner/backends/copy.hpp \
slave/containerizer/provisioner/docker.hpp \
slave/containerizer/provisioner/docker/local_store.hpp \
- slave/containerizer/provisioners/docker/metadata_manager.hpp \
+ slave/containerizer/provisioner/docker/metadata_manager.hpp \
slave/containerizer/provisioner/docker/paths.hpp \
slave/containerizer/provisioner/docker/registry_client.hpp \
slave/containerizer/provisioner/docker/store.hpp \
http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/messages/docker_provisioner.hpp
----------------------------------------------------------------------
diff --git a/src/messages/docker_provisioner.hpp b/src/messages/docker_provisioner.hpp
deleted file mode 100644
index 684bd46..0000000
--- a/src/messages/docker_provisioner.hpp
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MESSAGES_DOCKER_PROVISIONER_HPP__
-#define __MESSAGES_DOCKER_PROVISIONER_HPP__
-
-#include "messages/docker_provisioner.pb.h"
-
-#endif // __MESSAGES_DOCKER_PROVISIONER_HPP__
http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/messages/docker_provisioner.proto
----------------------------------------------------------------------
diff --git a/src/messages/docker_provisioner.proto b/src/messages/docker_provisioner.proto
deleted file mode 100644
index 9de6707..0000000
--- a/src/messages/docker_provisioner.proto
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import "mesos/mesos.proto";
-
-package mesos.internal;
-
-/**
- * A Docker Image name and the layer ids of the layers that comprise the image.
- * The layerIds are ordered, with the root layer id (no parent layer id) first
- * and the leaf layer id last.
- */
-message DockerProvisionerImages {
- message Image {
- required string name = 1;
- repeated string layer_ids = 2;
- }
-
- repeated Image images = 1;
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioner.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner.cpp b/src/slave/containerizer/provisioner.cpp
index 1ff3f10..6a0faac 100644
--- a/src/slave/containerizer/provisioner.cpp
+++ b/src/slave/containerizer/provisioner.cpp
@@ -23,6 +23,7 @@
#include "slave/containerizer/provisioner.hpp"
#include "slave/containerizer/provisioners/appc/provisioner.hpp"
+
#include "slave/containerizer/provisioners/docker/provisioner.hpp"
using namespace process;
http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioner/docker/local_store.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/docker/local_store.cpp b/src/slave/containerizer/provisioner/docker/local_store.cpp
new file mode 100644
index 0000000..6a73dbb
--- /dev/null
+++ b/src/slave/containerizer/provisioner/docker/local_store.cpp
@@ -0,0 +1,460 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <list>
+#include <vector>
+
+#include <glog/logging.h>
+
+#include <stout/json.hpp>
+#include <stout/os.hpp>
+#include <stout/result.hpp>
+
+#include <process/collect.hpp>
+#include <process/defer.hpp>
+#include <process/dispatch.hpp>
+
+#include "common/status_utils.hpp"
+
+#include "slave/containerizer/provisioner/docker/local_store.hpp"
+#include "slave/containerizer/provisioner/docker/metadata_manager.hpp"
+#include "slave/containerizer/provisioner/docker/paths.hpp"
+#include "slave/containerizer/provisioner/docker/store.hpp"
+
+#include "slave/flags.hpp"
+
+using namespace process;
+
+using std::list;
+using std::string;
+using std::vector;
+
+namespace mesos {
+namespace internal {
+namespace slave {
+namespace docker {
+
+class LocalStoreProcess : public process::Process<LocalStoreProcess>
+{
+public:
+ LocalStoreProcess(
+ const Flags& _flags,
+ Owned<MetadataManager> _metadataManager)
+ : flags(_flags), metadataManager(_metadataManager) {}
+
+ ~LocalStoreProcess() {}
+
+ static Try<process::Owned<LocalStoreProcess>> create(const Flags& flags);
+
+ process::Future<vector<string>> get(const Image& image);
+
+ process::Future<Nothing> recover();
+
+private:
+ process::Future<DockerImage> _get(
+ const DockerImage::Name& name,
+ const Option<DockerImage>& image);
+
+ process::Future<Nothing> untarImage(
+ const std::string& tarPath,
+ const std::string& staging);
+
+ process::Future<DockerImage> putImage(
+ const DockerImage::Name& name,
+ const std::string& staging);
+
+ Result<std::string> getParentId(
+ const std::string& staging,
+ const std::string& layerId);
+
+ process::Future<Nothing> putLayers(
+ const std::string& staging,
+ const std::list<std::string>& layerIds);
+
+ process::Future<Nothing> putLayer(
+ const std::string& staging,
+ const std::string& id);
+
+ process::Future<Nothing> moveLayer(
+ const std::string& staging,
+ const std::string& id);
+
+ const Flags flags;
+ process::Owned<MetadataManager> metadataManager;
+};
+
+
+Try<Owned<slave::Store>> LocalStore::create(const Flags& flags)
+{
+ if (!os::exists(flags.docker_store_dir)) {
+ Try<Nothing> mkdir = os::mkdir(flags.docker_store_dir);
+ if (mkdir.isError()) {
+ return Error("Failed to create Docker store directory: " + mkdir.error());
+ }
+ }
+
+ if (!os::exists(paths::getStagingDir(flags.docker_store_dir))) {
+ Try<Nothing> mkdir =
+ os::mkdir(paths::getStagingDir(flags.docker_store_dir));
+ if (mkdir.isError()) {
+ return Error("Failed to create Docker store staging directory: " +
+ mkdir.error());
+ }
+ }
+
+ Try<Owned<MetadataManager>> metadataManager = MetadataManager::create(flags);
+ if (metadataManager.isError()) {
+ return Error(metadataManager.error());
+ }
+
+ Owned<LocalStoreProcess> process(
+ new LocalStoreProcess(flags, metadataManager.get()));
+
+ return Owned<slave::Store>(new LocalStore(process));
+}
+
+
+LocalStore::LocalStore(Owned<LocalStoreProcess> _process) : process(_process)
+{
+ process::spawn(CHECK_NOTNULL(process.get()));
+}
+
+
+LocalStore::~LocalStore()
+{
+ process::terminate(process.get());
+ process::wait(process.get());
+}
+
+
+Future<vector<string>> LocalStore::get(const Image& image)
+{
+ return dispatch(process.get(), &LocalStoreProcess::get, image);
+}
+
+
+Future<Nothing> LocalStore::recover()
+{
+ return dispatch(process.get(), &LocalStoreProcess::recover);
+}
+
+
+Future<vector<string>> LocalStoreProcess::get(const Image& image)
+{
+ CHECK_EQ(image.type(), Image::DOCKER);
+
+ Try<DockerImage::Name> dockerName = parseName(image.docker().name());
+ if (dockerName.isError()) {
+ return Failure("Unable to parse docker image name: " + dockerName.error());
+ }
+
+ return metadataManager->get(dockerName.get())
+ .then(defer(self(), &Self::_get, dockerName.get(), lambda::_1))
+ .then([](const DockerImage& dockerImage) {
+ vector<string> layers;
+ foreach (const string& layer, dockerImage.layer_ids()) {
+ layers.push_back(layer);
+ }
+
+ return layers;
+ });
+}
+
+
+Future<DockerImage> LocalStoreProcess::_get(
+ const DockerImage::Name& name,
+ const Option<DockerImage>& image)
+{
+ if (image.isSome()) {
+ return image.get();
+ }
+
+ string tarPath = paths::getLocalImageTarPath(
+ flags.docker_store_discovery_local_dir,
+ stringify(name));
+
+ if (!os::exists(tarPath)) {
+ VLOG(1) << "Unable to find image in local store with path: " << tarPath;
+ return Failure("No Docker image tar archive found");
+ }
+
+ // Create a temporary staging directory.
+ Try<string> staging =
+ os::mkdtemp(paths::getTempStaging(flags.docker_store_dir));
+ if (staging.isError()) {
+ return Failure("Failed to create a staging directory");
+ }
+
+ return untarImage(tarPath, staging.get())
+ .then(defer(self(), &Self::putImage, name, staging.get()));
+}
+
+
+Future<Nothing> LocalStoreProcess::recover()
+{
+ return metadataManager->recover();
+}
+
+
+Future<Nothing> LocalStoreProcess::untarImage(
+ const string& tarPath,
+ const string& staging)
+{
+ VLOG(1) << "Untarring image at: " << tarPath;
+
+ // Untar store_discovery_local_dir/name.tar into staging/.
+ vector<string> argv = {
+ "tar",
+ "-C",
+ staging,
+ "-x",
+ "-f",
+ tarPath
+ };
+
+ Try<Subprocess> s = subprocess(
+ "tar",
+ argv,
+ Subprocess::PATH("/dev/null"),
+ Subprocess::PATH("/dev/null"),
+ Subprocess::PATH("/dev/null"));
+
+ if (s.isError()) {
+ return Failure("Failed to create tar subprocess: " + s.error());
+ }
+
+ return s.get().status()
+ .then([=](const Option<int>& status) -> Future<Nothing> {
+ if (status.isNone()) {
+ return Failure("Failed to reap status for tar subprocess in " +
+ tarPath);
+ }
+ if (!WIFEXITED(status.get()) || WEXITSTATUS(status.get()) != 0) {
+ return Failure("Untar image failed with exit code: " +
+ WSTRINGIFY(status.get()));
+ }
+
+ return Nothing();
+ });
+}
+
+
+Future<DockerImage> LocalStoreProcess::putImage(
+ const DockerImage::Name& name,
+ const string& staging)
+{
+ Try<string> value = os::read(paths::getLocalImageRepositoriesPath(staging));
+ if (value.isError()) {
+ return Failure("Failed to read repository JSON: " + value.error());
+ }
+
+ Try<JSON::Object> json = JSON::parse<JSON::Object>(value.get());
+ if (json.isError()) {
+ return Failure("Failed to parse JSON: " + json.error());
+ }
+
+ Result<JSON::Object> repositoryValue =
+ json.get().find<JSON::Object>(name.repository());
+ if (repositoryValue.isError()) {
+ return Failure("Failed to find repository: " + repositoryValue.error());
+ } else if (repositoryValue.isNone()) {
+ return Failure("Repository '" + name.repository() + "' is not found");
+ }
+
+ JSON::Object repositoryJson = repositoryValue.get();
+
+ // We don't use JSON find here because a tag might contain a '.'.
+ std::map<string, JSON::Value>::const_iterator entry =
+ repositoryJson.values.find(name.tag());
+ if (entry == repositoryJson.values.end()) {
+ return Failure("Tag '" + name.tag() + "' is not found");
+ } else if (!entry->second.is<JSON::String>()) {
+ return Failure("Tag JSON value expected to be JSON::String");
+ }
+
+ string layerId = entry->second.as<JSON::String>().value;
+
+ Try<string> manifest =
+ os::read(paths::getLocalImageLayerManifestPath(staging, layerId));
+ if (manifest.isError()) {
+ return Failure("Failed to read manifest: " + manifest.error());
+ }
+
+ Try<JSON::Object> manifestJson = JSON::parse<JSON::Object>(manifest.get());
+ if (manifestJson.isError()) {
+ return Failure("Failed to parse manifest: " + manifestJson.error());
+ }
+
+ list<string> layerIds;
+ layerIds.push_back(layerId);
+ Result<string> parentId = getParentId(staging, layerId);
+ while(parentId.isSome()) {
+ layerIds.push_front(parentId.get());
+ parentId = getParentId(staging, parentId.get());
+ }
+ if (parentId.isError()) {
+ return Failure("Failed to obtain parent layer id: " + parentId.error());
+ }
+
+ return putLayers(staging, layerIds)
+ .then([=]() -> Future<DockerImage> {
+ return metadataManager->put(name, layerIds);
+ });
+}
+
+
+Result<string> LocalStoreProcess::getParentId(
+ const string& staging,
+ const string& layerId)
+{
+ Try<string> manifest =
+ os::read(paths::getLocalImageLayerManifestPath(staging, layerId));
+ if (manifest.isError()) {
+ return Error("Failed to read manifest: " + manifest.error());
+ }
+
+ Try<JSON::Object> json = JSON::parse<JSON::Object>(manifest.get());
+ if (json.isError()) {
+ return Error("Failed to parse manifest: " + json.error());
+ }
+
+ Result<JSON::String> parentId = json.get().find<JSON::String>("parent");
+ if (parentId.isNone() || (parentId.isSome() && parentId.get() == "")) {
+ return None();
+ } else if (parentId.isError()) {
+ return Error("Failed to read parent of layer: " + parentId.error());
+ }
+ return parentId.get().value;
+}
+
+
+Future<Nothing> LocalStoreProcess::putLayers(
+ const string& staging,
+ const list<string>& layerIds)
+{
+ list<Future<Nothing>> futures{ Nothing() };
+ foreach (const string& layer, layerIds) {
+ futures.push_back(
+ futures.back().then(
+ defer(self(), &Self::putLayer, staging, layer)));
+ }
+
+ return collect(futures)
+ .then([]() -> Future<Nothing> { return Nothing(); });
+}
+
+
+Future<Nothing> LocalStoreProcess::putLayer(
+ const string& staging,
+ const string& id)
+{
+ // We untar the layer from source into a staging directory, then
+ // move the layer into store. We do this instead of untarring
+ // directly to store to make sure we don't end up with partially
+ // untarred layer rootfs.
+
+ // Check if image layer rootfs is already in store.
+ if (os::exists(paths::getImageLayerRootfsPath(flags.docker_store_dir, id))) {
+ VLOG(1) << "Image layer '" << id << "' rootfs already in store. "
+ << "Skipping put.";
+ return Nothing();
+ }
+
+ const string imageLayerPath =
+ paths::getImageLayerPath(flags.docker_store_dir, id);
+ if (!os::exists(imageLayerPath)) {
+ Try<Nothing> mkdir = os::mkdir(imageLayerPath);
+ if (mkdir.isError()) {
+ return Failure("Failed to create Image layer directory '" +
+ imageLayerPath + "': " + mkdir.error());
+ }
+ }
+
+ // Image layer has been untarred but is not present in the store directory.
+ string localRootfsPath = paths::getLocalImageLayerRootfsPath(staging, id);
+ if (os::exists(localRootfsPath)) {
+ LOG(WARNING) << "Image layer '" << id << "' rootfs present at but not in "
+ << "store directory '" << localRootfsPath << "'. Removing "
+ << "staged rootfs and untarring layer again.";
+ Try<Nothing> rmdir = os::rmdir(localRootfsPath);
+ if (rmdir.isError()) {
+ return Failure("Failed to remove incomplete staged rootfs for layer '" +
+ id + "': " + rmdir.error());
+ }
+ }
+
+ Try<Nothing> mkdir = os::mkdir(localRootfsPath);
+ if (mkdir.isError()) {
+ return Failure("Failed to create rootfs path '" + localRootfsPath + "': " +
+ mkdir.error());
+ }
+ // Untar staging/id/layer.tar into staging/id/rootfs.
+ vector<string> argv = {
+ "tar",
+ "-C",
+ localRootfsPath,
+ "-x",
+ "-f",
+ paths::getLocalImageLayerTarPath(staging, id)
+ };
+
+ Try<Subprocess> s = subprocess(
+ "tar",
+ argv,
+ Subprocess::PATH("/dev/null"),
+ Subprocess::PATH("/dev/null"),
+ Subprocess::PATH("/dev/null"));
+ if (s.isError()) {
+ return Failure("Failed to create tar subprocess: " + s.error());
+ }
+
+ return s.get().status()
+ .then([=](const Option<int>& status) -> Future<Nothing> {
+ if (status.isNone()) {
+ return Failure("Failed to reap subprocess to untar image");
+ } else if (!WIFEXITED(status.get()) || WEXITSTATUS(status.get()) != 0) {
+ return Failure("Untar failed with exit code: " +
+ WSTRINGIFY(status.get()));
+ }
+
+ return moveLayer(staging, id);
+ });
+}
+
+
+Future<Nothing> LocalStoreProcess::moveLayer(
+ const string& staging,
+ const string& id)
+{
+ Try<Nothing> status = os::rename(
+ paths::getLocalImageLayerRootfsPath(staging, id),
+ paths::getImageLayerRootfsPath(flags.docker_store_dir, id));
+
+ if (status.isError()) {
+ return Failure("Failed to move layer to store directory: "
+ + status.error());
+ }
+
+ return Nothing();
+}
+
+
+} // namespace docker {
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioner/docker/local_store.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/docker/local_store.hpp b/src/slave/containerizer/provisioner/docker/local_store.hpp
new file mode 100644
index 0000000..5f6152b
--- /dev/null
+++ b/src/slave/containerizer/provisioner/docker/local_store.hpp
@@ -0,0 +1,64 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MESOS_DOCKER_LOCAL_STORE_HPP__
+#define __MESOS_DOCKER_LOCAL_STORE_HPP__
+
+#include "slave/containerizer/provisioner/store.hpp"
+
+namespace mesos {
+namespace internal {
+namespace slave {
+namespace docker {
+
+// Forward declaration.
+class LocalStoreProcess;
+
+
+/**
+ * LocalStore assumes Docker images are stored in a local directory
+ * (configured with flags.docker_discovery_local_dir), with all the
+ * images saved as tar with the name as the image name with tag (e.g:
+ * ubuntu:14.04.tar).
+ */
+class LocalStore : public slave::Store
+{
+public:
+ static Try<process::Owned<Store>> create(const Flags& flags);
+
+ virtual ~LocalStore();
+
+ virtual process::Future<Nothing> recover();
+
+ virtual process::Future<std::vector<std::string>> get(const Image& image);
+
+private:
+ explicit LocalStore(process::Owned<LocalStoreProcess> _process);
+
+ LocalStore& operator=(const LocalStore&) = delete; // Not assignable.
+ LocalStore(const LocalStore&) = delete; // Not copyable.
+
+ process::Owned<LocalStoreProcess> process;
+};
+
+} // namespace docker {
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
+
+#endif // __MESOS_DOCKER_LOCAL_STORE_HPP__
http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioner/docker/message.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/docker/message.hpp b/src/slave/containerizer/provisioner/docker/message.hpp
new file mode 100644
index 0000000..c1596df
--- /dev/null
+++ b/src/slave/containerizer/provisioner/docker/message.hpp
@@ -0,0 +1,71 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MESSAGES_DOCKER_PROVISIONER_HPP__
+#define __MESSAGES_DOCKER_PROVISIONER_HPP__
+
+#include <stout/strings.hpp>
+
+// ONLY USEFUL AFTER RUNNING PROTOC.
+#include "slave/containerizer/provisioner/docker/message.pb.h"
+
+namespace mesos {
+namespace internal {
+namespace slave {
+namespace docker {
+
+inline DockerImage::Name parseName(const std::string& value)
+{
+ DockerImage::Name imageName;
+ Option<std::string> registry = None();
+ std::vector<std::string> components = strings::split(value, "/");
+ if (components.size() > 2) {
+ imageName.set_registry(value.substr(0, value.find_last_of("/")));
+ }
+
+ std::size_t found = components.back().find_last_of(':');
+ if (found == std::string::npos) {
+ imageName.set_repository(components.back());
+ imageName.set_tag("latest");
+ } else {
+ imageName.set_repository(components.back().substr(0, found));
+ imageName.set_tag(components.back().substr(found + 1));
+ }
+
+ return imageName;
+}
+
+
+inline std::ostream& operator<<(
+ std::ostream& stream,
+ const DockerImage::Name& name)
+{
+ if (name.has_registry()) {
+ return stream << name.registry() << "/" << name.repository() << ":"
+ << name.tag();
+ }
+
+ return stream << name.repository() << ":" << name.tag();
+}
+
+} // namespace docker {
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
+
+#endif // __MESSAGES_DOCKER_PROVISIONER_HPP__
http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioner/docker/message.proto
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/docker/message.proto b/src/slave/containerizer/provisioner/docker/message.proto
new file mode 100644
index 0000000..d771968
--- /dev/null
+++ b/src/slave/containerizer/provisioner/docker/message.proto
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import "mesos/mesos.proto";
+
+package mesos.internal.slave.docker;
+
+/**
+ * A Docker Image name and the layer ids of the layers that comprise the image.
+ * The layerIds are ordered, with the root layer id (no parent layer id) first
+ * and the leaf layer id last.
+ */
+message DockerImage {
+ message Name {
+ optional string registry = 1;
+ required string repository = 2;
+ required string tag = 3;
+ }
+
+ required Name name = 1;
+
+ // The order of the layers represents the dependency between layers.
+ repeated string layer_ids = 2;
+}
+
+message DockerImages {
+ repeated DockerImage images = 1;
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioner/docker/metadata_manager.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/docker/metadata_manager.cpp b/src/slave/containerizer/provisioner/docker/metadata_manager.cpp
new file mode 100644
index 0000000..197931c
--- /dev/null
+++ b/src/slave/containerizer/provisioner/docker/metadata_manager.cpp
@@ -0,0 +1,251 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vector>
+
+#include <glog/logging.h>
+
+#include <stout/foreach.hpp>
+#include <stout/hashset.hpp>
+#include <stout/os.hpp>
+#include <stout/protobuf.hpp>
+
+#include <process/defer.hpp>
+#include <process/dispatch.hpp>
+#include <process/owned.hpp>
+
+#include "common/status_utils.hpp"
+
+#include "slave/containerizer/provisioner/docker/paths.hpp"
+#include "slave/containerizer/provisioner/docker/message.hpp"
+#include "slave/containerizer/provisioner/docker/metadata_manager.hpp"
+
+#include "slave/state.hpp"
+
+using namespace process;
+
+using std::list;
+using std::string;
+using std::vector;
+
+namespace mesos {
+namespace internal {
+namespace slave {
+namespace docker {
+
+
+class MetadataManagerProcess : public process::Process<MetadataManagerProcess>
+{
+public:
+ ~MetadataManagerProcess() {}
+
+ static Try<process::Owned<MetadataManagerProcess>> create(
+ const Flags& flags);
+
+ Future<DockerImage> put(
+ const DockerImage::Name& name,
+ const std::list<std::string>& layerIds);
+
+ Future<Option<DockerImage>> get(const DockerImage::Name& name);
+
+ Future<Nothing> recover();
+
+ // TODO(chenlily): Implement removal of unreferenced images.
+
+private:
+ MetadataManagerProcess(const Flags& flags);
+
+ // Write out metadata manager state to persistent store.
+ Try<Nothing> persist();
+
+ const Flags flags;
+
+ // This is a lookup table for images that are stored in memory. It is keyed
+ // by the name of the DockerImage.
+ // For example, "ubuntu:14.04" -> ubuntu14:04 DockerImage.
+ hashmap<std::string, DockerImage> storedImages;
+};
+
+
+Try<Owned<MetadataManager>> MetadataManager::create(const Flags& flags)
+{
+ Try<Owned<MetadataManagerProcess>> process =
+ MetadataManagerProcess::create(flags);
+ if (process.isError()) {
+ return Error("Failed to create Metadata Manager: " + process.error());
+ }
+ return Owned<MetadataManager>(new MetadataManager(process.get()));
+}
+
+
+MetadataManager::MetadataManager(Owned<MetadataManagerProcess> process)
+ : process(process)
+{
+ process::spawn(CHECK_NOTNULL(process.get()));
+}
+
+
+MetadataManager::~MetadataManager()
+{
+ process::terminate(process.get());
+ process::wait(process.get());
+}
+
+
+Future<Nothing> MetadataManager::recover()
+{
+ return process::dispatch(process.get(), &MetadataManagerProcess::recover);
+}
+
+
+Future<DockerImage> MetadataManager::put(
+ const DockerImage::Name& name,
+ const list<string>& layerIds)
+{
+ return dispatch(
+ process.get(), &MetadataManagerProcess::put, name, layerIds);
+}
+
+
+Future<Option<DockerImage>> MetadataManager::get(const DockerImage::Name& name)
+{
+ return dispatch(process.get(), &MetadataManagerProcess::get, name);
+}
+
+
+MetadataManagerProcess::MetadataManagerProcess(const Flags& flags)
+ : flags(flags) {}
+
+
+Try<Owned<MetadataManagerProcess>> MetadataManagerProcess::create(
+ const Flags& flags)
+{
+ Owned<MetadataManagerProcess> metadataManager =
+ Owned<MetadataManagerProcess>(new MetadataManagerProcess(flags));
+
+ return metadataManager;
+}
+
+
+Future<DockerImage> MetadataManagerProcess::put(
+ const DockerImage::Name& name,
+ const list<string>& layerIds)
+{
+ const string imageName = stringify(name);
+
+ DockerImage dockerImage;
+ dockerImage.mutable_name()->CopyFrom(name);
+ foreach (const string& layerId, layerIds) {
+ dockerImage.add_layer_ids(layerId);
+ }
+
+ storedImages[imageName] = dockerImage;
+
+ Try<Nothing> status = persist();
+ if (status.isError()) {
+ return Failure("Failed to save state of Docker images" + status.error());
+ }
+
+ return dockerImage;
+}
+
+
+Future<Option<DockerImage>> MetadataManagerProcess::get(
+ const DockerImage::Name& name)
+{
+ const string imageName = stringify(name);
+
+ if (!storedImages.contains(imageName)) {
+ return None();
+ }
+
+ return storedImages[imageName];
+}
+
+
+Try<Nothing> MetadataManagerProcess::persist()
+{
+ DockerImages images;
+
+ foreachvalue (const DockerImage& image, storedImages) {
+ images.add_images()->CopyFrom(image);
+ }
+
+ Try<Nothing> status = mesos::internal::slave::state::checkpoint(
+ paths::getStoredImagesPath(flags.docker_store_dir), images);
+ if (status.isError()) {
+ return Error("Failed to perform checkpoint: " + status.error());
+ }
+
+ return Nothing();
+}
+
+
+Future<Nothing> MetadataManagerProcess::recover()
+{
+ string storedImagesPath = paths::getStoredImagesPath(flags.docker_store_dir);
+
+ storedImages.clear();
+ if (!os::exists(storedImagesPath)) {
+ LOG(INFO) << "No images to load from disk. Docker provisioner image "
+ << "storage path: " << storedImagesPath << " does not exist.";
+ return Nothing();
+ }
+
+ Result<DockerImages> images =
+ ::protobuf::read<DockerImages>(storedImagesPath);
+ if (images.isError()) {
+ return Failure("Failed to read protobuf for Docker provisioner image: " +
+ images.error());
+ }
+
+ foreach (const DockerImage image, images.get().images()) {
+ vector<string> missingLayerIds;
+ foreach (const string layerId, image.layer_ids()) {
+ const string rootfsPath =
+ paths::getImageLayerRootfsPath(flags.docker_store_dir, layerId);
+
+ if (!os::exists(rootfsPath)) {
+ missingLayerIds.push_back(layerId);
+ }
+ }
+
+ if (!missingLayerIds.empty()) {
+ LOG(WARNING) << "Skipped loading image: " << stringify(image.name())
+ << " due to missing layers: " << stringify(missingLayerIds);
+ continue;
+ }
+
+ const string imageName = stringify(image.name());
+ if (storedImages.contains(imageName)) {
+ LOG(WARNING) << "Found duplicate image in recovery for image name '" << imageName
+ << "'";
+ } else {
+ storedImages[imageName] = image;
+ }
+ }
+
+ LOG(INFO) << "Loaded " << storedImages.size() << " Docker images.";
+
+ return Nothing();
+}
+
+} // namespace docker {
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioner/docker/metadata_manager.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/docker/metadata_manager.hpp b/src/slave/containerizer/provisioner/docker/metadata_manager.hpp
new file mode 100644
index 0000000..647a478
--- /dev/null
+++ b/src/slave/containerizer/provisioner/docker/metadata_manager.hpp
@@ -0,0 +1,106 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MESOS_DOCKER_METADATA_MANAGER_HPP__
+#define __MESOS_DOCKER_METADATA_MANAGER_HPP__
+
+#include <list>
+#include <string>
+
+#include <stout/hashmap.hpp>
+#include <stout/json.hpp>
+#include <stout/option.hpp>
+#include <stout/protobuf.hpp>
+#include <stout/try.hpp>
+
+#include <process/future.hpp>
+#include <process/owned.hpp>
+#include <process/process.hpp>
+
+#include "slave/containerizer/provisioner/provisioner.hpp"
+
+#include "slave/containerizer/provisioner/docker/message.hpp"
+
+#include "slave/flags.hpp"
+
+namespace mesos {
+namespace internal {
+namespace slave {
+namespace docker {
+
+// Forward Declaration.
+class MetadataManagerProcess;
+
+/**
+ * The MetadataManager tracks the Docker images cached by the
+ * provisioner that are stored on disk. It keeps track of the layers
+ * that Docker images are composed of and recovers DockerImage objects
+ * upon initialization by checking for dependent layers stored on disk.
+ * Currently, image layers are stored indefinitely, with no garbage
+ * collection of unreferenced image layers.
+ */
+class MetadataManager
+{
+public:
+ static Try<process::Owned<MetadataManager>> create(const Flags& flags);
+
+ ~MetadataManager();
+
+ /**
+ * Create a Image, put it in metadata manager and persist the reference
+ * store state to disk.
+ *
+ * @param name the name of the Docker image to place in the reference
+ * store.
+ * @param layerIds the list of layer ids that comprise the Docker image in
+ * order where the root layer's id (no parent layer) is first
+ * and the leaf layer's id is last.
+ */
+ process::Future<DockerImage> put(
+ const DockerImage::Name& name,
+ const std::list<std::string>& layerIds);
+
+ /**
+ * Retrieve DockerImage based on image name if it is among the DockerImages
+ * stored in memory.
+ *
+ * @param name the name of the Docker image to retrieve
+ */
+ process::Future<Option<DockerImage>> get(const DockerImage::Name& name);
+
+ /**
+ * Recover all stored DockerImage and its layer references.
+ */
+ process::Future<Nothing> recover();
+
+private:
+ explicit MetadataManager(process::Owned<MetadataManagerProcess> process);
+
+ MetadataManager(const MetadataManager&); // Not copyable.
+ MetadataManager& operator=(const MetadataManager&); // Not assignable.
+
+ process::Owned<MetadataManagerProcess> process;
+};
+
+
+} // namespace docker {
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
+
+#endif // __MESOS_DOCKER_METADATA_MANAGER_HPP__
http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioner/docker/paths.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/docker/paths.cpp b/src/slave/containerizer/provisioner/docker/paths.cpp
new file mode 100644
index 0000000..81a2176
--- /dev/null
+++ b/src/slave/containerizer/provisioner/docker/paths.cpp
@@ -0,0 +1,104 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "slave/containerizer/provisioner/docker/paths.hpp"
+
+#include <stout/path.hpp>
+
+using std::string;
+
+namespace mesos {
+namespace internal {
+namespace slave {
+namespace docker {
+namespace paths {
+
+string getStagingDir(const string& storeDir)
+{
+ return path::join(storeDir, "staging");
+}
+
+string getTempStaging(const string& storeDir)
+{
+ return path::join(getStagingDir(storeDir), "XXXXXX");
+}
+
+string getLocalImageTarPath(
+ const string& discoveryDir,
+ const string& name)
+{
+ return path::join(discoveryDir, name + ".tar");
+}
+
+string getLocalImageRepositoriesPath(const string& staging)
+{
+ return path::join(staging, "repositories");
+}
+
+std::string getLocalImageLayerPath(
+ const string& staging,
+ const string& layerId)
+{
+ return path::join(staging, layerId);
+}
+
+string getLocalImageLayerManifestPath(
+ const string& staging,
+ const string& layerId)
+{
+ return path::join(getLocalImageLayerPath(staging, layerId), "json");
+}
+
+string getLocalImageLayerTarPath(
+ const string& staging,
+ const string& layerId)
+{
+ return path::join(getLocalImageLayerPath(staging, layerId), "layer.tar");
+}
+
+string getLocalImageLayerRootfsPath(
+ const string& staging,
+ const string& layerId)
+{
+ return path::join(getLocalImageLayerPath(staging, layerId), "rootfs");
+}
+
+string getImageLayerPath(
+ const string& storeDir,
+ const string& layerId)
+{
+ return path::join(storeDir, "layers", layerId);
+}
+
+string getImageLayerRootfsPath(
+ const string& storeDir,
+ const string& layerId)
+{
+ return path::join(getImageLayerPath(storeDir, layerId), "rootfs");
+}
+
+string getStoredImagesPath(const string& storeDir)
+{
+ return path::join(storeDir, "storedImages");
+}
+
+} // namespace paths {
+} // namespace docker {
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioner/docker/paths.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/docker/paths.hpp b/src/slave/containerizer/provisioner/docker/paths.hpp
new file mode 100644
index 0000000..02f129f
--- /dev/null
+++ b/src/slave/containerizer/provisioner/docker/paths.hpp
@@ -0,0 +1,86 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MESOS_DOCKER_PATHS_HPP__
+#define __MESOS_DOCKER_PATHS_HPP__
+
+#include <list>
+#include <string>
+
+#include <mesos/mesos.hpp>
+
+namespace mesos {
+namespace internal {
+namespace slave {
+namespace docker {
+namespace paths {
+
+/**
+ * The Docker store file system layout is as follows:
+ * Image store dir ('--docker_store_dir' slave flag)
+ * |--staging (contains temp directories for downloads and extract)
+ * |--layers
+ * |--<layer_id>
+ * |--rootfs
+ * |--rootfses
+ * |--storedImages (file holding on cached images)
+ */
+
+std::string getStagingDir(const std::string& storeDir);
+
+std::string getTempStaging(const std::string& storeDir);
+
+std::string getLocalImageTarPath(
+ const std::string& discoveryDir,
+ const std::string& name);
+
+std::string getLocalImageRepositoriesPath(const std::string& staging);
+
+std::string getLocalImageLayerPath(
+ const std::string& staging,
+ const std::string& layerId);
+
+std::string getLocalImageLayerManifestPath(
+ const std::string& staging,
+ const std::string& layerId);
+
+std::string getLocalImageLayerTarPath(
+ const std::string& staging,
+ const std::string& layerId);
+
+std::string getLocalImageLayerRootfsPath(
+ const std::string& staging,
+ const std::string& layerId);
+
+std::string getImageLayerPath(
+ const std::string& storeDir,
+ const std::string& layerId);
+
+std::string getImageLayerRootfsPath(
+ const std::string& storeDir,
+ const std::string& layerId);
+
+std::string getStoredImagesPath(const std::string& storeDir);
+
+} // namespace paths {
+} // namespace docker {
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
+
+#endif // __MESOS_DOCKER_PATHS_HPP__
http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioner/docker/store.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/docker/store.cpp b/src/slave/containerizer/provisioner/docker/store.cpp
new file mode 100644
index 0000000..51f4d6b
--- /dev/null
+++ b/src/slave/containerizer/provisioner/docker/store.cpp
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "slave/containerizer/provisioner/docker/local_store.hpp"
+#include "slave/containerizer/provisioner/docker/store.hpp"
+
+using process::Owned;
+
+using std::string;
+
+namespace mesos {
+namespace internal {
+namespace slave {
+namespace docker {
+
+Try<Owned<slave::Store>> Store::create(const Flags& flags)
+{
+ hashmap<string, Try<Owned<slave::Store>>(*)(const Flags&)> creators{
+ {"local", &LocalStore::create}
+ };
+
+ if (!creators.contains(flags.docker_store_discovery)) {
+ return Error("Unknown Docker store: " + flags.docker_store_discovery);
+ }
+
+ return creators[flags.docker_store_discovery](flags);
+}
+
+} // namespace docker {
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioner/docker/store.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/docker/store.hpp b/src/slave/containerizer/provisioner/docker/store.hpp
new file mode 100644
index 0000000..ae06706
--- /dev/null
+++ b/src/slave/containerizer/provisioner/docker/store.hpp
@@ -0,0 +1,58 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MESOS_DOCKER_STORE_HPP__
+#define __MESOS_DOCKER_STORE_HPP__
+
+#include <string>
+
+#include <stout/hashmap.hpp>
+#include <stout/nothing.hpp>
+#include <stout/option.hpp>
+#include <stout/try.hpp>
+
+#include <process/future.hpp>
+
+#include "slave/containerizer/fetcher.hpp"
+
+#include "slave/containerizer/provisioner/store.hpp"
+
+#include "slave/containerizer/provisioner/docker/message.hpp"
+
+#include "slave/flags.hpp"
+
+namespace mesos {
+namespace internal {
+namespace slave {
+namespace docker {
+
+// Store fetches the Docker images and stores them on disk.
+// TODO(tnachen): Make this store the only docker store
+// implementation, and move local and remote to different pullers.
+class Store
+{
+public:
+ static Try<process::Owned<slave::Store>> create(const Flags& flags);
+};
+
+} // namespace docker {
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
+
+#endif // __MESOS_DOCKER_STORE_HPP__
http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioner/provisioner.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioner/provisioner.hpp b/src/slave/containerizer/provisioner/provisioner.hpp
index d42822b..912fc5a 100644
--- a/src/slave/containerizer/provisioner/provisioner.hpp
+++ b/src/slave/containerizer/provisioner/provisioner.hpp
@@ -67,7 +67,7 @@ public:
// image and return the absolute path to the root filesystem.
virtual process::Future<std::string> provision(
const ContainerID& containerId,
- const Image& image) = 0;
+ const Image& image);
// Destroy a previously provisioned root filesystem. Assumes that
// all references (e.g., mounts, open files) to the provisioned
http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioners/docker.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioners/docker.cpp b/src/slave/containerizer/provisioners/docker.cpp
deleted file mode 100644
index b5c5a7b..0000000
--- a/src/slave/containerizer/provisioners/docker.cpp
+++ /dev/null
@@ -1,425 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "slave/containerizer/provisioners/docker.hpp"
-
-#include <glog/logging.h>
-
-#include <stout/json.hpp>
-#include <stout/nothing.hpp>
-#include <stout/os.hpp>
-
-#include <process/collect.hpp>
-#include <process/defer.hpp>
-#include <process/dispatch.hpp>
-#include <process/owned.hpp>
-#include <process/sequence.hpp>
-
-#include "slave/containerizer/provisioners/backend.hpp"
-#include "slave/containerizer/provisioners/paths.hpp"
-
-#include "slave/containerizer/provisioners/docker/paths.hpp"
-#include "slave/containerizer/provisioners/docker/store.hpp"
-
-#include "slave/paths.hpp"
-
-using namespace process;
-using namespace mesos::internal::slave;
-
-using std::list;
-using std::string;
-using std::vector;
-
-using mesos::slave::ContainerState;
-
-namespace mesos {
-namespace internal {
-namespace slave {
-namespace docker {
-
-class DockerProvisionerProcess :
- public process::Process<DockerProvisionerProcess>
-{
-public:
- static Try<process::Owned<DockerProvisionerProcess>> create(
- const Flags& flags,
- Fetcher* fetcher);
-
- process::Future<Nothing> recover(
- const std::list<mesos::slave::ContainerState>& states,
- const hashset<ContainerID>& orphans);
-
- process::Future<std::string> provision(
- const ContainerID& containerId,
- const Image& image);
-
- process::Future<bool> destroy(const ContainerID& containerId);
-
-private:
- DockerProvisionerProcess(
- const string& _rootDir,
- const Flags& _flags,
- const process::Owned<Store>& _store,
- const hashmap<std::string, process::Owned<Backend>>& _backends);
-
- process::Future<std::string> _provision(
- const DockerImage& image,
- const ContainerID& containerId,
- const string& rootfs);
-
- const string& rootDir;
- const Flags flags;
- process::Owned<Store> store;
- hashmap<string, process::Owned<Backend>> backends;
-
- struct Info
- {
- // Mappings: backend -> rootfsId -> rootfsPath.
- hashmap<string, hashmap<string, string>> rootfses;
- };
-
- hashmap<ContainerID, Owned<Info>> infos;
-};
-
-
-Try<ImageName> ImageName::create(const std::string& name)
-{
- ImageName imageName;
- Option<string> registry = None();
- std::vector<std::string> components = strings::split(name, "/");
- if (components.size() > 2) {
- registry = name.substr(0, name.find_last_of("/"));
- }
-
- std::size_t found = components.back().find_last_of(':');
- if (found == std::string::npos) {
- imageName.repository = components.back();
- imageName.tag = "latest";
- } else {
- imageName.repository = components.back().substr(0, found);
- imageName.tag = components.back().substr(found + 1);
- }
-
- return imageName;
-}
-
-Try<Owned<Provisioner>> DockerProvisioner::create(
- const Flags& flags,
- Fetcher* fetcher)
-{
- Try<Owned<DockerProvisionerProcess>> create =
- DockerProvisionerProcess::create(flags, fetcher);
- if (create.isError()) {
- return Error(create.error());
- }
-
- return Owned<Provisioner>(new DockerProvisioner(create.get()));
-}
-
-
-DockerProvisioner::DockerProvisioner(Owned<DockerProvisionerProcess> _process)
- : process(_process)
-{
- process::spawn(CHECK_NOTNULL(process.get()));
-}
-
-
-DockerProvisioner::~DockerProvisioner()
-{
- process::terminate(process.get());
- process::wait(process.get());
-}
-
-
-Future<Nothing> DockerProvisioner::recover(
- const list<ContainerState>& states,
- const hashset<ContainerID>& orphans)
-{
- return dispatch(
- process.get(),
- &DockerProvisionerProcess::recover,
- states,
- orphans);
-}
-
-
-Future<string> DockerProvisioner::provision(
- const ContainerID& containerId,
- const Image& image)
-{
- return dispatch(
- process.get(),
- &DockerProvisionerProcess::provision,
- containerId,
- image);
-}
-
-
-Future<bool> DockerProvisioner::destroy(const ContainerID& containerId)
-{
- return dispatch(
- process.get(),
- &DockerProvisionerProcess::destroy,
- containerId);
-}
-
-
-Try<Owned<DockerProvisionerProcess>> DockerProvisionerProcess::create(
- const Flags& flags,
- Fetcher* fetcher)
-{
- string rootDir =
- slave::paths::getProvisionerDir(flags.work_dir, Image::DOCKER);
-
- if (!os::exists(rootDir)) {
- Try<Nothing> mkdir = os::mkdir(rootDir);
- if (mkdir.isError()) {
- return Error("Failed to create Docker provisioner root directory '" +
- rootDir + "': " + mkdir.error());
- }
- }
-
- const hashmap<string, Owned<Backend>> backends = Backend::create(flags);
- if (backends.empty()) {
- return Error("No usable Docker provisioner backend created");
- }
-
- if (!backends.contains(flags.docker_backend)) {
- return Error("The specified Docker provisioner backend '" +
- flags.docker_backend + "'is unsupported");
- }
-
- Try<Owned<Store>> store = Store::create(flags, fetcher);
- if (store.isError()) {
- return Error("Failed to create image store: " + store.error());
- }
-
- return Owned<DockerProvisionerProcess>(
- new DockerProvisionerProcess(
- rootDir,
- flags,
- store.get(),
- backends));
-}
-
-
-DockerProvisionerProcess::DockerProvisionerProcess(
- const string& _rootDir,
- const Flags& _flags,
- const Owned<Store>& _store,
- const hashmap<string, Owned<Backend>>& _backends)
- : rootDir(_rootDir),
- flags(_flags),
- store(_store),
- backends(_backends) {}
-
-
-Future<Nothing> DockerProvisionerProcess::recover(
- const list<ContainerState>& states,
- const hashset<ContainerID>& orphans)
-{
- // TODO(tnachen): Consider merging this with
- // AppcProvisionerProcess::recover.
-
- // Register living containers, including the ones that do not
- // provision Docker images.
- hashset<ContainerID> alive;
-
- foreach (const ContainerState& state, states) {
- if (state.executor_info().has_container() &&
- state.executor_info().container().type() == ContainerInfo::MESOS) {
- alive.insert(state.container_id());
- }
- }
-
- // List provisioned containers; recover living ones; destroy unknown orphans.
- // Note that known orphan containers are recovered as well and they will
- // be destroyed by the containerizer using the normal cleanup path. See
- // MESOS-2367 for details.
- Try<hashmap<ContainerID, string>> containers =
- provisioners::paths::listContainers(rootDir);
-
- if (containers.isError()) {
- return Failure("Failed to list the containers managed by Docker "
- "provisioner: " + containers.error());
- }
-
- // If no container has been launched the 'containers' directory will be empty.
- foreachkey (const ContainerID& containerId, containers.get()) {
- if (alive.contains(containerId) || orphans.contains(containerId)) {
- Owned<Info> info = Owned<Info>(new Info());
-
- Try<hashmap<string, hashmap<string, string>>> rootfses =
- provisioners::paths::listContainerRootfses(rootDir, containerId);
-
- if (rootfses.isError()) {
- return Failure("Unable to list rootfses belonged to container '" +
- containerId.value() + "': " + rootfses.error());
- }
-
- foreachkey (const string& backend, rootfses.get()) {
- if (!backends.contains(backend)) {
- return Failure("Found rootfses managed by an unrecognized backend: " +
- backend);
- }
-
- info->rootfses.put(backend, rootfses.get()[backend]);
- }
-
- VLOG(1) << "Recovered container " << containerId;
- infos.put(containerId, info);
-
- continue;
- }
-
- // Destroy (unknown) orphan container's rootfses.
- Try<hashmap<string, hashmap<string, string>>> rootfses =
- provisioners::paths::listContainerRootfses(rootDir, containerId);
-
- if (rootfses.isError()) {
- return Failure("Unable to find rootfses for container '" +
- containerId.value() + "': " + rootfses.error());
- }
-
- foreachkey (const string& backend, rootfses.get()) {
- if (!backends.contains(backend)) {
- return Failure("Found rootfses managed by an unrecognized backend: " +
- backend);
- }
-
- foreachvalue (const string& rootfs, rootfses.get()[backend]) {
- VLOG(1) << "Destroying orphan rootfs " << rootfs;
-
- // Not waiting for the destruction and we don't care about
- // the return value.
- backends.get(backend).get()->destroy(rootfs)
- .onFailed([rootfs](const std::string& error) {
- LOG(WARNING) << "Failed to destroy orphan rootfs '" << rootfs
- << "': "<< error;
- });
- }
- }
- }
-
- LOG(INFO) << "Recovered Docker provisioner rootfses";
-
- return store->recover()
- .then([]() -> Future<Nothing> {
- LOG(INFO) << "Recovered Docker image store";
- return Nothing();
- });
-}
-
-
-Future<string> DockerProvisionerProcess::provision(
- const ContainerID& containerId,
- const Image& image)
-{
- if (image.type() != Image::DOCKER) {
- return Failure("Unsupported container image type");
- }
-
- if (!image.has_docker()) {
- return Failure("Missing Docker image info");
- }
-
- string rootfsId = UUID::random().toString();
- string rootfs = provisioners::paths::getContainerRootfsDir(
- rootDir, containerId, flags.docker_backend, rootfsId);
-
- if (!infos.contains(containerId)) {
- infos.put(containerId, Owned<Info>(new Info()));
- }
-
- infos[containerId]->rootfses[flags.docker_backend].put(rootfsId, rootfs);
-
- Try<ImageName> imageName = ImageName::create(image.docker().name());
- if (imageName.isError()) {
- return Failure("Unable to able to parse Docker image name '" +
- image.docker().name() + "': " + imageName.error());
- }
-
- return store->get(imageName.get())
- .then(defer(self(), &Self::_provision, lambda::_1, containerId, rootfs));
-}
-
-
-Future<string> DockerProvisionerProcess::_provision(
- const DockerImage& image,
- const ContainerID& containerId,
- const string& rootfs)
-{
- CHECK(backends.contains(flags.docker_backend));
-
- LOG(INFO) << "Provisioning rootfs for container '" << containerId << "'"
- << " to '" << rootfs << "'";
-
- vector<string> layerPaths;
- foreach (const string& layerId, image.layerIds) {
- layerPaths.push_back(
- paths::getImageLayerRootfsPath(flags.docker_store_dir, layerId));
- }
-
- return backends[flags.docker_backend]->provision(layerPaths, rootfs)
- .then([rootfs]() -> Future<string> { return rootfs; });
-}
-
-
-Future<bool> DockerProvisionerProcess::destroy(
- const ContainerID& containerId)
-{
- // TODO(tnachen): Consider merging this with
- // AppcProvisionerProcess::destroy.
- if (!infos.contains(containerId)) {
- LOG(INFO) << "Ignoring destroy request for unknown container: "
- << containerId;
-
- return false;
- }
-
- // Unregister the container first. If destroy() fails, we can rely on
- // recover() to retry it later.
- Owned<Info> info = infos[containerId];
- infos.erase(containerId);
-
- list<Future<bool>> futures;
- foreachkey (const string& backend, info->rootfses) {
- foreachvalue (const string& rootfs, info->rootfses[backend]) {
- if (!backends.contains(backend)) {
- return Failure("Cannot destroy rootfs '" + rootfs +
- "' provisioned by an unknown backend '" + backend + "'");
- }
-
- LOG(INFO) << "Destroying container rootfs for container '"
- << containerId << "' at '" << rootfs << "'";
-
- futures.push_back(
- backends.get(backend).get()->destroy(rootfs));
- }
- }
-
- return collect(futures)
- .then([=](const list<bool>& results) -> Future<bool> {
- return true;
- });
-}
-
-} // namespace docker {
-} // namespace slave {
-} // namespace internal {
-} // namespace mesos {
http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioners/docker.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioners/docker.hpp b/src/slave/containerizer/provisioners/docker.hpp
deleted file mode 100644
index cd7911c..0000000
--- a/src/slave/containerizer/provisioners/docker.hpp
+++ /dev/null
@@ -1,166 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MESOS_DOCKER_HPP__
-#define __MESOS_DOCKER_HPP__
-
-#include <list>
-#include <ostream>
-#include <sstream>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <stout/hashmap.hpp>
-#include <stout/json.hpp>
-#include <stout/nothing.hpp>
-#include <stout/option.hpp>
-#include <stout/strings.hpp>
-#include <stout/try.hpp>
-
-#include <process/future.hpp>
-#include <process/owned.hpp>
-#include <process/process.hpp>
-#include <process/shared.hpp>
-
-#include <mesos/resources.hpp>
-
-#include "slave/containerizer/provisioner.hpp"
-
-#include "slave/flags.hpp"
-
-namespace mesos {
-namespace internal {
-namespace slave {
-namespace docker {
-
-// Forward declaration.
-class Store;
-
-/**
- * Represents Docker Image Name, which composes of a repository and a
- * tag.
- */
-struct ImageName
-{
- static Try<ImageName> create(const std::string& name);
-
- ImageName(
- const std::string& _repository,
- const std::string& _tag,
- const Option<std::string>& _registry = None())
- : repository(_repository),
- tag(_tag),
- registry(_registry) {}
-
- ImageName() {}
-
- /**
- * The string representation of this image.
- */
- std::string name() const
- {
- if (registry.isSome()) {
- return registry.get() + "/" + repository + ":" + tag;
- }
-
- return repository + ":" + tag;
- }
-
- /**
- * Repository of this image (e.g, ubuntu).
- */
- std::string repository;
-
- /**
- * Tag of this image (e.g: 14.04).
- */
- std::string tag;
-
- /**
- * Custom registry that the image points to.
- */
- Option<std::string> registry;
-};
-
-
-inline std::ostream& operator<<(std::ostream& stream, const ImageName& image)
-{
- return stream << image.name();
-}
-
-
-/**
- * Represents a Docker Image that holds its name and all of its layers
- * sorted by its dependency.
- */
-struct DockerImage
-{
- DockerImage() {}
-
- DockerImage(
- const ImageName& _imageName,
- const std::list<std::string>& _layerIds)
- : imageName(_imageName), layerIds(_layerIds) {}
-
- ImageName imageName;
- std::list<std::string> layerIds;
-};
-
-// Forward declaration.
-class DockerProvisionerProcess;
-
-/**
- * Docker Provisioner is responsible to provision rootfs for
- * containers with Docker images.
- */
-class DockerProvisioner : public mesos::internal::slave::Provisioner
-{
-public:
- static Try<process::Owned<mesos::internal::slave::Provisioner>> create(
- const Flags& flags,
- Fetcher* fetcher);
-
- virtual ~DockerProvisioner();
-
- virtual process::Future<Nothing> recover(
- const std::list<mesos::slave::ContainerState>& states,
- const hashset<ContainerID>& orphans);
-
- virtual process::Future<std::string> provision(
- const ContainerID& containerId,
- const Image& image);
-
- virtual process::Future<bool> destroy(const ContainerID& containerId);
-
-private:
- explicit DockerProvisioner(process::Owned<DockerProvisionerProcess> _process);
-
- DockerProvisioner& operator=(const DockerProvisioner&) = delete; // Not assignable.
- DockerProvisioner(const DockerProvisioner&) = delete; // Not copyable.
-
- process::Owned<DockerProvisionerProcess> process;
-};
-
-
-} // namespace docker {
-} // namespace slave {
-} // namespace internal {
-} // namespace mesos {
-
-#endif // __MESOS_DOCKER_HPP__
http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioners/docker/local_store.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioners/docker/local_store.cpp b/src/slave/containerizer/provisioners/docker/local_store.cpp
deleted file mode 100644
index ec0420e..0000000
--- a/src/slave/containerizer/provisioners/docker/local_store.cpp
+++ /dev/null
@@ -1,471 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "slave/containerizer/provisioners/docker/local_store.hpp"
-
-#include <list>
-#include <vector>
-
-#include <glog/logging.h>
-
-#include <stout/json.hpp>
-#include <stout/os.hpp>
-#include <stout/result.hpp>
-
-#include <process/collect.hpp>
-#include <process/defer.hpp>
-#include <process/dispatch.hpp>
-
-#include "common/status_utils.hpp"
-
-#include "slave/containerizer/fetcher.hpp"
-
-#include "slave/containerizer/provisioners/docker/metadata_manager.hpp"
-#include "slave/containerizer/provisioners/docker/paths.hpp"
-#include "slave/containerizer/provisioners/docker/store.hpp"
-
-#include "slave/flags.hpp"
-
-using namespace process;
-
-using std::list;
-using std::string;
-using std::vector;
-
-namespace mesos {
-namespace internal {
-namespace slave {
-namespace docker {
-
-class LocalStoreProcess : public process::Process<LocalStoreProcess>
-{
-public:
- ~LocalStoreProcess() {}
-
- static Try<process::Owned<LocalStoreProcess>> create(
- const Flags& flags,
- Fetcher* fetcher);
-
- process::Future<DockerImage> get(const ImageName& name);
-
- process::Future<Nothing> recover();
-
-private:
- LocalStoreProcess(
- const Flags& _flags,
- Owned<MetadataManager> _metadataManager)
- : flags(_flags), metadataManager(_metadataManager) {}
-
- process::Future<Nothing> untarImage(
- const std::string& tarPath,
- const std::string& staging);
-
- process::Future<DockerImage> putImage(
- const ImageName& name,
- const std::string& staging);
-
- Result<std::string> getParentId(
- const std::string& staging,
- const std::string& layerId);
-
- process::Future<Nothing> putLayers(
- const std::string& staging,
- const std::list<std::string>& layerIds);
-
- process::Future<Nothing> putLayer(
- const std::string& staging,
- const std::string& id);
-
- process::Future<Nothing> moveLayer(
- const std::string& staging,
- const std::string& id);
-
- const Flags flags;
- process::Owned<MetadataManager> metadataManager;
-};
-
-
-Try<Owned<Store>> Store::create(
- const Flags& flags,
- Fetcher* fetcher)
-{
- hashmap<string, Try<Owned<Store>>(*)(const Flags&, Fetcher*)> creators{
- {"local", &LocalStore::create}
- };
-
- if (!creators.contains(flags.docker_store)) {
- return Error("Unknown Docker store: " + flags.docker_store);
- }
-
- return creators[flags.docker_store](flags, fetcher);
-}
-
-
-Try<Owned<Store>> LocalStore::create(
- const Flags& flags,
- Fetcher* fetcher)
-{
- Try<Owned<LocalStoreProcess>> process =
- LocalStoreProcess::create(flags, fetcher);
- if (process.isError()) {
- return Error(process.error());
- }
-
- return Owned<Store>(new LocalStore(process.get()));
-}
-
-
-LocalStore::LocalStore(Owned<LocalStoreProcess> _process) : process(_process)
-{
- process::spawn(CHECK_NOTNULL(process.get()));
-}
-
-
-LocalStore::~LocalStore()
-{
- process::terminate(process.get());
- process::wait(process.get());
-}
-
-
-Future<DockerImage> LocalStore::get(const ImageName& name)
-{
- return dispatch(process.get(), &LocalStoreProcess::get, name);
-}
-
-
-Future<Nothing> LocalStore::recover()
-{
- return dispatch(process.get(), &LocalStoreProcess::recover);
-}
-
-
-Try<Owned<LocalStoreProcess>> LocalStoreProcess::create(
- const Flags& flags,
- Fetcher* fetcher)
-{
- if (!os::exists(flags.docker_store_dir)) {
- Try<Nothing> mkdir = os::mkdir(flags.docker_store_dir);
- if (mkdir.isError()) {
- return Error("Failed to create Docker store directory: " + mkdir.error());
- }
- }
-
- if (!os::exists(paths::getStagingDir(flags.docker_store_dir))) {
- Try<Nothing> mkdir =
- os::mkdir(paths::getStagingDir(flags.docker_store_dir));
- if (mkdir.isError()) {
- return Error("Failed to create Docker store staging directory: " +
- mkdir.error());
- }
- }
-
- Try<Owned<MetadataManager>> metadataManager = MetadataManager::create(flags);
- if (metadataManager.isError()) {
- return Error(metadataManager.error());
- }
-
- return Owned<LocalStoreProcess>(
- new LocalStoreProcess(flags, metadataManager.get()));
-}
-
-
-Future<DockerImage> LocalStoreProcess::get(const ImageName& name)
-{
- return metadataManager->get(name)
- .then(defer(self(),
- [this, name](
- const Option<DockerImage>& image) -> Future<DockerImage> {
- if (image.isSome()) {
- return image.get();
- }
-
- string tarPath = paths::getLocalImageTarPath(
- flags.docker_discovery_local_dir,
- name.name());
-
- if (!os::exists(tarPath)) {
- VLOG(1) << "Unable to find image in local store with path: " << tarPath;
- return Failure("No Docker image tar archive found");
- }
-
- // Create a temporary staging directory.
- Try<string> staging =
- os::mkdtemp(paths::getTempStaging(flags.docker_store_dir));
- if (staging.isError()) {
- return Failure("Failed to create a staging directory");
- }
-
- return untarImage(tarPath, staging.get())
- .then(defer(self(), &Self::putImage, name, staging.get()));
- }));
-}
-
-
-Future<Nothing> LocalStoreProcess::recover()
-{
- return metadataManager->recover();
-}
-
-Future<Nothing> LocalStoreProcess::untarImage(
- const string& tarPath,
- const string& staging)
-{
- VLOG(1) << "Untarring image at: " << tarPath;
-
- // Untar discovery_local_dir/name.tar into staging/.
- vector<string> argv = {
- "tar",
- "-C",
- staging,
- "-x",
- "-f",
- tarPath
- };
-
- Try<Subprocess> s = subprocess(
- "tar",
- argv,
- Subprocess::PATH("/dev/null"),
- Subprocess::PATH("/dev/null"),
- Subprocess::PATH("/dev/null"));
-
- if (s.isError()) {
- return Failure("Failed to create tar subprocess: " + s.error());
- }
-
- return s.get().status()
- .then([=](const Option<int>& status) -> Future<Nothing> {
- if (status.isNone()) {
- return Failure("Failed to reap status for tar subprocess in " +
- tarPath);
- }
- if (!WIFEXITED(status.get()) || WEXITSTATUS(status.get()) != 0) {
- return Failure("Untar image failed with exit code: " +
- WSTRINGIFY(status.get()));
- }
-
- return Nothing();
- });
-}
-
-
-Future<DockerImage> LocalStoreProcess::putImage(
- const ImageName& name,
- const string& staging)
-{
- Try<string> value = os::read(paths::getLocalImageRepositoriesPath(staging));
- if (value.isError()) {
- return Failure("Failed to read repository JSON: " + value.error());
- }
-
- Try<JSON::Object> json = JSON::parse<JSON::Object>(value.get());
- if (json.isError()) {
- return Failure("Failed to parse JSON: " + json.error());
- }
-
- Result<JSON::Object> repositoryValue =
- json.get().find<JSON::Object>(name.repository);
- if (repositoryValue.isError()) {
- return Failure("Failed to find repository: " + repositoryValue.error());
- } else if (repositoryValue.isNone()) {
- return Failure("Repository '" + name.repository + "' is not found");
- }
-
- JSON::Object repositoryJson = repositoryValue.get();
-
- // We don't use JSON find here because a tag might contain a '.'.
- std::map<string, JSON::Value>::const_iterator entry =
- repositoryJson.values.find(name.tag);
- if (entry == repositoryJson.values.end()) {
- return Failure("Tag '" + name.tag + "' is not found");
- } else if (!entry->second.is<JSON::String>()) {
- return Failure("Tag JSON value expected to be JSON::String");
- }
-
- string layerId = entry->second.as<JSON::String>().value;
-
- Try<string> manifest =
- os::read(paths::getLocalImageLayerManifestPath(staging, layerId));
- if (manifest.isError()) {
- return Failure("Failed to read manifest: " + manifest.error());
- }
-
- Try<JSON::Object> manifestJson = JSON::parse<JSON::Object>(manifest.get());
- if (manifestJson.isError()) {
- return Failure("Failed to parse manifest: " + manifestJson.error());
- }
-
- list<string> layerIds;
- layerIds.push_back(layerId);
- Result<string> parentId = getParentId(staging, layerId);
- while(parentId.isSome()) {
- layerIds.push_front(parentId.get());
- parentId = getParentId(staging, parentId.get());
- }
- if (parentId.isError()) {
- return Failure("Failed to obtain parent layer id: " + parentId.error());
- }
-
- return putLayers(staging, layerIds)
- .then([=]() -> Future<DockerImage> {
- return metadataManager->put(name, layerIds);
- });
-}
-
-
-Result<string> LocalStoreProcess::getParentId(
- const string& staging,
- const string& layerId)
-{
- Try<string> manifest =
- os::read(paths::getLocalImageLayerManifestPath(staging, layerId));
- if (manifest.isError()) {
- return Error("Failed to read manifest: " + manifest.error());
- }
-
- Try<JSON::Object> json = JSON::parse<JSON::Object>(manifest.get());
- if (json.isError()) {
- return Error("Failed to parse manifest: " + json.error());
- }
-
- Result<JSON::String> parentId = json.get().find<JSON::String>("parent");
- if (parentId.isNone() || (parentId.isSome() && parentId.get() == "")) {
- return None();
- } else if (parentId.isError()) {
- return Error("Failed to read parent of layer: " + parentId.error());
- }
- return parentId.get().value;
-}
-
-
-Future<Nothing> LocalStoreProcess::putLayers(
- const string& staging,
- const list<string>& layerIds)
-{
- list<Future<Nothing>> futures{ Nothing() };
- foreach (const string& layer, layerIds) {
- futures.push_back(
- futures.back().then(
- defer(self(), &Self::putLayer, staging, layer)));
- }
-
- return collect(futures)
- .then([]() -> Future<Nothing> { return Nothing(); });
-}
-
-
-Future<Nothing> LocalStoreProcess::putLayer(
- const string& staging,
- const string& id)
-{
- // We untar the layer from source into a staging directory, then
- // move the layer into store. We do this instead of untarring
- // directly to store to make sure we don't end up with partially
- // untarred layer rootfs.
-
- // Check if image layer rootfs is already in store.
- if (os::exists(paths::getImageLayerRootfsPath(flags.docker_store_dir, id))) {
- VLOG(1) << "Image layer '" << id << "' rootfs already in store. "
- << "Skipping put.";
- return Nothing();
- }
-
- const string imageLayerPath =
- paths::getImageLayerPath(flags.docker_store_dir, id);
- if (!os::exists(imageLayerPath)) {
- Try<Nothing> mkdir = os::mkdir(imageLayerPath);
- if (mkdir.isError()) {
- return Failure("Failed to create Image layer directory '" +
- imageLayerPath + "': " + mkdir.error());
- }
- }
-
- // Image layer has been untarred but is not present in the store directory.
- string localRootfsPath = paths::getLocalImageLayerRootfsPath(staging, id);
- if (os::exists(localRootfsPath)) {
- LOG(WARNING) << "Image layer '" << id << "' rootfs present at but not in "
- << "store directory '" << localRootfsPath << "'. Removing "
- << "staged rootfs and untarring layer again.";
- Try<Nothing> rmdir = os::rmdir(localRootfsPath);
- if (rmdir.isError()) {
- return Failure("Failed to remove incomplete staged rootfs for layer '" +
- id + "': " + rmdir.error());
- }
- }
-
- Try<Nothing> mkdir = os::mkdir(localRootfsPath);
- if (mkdir.isError()) {
- return Failure("Failed to create rootfs path '" + localRootfsPath + "': " +
- mkdir.error());
- }
- // Untar staging/id/layer.tar into staging/id/rootfs.
- vector<string> argv = {
- "tar",
- "-C",
- localRootfsPath,
- "-x",
- "-f",
- paths::getLocalImageLayerTarPath(staging, id)
- };
-
- Try<Subprocess> s = subprocess(
- "tar",
- argv,
- Subprocess::PATH("/dev/null"),
- Subprocess::PATH("/dev/null"),
- Subprocess::PATH("/dev/null"));
- if (s.isError()) {
- return Failure("Failed to create tar subprocess: " + s.error());
- }
-
- return s.get().status()
- .then([=](const Option<int>& status) -> Future<Nothing> {
- if (status.isNone()) {
- return Failure("Failed to reap subprocess to untar image");
- } else if (!WIFEXITED(status.get()) || WEXITSTATUS(status.get()) != 0) {
- return Failure("Untar failed with exit code: " +
- WSTRINGIFY(status.get()));
- }
-
- return moveLayer(staging, id);
- });
-}
-
-
-Future<Nothing> LocalStoreProcess::moveLayer(
- const string& staging,
- const string& id)
-{
- Try<Nothing> status = os::rename(
- paths::getLocalImageLayerRootfsPath(staging, id),
- paths::getImageLayerRootfsPath(flags.docker_store_dir, id));
-
- if (status.isError()) {
- return Failure("Failed to move layer to store directory: "
- + status.error());
- }
-
- return Nothing();
-}
-
-
-} // namespace docker {
-} // namespace slave {
-} // namespace internal {
-} // namespace mesos {
http://git-wip-us.apache.org/repos/asf/mesos/blob/4dedbf4a/src/slave/containerizer/provisioners/docker/local_store.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/provisioners/docker/local_store.hpp b/src/slave/containerizer/provisioners/docker/local_store.hpp
deleted file mode 100644
index 64a6fc0..0000000
--- a/src/slave/containerizer/provisioners/docker/local_store.hpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MESOS_DOCKER_LOCAL_STORE_HPP__
-#define __MESOS_DOCKER_LOCAL_STORE_HPP__
-
-#include "slave/containerizer/provisioners/docker/store.hpp"
-
-namespace mesos {
-namespace internal {
-namespace slave {
-namespace docker {
-
-// Forward declaration.
-class LocalStoreProcess;
-
-
-/**
- * LocalStore assumes Docker images are stored in a local directory
- * (configured with flags.docker_discovery_local_dir), with all the
- * images saved as tar with the name as the image name with tag (e.g:
- * ubuntu:14.04.tar).
- */
-class LocalStore : public Store
-{
-public:
- static Try<process::Owned<Store>> create(
- const Flags& flags,
- Fetcher* fetcher);
-
- virtual ~LocalStore();
-
- virtual process::Future<DockerImage> get(const ImageName& name);
-
- virtual process::Future<Nothing> recover();
-
-private:
- explicit LocalStore(process::Owned<LocalStoreProcess> _process);
-
- LocalStore& operator=(const LocalStore&) = delete; // Not assignable.
- LocalStore(const LocalStore&) = delete; // Not copyable.
-
- process::Owned<LocalStoreProcess> process;
-};
-
-} // namespace docker {
-} // namespace slave {
-} // namespace internal {
-} // namespace mesos {
-
-#endif // __MESOS_DOCKER_LOCAL_STORE_HPP__