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