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);

Reply via email to