Refactor store implementations to pullers.
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/9fb62cec Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/9fb62cec Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/9fb62cec Branch: refs/heads/master Commit: 9fb62ceccfd5eda3027560ef0d816e5d2caa937d Parents: 4dedbf4 Author: Timothy Chen <[email protected]> Authored: Sun Sep 20 23:18:53 2015 -0700 Committer: Timothy Chen <[email protected]> Committed: Fri Sep 25 09:02:05 2015 -0700 ---------------------------------------------------------------------- src/Makefile.am | 13 +- src/slave/containerizer/provisioner.cpp | 82 ---- .../provisioner/docker/local_puller.cpp | 349 ++++++++++++++ .../provisioner/docker/local_puller.hpp | 66 +++ .../provisioner/docker/local_store.cpp | 460 ------------------- .../provisioner/docker/local_store.hpp | 64 --- .../provisioner/docker/message.hpp | 6 +- .../provisioner/docker/message.proto | 7 +- .../provisioner/docker/metadata_manager.cpp | 101 ++-- .../provisioner/docker/metadata_manager.hpp | 32 +- .../containerizer/provisioner/docker/paths.cpp | 43 +- .../containerizer/provisioner/docker/paths.hpp | 44 +- .../containerizer/provisioner/docker/puller.cpp | 46 ++ .../containerizer/provisioner/docker/puller.hpp | 68 +++ .../containerizer/provisioner/docker/store.cpp | 247 +++++++++- .../containerizer/provisioner/docker/store.hpp | 36 +- src/slave/containerizer/provisioner/store.cpp | 3 + src/slave/flags.cpp | 20 +- src/slave/flags.hpp | 5 +- .../containerizer/provisioner_docker_tests.cpp | 74 +-- 20 files changed, 971 insertions(+), 795 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/src/Makefile.am ---------------------------------------------------------------------- diff --git a/src/Makefile.am b/src/Makefile.am index 818d62d..8aa4566 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -253,7 +253,6 @@ DOCKER_PROVISIONER_PROTOS = \ slave/containerizer/provisioner/docker/message.pb.cc \ slave/containerizer/provisioner/docker/message.pb.h - BUILT_SOURCES += $(DOCKER_PROVISIONER_PROTOS) CLEANFILES += $(DOCKER_PROVISIONER_PROTOS) @@ -488,7 +487,6 @@ libmesos_no_3rdparty_la_SOURCES = \ master/validation.cpp \ master/allocator/allocator.cpp \ master/allocator/sorter/drf/sorter.cpp \ - messages/docker_provisioner.proto \ messages/flags.proto \ messages/messages.cpp \ messages/messages.proto \ @@ -530,9 +528,11 @@ libmesos_no_3rdparty_la_SOURCES = \ slave/containerizer/provisioner/appc/store.cpp \ slave/containerizer/provisioner/backend.cpp \ slave/containerizer/provisioner/backends/copy.cpp \ - slave/containerizer/provisioner/docker/local_store.cpp \ + slave/containerizer/provisioner/docker/local_puller.cpp \ + slave/containerizer/provisioner/docker/message.proto \ slave/containerizer/provisioner/docker/metadata_manager.cpp \ slave/containerizer/provisioner/docker/paths.cpp \ + slave/containerizer/provisioner/docker/puller.cpp \ slave/containerizer/provisioner/docker/registry_client.cpp \ slave/containerizer/provisioner/docker/store.cpp \ slave/containerizer/provisioner/docker/token_manager.cpp \ @@ -800,7 +800,6 @@ libmesos_no_3rdparty_la_SOURCES += \ master/allocator/mesos/hierarchical.hpp \ master/allocator/sorter/drf/sorter.hpp \ master/allocator/sorter/sorter.hpp \ - messages/docker_provisioner.hpp \ messages/flags.hpp \ messages/messages.hpp \ module/manager.hpp \ @@ -830,10 +829,11 @@ libmesos_no_3rdparty_la_SOURCES += \ slave/containerizer/provisioner/backend.hpp \ slave/containerizer/provisioner/backends/bind.hpp \ slave/containerizer/provisioner/backends/copy.hpp \ - slave/containerizer/provisioner/docker.hpp \ - slave/containerizer/provisioner/docker/local_store.hpp \ + slave/containerizer/provisioner/docker/local_puller.hpp \ + slave/containerizer/provisioner/docker/message.hpp \ slave/containerizer/provisioner/docker/metadata_manager.hpp \ slave/containerizer/provisioner/docker/paths.hpp \ + slave/containerizer/provisioner/docker/puller.hpp \ slave/containerizer/provisioner/docker/registry_client.hpp \ slave/containerizer/provisioner/docker/store.hpp \ slave/containerizer/provisioner/docker/token_manager.hpp \ @@ -1776,7 +1776,6 @@ mesos_tests_DEPENDENCIES = # Initialized to allow += below. if OS_LINUX mesos_tests_SOURCES += tests/containerizer/cgroups_isolator_tests.cpp mesos_tests_SOURCES += tests/containerizer/cgroups_tests.cpp - mesos_tests_SOURCES += tests/containerizer/docker_provisioner_tests.cpp mesos_tests_SOURCES += tests/containerizer/filesystem_isolator_tests.cpp mesos_tests_SOURCES += tests/containerizer/fs_tests.cpp mesos_tests_SOURCES += tests/containerizer/launch_tests.cpp http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/src/slave/containerizer/provisioner.cpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/provisioner.cpp b/src/slave/containerizer/provisioner.cpp deleted file mode 100644 index 6a0faac..0000000 --- a/src/slave/containerizer/provisioner.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stout/hashset.hpp> -#include <stout/stringify.hpp> -#include <stout/strings.hpp> - -#include "slave/containerizer/provisioner.hpp" - -#include "slave/containerizer/provisioners/appc/provisioner.hpp" - -#include "slave/containerizer/provisioners/docker/provisioner.hpp" - -using namespace process; - -using std::string; - -namespace mesos { -namespace internal { -namespace slave { - -Try<hashmap<Image::Type, Owned<Provisioner>>> Provisioner::create( - const Flags& flags, - Fetcher* fetcher) -{ - if (flags.provisioners.isNone()) { - return hashmap<Image::Type, Owned<Provisioner>>(); - } - - hashmap<Image::Type, - Try<Owned<Provisioner>>(*)(const Flags&, Fetcher*)> creators; - - // Register all supported creators. - creators.put(Image::APPC, &appc::AppcProvisioner::create); - creators.put(Image::DOCKER, &docker::DockerProvisioner::create); - - hashmap<Image::Type, Owned<Provisioner>> provisioners; - - // NOTE: Change in '--provisioners' flag may result in leaked rootfs - // files on the disk but it's at least safe because files managed by - // different provisioners are totally separated. - foreach (const string& type, - strings::tokenize(flags.provisioners.get(), ",")) { - Image::Type imageType; - if (!Image::Type_Parse(strings::upper(type), &imageType)) { - return Error("Unknown provisioner '" + type + "'"); - } - - if (!creators.contains(imageType)) { - return Error("Unsupported provisioner '" + type + "'"); - } - - Try<Owned<Provisioner>> provisioner = creators[imageType](flags, fetcher); - if (provisioner.isError()) { - return Error("Failed to create '" + stringify(imageType) + - "' provisioner: " + provisioner.error()); - } - - provisioners[imageType] = provisioner.get(); - } - - return provisioners; -} - -} // namespace slave { -} // namespace internal { -} // namespace mesos { http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/src/slave/containerizer/provisioner/docker/local_puller.cpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/provisioner/docker/local_puller.cpp b/src/slave/containerizer/provisioner/docker/local_puller.cpp new file mode 100644 index 0000000..4a0b7d1 --- /dev/null +++ b/src/slave/containerizer/provisioner/docker/local_puller.cpp @@ -0,0 +1,349 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <list> +#include <vector> + +#include <glog/logging.h> + +#include <stout/json.hpp> +#include <stout/os.hpp> +#include <stout/result.hpp> + +#include <process/collect.hpp> +#include <process/defer.hpp> +#include <process/dispatch.hpp> +#include <process/subprocess.hpp> + +#include "common/status_utils.hpp" + +#include "slave/containerizer/provisioner/docker/local_puller.hpp" +#include "slave/containerizer/provisioner/docker/paths.hpp" +#include "slave/containerizer/provisioner/docker/store.hpp" + +using namespace process; + +using std::list; +using std::pair; +using std::string; +using std::vector; + +namespace mesos { +namespace internal { +namespace slave { +namespace docker { + +class LocalPullerProcess : public process::Process<LocalPullerProcess> +{ +public: + LocalPullerProcess(const Flags& _flags) : flags(_flags) {} + + ~LocalPullerProcess() {} + + process::Future<list<pair<string, string>>> pull( + const Image::Name& name, + const string& directory); + +private: + process::Future<Nothing> untarImage( + const std::string& tarPath, + const std::string& directory); + + process::Future<list<pair<string, string>>> putImage( + const Image::Name& name, + const std::string& directory); + + process::Future<list<pair<string, string>>> putLayers( + const std::string& directory, + const std::vector<std::string>& layerIds); + + process::Future<pair<string, string>> putLayer( + const std::string& directory, + const std::string& layerId); + + const Flags flags; +}; + + +LocalPuller::LocalPuller(const Flags& flags) +{ + process = Owned<LocalPullerProcess>(new LocalPullerProcess(flags)); + process::spawn(process.get()); +} + + +LocalPuller::~LocalPuller() +{ + process::terminate(process.get()); + process::wait(process.get()); +} + + +Future<list<pair<string, string>>> LocalPuller::pull( + const Image::Name& name, + const string& directory) +{ + return dispatch(process.get(), &LocalPullerProcess::pull, name, directory); +} + + +Future<list<pair<string, string>>> LocalPullerProcess::pull( + const Image::Name& name, + const string& directory) +{ + string tarPath = paths::getImageArchiveTarPath( + flags.docker_local_archives_dir, + stringify(name)); + + if (!os::exists(tarPath)) { + return Failure("Failed to find archive for image '" + stringify(name) + + "' at '" + tarPath + "'"); + } + + return untarImage(tarPath, directory) + .then(defer(self(), &Self::putImage, name, directory)); +} + + +Future<Nothing> LocalPullerProcess::untarImage( + const string& tarPath, + const string& directory) +{ + VLOG(1) << "Untarring image from '" << directory + << "' to '" << tarPath << "'"; + + // Untar store_discovery_local_dir/name.tar into directory/. + // TODO(tnachen): Terminate tar process when slave exits. + vector<string> argv = { + "tar", + "-C", + directory, + "-x", + "-f", + tarPath + }; + + Try<Subprocess> s = subprocess( + "tar", + argv, + Subprocess::PATH("/dev/null"), + Subprocess::PATH("/dev/null"), + Subprocess::PATH("/dev/null")); + + if (s.isError()) { + return Failure("Failed to create tar subprocess: " + s.error()); + } + + return s.get().status() + .then([tarPath](const Option<int>& status) -> Future<Nothing> { + if (status.isNone()) { + return Failure("Failed to reap status for tar subprocess in " + + tarPath); + } + if (!WIFEXITED(status.get()) || WEXITSTATUS(status.get()) != 0) { + return Failure("Untar image failed with exit code: " + + WSTRINGIFY(status.get())); + } + + return Nothing(); + }); +} + + +static Result<string> getParentId( + const string& directory, + const string& layerId) +{ + Try<string> manifest = + os::read(paths::getImageArchiveLayerManifestPath(directory, layerId)); + if (manifest.isError()) { + return Error("Failed to read manifest: " + manifest.error()); + } + + Try<JSON::Object> json = JSON::parse<JSON::Object>(manifest.get()); + if (json.isError()) { + return Error("Failed to parse manifest: " + json.error()); + } + + Result<JSON::String> parentId = json.get().find<JSON::String>("parent"); + if (parentId.isNone() || (parentId.isSome() && parentId.get() == "")) { + return None(); + } else if (parentId.isError()) { + return Error("Failed to read parent of layer: " + parentId.error()); + } + return parentId.get().value; +} + + +Future<list<pair<string, string>>> LocalPullerProcess::putImage( + const Image::Name& name, + const string& directory) +{ + Try<string> value = + os::read(paths::getImageArchiveRepositoriesPath(directory)); + if (value.isError()) { + return Failure("Failed to read repository JSON: " + value.error()); + } + + Try<JSON::Object> json = JSON::parse<JSON::Object>(value.get()); + if (json.isError()) { + return Failure("Failed to parse JSON: " + json.error()); + } + + Result<JSON::Object> repositoryValue = + json.get().find<JSON::Object>(name.repository()); + if (repositoryValue.isError()) { + return Failure("Failed to find repository: " + repositoryValue.error()); + } else if (repositoryValue.isNone()) { + return Failure("Repository '" + name.repository() + "' is not found"); + } + + JSON::Object repositoryJson = repositoryValue.get(); + + // We don't use JSON find here because a tag might contain a '.'. + std::map<string, JSON::Value>::const_iterator entry = + repositoryJson.values.find(name.tag()); + if (entry == repositoryJson.values.end()) { + return Failure("Tag '" + name.tag() + "' is not found"); + } else if (!entry->second.is<JSON::String>()) { + return Failure("Tag JSON value expected to be JSON::String"); + } + + string layerId = entry->second.as<JSON::String>().value; + + Try<string> manifest = + os::read(paths::getImageArchiveLayerManifestPath(directory, layerId)); + if (manifest.isError()) { + return Failure("Failed to read manifest: " + manifest.error()); + } + + Try<JSON::Object> manifestJson = JSON::parse<JSON::Object>(manifest.get()); + if (manifestJson.isError()) { + return Failure("Failed to parse manifest: " + manifestJson.error()); + } + + vector<string> layerIds; + layerIds.push_back(layerId); + Result<string> parentId = getParentId(directory, layerId); + while (parentId.isSome()) { + layerIds.insert(layerIds.begin(), parentId.get()); + parentId = getParentId(directory, parentId.get()); + } + + if (parentId.isError()) { + return Failure("Failed to find parent layer id of layer '" + layerId + + "': " + parentId.error()); + } + + return putLayers(directory, layerIds); +} + + +Future<list<pair<string, string>>> LocalPullerProcess::putLayers( + const string& directory, + const vector<string>& layerIds) +{ + list<Future<pair<string, string>>> futures; + foreach (const string& layerId, layerIds) { + futures.push_back(putLayer(directory, layerId)); + } + + return collect(futures); +} + + +Future<pair<string, string>> LocalPullerProcess::putLayer( + const string& directory, + const string& layerId) +{ + // We untar the layer from source into a directory, then move the + // layer into store. We do this instead of untarring directly to + // store to make sure we don't end up with partially untarred layer + // rootfs. + + string localRootfsPath = + paths::getImageArchiveLayerRootfsPath(directory, layerId); + + // Image layer has been untarred but is not present in the store directory. + if (os::exists(localRootfsPath)) { + LOG(WARNING) << "Image layer '" << layerId << "' rootfs present at but not " + << "in store directory '" << localRootfsPath << "'. Removing " + << "staged rootfs and untarring layer again."; + + Try<Nothing> rmdir = os::rmdir(localRootfsPath); + if (rmdir.isError()) { + return Failure("Failed to remove incomplete staged rootfs for layer '" + + layerId + "': " + rmdir.error()); + } + } + + Try<Nothing> mkdir = os::mkdir(localRootfsPath); + if (mkdir.isError()) { + return Failure("Failed to create rootfs path '" + localRootfsPath + + "': " + mkdir.error()); + } + + // Untar directory/id/layer.tar into directory/id/rootfs. + // The tar file will be removed when the staging directory is + // removed. + vector<string> argv = { + "tar", + "-C", + localRootfsPath, + "-x", + "-f", + paths::getImageArchiveLayerTarPath(directory, layerId) + }; + + Try<Subprocess> s = subprocess( + "tar", + argv, + Subprocess::PATH("/dev/null"), + Subprocess::PATH("/dev/null"), + Subprocess::PATH("/dev/null")); + + if (s.isError()) { + return Failure("Failed to create tar subprocess: " + s.error()); + } + + return s.get().status() + .then([directory, layerId]( + const Option<int>& status) -> Future<pair<string, string>> { + if (status.isNone()) { + return Failure("Failed to reap subprocess to untar image"); + } else if (!WIFEXITED(status.get()) || WEXITSTATUS(status.get()) != 0) { + return Failure("Untar failed with exit code: " + + WSTRINGIFY(status.get())); + } + + const string rootfsPath = + paths::getImageArchiveLayerRootfsPath(directory, layerId); + + if (!os::exists(rootfsPath)) { + return Failure("Failed to find the rootfs path after extracting layer" + " '" + layerId + "'"); + } + + return pair<string, string>(layerId, rootfsPath); + }); +} + +} // namespace docker { +} // namespace slave { +} // namespace internal { +} // namespace mesos { http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/src/slave/containerizer/provisioner/docker/local_puller.hpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/provisioner/docker/local_puller.hpp b/src/slave/containerizer/provisioner/docker/local_puller.hpp new file mode 100644 index 0000000..4574e8a --- /dev/null +++ b/src/slave/containerizer/provisioner/docker/local_puller.hpp @@ -0,0 +1,66 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __PROVISIONER_DOCKER_LOCAL_PULLER_HPP__ +#define __PROVISIONER_DOCKER_LOCAL_PULLER_HPP__ + +#include "slave/containerizer/provisioner/store.hpp" + +#include "slave/containerizer/provisioner/docker/message.hpp" +#include "slave/containerizer/provisioner/docker/puller.hpp" + +#include "slave/flags.hpp" + +namespace mesos { +namespace internal { +namespace slave { +namespace docker { + +// Forward declaration. +class LocalPullerProcess; + + +/** + * LocalPuller assumes Docker images are stored in a local directory + * (configured with flags.docker_local_archives_dir), with all the + * images saved as tars with file names in the form of <repo>:<tag>.tar. + */ +class LocalPuller : public Puller +{ +public: + explicit LocalPuller(const Flags& flags); + + ~LocalPuller(); + + process::Future<std::list<std::pair<std::string, std::string>>> pull( + const Image::Name& name, + const std::string& directory); + +private: + LocalPuller& operator=(const LocalPuller&) = delete; // Not assignable. + LocalPuller(const LocalPuller&) = delete; // Not copyable. + + process::Owned<LocalPullerProcess> process; +}; + +} // namespace docker { +} // namespace slave { +} // namespace internal { +} // namespace mesos { + +#endif // __PROVISIONER_DOCKER_LOCAL_PULLER_HPP__ http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/src/slave/containerizer/provisioner/docker/local_store.cpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/provisioner/docker/local_store.cpp b/src/slave/containerizer/provisioner/docker/local_store.cpp deleted file mode 100644 index 6a73dbb..0000000 --- a/src/slave/containerizer/provisioner/docker/local_store.cpp +++ /dev/null @@ -1,460 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <list> -#include <vector> - -#include <glog/logging.h> - -#include <stout/json.hpp> -#include <stout/os.hpp> -#include <stout/result.hpp> - -#include <process/collect.hpp> -#include <process/defer.hpp> -#include <process/dispatch.hpp> - -#include "common/status_utils.hpp" - -#include "slave/containerizer/provisioner/docker/local_store.hpp" -#include "slave/containerizer/provisioner/docker/metadata_manager.hpp" -#include "slave/containerizer/provisioner/docker/paths.hpp" -#include "slave/containerizer/provisioner/docker/store.hpp" - -#include "slave/flags.hpp" - -using namespace process; - -using std::list; -using std::string; -using std::vector; - -namespace mesos { -namespace internal { -namespace slave { -namespace docker { - -class LocalStoreProcess : public process::Process<LocalStoreProcess> -{ -public: - LocalStoreProcess( - const Flags& _flags, - Owned<MetadataManager> _metadataManager) - : flags(_flags), metadataManager(_metadataManager) {} - - ~LocalStoreProcess() {} - - static Try<process::Owned<LocalStoreProcess>> create(const Flags& flags); - - process::Future<vector<string>> get(const Image& image); - - process::Future<Nothing> recover(); - -private: - process::Future<DockerImage> _get( - const DockerImage::Name& name, - const Option<DockerImage>& image); - - process::Future<Nothing> untarImage( - const std::string& tarPath, - const std::string& staging); - - process::Future<DockerImage> putImage( - const DockerImage::Name& name, - const std::string& staging); - - Result<std::string> getParentId( - const std::string& staging, - const std::string& layerId); - - process::Future<Nothing> putLayers( - const std::string& staging, - const std::list<std::string>& layerIds); - - process::Future<Nothing> putLayer( - const std::string& staging, - const std::string& id); - - process::Future<Nothing> moveLayer( - const std::string& staging, - const std::string& id); - - const Flags flags; - process::Owned<MetadataManager> metadataManager; -}; - - -Try<Owned<slave::Store>> LocalStore::create(const Flags& flags) -{ - if (!os::exists(flags.docker_store_dir)) { - Try<Nothing> mkdir = os::mkdir(flags.docker_store_dir); - if (mkdir.isError()) { - return Error("Failed to create Docker store directory: " + mkdir.error()); - } - } - - if (!os::exists(paths::getStagingDir(flags.docker_store_dir))) { - Try<Nothing> mkdir = - os::mkdir(paths::getStagingDir(flags.docker_store_dir)); - if (mkdir.isError()) { - return Error("Failed to create Docker store staging directory: " + - mkdir.error()); - } - } - - Try<Owned<MetadataManager>> metadataManager = MetadataManager::create(flags); - if (metadataManager.isError()) { - return Error(metadataManager.error()); - } - - Owned<LocalStoreProcess> process( - new LocalStoreProcess(flags, metadataManager.get())); - - return Owned<slave::Store>(new LocalStore(process)); -} - - -LocalStore::LocalStore(Owned<LocalStoreProcess> _process) : process(_process) -{ - process::spawn(CHECK_NOTNULL(process.get())); -} - - -LocalStore::~LocalStore() -{ - process::terminate(process.get()); - process::wait(process.get()); -} - - -Future<vector<string>> LocalStore::get(const Image& image) -{ - return dispatch(process.get(), &LocalStoreProcess::get, image); -} - - -Future<Nothing> LocalStore::recover() -{ - return dispatch(process.get(), &LocalStoreProcess::recover); -} - - -Future<vector<string>> LocalStoreProcess::get(const Image& image) -{ - CHECK_EQ(image.type(), Image::DOCKER); - - Try<DockerImage::Name> dockerName = parseName(image.docker().name()); - if (dockerName.isError()) { - return Failure("Unable to parse docker image name: " + dockerName.error()); - } - - return metadataManager->get(dockerName.get()) - .then(defer(self(), &Self::_get, dockerName.get(), lambda::_1)) - .then([](const DockerImage& dockerImage) { - vector<string> layers; - foreach (const string& layer, dockerImage.layer_ids()) { - layers.push_back(layer); - } - - return layers; - }); -} - - -Future<DockerImage> LocalStoreProcess::_get( - const DockerImage::Name& name, - const Option<DockerImage>& image) -{ - if (image.isSome()) { - return image.get(); - } - - string tarPath = paths::getLocalImageTarPath( - flags.docker_store_discovery_local_dir, - stringify(name)); - - if (!os::exists(tarPath)) { - VLOG(1) << "Unable to find image in local store with path: " << tarPath; - return Failure("No Docker image tar archive found"); - } - - // Create a temporary staging directory. - Try<string> staging = - os::mkdtemp(paths::getTempStaging(flags.docker_store_dir)); - if (staging.isError()) { - return Failure("Failed to create a staging directory"); - } - - return untarImage(tarPath, staging.get()) - .then(defer(self(), &Self::putImage, name, staging.get())); -} - - -Future<Nothing> LocalStoreProcess::recover() -{ - return metadataManager->recover(); -} - - -Future<Nothing> LocalStoreProcess::untarImage( - const string& tarPath, - const string& staging) -{ - VLOG(1) << "Untarring image at: " << tarPath; - - // Untar store_discovery_local_dir/name.tar into staging/. - vector<string> argv = { - "tar", - "-C", - staging, - "-x", - "-f", - tarPath - }; - - Try<Subprocess> s = subprocess( - "tar", - argv, - Subprocess::PATH("/dev/null"), - Subprocess::PATH("/dev/null"), - Subprocess::PATH("/dev/null")); - - if (s.isError()) { - return Failure("Failed to create tar subprocess: " + s.error()); - } - - return s.get().status() - .then([=](const Option<int>& status) -> Future<Nothing> { - if (status.isNone()) { - return Failure("Failed to reap status for tar subprocess in " + - tarPath); - } - if (!WIFEXITED(status.get()) || WEXITSTATUS(status.get()) != 0) { - return Failure("Untar image failed with exit code: " + - WSTRINGIFY(status.get())); - } - - return Nothing(); - }); -} - - -Future<DockerImage> LocalStoreProcess::putImage( - const DockerImage::Name& name, - const string& staging) -{ - Try<string> value = os::read(paths::getLocalImageRepositoriesPath(staging)); - if (value.isError()) { - return Failure("Failed to read repository JSON: " + value.error()); - } - - Try<JSON::Object> json = JSON::parse<JSON::Object>(value.get()); - if (json.isError()) { - return Failure("Failed to parse JSON: " + json.error()); - } - - Result<JSON::Object> repositoryValue = - json.get().find<JSON::Object>(name.repository()); - if (repositoryValue.isError()) { - return Failure("Failed to find repository: " + repositoryValue.error()); - } else if (repositoryValue.isNone()) { - return Failure("Repository '" + name.repository() + "' is not found"); - } - - JSON::Object repositoryJson = repositoryValue.get(); - - // We don't use JSON find here because a tag might contain a '.'. - std::map<string, JSON::Value>::const_iterator entry = - repositoryJson.values.find(name.tag()); - if (entry == repositoryJson.values.end()) { - return Failure("Tag '" + name.tag() + "' is not found"); - } else if (!entry->second.is<JSON::String>()) { - return Failure("Tag JSON value expected to be JSON::String"); - } - - string layerId = entry->second.as<JSON::String>().value; - - Try<string> manifest = - os::read(paths::getLocalImageLayerManifestPath(staging, layerId)); - if (manifest.isError()) { - return Failure("Failed to read manifest: " + manifest.error()); - } - - Try<JSON::Object> manifestJson = JSON::parse<JSON::Object>(manifest.get()); - if (manifestJson.isError()) { - return Failure("Failed to parse manifest: " + manifestJson.error()); - } - - list<string> layerIds; - layerIds.push_back(layerId); - Result<string> parentId = getParentId(staging, layerId); - while(parentId.isSome()) { - layerIds.push_front(parentId.get()); - parentId = getParentId(staging, parentId.get()); - } - if (parentId.isError()) { - return Failure("Failed to obtain parent layer id: " + parentId.error()); - } - - return putLayers(staging, layerIds) - .then([=]() -> Future<DockerImage> { - return metadataManager->put(name, layerIds); - }); -} - - -Result<string> LocalStoreProcess::getParentId( - const string& staging, - const string& layerId) -{ - Try<string> manifest = - os::read(paths::getLocalImageLayerManifestPath(staging, layerId)); - if (manifest.isError()) { - return Error("Failed to read manifest: " + manifest.error()); - } - - Try<JSON::Object> json = JSON::parse<JSON::Object>(manifest.get()); - if (json.isError()) { - return Error("Failed to parse manifest: " + json.error()); - } - - Result<JSON::String> parentId = json.get().find<JSON::String>("parent"); - if (parentId.isNone() || (parentId.isSome() && parentId.get() == "")) { - return None(); - } else if (parentId.isError()) { - return Error("Failed to read parent of layer: " + parentId.error()); - } - return parentId.get().value; -} - - -Future<Nothing> LocalStoreProcess::putLayers( - const string& staging, - const list<string>& layerIds) -{ - list<Future<Nothing>> futures{ Nothing() }; - foreach (const string& layer, layerIds) { - futures.push_back( - futures.back().then( - defer(self(), &Self::putLayer, staging, layer))); - } - - return collect(futures) - .then([]() -> Future<Nothing> { return Nothing(); }); -} - - -Future<Nothing> LocalStoreProcess::putLayer( - const string& staging, - const string& id) -{ - // We untar the layer from source into a staging directory, then - // move the layer into store. We do this instead of untarring - // directly to store to make sure we don't end up with partially - // untarred layer rootfs. - - // Check if image layer rootfs is already in store. - if (os::exists(paths::getImageLayerRootfsPath(flags.docker_store_dir, id))) { - VLOG(1) << "Image layer '" << id << "' rootfs already in store. " - << "Skipping put."; - return Nothing(); - } - - const string imageLayerPath = - paths::getImageLayerPath(flags.docker_store_dir, id); - if (!os::exists(imageLayerPath)) { - Try<Nothing> mkdir = os::mkdir(imageLayerPath); - if (mkdir.isError()) { - return Failure("Failed to create Image layer directory '" + - imageLayerPath + "': " + mkdir.error()); - } - } - - // Image layer has been untarred but is not present in the store directory. - string localRootfsPath = paths::getLocalImageLayerRootfsPath(staging, id); - if (os::exists(localRootfsPath)) { - LOG(WARNING) << "Image layer '" << id << "' rootfs present at but not in " - << "store directory '" << localRootfsPath << "'. Removing " - << "staged rootfs and untarring layer again."; - Try<Nothing> rmdir = os::rmdir(localRootfsPath); - if (rmdir.isError()) { - return Failure("Failed to remove incomplete staged rootfs for layer '" + - id + "': " + rmdir.error()); - } - } - - Try<Nothing> mkdir = os::mkdir(localRootfsPath); - if (mkdir.isError()) { - return Failure("Failed to create rootfs path '" + localRootfsPath + "': " + - mkdir.error()); - } - // Untar staging/id/layer.tar into staging/id/rootfs. - vector<string> argv = { - "tar", - "-C", - localRootfsPath, - "-x", - "-f", - paths::getLocalImageLayerTarPath(staging, id) - }; - - Try<Subprocess> s = subprocess( - "tar", - argv, - Subprocess::PATH("/dev/null"), - Subprocess::PATH("/dev/null"), - Subprocess::PATH("/dev/null")); - if (s.isError()) { - return Failure("Failed to create tar subprocess: " + s.error()); - } - - return s.get().status() - .then([=](const Option<int>& status) -> Future<Nothing> { - if (status.isNone()) { - return Failure("Failed to reap subprocess to untar image"); - } else if (!WIFEXITED(status.get()) || WEXITSTATUS(status.get()) != 0) { - return Failure("Untar failed with exit code: " + - WSTRINGIFY(status.get())); - } - - return moveLayer(staging, id); - }); -} - - -Future<Nothing> LocalStoreProcess::moveLayer( - const string& staging, - const string& id) -{ - Try<Nothing> status = os::rename( - paths::getLocalImageLayerRootfsPath(staging, id), - paths::getImageLayerRootfsPath(flags.docker_store_dir, id)); - - if (status.isError()) { - return Failure("Failed to move layer to store directory: " - + status.error()); - } - - return Nothing(); -} - - -} // namespace docker { -} // namespace slave { -} // namespace internal { -} // namespace mesos { http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/src/slave/containerizer/provisioner/docker/local_store.hpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/provisioner/docker/local_store.hpp b/src/slave/containerizer/provisioner/docker/local_store.hpp deleted file mode 100644 index 5f6152b..0000000 --- a/src/slave/containerizer/provisioner/docker/local_store.hpp +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __MESOS_DOCKER_LOCAL_STORE_HPP__ -#define __MESOS_DOCKER_LOCAL_STORE_HPP__ - -#include "slave/containerizer/provisioner/store.hpp" - -namespace mesos { -namespace internal { -namespace slave { -namespace docker { - -// Forward declaration. -class LocalStoreProcess; - - -/** - * LocalStore assumes Docker images are stored in a local directory - * (configured with flags.docker_discovery_local_dir), with all the - * images saved as tar with the name as the image name with tag (e.g: - * ubuntu:14.04.tar). - */ -class LocalStore : public slave::Store -{ -public: - static Try<process::Owned<Store>> create(const Flags& flags); - - virtual ~LocalStore(); - - virtual process::Future<Nothing> recover(); - - virtual process::Future<std::vector<std::string>> get(const Image& image); - -private: - explicit LocalStore(process::Owned<LocalStoreProcess> _process); - - LocalStore& operator=(const LocalStore&) = delete; // Not assignable. - LocalStore(const LocalStore&) = delete; // Not copyable. - - process::Owned<LocalStoreProcess> process; -}; - -} // namespace docker { -} // namespace slave { -} // namespace internal { -} // namespace mesos { - -#endif // __MESOS_DOCKER_LOCAL_STORE_HPP__ http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/src/slave/containerizer/provisioner/docker/message.hpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/provisioner/docker/message.hpp b/src/slave/containerizer/provisioner/docker/message.hpp index c1596df..6368bf4 100644 --- a/src/slave/containerizer/provisioner/docker/message.hpp +++ b/src/slave/containerizer/provisioner/docker/message.hpp @@ -29,9 +29,9 @@ namespace internal { namespace slave { namespace docker { -inline DockerImage::Name parseName(const std::string& value) +inline Image::Name parseName(const std::string& value) { - DockerImage::Name imageName; + Image::Name imageName; Option<std::string> registry = None(); std::vector<std::string> components = strings::split(value, "/"); if (components.size() > 2) { @@ -53,7 +53,7 @@ inline DockerImage::Name parseName(const std::string& value) inline std::ostream& operator<<( std::ostream& stream, - const DockerImage::Name& name) + const Image::Name& name) { if (name.has_registry()) { return stream << name.registry() << "/" << name.repository() << ":" http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/src/slave/containerizer/provisioner/docker/message.proto ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/provisioner/docker/message.proto b/src/slave/containerizer/provisioner/docker/message.proto index d771968..bbac2e6 100644 --- a/src/slave/containerizer/provisioner/docker/message.proto +++ b/src/slave/containerizer/provisioner/docker/message.proto @@ -25,7 +25,7 @@ package mesos.internal.slave.docker; * The layerIds are ordered, with the root layer id (no parent layer id) first * and the leaf layer id last. */ -message DockerImage { +message Image { message Name { optional string registry = 1; required string repository = 2; @@ -38,6 +38,7 @@ message DockerImage { repeated string layer_ids = 2; } -message DockerImages { - repeated DockerImage images = 1; + +message Images { + repeated Image images = 1; } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/src/slave/containerizer/provisioner/docker/metadata_manager.cpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/provisioner/docker/metadata_manager.cpp b/src/slave/containerizer/provisioner/docker/metadata_manager.cpp index 197931c..2b2de52 100644 --- a/src/slave/containerizer/provisioner/docker/metadata_manager.cpp +++ b/src/slave/containerizer/provisioner/docker/metadata_manager.cpp @@ -16,6 +16,8 @@ * limitations under the License. */ +#include "slave/containerizer/provisioner/docker/metadata_manager.hpp" + #include <vector> #include <glog/logging.h> @@ -33,7 +35,6 @@ #include "slave/containerizer/provisioner/docker/paths.hpp" #include "slave/containerizer/provisioner/docker/message.hpp" -#include "slave/containerizer/provisioner/docker/metadata_manager.hpp" #include "slave/state.hpp" @@ -48,48 +49,41 @@ namespace internal { namespace slave { namespace docker { - class MetadataManagerProcess : public process::Process<MetadataManagerProcess> { public: - ~MetadataManagerProcess() {} + MetadataManagerProcess(const Flags& _flags) : flags(_flags) {} - static Try<process::Owned<MetadataManagerProcess>> create( - const Flags& flags); + ~MetadataManagerProcess() {} - Future<DockerImage> put( - const DockerImage::Name& name, - const std::list<std::string>& layerIds); + Future<Nothing> recover(); - Future<Option<DockerImage>> get(const DockerImage::Name& name); + Future<Image> put( + const Image::Name& name, + const std::vector<std::string>& layerIds); - Future<Nothing> recover(); + Future<Option<Image>> get(const Image::Name& name); // TODO(chenlily): Implement removal of unreferenced images. private: - MetadataManagerProcess(const Flags& flags); - // Write out metadata manager state to persistent store. Try<Nothing> persist(); const Flags flags; // This is a lookup table for images that are stored in memory. It is keyed - // by the name of the DockerImage. - // For example, "ubuntu:14.04" -> ubuntu14:04 DockerImage. - hashmap<std::string, DockerImage> storedImages; + // by the name of the Image. + // For example, "ubuntu:14.04" -> ubuntu14:04 Image. + hashmap<std::string, Image> storedImages; }; Try<Owned<MetadataManager>> MetadataManager::create(const Flags& flags) { - Try<Owned<MetadataManagerProcess>> process = - MetadataManagerProcess::create(flags); - if (process.isError()) { - return Error("Failed to create Metadata Manager: " + process.error()); - } - return Owned<MetadataManager>(new MetadataManager(process.get())); + Owned<MetadataManagerProcess> process(new MetadataManagerProcess(flags)); + + return Owned<MetadataManager>(new MetadataManager(process)); } @@ -113,42 +107,31 @@ Future<Nothing> MetadataManager::recover() } -Future<DockerImage> MetadataManager::put( - const DockerImage::Name& name, - const list<string>& layerIds) +Future<Image> MetadataManager::put( + const Image::Name& name, + const vector<string>& layerIds) { return dispatch( - process.get(), &MetadataManagerProcess::put, name, layerIds); + process.get(), + &MetadataManagerProcess::put, + name, + layerIds); } -Future<Option<DockerImage>> MetadataManager::get(const DockerImage::Name& name) +Future<Option<Image>> MetadataManager::get(const Image::Name& name) { return dispatch(process.get(), &MetadataManagerProcess::get, name); } -MetadataManagerProcess::MetadataManagerProcess(const Flags& flags) - : flags(flags) {} - - -Try<Owned<MetadataManagerProcess>> MetadataManagerProcess::create( - const Flags& flags) -{ - Owned<MetadataManagerProcess> metadataManager = - Owned<MetadataManagerProcess>(new MetadataManagerProcess(flags)); - - return metadataManager; -} - - -Future<DockerImage> MetadataManagerProcess::put( - const DockerImage::Name& name, - const list<string>& layerIds) +Future<Image> MetadataManagerProcess::put( + const Image::Name& name, + const vector<string>& layerIds) { const string imageName = stringify(name); - DockerImage dockerImage; + Image dockerImage; dockerImage.mutable_name()->CopyFrom(name); foreach (const string& layerId, layerIds) { dockerImage.add_layer_ids(layerId); @@ -158,15 +141,15 @@ Future<DockerImage> MetadataManagerProcess::put( Try<Nothing> status = persist(); if (status.isError()) { - return Failure("Failed to save state of Docker images" + status.error()); + return Failure("Failed to save state of Docker images: " + status.error()); } return dockerImage; } -Future<Option<DockerImage>> MetadataManagerProcess::get( - const DockerImage::Name& name) +Future<Option<Image>> MetadataManagerProcess::get( + const Image::Name& name) { const string imageName = stringify(name); @@ -180,13 +163,13 @@ Future<Option<DockerImage>> MetadataManagerProcess::get( Try<Nothing> MetadataManagerProcess::persist() { - DockerImages images; + Images images; - foreachvalue (const DockerImage& image, storedImages) { + foreachvalue (const Image& image, storedImages) { images.add_images()->CopyFrom(image); } - Try<Nothing> status = mesos::internal::slave::state::checkpoint( + Try<Nothing> status = state::checkpoint( paths::getStoredImagesPath(flags.docker_store_dir), images); if (status.isError()) { return Error("Failed to perform checkpoint: " + status.error()); @@ -200,21 +183,19 @@ Future<Nothing> MetadataManagerProcess::recover() { string storedImagesPath = paths::getStoredImagesPath(flags.docker_store_dir); - storedImages.clear(); if (!os::exists(storedImagesPath)) { LOG(INFO) << "No images to load from disk. Docker provisioner image " - << "storage path: " << storedImagesPath << " does not exist."; + << "storage path '" << storedImagesPath << "' does not exist"; return Nothing(); } - Result<DockerImages> images = - ::protobuf::read<DockerImages>(storedImagesPath); + Result<Images> images = ::protobuf::read<Images>(storedImagesPath); if (images.isError()) { return Failure("Failed to read protobuf for Docker provisioner image: " + images.error()); } - foreach (const DockerImage image, images.get().images()) { + foreach (const Image image, images.get().images()) { vector<string> missingLayerIds; foreach (const string layerId, image.layer_ids()) { const string rootfsPath = @@ -226,21 +207,21 @@ Future<Nothing> MetadataManagerProcess::recover() } if (!missingLayerIds.empty()) { - LOG(WARNING) << "Skipped loading image: " << stringify(image.name()) - << " due to missing layers: " << stringify(missingLayerIds); + LOG(WARNING) << "Skipped loading image '" << stringify(image.name()) + << "' due to missing layers: " << stringify(missingLayerIds); continue; } const string imageName = stringify(image.name()); if (storedImages.contains(imageName)) { - LOG(WARNING) << "Found duplicate image in recovery for image name '" << imageName - << "'"; + LOG(WARNING) << "Found duplicate image in recovery for image name '" + << imageName << "'"; } else { storedImages[imageName] = image; } } - LOG(INFO) << "Loaded " << storedImages.size() << " Docker images."; + LOG(INFO) << "Loaded " << storedImages.size() << " Docker images"; return Nothing(); } http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/src/slave/containerizer/provisioner/docker/metadata_manager.hpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/provisioner/docker/metadata_manager.hpp b/src/slave/containerizer/provisioner/docker/metadata_manager.hpp index 647a478..885080d 100644 --- a/src/slave/containerizer/provisioner/docker/metadata_manager.hpp +++ b/src/slave/containerizer/provisioner/docker/metadata_manager.hpp @@ -16,8 +16,8 @@ * limitations under the License. */ -#ifndef __MESOS_DOCKER_METADATA_MANAGER_HPP__ -#define __MESOS_DOCKER_METADATA_MANAGER_HPP__ +#ifndef __PROVISIONER_DOCKER_METADATA_MANAGER_HPP__ +#define __PROVISIONER_DOCKER_METADATA_MANAGER_HPP__ #include <list> #include <string> @@ -49,7 +49,7 @@ class MetadataManagerProcess; /** * The MetadataManager tracks the Docker images cached by the * provisioner that are stored on disk. It keeps track of the layers - * that Docker images are composed of and recovers DockerImage objects + * that Docker images are composed of and recovers Image objects * upon initialization by checking for dependent layers stored on disk. * Currently, image layers are stored indefinitely, with no garbage * collection of unreferenced image layers. @@ -59,10 +59,15 @@ class MetadataManager public: static Try<process::Owned<MetadataManager>> create(const Flags& flags); - ~MetadataManager(); + ~MetadataManager(); /** - * Create a Image, put it in metadata manager and persist the reference + * Recover all stored Image and its layer references. + */ + process::Future<Nothing> recover(); + + /** + * Create an Image, put it in metadata manager and persist the reference * store state to disk. * * @param name the name of the Docker image to place in the reference @@ -71,22 +76,17 @@ public: * order where the root layer's id (no parent layer) is first * and the leaf layer's id is last. */ - process::Future<DockerImage> put( - const DockerImage::Name& name, - const std::list<std::string>& layerIds); + process::Future<Image> put( + const Image::Name& name, + const std::vector<std::string>& layerIds); /** - * Retrieve DockerImage based on image name if it is among the DockerImages + * Retrieve Image based on image name if it is among the Images * stored in memory. * * @param name the name of the Docker image to retrieve */ - process::Future<Option<DockerImage>> get(const DockerImage::Name& name); - - /** - * Recover all stored DockerImage and its layer references. - */ - process::Future<Nothing> recover(); + process::Future<Option<Image>> get(const Image::Name& name); private: explicit MetadataManager(process::Owned<MetadataManagerProcess> process); @@ -103,4 +103,4 @@ private: } // namespace internal { } // namespace mesos { -#endif // __MESOS_DOCKER_METADATA_MANAGER_HPP__ +#endif // __PROVISIONER_DOCKER_METADATA_MANAGER_HPP__ http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/src/slave/containerizer/provisioner/docker/paths.cpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/provisioner/docker/paths.cpp b/src/slave/containerizer/provisioner/docker/paths.cpp index 81a2176..5733fb7 100644 --- a/src/slave/containerizer/provisioner/docker/paths.cpp +++ b/src/slave/containerizer/provisioner/docker/paths.cpp @@ -33,51 +33,60 @@ string getStagingDir(const string& storeDir) return path::join(storeDir, "staging"); } -string getTempStaging(const string& storeDir) + +string getStagingTempDir(const string& storeDir) { return path::join(getStagingDir(storeDir), "XXXXXX"); } -string getLocalImageTarPath( + +string getImageArchiveTarPath( const string& discoveryDir, const string& name) { return path::join(discoveryDir, name + ".tar"); } -string getLocalImageRepositoriesPath(const string& staging) + +string getImageArchiveRepositoriesPath(const string& archivePath) { - return path::join(staging, "repositories"); + return path::join(archivePath, "repositories"); } -std::string getLocalImageLayerPath( - const string& staging, + +std::string getImageArchiveLayerPath( + const string& archivePath, const string& layerId) { - return path::join(staging, layerId); + return path::join(archivePath, layerId); } -string getLocalImageLayerManifestPath( - const string& staging, + +string getImageArchiveLayerManifestPath( + const string& archivePath, const string& layerId) { - return path::join(getLocalImageLayerPath(staging, layerId), "json"); + return path::join(getImageArchiveLayerPath(archivePath, layerId), "json"); } -string getLocalImageLayerTarPath( - const string& staging, + +string getImageArchiveLayerTarPath( + const string& archivePath, const string& layerId) { - return path::join(getLocalImageLayerPath(staging, layerId), "layer.tar"); + return path::join( + getImageArchiveLayerPath(archivePath, layerId), "layer.tar"); } -string getLocalImageLayerRootfsPath( - const string& staging, + +string getImageArchiveLayerRootfsPath( + const string& archivePath, const string& layerId) { - return path::join(getLocalImageLayerPath(staging, layerId), "rootfs"); + return path::join(getImageArchiveLayerPath(archivePath, layerId), "rootfs"); } + string getImageLayerPath( const string& storeDir, const string& layerId) @@ -85,6 +94,7 @@ string getImageLayerPath( return path::join(storeDir, "layers", layerId); } + string getImageLayerRootfsPath( const string& storeDir, const string& layerId) @@ -92,6 +102,7 @@ string getImageLayerRootfsPath( return path::join(getImageLayerPath(storeDir, layerId), "rootfs"); } + string getStoredImagesPath(const string& storeDir) { return path::join(storeDir, "storedImages"); http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/src/slave/containerizer/provisioner/docker/paths.hpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/provisioner/docker/paths.hpp b/src/slave/containerizer/provisioner/docker/paths.hpp index 02f129f..18beb2e 100644 --- a/src/slave/containerizer/provisioner/docker/paths.hpp +++ b/src/slave/containerizer/provisioner/docker/paths.hpp @@ -16,8 +16,8 @@ * limitations under the License. */ -#ifndef __MESOS_DOCKER_PATHS_HPP__ -#define __MESOS_DOCKER_PATHS_HPP__ +#ifndef __PROVISIONER_DOCKER_PATHS_HPP__ +#define __PROVISIONER_DOCKER_PATHS_HPP__ #include <list> #include <string> @@ -33,48 +33,60 @@ namespace paths { /** * The Docker store file system layout is as follows: * Image store dir ('--docker_store_dir' slave flag) - * |--staging (contains temp directories for downloads and extract) + * |--staging + * |-- <temp_dir_archive> + * |-- <layer_id> + * |-- rootfs * |--layers * |--<layer_id> * |--rootfs - * |--rootfses * |--storedImages (file holding on cached images) */ std::string getStagingDir(const std::string& storeDir); -std::string getTempStaging(const std::string& storeDir); -std::string getLocalImageTarPath( +std::string getStagingTempDir(const std::string& storeDir); + + +std::string getImageArchiveTarPath( const std::string& discoveryDir, const std::string& name); -std::string getLocalImageRepositoriesPath(const std::string& staging); -std::string getLocalImageLayerPath( - const std::string& staging, +std::string getImageArchiveRepositoriesPath(const std::string& archivePath); + + +std::string getImageArchiveLayerPath( + const std::string& archivePath, const std::string& layerId); -std::string getLocalImageLayerManifestPath( - const std::string& staging, + +std::string getImageArchiveLayerManifestPath( + const std::string& archivePath, const std::string& layerId); -std::string getLocalImageLayerTarPath( - const std::string& staging, + +std::string getImageArchiveLayerTarPath( + const std::string& archivePath, const std::string& layerId); -std::string getLocalImageLayerRootfsPath( - const std::string& staging, + +std::string getImageArchiveLayerRootfsPath( + const std::string& archivePath, const std::string& layerId); + std::string getImageLayerPath( const std::string& storeDir, const std::string& layerId); + std::string getImageLayerRootfsPath( const std::string& storeDir, const std::string& layerId); + std::string getStoredImagesPath(const std::string& storeDir); } // namespace paths { @@ -83,4 +95,4 @@ std::string getStoredImagesPath(const std::string& storeDir); } // namespace internal { } // namespace mesos { -#endif // __MESOS_DOCKER_PATHS_HPP__ +#endif // __PROVISIONER_DOCKER_PATHS_HPP__ http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/src/slave/containerizer/provisioner/docker/puller.cpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/provisioner/docker/puller.cpp b/src/slave/containerizer/provisioner/docker/puller.cpp new file mode 100644 index 0000000..cb05324 --- /dev/null +++ b/src/slave/containerizer/provisioner/docker/puller.cpp @@ -0,0 +1,46 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "slave/containerizer/provisioner/docker/puller.hpp" + +#include "slave/containerizer/provisioner/docker/local_puller.hpp" + +using std::string; + +using process::Owned; + +namespace mesos { +namespace internal { +namespace slave { +namespace docker { + +Try<Owned<Puller>> Puller::create(const Flags& flags) +{ + const string puller = flags.docker_puller; + + if (puller == "local") { + return Owned<Puller>(new LocalPuller(flags)); + } + + return Error("Unknown or unsupported docker puller: " + puller); +} + +} // namespace docker { +} // namespace slave { +} // namespace internal { +} // namespace mesos { http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/src/slave/containerizer/provisioner/docker/puller.hpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/provisioner/docker/puller.hpp b/src/slave/containerizer/provisioner/docker/puller.hpp new file mode 100644 index 0000000..105b4e7 --- /dev/null +++ b/src/slave/containerizer/provisioner/docker/puller.hpp @@ -0,0 +1,68 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __PROVISIONER_DOCKER_PULLER_HPP__ +#define __PROVISIONER_DOCKER_PULLER_HPP__ + +#include <list> +#include <utility> + +#include <stout/try.hpp> + +#include <process/future.hpp> +#include <process/owned.hpp> + +#include "slave/containerizer/provisioner/docker/message.hpp" + +#include "slave/flags.hpp" + +namespace mesos { +namespace internal { +namespace slave { +namespace docker { + +class Puller +{ +public: + static Try<process::Owned<Puller>> create(const Flags& flags); + + virtual ~Puller() {} + + /** + * Pull a Docker image layers into the specified directory, and + * return the list of layer ids in that image in the right + * dependency order, and also return the directory where + * the puller puts its changeset. + * + * @param name The name of the image. + * @param directory The target directory to store the layers. + * @return list of layers maped to its local directory ordered by its + * dependency. + */ + virtual process::Future<std::list<std::pair<std::string, std::string>>> pull( + const docker::Image::Name& name, + const std::string& directory) = 0; +}; + +} // namespace docker { +} // namespace slave { +} // namespace internal { +} // namespace mesos { + + +#endif // __PROVISIONER_DOCKER_PULLER_HPP__ http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/src/slave/containerizer/provisioner/docker/store.cpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/provisioner/docker/store.cpp b/src/slave/containerizer/provisioner/docker/store.cpp index 51f4d6b..cbb6768 100644 --- a/src/slave/containerizer/provisioner/docker/store.cpp +++ b/src/slave/containerizer/provisioner/docker/store.cpp @@ -16,29 +16,260 @@ * limitations under the License. */ -#include "slave/containerizer/provisioner/docker/local_store.hpp" #include "slave/containerizer/provisioner/docker/store.hpp" -using process::Owned; +#include <list> +#include <vector> +#include <glog/logging.h> + +#include <stout/json.hpp> +#include <stout/os.hpp> +#include <stout/result.hpp> + +#include <process/collect.hpp> +#include <process/defer.hpp> +#include <process/dispatch.hpp> +#include <process/subprocess.hpp> + +#include "common/status_utils.hpp" + +#include "slave/containerizer/provisioner/docker/metadata_manager.hpp" +#include "slave/containerizer/provisioner/docker/paths.hpp" +#include "slave/containerizer/provisioner/docker/puller.hpp" + +#include "slave/flags.hpp" + +using namespace process; + +using std::list; +using std::pair; using std::string; +using std::vector; namespace mesos { namespace internal { namespace slave { namespace docker { +class StoreProcess : public Process<StoreProcess> +{ +public: + StoreProcess( + const Flags& _flags, + const Owned<MetadataManager>& _metadataManager, + const Owned<Puller>& _puller) + : flags(_flags), metadataManager(_metadataManager), puller(_puller) {} + + ~StoreProcess() {} + + Future<Nothing> recover(); + + Future<vector<string>> get(const mesos::Image& image); + +private: + Future<Image> _get( + const Image::Name& name, + const Option<Image>& image); + + Future<vector<string>> __get(const Image& image); + + Future<vector<string>> moveLayers( + const std::string& staging, + const std::list<pair<string, string>>& layerPaths); + + Future<Image> storeImage( + const Image::Name& name, + const std::vector<std::string>& layerIds); + + Future<Nothing> moveLayer(const pair<string, string>& layerPath); + + const Flags flags; + Owned<MetadataManager> metadataManager; + Owned<Puller> puller; +}; + + Try<Owned<slave::Store>> Store::create(const Flags& flags) { - hashmap<string, Try<Owned<slave::Store>>(*)(const Flags&)> creators{ - {"local", &LocalStore::create} - }; + Try<Owned<Puller>> puller = Puller::create(flags); + if (puller.isError()) { + return Error("Failed to create Docker puller: " + puller.error()); + } + + if (!os::exists(flags.docker_store_dir)) { + Try<Nothing> mkdir = os::mkdir(flags.docker_store_dir); + if (mkdir.isError()) { + return Error("Failed to create Docker store directory: " + mkdir.error()); + } + } + + if (!os::exists(paths::getStagingDir(flags.docker_store_dir))) { + Try<Nothing> mkdir = + os::mkdir(paths::getStagingDir(flags.docker_store_dir)); + if (mkdir.isError()) { + return Error("Failed to create Docker store staging directory: " + + mkdir.error()); + } + } + + Try<Owned<MetadataManager>> metadataManager = MetadataManager::create(flags); + if (metadataManager.isError()) { + return Error(metadataManager.error()); + } + + Owned<StoreProcess> process( + new StoreProcess(flags, metadataManager.get(), puller.get())); + + return Owned<slave::Store>(new Store(process)); +} + + +Store::Store(const Owned<StoreProcess>& _process) : process(_process) +{ + process::spawn(CHECK_NOTNULL(process.get())); +} + + +Store::~Store() +{ + process::terminate(process.get()); + process::wait(process.get()); +} + + +Future<Nothing> Store::recover() +{ + return dispatch(process.get(), &StoreProcess::recover); +} + + +Future<vector<string>> Store::get(const mesos::Image& image) +{ + return dispatch(process.get(), &StoreProcess::get, image); +} + + +Future<vector<string>> StoreProcess::get(const mesos::Image& image) +{ + if (image.type() != mesos::Image::DOCKER) { + return Failure("Docker provisioner store only supports Docker images"); + } + + Try<Image::Name> imageName = parseName(image.docker().name()); + if (imageName.isError()) { + return Failure("Unable to parse docker image name: " + imageName.error()); + } + + return metadataManager->get(imageName.get()) + .then(defer(self(), &Self::_get, imageName.get(), lambda::_1)) + .then(defer(self(), &Self::__get, lambda::_1)); +} + + +Future<Image> StoreProcess::_get( + const Image::Name& name, + const Option<Image>& image) +{ + if (image.isSome()) { + return image.get(); + } + + Try<string> staging = + os::mkdtemp(paths::getStagingTempDir(flags.docker_store_dir)); + if (staging.isError()) { + return Failure("Failed to create a staging directory"); + } + + return puller->pull(name, staging.get()) + .then(defer(self(), &Self::moveLayers, staging.get(), lambda::_1)) + .then(defer(self(), &Self::storeImage, name, lambda::_1)) + .onAny([staging]() { + Try<Nothing> rmdir = os::rmdir(staging.get()); + if (rmdir.isError()) { + LOG(WARNING) << "Failed to remove staging directory: " << rmdir.error(); + } + }); +} + + +Future<vector<string>> StoreProcess::__get(const Image& image) +{ + vector<string> layerDirectories; + foreach (const string& layer, image.layer_ids()) { + layerDirectories.push_back( + paths::getImageLayerRootfsPath( + flags.docker_store_dir, layer)); + } + + return layerDirectories; +} + + +Future<Nothing> StoreProcess::recover() +{ + return metadataManager->recover(); +} + + +Future<vector<string>> StoreProcess::moveLayers( + const string& staging, + const list<pair<string, string>>& layerPaths) +{ + list<Future<Nothing>> futures; + foreach (const auto& layerPath, layerPaths) { + futures.push_back(moveLayer(layerPath)); + } + + return collect(futures) + .then([layerPaths]() { + vector<string> layerIds; + foreach (const auto& layerPath, layerPaths) { + layerIds.push_back(layerPath.first); + } + + return layerIds; + }); +} + + +Future<Image> StoreProcess::storeImage( + const Image::Name& name, + const vector<string>& layerIds) +{ + return metadataManager->put(name, layerIds); +} + + +Future<Nothing> StoreProcess::moveLayer(const pair<string, string>& layerPath) +{ + if (!os::exists(layerPath.second)) { + return Failure("Unable to find layer '" + layerPath.first + "' in '" + + layerPath.second + "'"); + } + + const string imageLayerPath = + paths::getImageLayerPath(flags.docker_store_dir, layerPath.first); + + if (!os::exists(imageLayerPath)) { + Try<Nothing> mkdir = os::mkdir(imageLayerPath); + if (mkdir.isError()) { + return Failure("Failed to create layer path in store for id '" + + layerPath.first + "': " + mkdir.error()); + } + } + + Try<Nothing> status = os::rename( + layerPath.second, + paths::getImageLayerRootfsPath( + flags.docker_store_dir, layerPath.first)); - if (!creators.contains(flags.docker_store_discovery)) { - return Error("Unknown Docker store: " + flags.docker_store_discovery); + if (status.isError()) { + return Failure("Failed to move layer '" + layerPath.first + + "' to store directory: " + status.error()); } - return creators[flags.docker_store_discovery](flags); + return Nothing(); } } // namespace docker { http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/src/slave/containerizer/provisioner/docker/store.hpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/provisioner/docker/store.hpp b/src/slave/containerizer/provisioner/docker/store.hpp index ae06706..b5e6c87 100644 --- a/src/slave/containerizer/provisioner/docker/store.hpp +++ b/src/slave/containerizer/provisioner/docker/store.hpp @@ -16,24 +16,17 @@ * limitations under the License. */ -#ifndef __MESOS_DOCKER_STORE_HPP__ -#define __MESOS_DOCKER_STORE_HPP__ +#ifndef __PROVISIONER_DOCKER_STORE_HPP__ +#define __PROVISIONER_DOCKER_STORE_HPP__ #include <string> -#include <stout/hashmap.hpp> -#include <stout/nothing.hpp> -#include <stout/option.hpp> #include <stout/try.hpp> #include <process/future.hpp> -#include "slave/containerizer/fetcher.hpp" - #include "slave/containerizer/provisioner/store.hpp" -#include "slave/containerizer/provisioner/docker/message.hpp" - #include "slave/flags.hpp" namespace mesos { @@ -41,13 +34,30 @@ namespace internal { namespace slave { namespace docker { +// Forward Declarations. +class Puller; +class StoreProcess; + + // Store fetches the Docker images and stores them on disk. -// TODO(tnachen): Make this store the only docker store -// implementation, and move local and remote to different pullers. -class Store +class Store : public slave::Store { public: static Try<process::Owned<slave::Store>> create(const Flags& flags); + + ~Store(); + + process::Future<Nothing> recover(); + + process::Future<std::vector<std::string>> get(const mesos::Image& image); + +private: + explicit Store(const process::Owned<StoreProcess>& _process); + + Store& operator=(const Store&) = delete; // Not assignable. + Store(const Store&) = delete; // Not copyable. + + process::Owned<StoreProcess> process; }; } // namespace docker { @@ -55,4 +65,4 @@ public: } // namespace internal { } // namespace mesos { -#endif // __MESOS_DOCKER_STORE_HPP__ +#endif // __PROVISIONER_DOCKER_STORE_HPP__ http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/src/slave/containerizer/provisioner/store.cpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/provisioner/store.cpp b/src/slave/containerizer/provisioner/store.cpp index 35d1199..a8bc302 100644 --- a/src/slave/containerizer/provisioner/store.cpp +++ b/src/slave/containerizer/provisioner/store.cpp @@ -28,6 +28,8 @@ #include "slave/containerizer/provisioner/appc/store.hpp" +#include "slave/containerizer/provisioner/docker/store.hpp" + using namespace process; using std::string; @@ -44,6 +46,7 @@ Try<hashmap<Image::Type, Owned<Store>>> Store::create(const Flags& flags) hashmap<Image::Type, Try<Owned<Store>>(*)(const Flags&)> creators; creators.put(Image::APPC, &appc::Store::create); + creators.put(Image::DOCKER, &docker::Store::create); hashmap<Image::Type, Owned<Store>> stores; http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/src/slave/flags.cpp ---------------------------------------------------------------------- diff --git a/src/slave/flags.cpp b/src/slave/flags.cpp index bf26b98..029aa1e 100644 --- a/src/slave/flags.cpp +++ b/src/slave/flags.cpp @@ -93,21 +93,21 @@ mesos::internal::slave::Flags::Flags() "Directory the appc provisioner will store images in.", "/tmp/mesos/store/appc"); + add(&Flags::docker_local_archives_dir, + "docker_local_archives_dir", + "Directory for docker local puller to look in for image archives", + "/tmp/mesos/images/docker"); + + add(&Flags::docker_puller, + "docker_puller", + "Strategy for docker puller to fetch images", + "local"); + add(&Flags::docker_store_dir, "docker_store_dir", "Directory the docker provisioner will store images in", "/tmp/mesos/store/docker"); - add(&Flags::docker_store_discovery, - "docker_store_discovery", - "Strategy for docker store to fetch images", - "local"); - - add(&Flags::docker_store_discovery_local_dir, - "docker_store_discovery_local_dir", - "Directory for docker provisioner to look in for local images", - "/tmp/mesos/images/docker"); - add(&Flags::default_role, "default_role", "Any resources in the --resources flag that\n" http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/src/slave/flags.hpp ---------------------------------------------------------------------- diff --git a/src/slave/flags.hpp b/src/slave/flags.hpp index 1ce123c..f76f0f6 100644 --- a/src/slave/flags.hpp +++ b/src/slave/flags.hpp @@ -53,10 +53,9 @@ public: std::string image_provisioner_backend; std::string appc_store_dir; - std::string docker_provisioner_backend; + std::string docker_local_archives_dir; + std::string docker_puller; std::string docker_store_dir; - std::string docker_store_discovery; - std::string docker_store_discovery_local_dir; std::string default_role; Option<std::string> attributes; http://git-wip-us.apache.org/repos/asf/mesos/blob/9fb62cec/src/tests/containerizer/provisioner_docker_tests.cpp ---------------------------------------------------------------------- diff --git a/src/tests/containerizer/provisioner_docker_tests.cpp b/src/tests/containerizer/provisioner_docker_tests.cpp index 3a817c0..d895eb9 100644 --- a/src/tests/containerizer/provisioner_docker_tests.cpp +++ b/src/tests/containerizer/provisioner_docker_tests.cpp @@ -38,6 +38,7 @@ #include <process/ssl/gtest.hpp> #include "slave/containerizer/provisioner/docker/metadata_manager.hpp" +#include "slave/containerizer/provisioner/docker/paths.hpp" #include "slave/containerizer/provisioner/docker/registry_client.hpp" #include "slave/containerizer/provisioner/docker/store.hpp" #include "slave/containerizer/provisioner/docker/token_manager.hpp" @@ -50,8 +51,6 @@ using std::map; using std::string; using std::vector; -using namespace mesos::internal::slave::docker::registry; - using process::Clock; using process::Future; using process::Owned; @@ -61,6 +60,8 @@ using process::network::Socket; using namespace process; using namespace mesos::internal::slave; using namespace mesos::internal::slave::docker; +using namespace mesos::internal::slave::docker::paths; +using namespace mesos::internal::slave::docker::registry; using ManifestResponse = RegistryClient::ManifestResponse; @@ -68,7 +69,6 @@ namespace mesos { namespace internal { namespace tests { - /** * Provides token operations and defaults. */ @@ -695,9 +695,11 @@ TEST_F(RegistryClientTest, BadRequest) ASSERT_TRUE(strings::contains(resultFuture.failure(), "Error2")); } + #endif // USE_SSL_SOCKET -class DockerProvisionerLocalStoreTest : public TemporaryDirectoryTest + +class ProvisionerDockerLocalStoreTest : public TemporaryDirectoryTest { public: void verifyLocalDockerImage( @@ -707,19 +709,21 @@ public: string layersPath = path::join(flags.docker_store_dir, "layers"); // Verify contents of the image in store directory. - EXPECT_TRUE(os::exists(path::join(layersPath, "123", "rootfs"))); - EXPECT_TRUE(os::exists(path::join(layersPath, "456", "rootfs"))); + string layerPath1 = getImageLayerRootfsPath(flags.docker_store_dir, "123"); + string layerPath2 = getImageLayerRootfsPath(flags.docker_store_dir, "456"); + EXPECT_TRUE(os::exists(layerPath1)); + EXPECT_TRUE(os::exists(layerPath2)); EXPECT_SOME_EQ( "foo 123", - os::read(path::join(layersPath, "123", "rootfs" , "temp"))); + os::read(path::join(layerPath1 , "temp"))); EXPECT_SOME_EQ( "bar 456", - os::read(path::join(layersPath, "456", "rootfs", "temp"))); + os::read(path::join(layerPath2, "temp"))); // Verify the Docker Image provided. vector<string> expectedLayers; - expectedLayers.push_back("123"); - expectedLayers.push_back("456"); + expectedLayers.push_back(layerPath1); + expectedLayers.push_back(layerPath2); EXPECT_EQ(expectedLayers, layers); } @@ -734,19 +738,19 @@ protected: ASSERT_SOME(os::mkdir(image)); JSON::Value repositories = JSON::parse( - "{" - " \"abc\": {" - " \"latest\": \"456\"" - " }" - "}").get(); + "{" + " \"abc\": {" + " \"latest\": \"456\"" + " }" + "}").get(); ASSERT_SOME( os::write(path::join(image, "repositories"), stringify(repositories))); ASSERT_SOME(os::mkdir(path::join(image, "123"))); JSON::Value manifest123 = JSON::parse( - "{" - " \"parent\": \"\"" - "}").get(); + "{" + " \"parent\": \"\"" + "}").get(); ASSERT_SOME(os::write( path::join(image, "123", "json"), stringify(manifest123))); ASSERT_SOME(os::mkdir(path::join(image, "123", "layer"))); @@ -762,9 +766,9 @@ protected: ASSERT_SOME(os::mkdir(path::join(image, "456"))); JSON::Value manifest456 = JSON::parse( - "{" - " \"parent\": \"123\"" - "}").get(); + "{" + " \"parent\": \"123\"" + "}").get(); ASSERT_SOME( os::write(path::join(image, "456", "json"), stringify(manifest456))); ASSERT_SOME(os::mkdir(path::join(image, "456", "layer"))); @@ -783,10 +787,11 @@ protected: } }; + // This test verifies that a locally stored Docker image in the form of a // tar achive created from a 'docker save' command can be unpacked and // stored in the proper locations accessible to the Docker provisioner. -TEST_F(DockerProvisionerLocalStoreTest, LocalStoreTestWithTar) +TEST_F(ProvisionerDockerLocalStoreTest, LocalStoreTestWithTar) { string imageDir = path::join(os::getcwd(), "images"); string image = path::join(imageDir, "abc:latest"); @@ -794,12 +799,11 @@ TEST_F(DockerProvisionerLocalStoreTest, LocalStoreTestWithTar) ASSERT_SOME(os::mkdir(image)); slave::Flags flags; - flags.docker_store_discovery = "local"; + flags.docker_puller = "local"; flags.docker_store_dir = path::join(os::getcwd(), "store"); - flags.docker_store_discovery_local_dir = imageDir; + flags.docker_local_archives_dir = imageDir; - Try<Owned<slave::Store>> store = - mesos::internal::slave::docker::Store::create(flags); + Try<Owned<slave::Store>> store = slave::docker::Store::create(flags); ASSERT_SOME(store); string sandbox = path::join(os::getcwd(), "sandbox"); @@ -815,17 +819,17 @@ TEST_F(DockerProvisionerLocalStoreTest, LocalStoreTestWithTar) verifyLocalDockerImage(flags, layers.get()); } -// This tests the ability of the reference store to recover the images it has + +// This tests the ability of the metadata manger to recover the images it has // already stored on disk when it is initialized. -TEST_F(DockerProvisionerLocalStoreTest, MetadataManagerInitialization) +TEST_F(ProvisionerDockerLocalStoreTest, MetadataManagerInitialization) { slave::Flags flags; - flags.docker_store_discovery = "local"; + flags.docker_puller = "local"; flags.docker_store_dir = path::join(os::getcwd(), "store"); - flags.docker_store_discovery_local_dir = path::join(os::getcwd(), "images"); + flags.docker_local_archives_dir = path::join(os::getcwd(), "images"); - Try<Owned<slave::Store>> store = - mesos::internal::slave::docker::Store::create(flags); + Try<Owned<slave::Store>> store = slave::docker::Store::create(flags); ASSERT_SOME(store); string sandbox = path::join(os::getcwd(), "sandbox"); @@ -838,11 +842,13 @@ TEST_F(DockerProvisionerLocalStoreTest, MetadataManagerInitialization) Future<vector<string>> layers = store.get()->get(image); AWAIT_READY(layers); - // Store is deleted and recreated. Reference Store is initialized upon + // Store is deleted and recreated. Metadata Manager is initialized upon // creation of the store. store.get().reset(); - store = mesos::internal::slave::docker::Store::create(flags); + store = slave::docker::Store::create(flags); ASSERT_SOME(store); + Future<Nothing> recover = store.get()->recover(); + AWAIT_READY(recover); layers = store.get()->get(image); AWAIT_READY(layers);
