http://git-wip-us.apache.org/repos/asf/mesos/blob/cc1f8f54/src/slave/containerizer/provisioners/appc/store.cpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/provisioners/appc/store.cpp b/src/slave/containerizer/provisioners/appc/store.cpp deleted file mode 100644 index 33f692c..0000000 --- a/src/slave/containerizer/provisioners/appc/store.cpp +++ /dev/null @@ -1,280 +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 <glog/logging.h> - -#include <process/defer.hpp> -#include <process/dispatch.hpp> - -#include <stout/check.hpp> -#include <stout/hashmap.hpp> -#include <stout/os.hpp> -#include <stout/path.hpp> - -#include "slave/containerizer/provisioners/appc/paths.hpp" -#include "slave/containerizer/provisioners/appc/spec.hpp" -#include "slave/containerizer/provisioners/appc/store.hpp" - -using namespace process; - -using std::list; -using std::string; -using std::vector; - -namespace mesos { -namespace internal { -namespace slave { -namespace appc { - -// Defines a locally cached image (which has passed validation). -struct CachedImage -{ - CachedImage( - const AppcImageManifest& _manifest, - const string& _id, - const string& _path) - : manifest(_manifest), id(_id), path(_path) {} - - string rootfs() const - { - return path::join(path, "rootfs"); - } - - const AppcImageManifest manifest; - - // Image ID of the format "sha512-value" where "value" is the hex - // encoded string of the sha512 digest of the uncompressed tar file - // of the image. - const string id; - - // Absolute path to the extracted image. - const string path; -}; - - -// Helper that implements this: -// https://github.com/appc/spec/blob/master/spec/aci.md#dependency-matching -static bool matches(Image::Appc requirements, const CachedImage& candidate) -{ - // The name must match. - if (candidate.manifest.name() != requirements.name()) { - return false; - } - - // If an id is specified the candidate must match. - if (requirements.has_id() && (candidate.id != requirements.id())) { - return false; - } - - // Extract labels for easier comparison, this also weeds out duplicates. - // TODO(xujyan): Detect duplicate labels in image manifest validation - // and Image::Appc validation. - hashmap<string, string> requiredLabels; - foreach (const Label& label, requirements.labels().labels()) { - requiredLabels[label.key()] = label.value(); - } - - hashmap<string, string> candidateLabels; - foreach (const AppcImageManifest::Label& label, - candidate.manifest.labels()) { - candidateLabels[label.name()] = label.value(); - } - - // Any label specified must be present and match in the candidate. - foreachpair (const string& name, - const string& value, - requiredLabels) { - if (!candidateLabels.contains(name) || - candidateLabels.get(name).get() != value) { - return false; - } - } - - return true; -} - - -class StoreProcess : public Process<StoreProcess> -{ -public: - StoreProcess(const string& root); - - ~StoreProcess() {} - - Future<Nothing> recover(); - - Future<vector<string>> get(const Image::Appc& image); - -private: - // Absolute path to the root directory of the store as defined by - // --appc_store_dir. - const string root; - - // Mappings: name -> id -> image. - hashmap<string, hashmap<string, CachedImage>> images; -}; - - -Try<Owned<Store>> Store::create(const Flags& flags) -{ - Try<Nothing> mkdir = os::mkdir(paths::getImagesDir(flags.appc_store_dir)); - if (mkdir.isError()) { - return Error("Failed to create the images directory: " + mkdir.error()); - } - - // Make sure the root path is canonical so all image paths derived - // from it are canonical too. - Result<string> root = os::realpath(flags.appc_store_dir); - if (!root.isSome()) { - // The above mkdir call recursively creates the store directory - // if necessary so it cannot be None here. - CHECK_ERROR(root); - return Error( - "Failed to get the realpath of the store directory: " + root.error()); - } - - return Owned<Store>(new Store( - Owned<StoreProcess>(new StoreProcess(root.get())))); -} - - -Store::Store(Owned<StoreProcess> _process) - : process(_process) -{ - spawn(CHECK_NOTNULL(process.get())); -} - - -Store::~Store() -{ - terminate(process.get()); - wait(process.get()); -} - - -Future<Nothing> Store::recover() -{ - return dispatch(process.get(), &StoreProcess::recover); -} - - -Future<vector<string>> Store::get(const Image::Appc& image) -{ - return dispatch(process.get(), &StoreProcess::get, image); -} - - -StoreProcess::StoreProcess(const string& _root) : root(_root) {} - - -// Implemented as a helper function because it's going to be used for a -// newly downloaded image too. -static Try<CachedImage> createImage(const string& imagePath) -{ - Option<Error> error = spec::validateLayout(imagePath); - if (error.isSome()) { - return Error("Invalid image layout: " + error.get().message); - } - - string imageId = Path(imagePath).basename(); - - error = spec::validateImageID(imageId); - if (error.isSome()) { - return Error("Invalid image ID: " + error.get().message); - } - - Try<string> read = os::read(paths::getImageManifestPath(imagePath)); - if (read.isError()) { - return Error("Failed to read manifest: " + read.error()); - } - - Try<AppcImageManifest> manifest = spec::parse(read.get()); - if (manifest.isError()) { - return Error("Failed to parse manifest: " + manifest.error()); - } - - return CachedImage(manifest.get(), imageId, imagePath); -} - - -Future<vector<string>> StoreProcess::get(const Image::Appc& image) -{ - if (!images.contains(image.name())) { - return Failure("No image named '" + image.name() + "' can be found"); - } - - // Get local candidates. - vector<CachedImage> candidates; - foreach (const CachedImage& candidate, images[image.name()].values()) { - // The first match is returned. - // TODO(xujyan): Some tie-breaking rules are necessary. - if (matches(image, candidate)) { - LOG(INFO) << "Found match for image '" << image.name() - << "' in the store"; - - // The Appc store current doesn't support dependencies and this is - // enforced by manifest validation: if the image's manifest contains - // dependencies it would fail the validation and wouldn't be stored - // in the store. - return vector<string>({candidate.rootfs()}); - } - } - - return Failure("No image named '" + image.name() + - "' can match the requirements"); -} - - -Future<Nothing> StoreProcess::recover() -{ - // Recover everything in the store. - Try<list<string>> imageIds = os::ls(paths::getImagesDir(root)); - if (imageIds.isError()) { - return Failure( - "Failed to list images under '" + - paths::getImagesDir(root) + "': " + - imageIds.error()); - } - - foreach (const string& imageId, imageIds.get()) { - string path = paths::getImagePath(root, imageId); - if (!os::stat::isdir(path)) { - LOG(WARNING) << "Unexpected entry in storage: " << imageId; - continue; - } - - Try<CachedImage> image = createImage(path); - if (image.isError()) { - LOG(WARNING) << "Unexpected entry in storage: " << image.error(); - continue; - } - - LOG(INFO) << "Restored image '" << image.get().manifest.name() << "'"; - - images[image.get().manifest.name()].put(image.get().id, image.get()); - } - - return Nothing(); -} - -} // namespace appc { -} // namespace slave { -} // namespace internal { -} // namespace mesos {
http://git-wip-us.apache.org/repos/asf/mesos/blob/cc1f8f54/src/slave/containerizer/provisioners/appc/store.hpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/provisioners/appc/store.hpp b/src/slave/containerizer/provisioners/appc/store.hpp deleted file mode 100644 index c4ce4b9..0000000 --- a/src/slave/containerizer/provisioners/appc/store.hpp +++ /dev/null @@ -1,90 +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_APPC_STORE_HPP__ -#define __MESOS_APPC_STORE_HPP__ - -#include <string> -#include <vector> - -#include <mesos/mesos.hpp> - -#include <process/future.hpp> -#include <process/owned.hpp> - -#include <stout/try.hpp> - -#include "slave/flags.hpp" - -namespace mesos { -namespace internal { -namespace slave { -namespace appc { - -// Forward declaration. -class StoreProcess; - - -// An image store abstraction that "stores" images. It serves as a read-through -// cache (cache misses are fetched remotely and transparently) for images. -// TODO(xujyan): The store currently keeps cached images indefinitely and we -// should introduce cache eviction policies. -class Store -{ -public: - static Try<process::Owned<Store>> create(const Flags& flags); - - ~Store(); - - process::Future<Nothing> recover(); - - // Get the specified image (and all its recursive dependencies) as a list - // of rootfs layers in the topological order (dependencies go before - // dependents in the list). The images required to build this list are - // either retrieved from the local cache or fetched remotely. - // NOTE: The returned list should not have duplicates. e.g., in the - // following scenario the result should be [C, B, D, A] (B before D in this - // example is decided by the order in which A specifies its dependencies). - // - // A --> B --> C - // | ^ - // |---> D ----| - // - // The returned future fails if the requested image or any of its - // dependencies cannot be found or failed to be fetched. - // TODO(xujyan): Fetching remotely is not implemented for now and until - // then the future fails directly if the image is not in the local cache. - // TODO(xujyan): The store currently doesn't support images that have - // dependencies and we should add it later. - process::Future<std::vector<std::string>> get(const Image::Appc& image); - -private: - Store(process::Owned<StoreProcess> process); - - Store(const Store&); // Not copyable. - Store& operator=(const Store&); // Not assignable. - - process::Owned<StoreProcess> process; -}; - -} // namespace appc { -} // namespace slave { -} // namespace internal { -} // namespace mesos { - -#endif // __MESOS_APPC_STORE_HPP__ http://git-wip-us.apache.org/repos/asf/mesos/blob/cc1f8f54/src/slave/containerizer/provisioners/backend.cpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/provisioners/backend.cpp b/src/slave/containerizer/provisioners/backend.cpp deleted file mode 100644 index 6560ece..0000000 --- a/src/slave/containerizer/provisioners/backend.cpp +++ /dev/null @@ -1,62 +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 <glog/logging.h> - -#include <stout/os.hpp> - -#include "slave/containerizer/provisioners/backend.hpp" - -#include "slave/containerizer/provisioners/backends/bind.hpp" -#include "slave/containerizer/provisioners/backends/copy.hpp" - -using namespace process; - -using std::string; - -namespace mesos { -namespace internal { -namespace slave { - -hashmap<string, Owned<Backend>> Backend::create(const Flags& flags) -{ - hashmap<string, Try<Owned<Backend>>(*)(const Flags&)> creators; - -#ifdef __linux__ - creators.put("bind", &BindBackend::create); -#endif // __linux__ - creators.put("copy", &CopyBackend::create); - - hashmap<string, Owned<Backend>> backends; - - foreachkey (const string& name, creators) { - Try<Owned<Backend>> backend = creators[name](flags); - if (backend.isError()) { - LOG(WARNING) << "Failed to create '" << name << "' backend: " - << backend.error(); - continue; - } - backends.put(name, backend.get()); - } - - return backends; -} - -} // namespace slave { -} // namespace internal { -} // namespace mesos { http://git-wip-us.apache.org/repos/asf/mesos/blob/cc1f8f54/src/slave/containerizer/provisioners/backend.hpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/provisioners/backend.hpp b/src/slave/containerizer/provisioners/backend.hpp deleted file mode 100644 index a25b4ea..0000000 --- a/src/slave/containerizer/provisioners/backend.hpp +++ /dev/null @@ -1,67 +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_PROVISIONER_BACKEND_HPP__ -#define __MESOS_PROVISIONER_BACKEND_HPP__ - -#include <string> -#include <vector> - -#include <process/future.hpp> -#include <process/owned.hpp> - -#include <stout/hashmap.hpp> -#include <stout/try.hpp> - -#include "slave/flags.hpp" - -namespace mesos { -namespace internal { -namespace slave { - -// Provision a root filesystem for a container. -class Backend -{ -public: - virtual ~Backend() {} - - // Return a map of all supported backends keyed by their names. Note - // that Backends that failed to be created due to incorrect flags are - // simply not added to the result. - static hashmap<std::string, process::Owned<Backend>> create( - const Flags& flags); - - // Provision a root filesystem for a container into the specified 'rootfs' - // directory by applying the specified list of root filesystem layers in - // the list order, i.e., files in a layer can overwrite/shadow those from - // another layer earlier in the list. - virtual process::Future<Nothing> provision( - const std::vector<std::string>& layers, - const std::string& rootfs) = 0; - - // Destroy the root filesystem provisioned at the specified 'rootfs' - // directory. Return false if there is no provisioned root filesystem - // to destroy for the given directory. - virtual process::Future<bool> destroy(const std::string& rootfs) = 0; -}; - -} // namespace slave { -} // namespace internal { -} // namespace mesos { - -#endif // __MESOS_PROVISIONER_BACKEND_HPP__ http://git-wip-us.apache.org/repos/asf/mesos/blob/cc1f8f54/src/slave/containerizer/provisioners/backends/bind.cpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/provisioners/backends/bind.cpp b/src/slave/containerizer/provisioners/backends/bind.cpp deleted file mode 100644 index 71861a9..0000000 --- a/src/slave/containerizer/provisioners/backends/bind.cpp +++ /dev/null @@ -1,197 +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 <errno.h> -#include <stdio.h> -#include <unistd.h> - -#include <process/dispatch.hpp> -#include <process/process.hpp> - -#include <stout/foreach.hpp> -#include <stout/os.hpp> - -#include "linux/fs.hpp" - -#include "slave/containerizer/provisioners/backends/bind.hpp" - -using namespace process; - -using std::string; -using std::vector; - -namespace mesos { -namespace internal { -namespace slave { - -class BindBackendProcess : public Process<BindBackendProcess> -{ -public: - Future<Nothing> provision(const vector<string>& layers, const string& rootfs); - - Future<bool> destroy(const string& rootfs); -}; - - -Try<Owned<Backend>> BindBackend::create(const Flags&) -{ - Result<string> user = os::user(); - if (!user.isSome()) { - return Error("Failed to determine user: " + - (user.isError() ? user.error() : "username not found")); - } - - if (user.get() != "root") { - return Error("BindBackend requires root privileges"); - } - - return Owned<Backend>(new BindBackend( - Owned<BindBackendProcess>(new BindBackendProcess()))); -} - - -BindBackend::~BindBackend() -{ - terminate(process.get()); - wait(process.get()); -} - - -BindBackend::BindBackend(Owned<BindBackendProcess> _process) - : process(_process) -{ - spawn(CHECK_NOTNULL(process.get())); -} - - -Future<Nothing> BindBackend::provision( - const vector<string>& layers, - const string& rootfs) -{ - return dispatch( - process.get(), &BindBackendProcess::provision, layers, rootfs); -} - - -Future<bool> BindBackend::destroy(const string& rootfs) -{ - return dispatch(process.get(), &BindBackendProcess::destroy, rootfs); -} - - -Future<Nothing> BindBackendProcess::provision( - const vector<string>& layers, - const string& rootfs) -{ - if (layers.size() > 1) { - return Failure( - "Multiple layers are not supported by the bind backend"); - } - - if (layers.size() == 0) { - return Failure("No filesystem layer provided"); - } - - Try<Nothing> mkdir = os::mkdir(rootfs); - if (mkdir.isError()) { - return Failure("Failed to create container rootfs at " + rootfs); - } - - // TODO(xujyan): Use MS_REC? Does any provisioner use mounts within - // its image store in a single layer? - Try<Nothing> mount = fs::mount( - layers.front(), - rootfs, - None(), - MS_BIND, - NULL); - - if (mount.isError()) { - return Failure( - "Failed to bind mount rootfs '" + layers.front() + - "' to '" + rootfs + "': " + mount.error()); - } - - // And remount it read-only. - mount = fs::mount( - None(), // Ignored. - rootfs, - None(), - MS_BIND | MS_RDONLY | MS_REMOUNT, - NULL); - - if (mount.isError()) { - return Failure( - "Failed to remount rootfs '" + rootfs + "' read-only: " + - mount.error()); - } - - return Nothing(); -} - - -Future<bool> BindBackendProcess::destroy(const string& rootfs) -{ - Try<fs::MountInfoTable> mountTable = fs::MountInfoTable::read(); - - if (mountTable.isError()) { - return Failure("Failed to read mount table: " + mountTable.error()); - } - - foreach (const fs::MountInfoTable::Entry& entry, mountTable.get().entries) { - // TODO(xujyan): If MS_REC was used in 'provision()' we would need - // to check `strings::startsWith(entry.target, rootfs)` here to - // unmount all nested mounts. - if (entry.target == rootfs) { - // NOTE: This would fail if the rootfs is still in use. - Try<Nothing> unmount = fs::unmount(entry.target); - if (unmount.isError()) { - return Failure( - "Failed to destroy bind-mounted rootfs '" + rootfs + "': " + - unmount.error()); - } - - // TODO(jieyu): If 'rmdir' here returns EBUSY, we still returns - // a success. This is currently possible because the parent - // mount of 'rootfs' might not be a shared mount. Thus, - // containers in different mount namespaces might hold extra - // references to this mount. It is OK to ignore the EBUSY error - // because the provisioner will later try to delete all the - // rootfses for the terminated containers. - if (::rmdir(rootfs.c_str()) != 0) { - string message = - "Failed to remove rootfs mount point '" + rootfs + - "': " + strerror(errno); - - if (errno == EBUSY) { - LOG(ERROR) << message; - } else { - return Failure(message); - } - } - - return true; - } - } - - return false; -} - -} // namespace slave { -} // namespace internal { -} // namespace mesos { http://git-wip-us.apache.org/repos/asf/mesos/blob/cc1f8f54/src/slave/containerizer/provisioners/backends/bind.hpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/provisioners/backends/bind.hpp b/src/slave/containerizer/provisioners/backends/bind.hpp deleted file mode 100644 index 61a8838..0000000 --- a/src/slave/containerizer/provisioners/backends/bind.hpp +++ /dev/null @@ -1,75 +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_PROVISIONER_BIND_HPP__ -#define __MESOS_PROVISIONER_BIND_HPP__ - -#include "slave/containerizer/provisioners/backend.hpp" - -namespace mesos { -namespace internal { -namespace slave { - -// Forward declaration. -class BindBackendProcess; - - -// This is a specialized backend that may be useful for deployments -// using large (multi-GB) single-layer images *and* where more recent -// kernel features such as overlayfs are not available (overlayfs-based -// backend tracked by MESOS-2971). For small images (10's to 100's of MB) -// the copy backend may be sufficient. NOTE: -// 1) BindBackend supports only a single layer. Multi-layer images will -// fail to provision and the container will fail to launch! -// 2) The filesystem is read-only because all containers using this -// image share the source. Select writable areas can be achieved by -// mounting read-write volumes to places like /tmp, /var/tmp, -// /home, etc. using the ContainerInfo. These can be relative to -// the executor work directory. -// N.B. Since the filesystem is read-only, '--sandbox_directory' must -// already exist within the filesystem because the filesystem isolator -// is unable to create it! -// 3) It's fast because the bind mount requires (nearly) zero IO. -class BindBackend : public Backend -{ -public: - virtual ~BindBackend(); - - // BindBackend doesn't use any flag. - static Try<process::Owned<Backend>> create(const Flags&); - - virtual process::Future<Nothing> provision( - const std::vector<std::string>& layers, - const std::string& rootfs); - - virtual process::Future<bool> destroy(const std::string& rootfs); - -private: - explicit BindBackend(process::Owned<BindBackendProcess> process); - - BindBackend(const BindBackend&); // Not copyable. - BindBackend& operator=(const BindBackend&); // Not assignable. - - process::Owned<BindBackendProcess> process; -}; - -} // namespace slave { -} // namespace internal { -} // namespace mesos { - -#endif // __MESOS_PROVISIONER_BIND_HPP__ http://git-wip-us.apache.org/repos/asf/mesos/blob/cc1f8f54/src/slave/containerizer/provisioners/backends/copy.cpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/provisioners/backends/copy.cpp b/src/slave/containerizer/provisioners/backends/copy.cpp deleted file mode 100644 index b569465..0000000 --- a/src/slave/containerizer/provisioners/backends/copy.cpp +++ /dev/null @@ -1,203 +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 <process/collect.hpp> -#include <process/defer.hpp> -#include <process/dispatch.hpp> -#include <process/io.hpp> -#include <process/process.hpp> -#include <process/subprocess.hpp> - - -#include <stout/foreach.hpp> -#include <stout/os.hpp> - -#include "common/status_utils.hpp" - -#include "slave/containerizer/provisioners/backends/copy.hpp" - - -using namespace process; - -using std::string; -using std::list; -using std::vector; - -namespace mesos { -namespace internal { -namespace slave { - -class CopyBackendProcess : public Process<CopyBackendProcess> -{ -public: - Future<Nothing> provision(const vector<string>& layers, const string& rootfs); - - Future<bool> destroy(const string& rootfs); - -private: - Future<Nothing> _provision(string layer, const string& rootfs); -}; - - -Try<Owned<Backend>> CopyBackend::create(const Flags&) -{ - return Owned<Backend>(new CopyBackend( - Owned<CopyBackendProcess>(new CopyBackendProcess()))); -} - - -CopyBackend::~CopyBackend() -{ - terminate(process.get()); - wait(process.get()); -} - - -CopyBackend::CopyBackend(Owned<CopyBackendProcess> _process) - : process(_process) -{ - spawn(CHECK_NOTNULL(process.get())); -} - - -Future<Nothing> CopyBackend::provision( - const vector<string>& layers, - const string& rootfs) -{ - return dispatch( - process.get(), &CopyBackendProcess::provision, layers, rootfs); -} - - -Future<bool> CopyBackend::destroy(const string& rootfs) -{ - return dispatch(process.get(), &CopyBackendProcess::destroy, rootfs); -} - - -Future<Nothing> CopyBackendProcess::provision( - const vector<string>& layers, - const string& rootfs) -{ - if (layers.size() == 0) { - return Failure("No filesystem layers provided"); - } - - if (os::exists(rootfs)) { - return Failure("Rootfs is already provisioned"); - } - - Try<Nothing> mkdir = os::mkdir(rootfs); - if (mkdir.isError()) { - return Failure("Failed to create rootfs directory: " + mkdir.error()); - } - - list<Future<Nothing>> futures{Nothing()}; - - foreach (const string layer, layers) { - futures.push_back( - futures.back().then( - defer(self(), &Self::_provision, layer, rootfs))); - } - - return collect(futures) - .then([]() -> Future<Nothing> { return Nothing(); }); -} - - -Future<Nothing> CopyBackendProcess::_provision( - string layer, - const string& rootfs) -{ - VLOG(1) << "Copying layer path '" << layer << "' to rootfs '" << rootfs - << "'"; - -#ifdef __APPLE__ - if (!strings::endsWith(layer, "/")) { - layer += "/"; - } - - // OSX cp doesn't support -T flag, but supports source trailing - // slash so we only copy the content but not the folder. - vector<string> args{"cp", "-a", layer, rootfs}; -#else - vector<string> args{"cp", "-aT", layer, rootfs}; -#endif // __APPLE__ - - Try<Subprocess> s = subprocess( - "cp", - args, - Subprocess::PATH("/dev/null"), - Subprocess::PATH("/dev/null"), - Subprocess::PIPE()); - - if (s.isError()) { - return Failure("Failed to create 'cp' subprocess: " + s.error()); - } - - Subprocess cp = s.get(); - - return cp.status() - .then([cp](const Option<int>& status) -> Future<Nothing> { - if (status.isNone()) { - return Failure("Failed to reap subprocess to copy image"); - } else if (status.get() != 0) { - return io::read(cp.err().get()) - .then([](const string& err) -> Future<Nothing> { - return Failure("Failed to copy layer: " + err); - }); - } - - return Nothing(); - }); -} - - -Future<bool> CopyBackendProcess::destroy(const string& rootfs) -{ - vector<string> argv{"rm", "-rf", rootfs}; - - Try<Subprocess> s = subprocess( - "rm", - argv, - Subprocess::PATH("/dev/null"), - Subprocess::FD(STDOUT_FILENO), - Subprocess::FD(STDERR_FILENO)); - - if (s.isError()) { - return Failure("Failed to create 'rm' subprocess: " + s.error()); - } - - return s.get().status() - .then([](const Option<int>& status) -> Future<bool> { - if (status.isNone()) { - return Failure("Failed to reap subprocess to destroy rootfs"); - } else if (status.get() != 0) { - return Failure("Failed to destroy rootfs, exit status: " + - WSTRINGIFY(status.get())); - } - - return true; - }); -} - -} // namespace slave { -} // namespace internal { -} // namespace mesos { http://git-wip-us.apache.org/repos/asf/mesos/blob/cc1f8f54/src/slave/containerizer/provisioners/backends/copy.hpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/provisioners/backends/copy.hpp b/src/slave/containerizer/provisioners/backends/copy.hpp deleted file mode 100644 index 2abca37..0000000 --- a/src/slave/containerizer/provisioners/backends/copy.hpp +++ /dev/null @@ -1,61 +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_PROVISIONER_COPY_HPP__ -#define __MESOS_PROVISIONER_COPY_HPP__ - -#include "slave/containerizer/provisioners/backend.hpp" - -namespace mesos { -namespace internal { -namespace slave { - -// Forward declaration. -class CopyBackendProcess; - - -class CopyBackend : public Backend -{ -public: - virtual ~CopyBackend(); - - // CopyBackend doesn't use any flag. - static Try<process::Owned<Backend>> create(const Flags&); - - // Provisions a rootfs given the layers' paths and target rootfs - // path. - virtual process::Future<Nothing> provision( - const std::vector<std::string>& layers, - const std::string& rootfs); - - virtual process::Future<bool> destroy(const std::string& rootfs); - -private: - explicit CopyBackend(process::Owned<CopyBackendProcess> process); - - CopyBackend(const CopyBackend&); // Not copyable. - CopyBackend& operator=(const CopyBackend&); // Not assignable. - - process::Owned<CopyBackendProcess> process; -}; - -} // namespace slave { -} // namespace internal { -} // namespace mesos { - -#endif // __MESOS_PROVISIONER_COPY_HPP__ http://git-wip-us.apache.org/repos/asf/mesos/blob/cc1f8f54/src/slave/containerizer/provisioners/docker/registry_client.cpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/provisioners/docker/registry_client.cpp b/src/slave/containerizer/provisioners/docker/registry_client.cpp deleted file mode 100644 index b262ef0..0000000 --- a/src/slave/containerizer/provisioners/docker/registry_client.cpp +++ /dev/null @@ -1,575 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <vector> - -#include <process/defer.hpp> -#include <process/dispatch.hpp> -#include <process/io.hpp> - -#include "slave/containerizer/provisioners/docker/registry_client.hpp" -#include "slave/containerizer/provisioners/docker/token_manager.hpp" - -using std::string; -using std::vector; - -using process::Failure; -using process::Future; -using process::Owned; -using process::Process; - -using process::http::Request; -using process::http::Response; -using process::http::URL; - -namespace mesos { -namespace internal { -namespace slave { -namespace docker { -namespace registry { - -using FileSystemLayerInfo = RegistryClient::FileSystemLayerInfo; - -using ManifestResponse = RegistryClient::ManifestResponse; - -const Duration RegistryClient::DEFAULT_MANIFEST_TIMEOUT_SECS = Seconds(10); - -const size_t RegistryClient::DEFAULT_MANIFEST_MAXSIZE_BYTES = 4096; - -static const uint16_t DEFAULT_SSL_PORT = 443; - -class RegistryClientProcess : public Process<RegistryClientProcess> -{ -public: - static Try<Owned<RegistryClientProcess>> create( - const URL& authServer, - const URL& registry, - const Option<RegistryClient::Credentials>& creds); - - Future<RegistryClient::ManifestResponse> getManifest( - const string& path, - const Option<string>& tag, - const Duration& timeout); - - Future<size_t> getBlob( - const string& path, - const Option<string>& digest, - const Path& filePath, - const Duration& timeout, - size_t maxSize); - -private: - RegistryClientProcess( - const Owned<TokenManager>& tokenMgr, - const URL& registryServer, - const Option<RegistryClient::Credentials>& creds); - - Future<Response> doHttpGet( - const URL& url, - const Option<hashmap<string, string>>& headers, - const Duration& timeout, - bool resend, - const Option<string>& lastResponse) const; - - Try<hashmap<string, string>> getAuthenticationAttributes( - const Response& httpResponse) const; - - Owned<TokenManager> tokenManager_; - const URL registryServer_; - const Option<RegistryClient::Credentials> credentials_; - - RegistryClientProcess(const RegistryClientProcess&) = delete; - RegistryClientProcess& operator = (const RegistryClientProcess&) = delete; -}; - - -Try<Owned<RegistryClient>> RegistryClient::create( - const URL& authServer, - const URL& registryServer, - const Option<Credentials>& creds) -{ - Try<Owned<RegistryClientProcess>> process = - RegistryClientProcess::create(authServer, registryServer, creds); - - if (process.isError()) { - return Error(process.error()); - } - - return Owned<RegistryClient>( - new RegistryClient(authServer, registryServer, creds, process.get())); -} - - -RegistryClient::RegistryClient( - const URL& authServer, - const URL& registryServer, - const Option<Credentials>& creds, - const Owned<RegistryClientProcess>& process) - : authServer_(authServer), - registryServer_(registryServer), - credentials_(creds), - process_(process) -{ - spawn(CHECK_NOTNULL(process_.get())); -} - - -RegistryClient::~RegistryClient() -{ - terminate(process_.get()); - process::wait(process_.get()); -} - - -Future<ManifestResponse> RegistryClient::getManifest( - const string& _path, - const Option<string>& _tag, - const Option<Duration>& _timeout) -{ - Duration timeout = _timeout.getOrElse(DEFAULT_MANIFEST_TIMEOUT_SECS); - - return dispatch( - process_.get(), - &RegistryClientProcess::getManifest, - _path, - _tag, - timeout); -} - - -Future<size_t> RegistryClient::getBlob( - const string& _path, - const Option<string>& _digest, - const Path& _filePath, - const Option<Duration>& _timeout, - const Option<size_t>& _maxSize) -{ - Duration timeout = _timeout.getOrElse(DEFAULT_MANIFEST_TIMEOUT_SECS); - size_t maxSize = _maxSize.getOrElse(DEFAULT_MANIFEST_MAXSIZE_BYTES); - - return dispatch( - process_.get(), - &RegistryClientProcess::getBlob, - _path, - _digest, - _filePath, - timeout, - maxSize); -} - - -Try<Owned<RegistryClientProcess>> RegistryClientProcess::create( - const URL& authServer, - const URL& registryServer, - const Option<RegistryClient::Credentials>& creds) -{ - Try<Owned<TokenManager>> tokenMgr = TokenManager::create(authServer); - if (tokenMgr.isError()) { - return Error("Failed to create token manager: " + tokenMgr.error()); - } - - return Owned<RegistryClientProcess>( - new RegistryClientProcess(tokenMgr.get(), registryServer, creds)); -} - - -RegistryClientProcess::RegistryClientProcess( - const Owned<TokenManager>& tokenMgr, - const URL& registryServer, - const Option<RegistryClient::Credentials>& creds) - : tokenManager_(tokenMgr), - registryServer_(registryServer), - credentials_(creds) {} - - -Try<hashmap<string, string>> -RegistryClientProcess::getAuthenticationAttributes( - const Response& httpResponse) const -{ - if (httpResponse.headers.find("WWW-Authenticate") == - httpResponse.headers.end()) { - return Error("Failed to find WWW-Authenticate header value"); - } - - const string& authString = httpResponse.headers.at("WWW-Authenticate"); - - const vector<string> authStringTokens = strings::tokenize(authString, " "); - if ((authStringTokens.size() != 2) || (authStringTokens[0] != "Bearer")) { - // TODO(jojy): Look at various possibilities of auth response. We currently - // assume that the string will have realm information. - return Error("Invalid authentication header value: " + authString); - } - - const vector<string> authParams = strings::tokenize(authStringTokens[1], ","); - - hashmap<string, string> authAttributes; - auto addAttribute = [&authAttributes]( - const string& param) -> Try<Nothing> { - const vector<string> paramTokens = - strings::tokenize(param, "=\""); - - if (paramTokens.size() != 2) { - return Error( - "Failed to get authentication attribute from response parameter " + - param); - } - - authAttributes.insert({paramTokens[0], paramTokens[1]}); - - return Nothing(); - }; - - foreach (const string& param, authParams) { - Try<Nothing> addRes = addAttribute(param); - if (addRes.isError()) { - return Error(addRes.error()); - } - } - - return authAttributes; -} - - -Future<Response> -RegistryClientProcess::doHttpGet( - const URL& url, - const Option<hashmap<string, string>>& headers, - const Duration& timeout, - bool resend, - const Option<string>& lastResponseStatus) const -{ - return process::http::get(url, headers) - .after(timeout, []( - const Future<Response>& httpResponseFuture) -> Future<Response> { - return Failure("Response timeout"); - }) - .then(defer(self(), [=]( - const Response& httpResponse) -> Future<Response> { - VLOG(1) << "Response status: " + httpResponse.status; - - // Set the future if we get a OK response. - if (httpResponse.status == "200 OK") { - return httpResponse; - } else if (httpResponse.status == "400 Bad Request") { - Try<JSON::Object> errorResponse = - JSON::parse<JSON::Object>(httpResponse.body); - - if (errorResponse.isError()) { - return Failure("Failed to parse bad request response JSON: " + - errorResponse.error()); - } - - std::ostringstream out; - bool first = true; - Result<JSON::Array> errorObjects = - errorResponse.get().find<JSON::Array>("errors"); - - if (errorObjects.isError()) { - return Failure("Failed to find 'errors' in bad request response: " + - errorObjects.error()); - } else if (errorObjects.isNone()) { - return Failure("Errors not found in bad request response"); - } - - foreach (const JSON::Value& error, errorObjects.get().values) { - Result<JSON::String> message = - error.as<JSON::Object>().find<JSON::String>("message"); - if (message.isError()) { - return Failure("Failed to parse bad request error message: " + - message.error()); - } else if (message.isNone()) { - continue; - } - - if (first) { - out << message.get().value; - first = false; - } else { - out << ", " << message.get().value; - } - } - return Failure("Received Bad request, errors: [" + out.str() + "]"); - } - - // Prevent infinite recursion. - if (lastResponseStatus.isSome() && - (lastResponseStatus.get() == httpResponse.status)) { - return Failure("Invalid response: " + httpResponse.status); - } - - // If resend is not set, we dont try again and stop here. - if (!resend) { - return Failure("Bad response: " + httpResponse.status); - } - - // Handle 401 Unauthorized. - if (httpResponse.status == "401 Unauthorized") { - Try<hashmap<string, string>> authAttributes = - getAuthenticationAttributes(httpResponse); - - if (authAttributes.isError()) { - return Failure( - "Failed to get authentication attributes: " + - authAttributes.error()); - } - - // TODO(jojy): Currently only handling TLS/cert authentication. - Future<Token> tokenResponse = tokenManager_->getToken( - authAttributes.get().at("service"), - authAttributes.get().at("scope"), - None()); - - return tokenResponse - .after(timeout, [=]( - Future<Token> tokenResponse) -> Future<Token> { - tokenResponse.discard(); - return Failure("Token response timeout"); - }) - .then(defer(self(), [=]( - const Future<Token>& tokenResponse) { - // Send request with acquired token. - hashmap<string, string> authHeaders = { - {"Authorization", "Bearer " + tokenResponse.get().raw} - }; - - return doHttpGet( - url, - authHeaders, - timeout, - true, - httpResponse.status); - })); - } else if (httpResponse.status == "307 Temporary Redirect") { - // Handle redirect. - - // TODO(jojy): Add redirect functionality in http::get. - - auto toURL = []( - const string& urlString) -> Try<URL> { - // TODO(jojy): Need to add functionality to URL class that parses a - // string to its URL components. For now, assuming: - // - scheme is https - // - path always ends with / - - static const string schemePrefix = "https://"; - - if (!strings::contains(urlString, schemePrefix)) { - return Error( - "Failed to find expected token '" + schemePrefix + - "' in redirect url"); - } - - const string schemeSuffix = urlString.substr(schemePrefix.length()); - - const vector<string> components = - strings::tokenize(schemeSuffix, "/"); - - const string path = schemeSuffix.substr(components[0].length()); - - const vector<string> addrComponents = - strings::tokenize(components[0], ":"); - - uint16_t port = DEFAULT_SSL_PORT; - string domain = components[0]; - - // Parse the port. - if (addrComponents.size() == 2) { - domain = addrComponents[0]; - - Try<uint16_t> tryPort = numify<uint16_t>(addrComponents[1]); - if (tryPort.isError()) { - return Error( - "Failed to parse location: " + urlString + " for port."); - } - - port = tryPort.get(); - } - - return URL("https", domain, port, path); - }; - - if (httpResponse.headers.find("Location") == - httpResponse.headers.end()) { - return Failure( - "Invalid redirect response: 'Location' not found in headers."); - } - - const string& location = httpResponse.headers.at("Location"); - Try<URL> tryUrl = toURL(location); - if (tryUrl.isError()) { - return Failure( - "Failed to parse '" + location + "': " + tryUrl.error()); - } - - return doHttpGet( - tryUrl.get(), - headers, - timeout, - false, - httpResponse.status); - } else { - return Failure("Invalid response: " + httpResponse.status); - } - })); -} - - -Future<ManifestResponse> RegistryClientProcess::getManifest( - const string& path, - const Option<string>& tag, - const Duration& timeout) -{ - //TODO(jojy): These validations belong in the URL class. - if (strings::contains(path, " ")) { - return Failure("Invalid repository path: " + path); - } - - string repoTag = tag.getOrElse("latest"); - if (strings::contains(repoTag, " ")) { - return Failure("Invalid repository tag: " + repoTag); - } - - URL manifestURL(registryServer_); - manifestURL.path = - "v2/" + path + "/manifests/" + repoTag; - - auto getManifestResponse = []( - const Response& httpResponse) -> Try<ManifestResponse> { - if (!httpResponse.headers.contains("Docker-Content-Digest")) { - return Error("Docker-Content-Digest header missing in response"); - } - - Try<JSON::Object> responseJSON = - JSON::parse<JSON::Object>(httpResponse.body); - - if (responseJSON.isError()) { - return Error(responseJSON.error()); - } - - Result<JSON::String> name = responseJSON.get().find<JSON::String>("name"); - if (name.isNone()) { - return Error("Failed to find \"name\" in manifest response"); - } - - Result<JSON::Array> fsLayers = - responseJSON.get().find<JSON::Array>("fsLayers"); - - if (fsLayers.isNone()) { - return Error("Failed to find \"fsLayers\" in manifest response"); - } - - vector<FileSystemLayerInfo> fsLayerInfoList; - foreach (const JSON::Value& layer, fsLayers.get().values) { - const JSON::Object& layerInfoJSON = layer.as<JSON::Object>(); - Result<JSON::String> blobSumInfo = - layerInfoJSON.find<JSON::String>("blobSum"); - - if (blobSumInfo.isNone()) { - return Error("Failed to find \"blobSum\" in manifest response"); - } - - fsLayerInfoList.emplace_back( - FileSystemLayerInfo{blobSumInfo.get().value}); - } - - return ManifestResponse { - name.get().value, - httpResponse.headers.at("Docker-Content-Digest"), - fsLayerInfoList, - }; - }; - - return doHttpGet(manifestURL, None(), timeout, true, None()) - .then([getManifestResponse] ( - const Response& response) -> Future<ManifestResponse> { - Try<ManifestResponse> manifestResponse = getManifestResponse(response); - - if (manifestResponse.isError()) { - return Failure( - "Failed to parse manifest response: " + manifestResponse.error()); - } - - return manifestResponse.get(); - }); -} - - -Future<size_t> RegistryClientProcess::getBlob( - const string& path, - const Option<string>& digest, - const Path& filePath, - const Duration& timeout, - size_t maxSize) -{ - auto prepare = ([&filePath]() -> Try<Nothing> { - const string dirName = filePath.dirname(); - - //TODO(jojy): Return more state, for example - if the directory is new. - Try<Nothing> dirResult = os::mkdir(dirName, true); - if (dirResult.isError()) { - return Error( - "Failed to create directory to download blob: " + - dirResult.error()); - } - - return dirResult; - })(); - - // TODO(jojy): This currently leaves a residue in failure cases. Would be - // ideal if we can completely rollback. - if (prepare.isError()) { - return Failure(prepare.error()); - } - - if (strings::contains(path, " ")) { - return Failure("Invalid repository path: " + path); - } - - URL blobURL(registryServer_); - blobURL.path = - "v2/" + path + "/blobs/" + digest.getOrElse(""); - - auto saveBlob = [filePath]( - const Response& httpResponse) -> Future<size_t> { - // TODO(jojy): Add verification step. - // TODO(jojy): Add check for max size. - size_t size = httpResponse.body.length(); - Try<int> fd = os::open( - filePath.value, - O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, - S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - - if (fd.isError()) { - return Failure("Failed to open file '" + filePath.value + "': " + - fd.error()); - } - - return process::io::write(fd.get(), httpResponse.body) - .then([size](const Future<Nothing>&) { return size; }) - .onAny([fd]() { os::close(fd.get()); } ); - }; - - return doHttpGet(blobURL, None(), timeout, true, None()) - .then([saveBlob](const Response& response) { return saveBlob(response); }); -} - -} // namespace registry { -} // namespace docker { -} // namespace slave { -} // namespace internal { -} // namespace mesos { http://git-wip-us.apache.org/repos/asf/mesos/blob/cc1f8f54/src/slave/containerizer/provisioners/docker/registry_client.hpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/provisioners/docker/registry_client.hpp b/src/slave/containerizer/provisioners/docker/registry_client.hpp deleted file mode 100644 index 3ec3741..0000000 --- a/src/slave/containerizer/provisioners/docker/registry_client.hpp +++ /dev/null @@ -1,163 +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 __PROVISIONERS_DOCKER_REGISTRY_CLIENT_HPP__ -#define __PROVISIONERS_DOCKER_REGISTRY_CLIENT_HPP__ - -#include <string> -#include <vector> - -#include <stout/duration.hpp> -#include <stout/hashmap.hpp> -#include <stout/json.hpp> -#include <stout/path.hpp> - -#include <process/future.hpp> -#include <process/http.hpp> -#include <process/process.hpp> - -namespace mesos { -namespace internal { -namespace slave { -namespace docker { -namespace registry { - -// Forward declarations. -class RegistryClientProcess; - - -class RegistryClient -{ -public: - /** - * Encapsulates information about a file system layer. - */ - struct FileSystemLayerInfo { - //TODO(jojy): This string includes the checksum type also now. Need to - //separate this into checksum method and checksum. - std::string checksumInfo; - }; - - /** - * Encapsulates response of "GET Manifest" request. - * - * Reference: https://docs.docker.com/registry/spec/api - */ - struct ManifestResponse { - const std::string name; - const std::string digest; - const std::vector<FileSystemLayerInfo> fsLayerInfoList; - }; - - /** - * Encapsulates auth credentials for the client sessions. - * TODO(jojy): Secure heap to protect the credentials. - */ - struct Credentials { - /** - * UserId for basic authentication. - */ - const Option<std::string> userId; - /** - * Password for basic authentication. - */ - const Option<std::string> password; - /** - * Account for fetching data from registry. - */ - const Option<std::string> account; - }; - - /** - * Factory method for creating RegistryClient objects. - * - * @param authServer URL of authorization server. - * @param registryServer URL of docker registry server. - * @param credentials credentials for client session (optional). - * @return RegistryClient on Success. - * Error on failure. - */ - static Try<process::Owned<RegistryClient>> create( - const process::http::URL& authServer, - const process::http::URL& registryServer, - const Option<Credentials>& credentials); - - /** - * Fetches manifest for a repository from the client's remote registry server. - * - * @param path path of the repository on the registry. - * @param tag unique tag that identifies the repository. Will default to - * latest. - * @param timeout Maximum time ater which the request will timeout and return - * a failure. Will default to RESPONSE_TIMEOUT. - * @return JSON object on success. - * Failure on process failure. - */ - process::Future<ManifestResponse> getManifest( - const std::string& path, - const Option<std::string>& tag, - const Option<Duration>& timeout); - - /** - * Fetches blob for a repository from the client's remote registry server. - * - * @param path path of the repository on the registry. - * @param digest digest of the blob (from manifest). - * @param filePath file path to store the fetched blob. - * @param timeout Maximum time ater which the request will timeout and return - * a failure. Will default to RESPONSE_TIMEOUT. - * @param maxSize Maximum size of the response thats acceptable. Will default - * to MAX_RESPONSE_SIZE. - * @return size of downloaded blob on success. - * Failure in case of any errors. - */ - process::Future<size_t> getBlob( - const std::string& path, - const Option<std::string>& digest, - const Path& filePath, - const Option<Duration>& timeout, - const Option<size_t>& maxSize); - - ~RegistryClient(); - -private: - RegistryClient( - const process::http::URL& authServer, - const process::http::URL& registryServer, - const Option<Credentials>& credentials, - const process::Owned<RegistryClientProcess>& process); - - static const Duration DEFAULT_MANIFEST_TIMEOUT_SECS; - static const size_t DEFAULT_MANIFEST_MAXSIZE_BYTES; - - const process::http::URL authServer_; - const process::http::URL registryServer_; - const Option<Credentials> credentials_; - process::Owned<RegistryClientProcess> process_; - - RegistryClient(const RegistryClient&) = delete; - RegistryClient& operator=(const RegistryClient&) = delete; -}; - -} // namespace registry { -} // namespace docker { -} // namespace slave { -} // namespace internal { -} // namespace mesos { - -#endif // __PROVISIONERS_DOCKER_REGISTRY_CLIENT_HPP__ http://git-wip-us.apache.org/repos/asf/mesos/blob/cc1f8f54/src/slave/containerizer/provisioners/docker/token_manager.cpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/provisioners/docker/token_manager.cpp b/src/slave/containerizer/provisioners/docker/token_manager.cpp deleted file mode 100644 index aec915f..0000000 --- a/src/slave/containerizer/provisioners/docker/token_manager.cpp +++ /dev/null @@ -1,361 +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 <process/defer.hpp> -#include <process/dispatch.hpp> - -#include "slave/containerizer/provisioners/docker/token_manager.hpp" - -using std::hash; -using std::string; -using std::vector; - -using process::Clock; -using process::Failure; -using process::Future; -using process::Owned; -using process::Process; -using process::Time; - -using process::http::Request; -using process::http::Response; -using process::http::URL; - -namespace mesos { -namespace internal { -namespace slave { -namespace docker { -namespace registry { - -class TokenManagerProcess : public Process<TokenManagerProcess> -{ -public: - static Try<Owned<TokenManagerProcess>> create(const URL& realm); - - Future<Token> getToken( - const string& service, - const string& scope, - const Option<string>& account); - -private: - static const string TOKEN_PATH_PREFIX; - static const Duration RESPONSE_TIMEOUT; - - TokenManagerProcess(const URL& realm) - : realm_(realm) {} - - Try<Token> getTokenFromResponse(const Response& response) const; - - /** - * Key for the token cache. - */ - struct TokenCacheKey - { - string service; - string scope; - }; - - struct TokenCacheKeyHash - { - size_t operator()(const TokenCacheKey& key) const - { - hash<string> hashFn; - - return (hashFn(key.service) ^ - (hashFn(key.scope) << 1)); - } - }; - - struct TokenCacheKeyEqual - { - bool operator()( - const TokenCacheKey& left, - const TokenCacheKey& right) const - { - return ((left.service == right.service) && - (left.scope == right.scope)); - } - }; - - typedef hashmap< - const TokenCacheKey, - Token, - TokenCacheKeyHash, - TokenCacheKeyEqual> TokenCacheType; - - const URL realm_; - TokenCacheType tokenCache_; - - TokenManagerProcess(const TokenManagerProcess&) = delete; - TokenManagerProcess& operator=(const TokenManagerProcess&) = delete; -}; - -const Duration TokenManagerProcess::RESPONSE_TIMEOUT = Seconds(10); -const string TokenManagerProcess::TOKEN_PATH_PREFIX = "/v2/token/"; - - -Token::Token( - const string& _raw, - const JSON::Object& _header, - const JSON::Object& _claims, - const Option<Time>& _expiration, - const Option<Time>& _notBefore) - : raw(_raw), - header(_header), - claims(_claims), - expiration(_expiration), - notBefore(_notBefore) {} - - -Try<Token> Token::create(const string& raw) -{ - auto decode = []( - const string& segment) -> Try<JSON::Object> { - const auto padding = segment.length() % 4; - string paddedSegment(segment); - - if (padding) { - paddedSegment.append(padding, '='); - } - - Try<string> decoded = base64::decode(paddedSegment); - if (decoded.isError()) { - return Error(decoded.error()); - } - - return JSON::parse<JSON::Object>(decoded.get()); - }; - - const vector<string> tokens = strings::tokenize(raw, "."); - - if (tokens.size() != 3) { - return Error("Invalid raw token string"); - } - - Try<JSON::Object> header = decode(tokens[0]); - if (header.isError()) { - return Error("Failed to decode 'header' segment: " + header.error()); - } - - Try<JSON::Object> claims = decode(tokens[1]); - if (claims.isError()) { - return Error("Failed to decode 'claims' segment: " + claims.error()); - } - - Result<Time> expirationTime = getTimeValue(claims.get(), "exp"); - if (expirationTime.isError()) { - return Error("Failed to decode expiration time: " + expirationTime.error()); - } - - Option<Time> expiration; - if (expirationTime.isSome()) { - expiration = expirationTime.get(); - } - - Result<Time> notBeforeTime = getTimeValue(claims.get(), "nbf"); - if (notBeforeTime.isError()) { - return Error("Failed to decode not-before time: " + notBeforeTime.error()); - } - - Option<Time> notBefore; - if (notBeforeTime.isSome()) { - notBefore = notBeforeTime.get(); - } - - Token token(raw, header.get(), claims.get(), expiration, notBefore); - - if (token.isExpired()) { - return Error("Token has expired"); - } - - // TODO(jojy): Add signature validation. - return token; -} - - -Result<Time> Token::getTimeValue(const JSON::Object& object, const string& key) -{ - Result<JSON::Number> jsonValue = object.find<JSON::Number>(key); - - Option<Time> timeValue; - - // If expiration is provided, we will process it for future validations. - if (jsonValue.isSome()) { - Try<Time> time = Time::create(jsonValue.get().value); - if (time.isError()) { - return Error("Failed to decode time: " + time.error()); - } - - timeValue = time.get(); - } - - return timeValue; -} - - -bool Token::isExpired() const -{ - if (expiration.isSome()) { - return (Clock::now() >= expiration.get()); - } - - return false; -} - - -bool Token::isValid() const -{ - if (!isExpired()) { - if (notBefore.isSome()) { - return (Clock::now() >= notBefore.get()); - } - - return true; - } - - // TODO(jojy): Add signature validation. - return false; -} - - -Try<Owned<TokenManager>> TokenManager::create( - const URL& realm) -{ - Try<Owned<TokenManagerProcess>> process = TokenManagerProcess::create(realm); - if (process.isError()) { - return Error(process.error()); - } - - return Owned<TokenManager>(new TokenManager(process.get())); -} - - -TokenManager::TokenManager(Owned<TokenManagerProcess>& process) - : process_(process) -{ - spawn(CHECK_NOTNULL(process_.get())); -} - - -TokenManager::~TokenManager() -{ - terminate(process_.get()); - process::wait(process_.get()); -} - - -Future<Token> TokenManager::getToken( - const string& service, - const string& scope, - const Option<string>& account) -{ - return dispatch( - process_.get(), - &TokenManagerProcess::getToken, - service, - scope, - account); -} - - -Try<Owned<TokenManagerProcess>> TokenManagerProcess::create(const URL& realm) -{ - return Owned<TokenManagerProcess>(new TokenManagerProcess(realm)); -} - - -Try<Token> TokenManagerProcess::getTokenFromResponse( - const Response& response) const -{ - Try<JSON::Object> tokenJSON = JSON::parse<JSON::Object>(response.body); - if (tokenJSON.isError()) { - return Error(tokenJSON.error()); - } - - Result<JSON::String> tokenString = - tokenJSON.get().find<JSON::String>("token"); - - if (tokenString.isError()) { - return Error(tokenString.error()); - } - - Try<Token> result = Token::create(tokenString.get().value); - if (result.isError()) { - return Error(result.error()); - } - - return result.get();; -} - - -Future<Token> TokenManagerProcess::getToken( - const string& service, - const string& scope, - const Option<string>& account) -{ - const TokenCacheKey tokenKey = {service, scope}; - - if (tokenCache_.contains(tokenKey)) { - Token token = tokenCache_.at(tokenKey); - - if (token.isValid()) { - return token; - } else { - LOG(WARNING) << "Cached token was invalid. Will fetch once again"; - } - } - - URL tokenUrl = realm_; - tokenUrl.path = TOKEN_PATH_PREFIX; - - tokenUrl.query = { - {"service", service}, - {"scope", scope}, - }; - - if (account.isSome()) { - tokenUrl.query.insert({"account", account.get()}); - } - - return process::http::get(tokenUrl, None()) - .after(RESPONSE_TIMEOUT, [] (Future<Response> resp) -> Future<Response> { - resp.discard(); - return Failure("Timeout waiting for response to token request"); - }) - .then(defer(self(), [this, tokenKey]( - const Future<Response>& response) -> Future<Token> { - Try<Token> token = getTokenFromResponse(response.get()); - if (token.isError()) { - return Failure( - "Failed to parse JSON Web Token object from response: " + - token.error()); - } - - tokenCache_.insert({tokenKey, token.get()}); - - return token.get(); - })); -} - -// TODO(jojy): Add implementation for basic authentication based getToken API. - -} // namespace registry { -} // namespace docker { -} // namespace slave { -} // namespace internal { -} // namespace mesos { http://git-wip-us.apache.org/repos/asf/mesos/blob/cc1f8f54/src/slave/containerizer/provisioners/docker/token_manager.hpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/provisioners/docker/token_manager.hpp b/src/slave/containerizer/provisioners/docker/token_manager.hpp deleted file mode 100644 index 879269d..0000000 --- a/src/slave/containerizer/provisioners/docker/token_manager.hpp +++ /dev/null @@ -1,179 +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 __PROVISIONERS_DOCKER_TOKEN_MANAGER_HPP__ -#define __PROVISIONERS_DOCKER_TOKEN_MANAGER_HPP__ - -#include <functional> -#include <string> - -#include <stout/base64.hpp> -#include <stout/duration.hpp> -#include <stout/hashmap.hpp> -#include <stout/strings.hpp> - -#include <process/future.hpp> -#include <process/http.hpp> -#include <process/process.hpp> -#include <process/time.hpp> - -namespace mesos { -namespace internal { -namespace slave { -namespace docker { -namespace registry { - - -/** - * Encapsulates JSON Web Token. - * - * Reference: https://tools.ietf.org/html/rfc7519. - */ -struct Token -{ - /** - * Factory method for Token object. - * - * Parses the raw token string and validates for token's expiration. - * - * @returns Token if parsing and validation succeeds. - * Error if parsing or validation fails. - */ - static Try<Token> create(const std::string& rawString); - - /** - * Compares token's expiration time(expressed in seconds) with current time. - * - * @returns True if token's expiration time is greater than current time. - * False if token's expiration time is less than or equal to current - * time. - */ - bool isExpired() const; - - /** - * Validates the token if its "exp" "nbf" values are in range. - * - * @returns True if current time is within token's "exp" and "nbf" values. - * False if current time is not within token's "exp" and "nbf" - * values. - */ - bool isValid() const; - - const std::string raw; - const JSON::Object header; - const JSON::Object claims; - // TODO(jojy): Add signature information. - -private: - Token( - const std::string& raw, - const JSON::Object& headerJson, - const JSON::Object& claimsJson, - const Option<process::Time>& expireTime, - const Option<process::Time>& notBeforeTime); - - static Result<process::Time> getTimeValue( - const JSON::Object& object, - const std::string& key); - - const Option<process::Time> expiration; - const Option<process::Time> notBefore; -}; - - -// Forward declaration. -class TokenManagerProcess; - - -/** - * Acquires and manages docker registry tokens. It keeps the tokens in its - * cache to server any future request for the same token. - * The cache grows unbounded. - * TODO(jojy): The cache can be optimized to prune based on the expiry time of - * the token and server's issue time. - */ -class TokenManager -{ -public: - /** - * Factory method for creating TokenManager object. - * - * TokenManager and registry authorization realm has a 1:1 relationship. - * - * @param realm URL of the authorization server from where token will be - * requested by this TokenManager. - * @returns Owned<TokenManager> if success. - * Error on failure. - */ - static Try<process::Owned<TokenManager>> create( - const process::http::URL& realm); - - /** - * Returns JSON Web Token from cache or from remote server using "Basic - * authorization". - * - * @param service Name of the service that hosts the resource for which - * token is being requested. - * @param scope unique scope returned by the 401 Unauthorized response - * from the registry. - * @param account Name of the account which the client is acting as. - * @param user base64 encoded userid for basic authorization. - * @param password base64 encoded password for basic authorization. - * @returns Token struct that encapsulates JSON Web Token. - */ - process::Future<Token> getToken( - const std::string& service, - const std::string& scope, - const Option<std::string>& account, - const std::string& user, - const Option<std::string>& password); - - /** - * Returns JSON Web Token from cache or from remote server using "TLS/Cert" - * based authorization. - * - * @param service Name of the service that hosts the resource for which - * token is being requested. - * @param scope unique scope returned by the 401 Unauthorized response - * from the registry. - * @param account Name of the account which the client is acting as. - * @returns Token struct that encapsulates JSON Web Token. - */ - process::Future<Token> getToken( - const std::string& service, - const std::string& scope, - const Option<std::string>& account); - - ~TokenManager(); - -private: - TokenManager(process::Owned<TokenManagerProcess>& process); - - TokenManager(const TokenManager&) = delete; - TokenManager& operator=(const TokenManager&) = delete; - - process::Owned<TokenManagerProcess> process_; -}; - -} // namespace registry { -} // namespace docker { -} // namespace slave { -} // namespace internal { -} // namespace mesos { - -#endif // __PROVISIONERS_DOCKER_TOKEN_MANAGER_HPP__ http://git-wip-us.apache.org/repos/asf/mesos/blob/cc1f8f54/src/slave/containerizer/provisioners/paths.cpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/provisioners/paths.cpp b/src/slave/containerizer/provisioners/paths.cpp deleted file mode 100644 index 4293dd2..0000000 --- a/src/slave/containerizer/provisioners/paths.cpp +++ /dev/null @@ -1,191 +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 <glog/logging.h> - -#include <mesos/type_utils.hpp> - -#include <stout/os.hpp> -#include <stout/path.hpp> - -#include <stout/os/stat.hpp> - -#include "slave/containerizer/provisioners/paths.hpp" -#include "slave/paths.hpp" - -using std::list; -using std::string; - -namespace mesos { -namespace internal { -namespace slave { -namespace provisioners { -namespace paths { - -static string getContainersDir(const string& provisionerDir) -{ - return path::join(provisionerDir, "containers"); -} - - -static string getBackendsDir(const string& containerDir) -{ - return path::join(containerDir, "backends"); -} - - -static string getBackendDir(const string& backendsDir, const string& backend) -{ - return path::join(backendsDir, backend); -} - - -static string getRootfsesDir(const string& backendDir) -{ - return path::join(backendDir, "rootfses"); -} - - -static string getRootfsDir(const string& rootfsesDir, const string& roofsId) -{ - return path::join(rootfsesDir, roofsId); -} - - -string getContainerDir( - const string& provisionerDir, - const ContainerID& containerId) -{ - return path::join(getContainersDir(provisionerDir), containerId.value()); -} - - -string getContainerRootfsDir( - const string& provisionerDir, - const ContainerID& containerId, - const string& backend, - const string& rootfsId) -{ - return getRootfsDir( - getRootfsesDir( - getBackendDir( - getBackendsDir( - getContainerDir( - provisionerDir, - containerId)), - backend)), - rootfsId); -} - - -Try<hashmap<ContainerID, string>> listContainers( - const string& provisionerDir) -{ - hashmap<ContainerID, string> results; - - string containersDir = getContainersDir(provisionerDir); - if (!os::exists(containersDir)) { - // No container has been created yet. - return results; - } - - Try<list<string>> containerIds = os::ls(containersDir); - if (containerIds.isError()) { - return Error("Unable to list the containers directory: " + - containerIds.error()); - } - - foreach (const string& entry, containerIds.get()) { - string containerPath = path::join(containersDir, entry); - - if (!os::stat::isdir(containerPath)) { - LOG(WARNING) << "Ignoring unexpected container entry at: " - << containerPath; - continue; - } - - ContainerID containerId; - containerId.set_value(entry); - results.put(containerId, containerPath); - } - - return results; -} - - -Try<hashmap<string, hashmap<string, string>>> listContainerRootfses( - const string& provisionerDir, - const ContainerID& containerId) -{ - hashmap<string, hashmap<string, string>> results; - - string backendsDir = getBackendsDir( - getContainerDir( - provisionerDir, - containerId)); - - Try<list<string>> backends = os::ls(backendsDir); - if (backends.isError()) { - return Error("Unable to list the container directory: " + backends.error()); - } - - foreach (const string& backend, backends.get()) { - string backendDir = getBackendDir(backendsDir, backend); - if (!os::stat::isdir(backendDir)) { - LOG(WARNING) << "Ignoring unexpected backend entry at: " << backendDir; - continue; - } - - Try<list<string>> rootfses = os::ls(getRootfsesDir(backendDir)); - if (rootfses.isError()) { - return Error("Unable to list the backend directory: " + rootfses.error()); - } - - hashmap<string, string> backendResults; - - foreach (const string& rootfsId, rootfses.get()) { - string rootfs = getRootfsDir(getRootfsesDir(backendDir), rootfsId); - - if (!os::stat::isdir(rootfs)) { - LOG(WARNING) << "Ignoring unexpected rootfs entry at: " << backendDir; - continue; - } - - backendResults.put(rootfsId, rootfs); - } - - if (backendResults.empty()) { - LOG(WARNING) << "Ignoring a backend directory with no rootfs in it: " - << backendDir; - continue; - } - - // The rootfs directory has passed validation. - results.put(backend, backendResults); - } - - return results; -} - -} // namespace paths { -} // namespace provisioners { -} // namespace slave { -} // namespace internal { -} // namespace mesos { http://git-wip-us.apache.org/repos/asf/mesos/blob/cc1f8f54/src/slave/containerizer/provisioners/paths.hpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/provisioners/paths.hpp b/src/slave/containerizer/provisioners/paths.hpp deleted file mode 100644 index 5b82591..0000000 --- a/src/slave/containerizer/provisioners/paths.hpp +++ /dev/null @@ -1,83 +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_PROVISIONERS_PATHS_HPP__ -#define __MESOS_PROVISIONERS_PATHS_HPP__ - -#include <string> - -#include <mesos/mesos.hpp> - -#include <stout/hashmap.hpp> -#include <stout/try.hpp> - -namespace mesos { -namespace internal { -namespace slave { -namespace provisioners { -namespace paths { - -// The provisioner rootfs directory is as follows: -// <work_dir> ('--work_dir' flag) -// |-- provisioners -// |-- <provisioner_type> (APPC, DOCKER, etc.) -// |-- containers -// |-- <container_id> -// |-- backends -// |-- <backend> (copy, bind, etc.) -// |-- rootfses -// |-- <rootfs_id> (the rootfs) -// -// NOTE: Each container could have multiple image types, therefore there -// can be the same <container_id> directory under other provisioners e.g., -// <work_dir>/provisioners/DOCKER, <work_dir>/provisioners/APPC, etc. -// Under each provisioner + container there can be multiple backends -// due to the change of backend flags. Under each backend a rootfs is -// identified by the 'rootfs_id' which is a UUID. - -std::string getContainerDir( - const std::string& provisionerDir, - const ContainerID& containerId); - - -std::string getContainerRootfsDir( - const std::string& provisionerDir, - const ContainerID& containerId, - const std::string& backend, - const std::string& rootfsId); - - -// Recursively "ls" the container directory and return a map of -// backend -> rootfsId -> rootfsPath. -Try<hashmap<std::string, hashmap<std::string, std::string>>> -listContainerRootfses( - const std::string& provisionerDir, - const ContainerID& containerId); - - -// Return a map of containerId -> containerPath; -Try<hashmap<ContainerID, std::string>> listContainers( - const std::string& provisionerDir); - -} // namespace paths { -} // namespace provisioners { -} // namespace slave { -} // namespace internal { -} // namespace mesos { - -#endif // __MESOS_PROVISIONERS_PATHS__
