You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by ji...@apache.org on 2016/02/29 02:00:49 UTC

mesos git commit: Added overlayfs provisioning backend.

Repository: mesos
Updated Branches:
  refs/heads/master 404cd1506 -> 465df71e8


Added overlayfs provisioning backend.

Review: https://reviews.apache.org/r/43932/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/465df71e
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/465df71e
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/465df71e

Branch: refs/heads/master
Commit: 465df71e828fa4904d969ab986aeb3357a214360
Parents: 404cd15
Author: Shuai Lin <li...@gmail.com>
Authored: Sun Feb 28 16:17:35 2016 -0800
Committer: Jie Yu <yu...@gmail.com>
Committed: Sun Feb 28 16:20:48 2016 -0800

----------------------------------------------------------------------
 src/CMakeLists.txt                              |   1 +
 src/Makefile.am                                 |   6 +-
 .../containerizer/mesos/provisioner/backend.cpp |  12 ++
 .../mesos/provisioner/backends/overlay.cpp      | 213 +++++++++++++++++++
 .../mesos/provisioner/backends/overlay.hpp      |  60 ++++++
 src/slave/flags.cpp                             |   2 +-
 .../containerizer/provisioner_backend_tests.cpp |  53 ++++-
 src/tests/environment.cpp                       |  36 ++++
 8 files changed, 377 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/465df71e/src/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index e494556..4fbe4da 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -136,6 +136,7 @@ set(LINUX_SRC
   slave/containerizer/mesos/isolators/filesystem/shared.cpp
   slave/containerizer/mesos/isolators/namespaces/pid.cpp
   slave/containerizer/mesos/provisioner/backends/bind.cpp
+  slave/containerizer/mesos/provisioner/backends/overlay.cpp
   )
 
 set(DOCKER_SRC

http://git-wip-us.apache.org/repos/asf/mesos/blob/465df71e/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index 3c015a4..5d8fe8b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -840,7 +840,8 @@ MESOS_LINUX_FILES =							\
   slave/containerizer/mesos/isolators/filesystem/linux.cpp		\
   slave/containerizer/mesos/isolators/filesystem/shared.cpp		\
   slave/containerizer/mesos/isolators/namespaces/pid.cpp		\
-  slave/containerizer/mesos/provisioner/backends/bind.cpp
+  slave/containerizer/mesos/provisioner/backends/bind.cpp		\
+  slave/containerizer/mesos/provisioner/backends/overlay.cpp
 
 MESOS_LINUX_FILES +=							\
   linux/cgroups.hpp							\
@@ -859,7 +860,8 @@ MESOS_LINUX_FILES +=							\
   slave/containerizer/mesos/isolators/filesystem/linux.hpp		\
   slave/containerizer/mesos/isolators/filesystem/shared.hpp		\
   slave/containerizer/mesos/isolators/namespaces/pid.hpp		\
-  slave/containerizer/mesos/provisioner/backends/bind.hpp
+  slave/containerizer/mesos/provisioner/backends/bind.hpp		\
+  slave/containerizer/mesos/provisioner/backends/overlay.hpp
 
 MESOS_NETWORK_ISOLATOR_FILES =						\
   linux/routing/handle.cpp						\

http://git-wip-us.apache.org/repos/asf/mesos/blob/465df71e/src/slave/containerizer/mesos/provisioner/backend.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/provisioner/backend.cpp b/src/slave/containerizer/mesos/provisioner/backend.cpp
index 01d06eb..ae66324 100644
--- a/src/slave/containerizer/mesos/provisioner/backend.cpp
+++ b/src/slave/containerizer/mesos/provisioner/backend.cpp
@@ -18,10 +18,13 @@
 
 #include <stout/os.hpp>
 
+#include "linux/fs.hpp"
+
 #include "slave/containerizer/mesos/provisioner/backend.hpp"
 
 #include "slave/containerizer/mesos/provisioner/backends/bind.hpp"
 #include "slave/containerizer/mesos/provisioner/backends/copy.hpp"
+#include "slave/containerizer/mesos/provisioner/backends/overlay.hpp"
 
 using namespace process;
 
@@ -37,7 +40,16 @@ hashmap<string, Owned<Backend>> Backend::create(const Flags& flags)
 
 #ifdef __linux__
   creators.put("bind", &BindBackend::create);
+
+  Try<bool> overlayfsSupported = fs::supported("overlayfs");
+  if (overlayfsSupported.isError()) {
+    LOG(WARNING) << "Failed to check overlayfs availability: '"
+                 << overlayfsSupported.error();
+  } else if (overlayfsSupported.get()) {
+    creators.put("overlay", &OverlayBackend::create);
+  }
 #endif // __linux__
+
   creators.put("copy", &CopyBackend::create);
 
   hashmap<string, Owned<Backend>> backends;

http://git-wip-us.apache.org/repos/asf/mesos/blob/465df71e/src/slave/containerizer/mesos/provisioner/backends/overlay.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/provisioner/backends/overlay.cpp b/src/slave/containerizer/mesos/provisioner/backends/overlay.cpp
new file mode 100644
index 0000000..5cc0f8b
--- /dev/null
+++ b/src/slave/containerizer/mesos/provisioner/backends/overlay.cpp
@@ -0,0 +1,213 @@
+// 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/dispatch.hpp>
+#include <process/process.hpp>
+
+#include <stout/adaptor.hpp>
+#include <stout/foreach.hpp>
+#include <stout/os.hpp>
+
+#include "linux/fs.hpp"
+
+#include "slave/containerizer/mesos/provisioner/backends/overlay.hpp"
+
+using process::Failure;
+using process::Future;
+using process::Owned;
+using process::Process;
+using process::Shared;
+
+using process::dispatch;
+using process::spawn;
+using process::wait;
+
+using std::string;
+using std::vector;
+
+namespace mesos {
+namespace internal {
+namespace slave {
+
+class OverlayBackendProcess : public Process<OverlayBackendProcess>
+{
+public:
+  Future<Nothing> provision(const vector<string>& layers, const string& rootfs);
+
+  Future<bool> destroy(const string& rootfs);
+};
+
+
+Try<Owned<Backend>> OverlayBackend::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(
+      "OverlayBackend requires root privileges, "
+      "but is running as user " +
+      user.get());
+  }
+
+  Try<bool> supported = fs::supported("overlayfs");
+  if (supported.isError()) {
+    return Error(supported.error());
+  }
+
+  if (!supported.get()) {
+    return Error("Overlay filesystem not supported");
+  }
+
+  return Owned<Backend>(new OverlayBackend(
+      Owned<OverlayBackendProcess>(new OverlayBackendProcess())));
+}
+
+
+OverlayBackend::~OverlayBackend()
+{
+  terminate(process.get());
+  wait(process.get());
+}
+
+
+OverlayBackend::OverlayBackend(Owned<OverlayBackendProcess> _process)
+  : process(_process)
+{
+  spawn(CHECK_NOTNULL(process.get()));
+}
+
+
+Future<Nothing> OverlayBackend::provision(
+    const vector<string>& layers,
+    const string& rootfs)
+{
+  return dispatch(
+      process.get(), &OverlayBackendProcess::provision, layers, rootfs);
+}
+
+
+Future<bool> OverlayBackend::destroy(const string& rootfs)
+{
+  return dispatch(process.get(), &OverlayBackendProcess::destroy, rootfs);
+}
+
+
+Future<Nothing> OverlayBackendProcess::provision(
+    const vector<string>& layers,
+    const string& rootfs)
+{
+  if (layers.size() == 0) {
+    return Failure("No filesystem layer provided");
+  }
+
+  if (layers.size() == 1) {
+    return Failure("Need more than one layer for overlay");
+  }
+
+  Try<Nothing> mkdir = os::mkdir(rootfs);
+  if (mkdir.isError()) {
+    return Failure("Failed to create container rootfs at '" + rootfs + "': " +
+                   mkdir.error());
+  }
+
+  // For overlayfs, the specified lower directories will be stacked beginning
+  // from the rightmost one and going left. But we need the first layer in the
+  // vector to be the the bottom most layer.
+  std::string lowerDir = "lowerdir=";
+  lowerDir += strings::join(":", adaptor::reverse(layers));
+
+  Try<Nothing> mount = fs::mount(
+      "overlay",
+      rootfs,
+      "overlay",
+      MS_RDONLY,
+      lowerDir);
+
+  if (mount.isError()) {
+    return Failure(
+        "Failed to mount rootfs '" + rootfs +
+        "' with overlayfs: " + mount.error());
+  }
+
+  // Mark the mount as shared+slave.
+  mount = fs::mount(
+      None(),
+      rootfs,
+      None(),
+      MS_SLAVE,
+      NULL);
+
+  if (mount.isError()) {
+    return Failure(
+        "Failed to mark mount '" + rootfs +
+        "' as a slave mount: " + mount.error());
+  }
+
+  mount = fs::mount(
+      None(),
+      rootfs,
+      None(),
+      MS_SHARED,
+      NULL);
+
+  if (mount.isError()) {
+    return Failure(
+        "Failed to mark mount '" + rootfs +
+        "' as a shared mount: " + mount.error());
+  }
+
+  return Nothing();
+}
+
+
+Future<bool> OverlayBackendProcess::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->entries) {
+    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 overlay-mounted rootfs '" + rootfs + "': " +
+            unmount.error());
+      }
+
+      Try<Nothing> rmdir = os::rmdir(rootfs);
+      if (rmdir.isError()) {
+        return Failure(
+            "Failed to remove rootfs mount point '" + rootfs + "': " +
+            rmdir.error());
+      }
+
+      return true;
+    }
+  }
+
+  return false;
+}
+
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/465df71e/src/slave/containerizer/mesos/provisioner/backends/overlay.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/provisioner/backends/overlay.hpp b/src/slave/containerizer/mesos/provisioner/backends/overlay.hpp
new file mode 100644
index 0000000..85cc737
--- /dev/null
+++ b/src/slave/containerizer/mesos/provisioner/backends/overlay.hpp
@@ -0,0 +1,60 @@
+// 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_OVERLAY_HPP__
+#define __MESOS_PROVISIONER_OVERLAY_HPP__
+
+#include "slave/containerizer/mesos/provisioner/backend.hpp"
+
+namespace mesos {
+namespace internal {
+namespace slave {
+
+// Forward declaration.
+class OverlayBackendProcess;
+
+
+// This is a specialized backend that is useful for deploying multi-layer
+// images using the overlayfs-based backend.
+// 1) OverlayBackend does not support images with a single layer.
+// 2) The filesystem is read-only.
+class OverlayBackend : public Backend
+{
+public:
+  virtual ~OverlayBackend();
+
+  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 OverlayBackend(process::Owned<OverlayBackendProcess> process);
+
+  OverlayBackend(const OverlayBackend&);
+  OverlayBackend& operator=(const OverlayBackend&);
+
+  process::Owned<OverlayBackendProcess> process;
+};
+
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
+
+#endif // __MESOS_PROVISIONER_OVERLAY_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/465df71e/src/slave/flags.cpp
----------------------------------------------------------------------
diff --git a/src/slave/flags.cpp b/src/slave/flags.cpp
index 1c6a87b..7f139e9 100644
--- a/src/slave/flags.cpp
+++ b/src/slave/flags.cpp
@@ -110,7 +110,7 @@ mesos::internal::slave::Flags::Flags()
   add(&Flags::image_provisioner_backend,
       "image_provisioner_backend",
       "Strategy for provisioning container rootfs from images,\n"
-      "e.g., `bind`, `copy`.",
+      "e.g., `bind`, `copy`, `overlay`.",
       "copy");
 
   add(&Flags::appc_simple_discovery_uri_prefix,

http://git-wip-us.apache.org/repos/asf/mesos/blob/465df71e/src/tests/containerizer/provisioner_backend_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/provisioner_backend_tests.cpp b/src/tests/containerizer/provisioner_backend_tests.cpp
index 73728f2..d49204f 100644
--- a/src/tests/containerizer/provisioner_backend_tests.cpp
+++ b/src/tests/containerizer/provisioner_backend_tests.cpp
@@ -31,6 +31,7 @@
 
 #include "slave/containerizer/mesos/provisioner/backends/bind.hpp"
 #include "slave/containerizer/mesos/provisioner/backends/copy.hpp"
+#include "slave/containerizer/mesos/provisioner/backends/overlay.hpp"
 
 #include "tests/flags.hpp"
 
@@ -46,12 +47,11 @@ namespace internal {
 namespace tests {
 
 #ifdef __linux__
-class BindBackendTest : public TemporaryDirectoryTest
+class MountBackendTest : public TemporaryDirectoryTest
 {
 protected:
-  void TearDown()
+  virtual void TearDown()
   {
-    // Clean up by unmounting any leftover mounts in 'sandbox'.
     Try<fs::MountInfoTable> mountTable = fs::MountInfoTable::read();
     ASSERT_SOME(mountTable);
 
@@ -68,6 +68,53 @@ protected:
 };
 
 
+class OverlayBackendTest : public MountBackendTest {};
+
+
+// Provision a rootfs using multiple layers with the overlay backend.
+TEST_F(OverlayBackendTest, ROOT_OVERLAYFS_OverlayFSBackend)
+{
+  string layer1 = path::join(os::getcwd(), "source1");
+  ASSERT_SOME(os::mkdir(layer1));
+  ASSERT_SOME(os::mkdir(path::join(layer1, "dir1")));
+  ASSERT_SOME(os::write(path::join(layer1, "dir1", "1"), "1"));
+  ASSERT_SOME(os::write(path::join(layer1, "file"), "test1"));
+
+  string layer2 = path::join(os::getcwd(), "source2");
+  ASSERT_SOME(os::mkdir(layer2));
+  ASSERT_SOME(os::mkdir(path::join(layer2, "dir2")));
+  ASSERT_SOME(os::write(path::join(layer2, "dir2", "2"), "2"));
+  ASSERT_SOME(os::write(path::join(layer2, "file"), "test2"));
+
+  string rootfs = path::join(os::getcwd(), "rootfs");
+
+  hashmap<string, Owned<Backend>> backends = Backend::create(slave::Flags());
+  ASSERT_TRUE(backends.contains("overlay"));
+
+  AWAIT_READY(backends["overlay"]->provision({layer1, layer2}, rootfs));
+
+  EXPECT_TRUE(os::exists(path::join(rootfs, "dir1", "1")));
+  Try<string> read = os::read(path::join(rootfs, "dir1", "1"));
+  EXPECT_SOME_EQ("1", read);
+
+  EXPECT_TRUE(os::exists(path::join(rootfs, "dir2", "2")));
+  read = os::read(path::join(rootfs, "dir2", "2"));
+  EXPECT_SOME_EQ("2", read);
+
+  EXPECT_TRUE(os::exists(path::join(rootfs, "file")));
+  read = os::read(path::join(rootfs, "file"));
+  // Last layer should overwrite existing file of earlier layers.
+  EXPECT_SOME_EQ("test2", read);
+
+  AWAIT_READY(backends["overlay"]->destroy(rootfs));
+
+  EXPECT_FALSE(os::exists(rootfs));
+}
+
+
+class BindBackendTest : public MountBackendTest {};
+
+
 // Provision a rootfs using a BindBackend to another directory and
 // verify if it is read-only within the mount.
 TEST_F(BindBackendTest, ROOT_BindBackend)

http://git-wip-us.apache.org/repos/asf/mesos/blob/465df71e/src/tests/environment.cpp
----------------------------------------------------------------------
diff --git a/src/tests/environment.cpp b/src/tests/environment.cpp
index 6cd295f..ee1bbe6 100644
--- a/src/tests/environment.cpp
+++ b/src/tests/environment.cpp
@@ -459,6 +459,41 @@ private:
 };
 
 
+class OverlayFSTestFilter : public TestFilter
+{
+public:
+  OverlayFSTestFilter()
+  {
+#ifdef __linux__
+    Try<bool> check = fs::supported("overlayfs");
+    if (check.isError()) {
+      overlayfsError = check.error();
+    } else if (!check.get()) {
+      overlayfsError = Error("Overlayfs is not supported on your systems");
+    }
+#else
+    overlayfsError =
+      Error("Overlayfs tests not supported on non-Linux systems");
+#endif // __linux__
+    if (overlayfsError.isSome()) {
+      std::cerr
+        << "-------------------------------------------------------------\n"
+        << "We cannot run any overlayfs tests because:\n"
+        << overlayfsError.get().message << "\n"
+        << "-------------------------------------------------------------\n";
+    }
+  }
+
+  bool disable(const ::testing::TestInfo* test) const
+  {
+    return overlayfsError.isSome() && matches(test, "OVERLAYFS_");
+  }
+
+private:
+  Option<Error> overlayfsError;
+};
+
+
 class PerfCPUCyclesFilter : public TestFilter
 {
 public:
@@ -641,6 +676,7 @@ Environment::Environment(const Flags& _flags) : flags(_flags)
   filters.push_back(Owned<TestFilter>(new NetcatFilter()));
   filters.push_back(Owned<TestFilter>(new NetClsCgroupsFilter()));
   filters.push_back(Owned<TestFilter>(new NetworkIsolatorTestFilter()));
+  filters.push_back(Owned<TestFilter>(new OverlayFSTestFilter()));
   filters.push_back(Owned<TestFilter>(new PerfCPUCyclesFilter()));
   filters.push_back(Owned<TestFilter>(new PerfFilter()));
   filters.push_back(Owned<TestFilter>(new RootFilter()));