Repository: mesos Updated Branches: refs/heads/1.1.x de3a1b58a -> b810e22d3
Added backport of MESOS-6360 (Mesos part) to 1.1.x. Project: http://git-wip-us.apache.org/repos/asf/mesos/repo Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/b810e22d Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/b810e22d Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/b810e22d Branch: refs/heads/1.1.x Commit: b810e22d37cffd4431599ea75ec509db56d10460 Parents: 2d65576 Author: Qian Zhang <zhq527...@gmail.com> Authored: Wed Nov 23 17:39:33 2016 +0800 Committer: Qian Zhang <zhq527...@gmail.com> Committed: Thu Nov 24 09:37:56 2016 +0800 ---------------------------------------------------------------------- src/CMakeLists.txt | 2 + src/Makefile.am | 3 + .../mesos/provisioner/backends/copy.cpp | 108 ++++++++++++++- .../mesos/provisioner/docker/store.cpp | 24 +++- .../mesos/provisioner/provisioner.cpp | 118 +--------------- .../mesos/provisioner/provisioner.hpp | 5 - .../containerizer/mesos/provisioner/utils.cpp | 133 +++++++++++++++++++ .../containerizer/mesos/provisioner/utils.hpp | 37 ++++++ src/slave/containerizer/mesos/utils.cpp | 40 ++++++ src/slave/containerizer/mesos/utils.hpp | 15 +-- .../containerizer/provisioner_docker_tests.cpp | 95 +++++++++---- 11 files changed, 411 insertions(+), 169 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mesos/blob/b810e22d/src/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e60d34c..85acbbe 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -325,9 +325,11 @@ set(AGENT_SRC slave/containerizer/mesos/launcher.cpp slave/containerizer/mesos/mount.cpp slave/containerizer/mesos/paths.cpp + slave/containerizer/mesos/utils.cpp slave/containerizer/mesos/provisioner/paths.cpp slave/containerizer/mesos/provisioner/provisioner.cpp slave/containerizer/mesos/provisioner/store.cpp + slave/containerizer/mesos/provisioner/utils.cpp slave/containerizer/mesos/provisioner/appc/cache.cpp slave/containerizer/mesos/provisioner/appc/fetcher.cpp slave/containerizer/mesos/provisioner/appc/paths.cpp http://git-wip-us.apache.org/repos/asf/mesos/blob/b810e22d/src/Makefile.am ---------------------------------------------------------------------- diff --git a/src/Makefile.am b/src/Makefile.am index 73d337d..f82c985 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -828,6 +828,7 @@ libmesos_no_3rdparty_la_SOURCES += \ slave/containerizer/mesos/launcher.cpp \ slave/containerizer/mesos/mount.cpp \ slave/containerizer/mesos/paths.cpp \ + slave/containerizer/mesos/utils.cpp \ slave/containerizer/mesos/isolators/docker/volume/driver.cpp \ slave/containerizer/mesos/isolators/docker/volume/paths.cpp \ slave/containerizer/mesos/isolators/filesystem/posix.cpp \ @@ -839,6 +840,7 @@ libmesos_no_3rdparty_la_SOURCES += \ slave/containerizer/mesos/provisioner/paths.cpp \ slave/containerizer/mesos/provisioner/provisioner.cpp \ slave/containerizer/mesos/provisioner/store.cpp \ + slave/containerizer/mesos/provisioner/utils.cpp \ slave/containerizer/mesos/provisioner/appc/cache.cpp \ slave/containerizer/mesos/provisioner/appc/fetcher.cpp \ slave/containerizer/mesos/provisioner/appc/paths.cpp \ @@ -971,6 +973,7 @@ libmesos_no_3rdparty_la_SOURCES += \ slave/containerizer/mesos/provisioner/paths.hpp \ slave/containerizer/mesos/provisioner/provisioner.hpp \ slave/containerizer/mesos/provisioner/store.hpp \ + slave/containerizer/mesos/provisioner/utils.hpp \ slave/containerizer/mesos/provisioner/appc/cache.hpp \ slave/containerizer/mesos/provisioner/appc/fetcher.hpp \ slave/containerizer/mesos/provisioner/appc/paths.hpp \ http://git-wip-us.apache.org/repos/asf/mesos/blob/b810e22d/src/slave/containerizer/mesos/provisioner/backends/copy.cpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/mesos/provisioner/backends/copy.cpp b/src/slave/containerizer/mesos/provisioner/backends/copy.cpp index 9c5354e..0ce3e1e 100644 --- a/src/slave/containerizer/mesos/provisioner/backends/copy.cpp +++ b/src/slave/containerizer/mesos/provisioner/backends/copy.cpp @@ -16,6 +16,8 @@ #include <list> +#include <mesos/docker/spec.hpp> + #include <process/collect.hpp> #include <process/defer.hpp> #include <process/dispatch.hpp> @@ -24,7 +26,6 @@ #include <process/process.hpp> #include <process/subprocess.hpp> - #include <stout/foreach.hpp> #include <stout/os.hpp> @@ -32,7 +33,6 @@ #include "slave/containerizer/mesos/provisioner/backends/copy.hpp" - using namespace process; using std::string; @@ -128,9 +128,93 @@ Future<Nothing> CopyBackendProcess::provision( Future<Nothing> CopyBackendProcess::_provision( - string layer, - const string& rootfs) + string layer, + const string& rootfs) { +#ifndef __WINDOWS__ + // Traverse the layer to check if there is any whiteout files, if + // yes, remove the corresponding files/directories from the rootfs. + // Note: We assume all image types use AUFS whiteout format. + char* source[] = {const_cast<char*>(layer.c_str()), nullptr}; + + FTS* tree = ::fts_open(source, FTS_NOCHDIR | FTS_PHYSICAL, nullptr); + if (tree == nullptr) { + return Failure("Failed to open '" + layer + "': " + os::strerror(errno)); + } + + vector<string> whiteouts; + for (FTSENT *node = ::fts_read(tree); + node != nullptr; node = ::fts_read(tree)) { + if (node->fts_info != FTS_F) { + continue; + } + + if (!strings::startsWith(node->fts_name, docker::spec::WHITEOUT_PREFIX)) { + continue; + } + + string ftsPath = string(node->fts_path); + Path whiteout = Path(ftsPath.substr(layer.length() + 1)); + + // Keep the relative paths of the whiteout files, we will + // remove them from rootfs after layer is copied to rootfs. + whiteouts.push_back(whiteout.string()); + + if (node->fts_name == string(docker::spec::WHITEOUT_OPAQUE_PREFIX)) { + const string path = path::join(rootfs, Path(whiteout).dirname()); + + // Remove the entries under the directory labeled + // as opaque whiteout from rootfs. + Try<Nothing> rmdir = os::rmdir(path, true, false); + if (rmdir.isError()) { + ::fts_close(tree); + return Failure( + "Failed to remove the entries under the directory labeled as" + " opaque whiteout '" + path + "': " + rmdir.error()); + } + } else { + const string path = path::join( + rootfs, + whiteout.dirname(), + whiteout.basename().substr(strlen(docker::spec::WHITEOUT_PREFIX))); + + // The file/directory labeled as whiteout may have already been + // removed with the code above due to its parent directory labeled + // as opaque whiteout, so here we need to check if it still exists + // before trying to remove it. + if (os::exists(path)) { + if (os::stat::isdir(path)) { + Try<Nothing> rmdir = os::rmdir(path); + if (rmdir.isError()) { + ::fts_close(tree); + return Failure( + "Failed to remove the directory labeled as whiteout '" + + path + "': " + rmdir.error()); + } + } else { + Try<Nothing> rm = os::rm(path); + if (rm.isError()) { + ::fts_close(tree); + return Failure( + "Failed to remove the file labeled as whiteout '" + + path + "': " + rm.error()); + } + } + } + } + } + + if (errno != 0) { + Error error = ErrnoError(); + ::fts_close(tree); + return Failure(error); + } + + if (::fts_close(tree) != 0) { + return Failure( + "Failed to stop traversing file system: " + os::strerror(errno)); + } + VLOG(1) << "Copying layer path '" << layer << "' to rootfs '" << rootfs << "'"; @@ -160,7 +244,7 @@ Future<Nothing> CopyBackendProcess::_provision( Subprocess cp = s.get(); return cp.status() - .then([cp](const Option<int>& status) -> Future<Nothing> { + .then([=](const Option<int>& status) -> Future<Nothing> { if (status.isNone()) { return Failure("Failed to reap subprocess to copy image"); } else if (status.get() != 0) { @@ -170,8 +254,22 @@ Future<Nothing> CopyBackendProcess::_provision( }); } + // Remove the whiteout files from rootfs. + foreach (const string whiteout, whiteouts) { + Try<Nothing> rm = os::rm(path::join(rootfs, whiteout)); + if (rm.isError()) { + return Failure( + "Failed to remove whiteout file '" + + whiteout + "': " + rm.error()); + } + } + return Nothing(); }); +#else + return Failure( + "Provisioning a rootfs from an image is not supported on Windows"); +#endif // __WINDOWS__ } http://git-wip-us.apache.org/repos/asf/mesos/blob/b810e22d/src/slave/containerizer/mesos/provisioner/docker/store.cpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/mesos/provisioner/docker/store.cpp b/src/slave/containerizer/mesos/provisioner/docker/store.cpp index e192f86..9dccd06 100644 --- a/src/slave/containerizer/mesos/provisioner/docker/store.cpp +++ b/src/slave/containerizer/mesos/provisioner/docker/store.cpp @@ -30,6 +30,9 @@ #include <mesos/docker/spec.hpp> +#include "slave/containerizer/mesos/provisioner/constants.hpp" +#include "slave/containerizer/mesos/provisioner/utils.hpp" + #include "slave/containerizer/mesos/provisioner/docker/metadata_manager.hpp" #include "slave/containerizer/mesos/provisioner/docker/paths.hpp" #include "slave/containerizer/mesos/provisioner/docker/puller.hpp" @@ -331,6 +334,23 @@ Future<Nothing> StoreProcess::moveLayer( flags.docker_store_dir, layerId); + const string sourceRootfs = paths::getImageLayerRootfsPath( + source, + flags.image_provisioner_backend); + +#ifdef __linux__ + // If the backend is "overlay", we need to convert + // AUFS whiteout files to OverlayFS whiteout files. + if (flags.image_provisioner_backend == OVERLAY_BACKEND) { + Try<Nothing> convert = convertWhiteouts(sourceRootfs); + if (convert.isError()) { + return Failure( + "Failed to convert the whiteout files under '" + + sourceRootfs + "': " + convert.error()); + } + } +#endif + if (!os::exists(target)) { // This is the case that we pull the layer for the first time. Try<Nothing> mkdir = os::mkdir(target); @@ -349,10 +369,6 @@ Future<Nothing> StoreProcess::moveLayer( } else { // This is the case where the layer has already been pulled with a // different backend. - const string sourceRootfs = paths::getImageLayerRootfsPath( - source, - flags.image_provisioner_backend); - Try<Nothing> rename = os::rename(sourceRootfs, targetRootfs); if (rename.isError()) { return Failure( http://git-wip-us.apache.org/repos/asf/mesos/blob/b810e22d/src/slave/containerizer/mesos/provisioner/provisioner.cpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/mesos/provisioner/provisioner.cpp b/src/slave/containerizer/mesos/provisioner/provisioner.cpp index de2c121..db2d267 100644 --- a/src/slave/containerizer/mesos/provisioner/provisioner.cpp +++ b/src/slave/containerizer/mesos/provisioner/provisioner.cpp @@ -311,124 +311,10 @@ Future<ProvisionInfo> ProvisionerProcess::_provision( imageInfo.layers, rootfs, backendDir) - .then(defer(self(), &Self::__provision, rootfs, image, imageInfo)); -} - - -// This function is currently docker image specific. Depending -// on docker v1 spec, a docker image may include filesystem -// changeset, which may need to delete directories or files. -// The file/directory to be deleted will be labeled by creating -// a 'whiteout' file, which is at the same location and with the -// basename of the deleted file or directory prefixed with '.wh.'. -// For the directory which has an opaque whiteout file '.wh..wh..opq' -// under it, we need to delete all the files/directories under it. -// Please see: -// https://github.com/docker/docker/blob/master/image/spec/v1.md -// https://github.com/docker/docker/blob/master/pkg/archive/whiteouts.go -// And OCI image spec also has the concepts 'whiteout' and 'opaque whiteout': -// https://github.com/opencontainers/image-spec/blob/master/layer.md#whiteouts -Future<ProvisionInfo> ProvisionerProcess::__provision( - const string& rootfs, - const Image& image, - const ImageInfo& imageInfo) -{ -#ifdef __WINDOWS__ - return ProvisionInfo{ - rootfs, imageInfo.dockerManifest, imageInfo.appcManifest}; -#else - // Skip single-layered images since no 'whiteout' files needs - // to be handled, and this excludes any image using the bind - // backend. - if (imageInfo.layers.size() == 1 || image.type() != Image::DOCKER) { + .then([=]() -> Future<ProvisionInfo> { return ProvisionInfo{ rootfs, imageInfo.dockerManifest, imageInfo.appcManifest}; - } - - // TODO(hausdorff): The FTS API is not available on some platforms, such as - // Windows. We will need to either (1) prove that this is not necessary for - // Windows Containers, which use much of the Docker spec themselves, or (2) - // make this code compatible with Windows, as we did with other code that - // depended on FTS, such as `os::rmdir`. See MESOS-5610. - char* _rootfs[] = {const_cast<char*>(rootfs.c_str()), nullptr}; - - FTS* tree = ::fts_open(_rootfs, FTS_NOCHDIR | FTS_PHYSICAL, nullptr); - if (tree == nullptr) { - return Failure("Failed to open '" + rootfs + "': " + os::strerror(errno)); - } - - vector<string> whiteout; - vector<string> whiteoutOpaque; - - for (FTSENT *node = ::fts_read(tree); - node != nullptr; node = ::fts_read(tree)) { - if (node->fts_info == FTS_F && - strings::startsWith(node->fts_name, string(spec::WHITEOUT_PREFIX))) { - Path path = Path(node->fts_path); - if (node->fts_name == string(spec::WHITEOUT_OPAQUE_PREFIX)) { - whiteoutOpaque.push_back(path.dirname()); - } else { - whiteout.push_back(path::join( - path.dirname(), - path.basename().substr(strlen(spec::WHITEOUT_PREFIX)))); - } - - Try<Nothing> rm = os::rm(path.string()); - if (rm.isError()) { - ::fts_close(tree); - return Failure( - "Failed to remove whiteout file '" + - path.string() + "': " + rm.error()); - } - } - } - - if (errno != 0) { - Error error = ErrnoError(); - ::fts_close(tree); - return Failure(error); - } - - if (::fts_close(tree) != 0) { - return Failure( - "Failed to stop traversing file system: " + os::strerror(errno)); - } - - foreach (const string& path, whiteoutOpaque) { - Try<Nothing> rmdir = os::rmdir(path, true, false); - if (rmdir.isError()) { - return Failure( - "Failed to remove the entries under the directory labeled as" - " opaque whiteout '" + path + "': " + rmdir.error()); - } - } - - foreach (const string& path, whiteout) { - // The file/directory labeled as whiteout may have already been - // removed with the code above due to its parent directory labeled - // as opaque whiteout, so here we need to check if it still exists - // before trying to remove it. - if (os::exists(path)) { - if (os::stat::isdir(path)) { - Try<Nothing> rmdir = os::rmdir(path); - if (rmdir.isError()) { - return Failure( - "Failed to remove the directory labeled as whiteout '" + - path + "': " + rmdir.error()); - } - } else { - Try<Nothing> rm = os::rm(path); - if (rm.isError()) { - return Failure( - "Failed to remove the file labeled as whiteout '" + - path + "': " + rm.error()); - } - } - } - } - - return ProvisionInfo{rootfs, imageInfo.dockerManifest, None()}; -#endif // __WINDOWS__ + }); } http://git-wip-us.apache.org/repos/asf/mesos/blob/b810e22d/src/slave/containerizer/mesos/provisioner/provisioner.hpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/mesos/provisioner/provisioner.hpp b/src/slave/containerizer/mesos/provisioner/provisioner.hpp index c5481c5..0a48617 100644 --- a/src/slave/containerizer/mesos/provisioner/provisioner.hpp +++ b/src/slave/containerizer/mesos/provisioner/provisioner.hpp @@ -134,11 +134,6 @@ private: const Image& image, const ImageInfo& imageInfo); - process::Future<ProvisionInfo> __provision( - const std::string& rootfs, - const Image& image, - const ImageInfo& imageInfo); - process::Future<bool> _destroy(const ContainerID& containerId); const Flags flags; http://git-wip-us.apache.org/repos/asf/mesos/blob/b810e22d/src/slave/containerizer/mesos/provisioner/utils.cpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/mesos/provisioner/utils.cpp b/src/slave/containerizer/mesos/provisioner/utils.cpp new file mode 100644 index 0000000..f480ee5 --- /dev/null +++ b/src/slave/containerizer/mesos/provisioner/utils.cpp @@ -0,0 +1,133 @@ +// 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 __WINDOWS__ +#include <fts.h> + +#include <sys/stat.h> +#endif // __WINDOWS__ + +#include <string> +#include <string.h> + +#include <mesos/docker/spec.hpp> + +#include <stout/error.hpp> +#include <stout/nothing.hpp> +#include <stout/os.hpp> +#include <stout/path.hpp> +#include <stout/strings.hpp> +#include <stout/try.hpp> + +#include <stout/os/rm.hpp> +#include <stout/os/strerror.hpp> +#include <stout/os/xattr.hpp> + +#include "slave/containerizer/mesos/provisioner/utils.hpp" + +using std::string; + +namespace mesos { +namespace internal { +namespace slave { + +#ifdef __linux__ +// This function is used to convert AUFS whiteouts to OverlayFS whiteouts and +// the AUFS whiteouts will be removed after the conversion is done. Whiteout is +// for hiding directories and files in the lower level layers. AUFS whiteout is +// the whiteout standard in Docker, and it relies on '.wh..wh..opq' to hide +// directory and '.wh.<filename>' to hide file, whereas OverlayFS relies on +// setting an extended attribute 'trusted.overlay.opaque' on the directory to +// hide it and creating character device with 0/0 device number to hide file. +// See the links below for more details: +// http://aufs.sourceforge.net/aufs.html +// https://www.kernel.org/doc/Documentation/filesystems/overlayfs.txt +Try<Nothing> convertWhiteouts(const string& directory) +{ + char* rootfs[] = {const_cast<char*>(directory.c_str()), nullptr}; + + FTS* tree = ::fts_open(rootfs, FTS_NOCHDIR | FTS_PHYSICAL, nullptr); + if (tree == nullptr) { + return Error("Failed to open '" + directory + "': " + os::strerror(errno)); + } + + for (FTSENT *node = ::fts_read(tree); + node != nullptr; node = ::fts_read(tree)) { + if (node->fts_info != FTS_F) { + continue; + } + + if (!strings::startsWith(node->fts_name, docker::spec::WHITEOUT_PREFIX)) { + continue; + } + + const Path path = Path(node->fts_path); + if (node->fts_name == string(docker::spec::WHITEOUT_OPAQUE_PREFIX)) { + Try<Nothing> setxattr = os::setxattr( + path.dirname(), + "trusted.overlay.opaque", + "y", + 0); + + if (setxattr.isError()) { + ::fts_close(tree); + return Error( + "Failed to set extended attribute 'trusted.overlay.opaque'" + " for the directory '" + path.dirname() + "': " + + setxattr.error()); + } + } else { + const string originalPath = path::join( + path.dirname(), + path.basename().substr(strlen(docker::spec::WHITEOUT_PREFIX))); + + Try<Nothing> mknod = os::mknod(originalPath, S_IFCHR, 0); + if (mknod.isError()) { + ::fts_close(tree); + return Error( + "Failed to create character device '" + + originalPath + "': " + mknod.error()); + } + } + + // Remove the AUFS whiteout file. + Try<Nothing> rm = os::rm(path.string()); + if (rm.isError()) { + ::fts_close(tree); + return Error( + "Failed to remove AUFS whiteout file '" + + path.string() + "': " + rm.error()); + } + } + + if (errno != 0) { + Error error = ErrnoError(); + ::fts_close(tree); + return error; + } + + if (::fts_close(tree) != 0) { + return Error( + "Failed to stop traversing file system: " + os::strerror(errno)); + } + + return Nothing(); +} +#endif + +} // namespace slave { +} // namespace internal { +} // namespace mesos { http://git-wip-us.apache.org/repos/asf/mesos/blob/b810e22d/src/slave/containerizer/mesos/provisioner/utils.hpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/mesos/provisioner/utils.hpp b/src/slave/containerizer/mesos/provisioner/utils.hpp new file mode 100644 index 0000000..5b6c162 --- /dev/null +++ b/src/slave/containerizer/mesos/provisioner/utils.hpp @@ -0,0 +1,37 @@ +// 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_CONTAINERIZER_PROVISIONER_UTILS_HPP__ +#define __MESOS_CONTAINERIZER_PROVISIONER_UTILS_HPP__ + +#include <string> + +#include <stout/nothing.hpp> +#include <stout/try.hpp> + +namespace mesos { +namespace internal { +namespace slave { + +#ifdef __linux__ +Try<Nothing> convertWhiteouts(const std::string& directory); +#endif + +} // namespace slave { +} // namespace internal { +} // namespace mesos { + +#endif // __MESOS_CONTAINERIZER_PROVISIONER_UTILS_HPP__ http://git-wip-us.apache.org/repos/asf/mesos/blob/b810e22d/src/slave/containerizer/mesos/utils.cpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/mesos/utils.cpp b/src/slave/containerizer/mesos/utils.cpp new file mode 100644 index 0000000..237aea4 --- /dev/null +++ b/src/slave/containerizer/mesos/utils.cpp @@ -0,0 +1,40 @@ +// 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/mesos/utils.hpp" + +namespace mesos { +namespace internal { +namespace slave { + +ContainerID getRootContainerId(const ContainerID& containerId) +{ + ContainerID rootContainerId = containerId; + while (rootContainerId.has_parent()) { + // NOTE: Looks like protobuf does not handle copying well when + // nesting message is involved, because the source and the target + // point to the same object. Therefore, we create a temporary + // variable and use an extra copy here. + ContainerID id = rootContainerId.parent(); + rootContainerId = id; + } + + return rootContainerId; +} + +} // namespace slave { +} // namespace internal { +} // namespace mesos { http://git-wip-us.apache.org/repos/asf/mesos/blob/b810e22d/src/slave/containerizer/mesos/utils.hpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/mesos/utils.hpp b/src/slave/containerizer/mesos/utils.hpp index 178ebf3..f24215b 100644 --- a/src/slave/containerizer/mesos/utils.hpp +++ b/src/slave/containerizer/mesos/utils.hpp @@ -23,20 +23,7 @@ namespace mesos { namespace internal { namespace slave { -static ContainerID getRootContainerId(const ContainerID& containerId) -{ - ContainerID rootContainerId = containerId; - while (rootContainerId.has_parent()) { - // NOTE: Looks like protobuf does not handle copying well when - // nesting message is involved, because the source and the target - // point to the same object. Therefore, we create a temporary - // variable and use an extra copy here. - ContainerID id = rootContainerId.parent(); - rootContainerId = id; - } - - return rootContainerId; -} +ContainerID getRootContainerId(const ContainerID& containerId); } // namespace slave { } // namespace internal { http://git-wip-us.apache.org/repos/asf/mesos/blob/b810e22d/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 d8fb7ab..9682169 100644 --- a/src/tests/containerizer/provisioner_docker_tests.cpp +++ b/src/tests/containerizer/provisioner_docker_tests.cpp @@ -28,6 +28,12 @@ #include <mesos/docker/spec.hpp> +#ifdef __linux__ +#include "linux/fs.hpp" +#endif + +#include "slave/containerizer/mesos/provisioner/constants.hpp" + #include "slave/containerizer/mesos/provisioner/docker/metadata_manager.hpp" #include "slave/containerizer/mesos/provisioner/docker/paths.hpp" #include "slave/containerizer/mesos/provisioner/docker/puller.hpp" @@ -55,6 +61,10 @@ using process::Promise; using master::Master; +using mesos::internal::slave::AUFS_BACKEND; +using mesos::internal::slave::COPY_BACKEND; +using mesos::internal::slave::OVERLAY_BACKEND; + using mesos::master::detector::MasterDetector; using slave::ImageInfo; @@ -64,6 +74,8 @@ using slave::docker::Puller; using slave::docker::RegistryPuller; using slave::docker::Store; +using testing::WithParamInterface; + namespace mesos { namespace internal { namespace tests { @@ -552,9 +564,10 @@ TEST_F(ProvisionerDockerPullerTest, ROOT_INTERNET_CURL_Normalize) } -// This test verifies that any docker image containing whiteout files -// will be processed correctly. -TEST_F(ProvisionerDockerPullerTest, ROOT_INTERNET_CURL_Whiteout) +// This test verifies that the scratch based docker image (that +// only contain a single binary and its dependencies) can be +// launched correctly. +TEST_F(ProvisionerDockerPullerTest, ROOT_INTERNET_CURL_ScratchImage) { Try<Owned<cluster::Master>> master = StartMaster(); ASSERT_SOME(master); @@ -585,17 +598,8 @@ TEST_F(ProvisionerDockerPullerTest, ROOT_INTERNET_CURL_Whiteout) const Offer& offer = offers.get()[0]; - // We are using the docker image 'cirros' to verify that - // the symlink /etc/rc3.d/S40-network does not exist in - // container's rootfs because it is labeled by a '.wh.' - // whiteout file, and both should be removed. CommandInfo command; command.set_shell(false); - command.set_value("/usr/bin/test"); - command.add_arguments("test"); - command.add_arguments("!"); - command.add_arguments("-f"); - command.add_arguments("/etc/rc3.d/S40-network"); TaskInfo task = createTask( offer.slave_id(), @@ -604,7 +608,10 @@ TEST_F(ProvisionerDockerPullerTest, ROOT_INTERNET_CURL_Whiteout) Image image; image.set_type(Image::DOCKER); - image.mutable_docker()->set_name("cirros"); + + // 'hello-world' is a scratch image. It contains only one + // binary 'hello' in its rootfs. + image.mutable_docker()->set_name("hello-world"); ContainerInfo* container = task.mutable_container(); container->set_type(ContainerInfo::MESOS); @@ -631,10 +638,40 @@ TEST_F(ProvisionerDockerPullerTest, ROOT_INTERNET_CURL_Whiteout) } -// This test verifies that the scratch based docker image (that -// only contain a single binary and its dependencies) can be -// launched correctly. -TEST_F(ProvisionerDockerPullerTest, ROOT_INTERNET_CURL_ScratchImage) +class ProvisionerDockerWhiteoutTest + : public MesosTest, + public WithParamInterface<string> +{ +public: + // Returns the supported backends. + static vector<string> parameters() + { + vector<string> backends = {COPY_BACKEND}; + + Try<bool> aufsSupported = fs::supported("aufs"); + if (aufsSupported.isSome() && aufsSupported.get()) { + backends.push_back(AUFS_BACKEND); + } + + Try<bool> overlayfsSupported = fs::supported("overlayfs"); + if (overlayfsSupported.isSome() && overlayfsSupported.get()) { + backends.push_back(OVERLAY_BACKEND); + } + + return backends; + } +}; + + +INSTANTIATE_TEST_CASE_P( + BackendFlag, + ProvisionerDockerWhiteoutTest, + ::testing::ValuesIn(ProvisionerDockerWhiteoutTest::parameters())); + + +// This test verifies that a docker image containing whiteout files +// will be processed correctly by copy, aufs and overlay backends. +TEST_P(ProvisionerDockerWhiteoutTest, ROOT_INTERNET_CURL_Whiteout) { Try<Owned<cluster::Master>> master = StartMaster(); ASSERT_SOME(master); @@ -642,6 +679,7 @@ TEST_F(ProvisionerDockerPullerTest, ROOT_INTERNET_CURL_ScratchImage) slave::Flags flags = CreateSlaveFlags(); flags.isolation = "docker/runtime,filesystem/linux"; flags.image_providers = "docker"; + flags.image_provisioner_backend = GetParam(); Owned<MasterDetector> detector = master.get()->createDetector(); Try<Owned<cluster::Slave>> slave = StartSlave(detector.get(), flags); @@ -665,20 +703,27 @@ TEST_F(ProvisionerDockerPullerTest, ROOT_INTERNET_CURL_ScratchImage) const Offer& offer = offers.get()[0]; - CommandInfo command; - command.set_shell(false); + // We are using the docker image 'zhq527725/whiteout' to verify that the + // files '/dir1/file1' and '/dir1/dir2/file2' do not exist in container's + // rootfs because of the following two whiteout files in the image: + // '/dir1/.wh.file1' + // '/dir1/dir2/.wh..wh..opq' + // And we also verify that the file '/dir1/dir2/file3' exists in container's + // rootfs which will NOT be applied by '/dir1/dir2/.wh..wh..opq' since they + // are in the same layer. + // See more details about this docker image in the link below: + // https://hub.docker.com/r/zhq527725/whiteout/ + CommandInfo command = createCommandInfo( + "test ! -f /dir1/file1 && " + "test ! -f /dir1/dir2/file2 && " + "test -f /dir1/dir2/file3"); TaskInfo task = createTask( offer.slave_id(), Resources::parse("cpus:1;mem:128").get(), command); - Image image; - image.set_type(Image::DOCKER); - - // 'hello-world' is a scratch image. It contains only one - // binary 'hello' in its rootfs. - image.mutable_docker()->set_name("hello-world"); + Image image = createDockerImage("zhq527725/whiteout"); ContainerInfo* container = task.mutable_container(); container->set_type(ContainerInfo::MESOS);