The document is being tracked here https://issues.apache.org/jira/browse/MESOS-5549
On Wed, Jun 8, 2016 at 7:29 PM, Neil Conway <neil.con...@gmail.com> wrote: > Can you update the documentation for this change, please? > > Thanks, > Neil > > On Tue, Jun 7, 2016 at 6:14 PM, <ji...@apache.org> wrote: > > Repository: mesos > > Updated Branches: > > refs/heads/master 90871a48f -> e5358ed1c > > > > > > Added aufs provisioning backend. > > > > Review: https://reviews.apache.org/r/47396/ > > > > > > Project: http://git-wip-us.apache.org/repos/asf/mesos/repo > > Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/e5358ed1 > > Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/e5358ed1 > > Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/e5358ed1 > > > > Branch: refs/heads/master > > Commit: e5358ed1c132923d5fa357d1e337e037d1f29c8a > > Parents: ca09304 > > Author: Shuai Lin <linshuai2...@gmail.com> > > Authored: Mon Jun 6 18:05:15 2016 -0700 > > Committer: Jie Yu <yujie....@gmail.com> > > Committed: Tue Jun 7 09:14:22 2016 -0700 > > > > ---------------------------------------------------------------------- > > src/Makefile.am | 2 + > > .../containerizer/mesos/provisioner/backend.cpp | 9 + > > .../mesos/provisioner/backends/aufs.cpp | 227 > +++++++++++++++++++ > > .../mesos/provisioner/backends/aufs.hpp | 70 ++++++ > > .../containerizer/provisioner_backend_tests.cpp | 51 +++++ > > src/tests/environment.cpp | 13 ++ > > 6 files changed, 372 insertions(+) > > ---------------------------------------------------------------------- > > > > > > > http://git-wip-us.apache.org/repos/asf/mesos/blob/e5358ed1/src/Makefile.am > > ---------------------------------------------------------------------- > > diff --git a/src/Makefile.am b/src/Makefile.am > > index a08ea40..b02b901 100644 > > --- a/src/Makefile.am > > +++ b/src/Makefile.am > > @@ -1001,6 +1001,7 @@ MESOS_LINUX_FILES = > \ > > slave/containerizer/mesos/isolators/filesystem/shared.cpp \ > > slave/containerizer/mesos/isolators/namespaces/pid.cpp \ > > slave/containerizer/mesos/isolators/network/cni/cni.cpp \ > > + slave/containerizer/mesos/provisioner/backends/aufs.cpp \ > > slave/containerizer/mesos/provisioner/backends/bind.cpp \ > > slave/containerizer/mesos/provisioner/backends/overlay.cpp > > > > @@ -1024,6 +1025,7 @@ MESOS_LINUX_FILES += > \ > > slave/containerizer/mesos/isolators/filesystem/shared.hpp \ > > slave/containerizer/mesos/isolators/namespaces/pid.hpp \ > > slave/containerizer/mesos/isolators/network/cni/cni.hpp \ > > + slave/containerizer/mesos/provisioner/backends/aufs.hpp \ > > slave/containerizer/mesos/provisioner/backends/bind.hpp \ > > slave/containerizer/mesos/provisioner/backends/overlay.hpp > > > > > > > http://git-wip-us.apache.org/repos/asf/mesos/blob/e5358ed1/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 b2a20b7..93a2c3a 100644 > > --- a/src/slave/containerizer/mesos/provisioner/backend.cpp > > +++ b/src/slave/containerizer/mesos/provisioner/backend.cpp > > @@ -25,6 +25,7 @@ > > #include "slave/containerizer/mesos/provisioner/backend.hpp" > > > > #ifdef __linux__ > > +#include "slave/containerizer/mesos/provisioner/backends/aufs.hpp" > > #include "slave/containerizer/mesos/provisioner/backends/bind.hpp" > > #endif > > #include "slave/containerizer/mesos/provisioner/backends/copy.hpp" > > @@ -47,6 +48,14 @@ hashmap<string, Owned<Backend>> Backend::create(const > Flags& flags) > > #ifdef __linux__ > > creators.put("bind", &BindBackend::create); > > > > + Try<bool> aufsSupported = fs::aufs::supported(); > > + if (aufsSupported.isError()) { > > + LOG(WARNING) << "Failed to check aufs availability: '" > > + << aufsSupported.error(); > > + } else if (aufsSupported.get()) { > > + creators.put("aufs", &AufsBackend::create); > > + } > > + > > Try<bool> overlayfsSupported = fs::overlay::supported(); > > if (overlayfsSupported.isError()) { > > LOG(WARNING) << "Failed to check overlayfs availability: '" > > > > > http://git-wip-us.apache.org/repos/asf/mesos/blob/e5358ed1/src/slave/containerizer/mesos/provisioner/backends/aufs.cpp > > ---------------------------------------------------------------------- > > diff --git a/src/slave/containerizer/mesos/provisioner/backends/aufs.cpp > b/src/slave/containerizer/mesos/provisioner/backends/aufs.cpp > > new file mode 100644 > > index 0000000..54c0057 > > --- /dev/null > > +++ b/src/slave/containerizer/mesos/provisioner/backends/aufs.cpp > > @@ -0,0 +1,227 @@ > > +// 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/aufs.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 AufsBackendProcess : public Process<AufsBackendProcess> > > +{ > > +public: > > + Future<Nothing> provision( > > + const vector<string>& layers, > > + const string& rootfs, > > + const string& backendDir); > > + > > + Future<bool> destroy(const string& rootfs); > > +}; > > + > > + > > +Try<Owned<Backend>> AufsBackend::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( > > + "AufsBackend requires root privileges, " > > + "but is running as user " + user.get()); > > + } > > + > > + return Owned<Backend>(new AufsBackend( > > + Owned<AufsBackendProcess>(new AufsBackendProcess()))); > > +} > > + > > + > > +AufsBackend::~AufsBackend() > > +{ > > + terminate(process.get()); > > + wait(process.get()); > > +} > > + > > + > > +AufsBackend::AufsBackend(Owned<AufsBackendProcess> _process) > > + : process(_process) > > +{ > > + spawn(CHECK_NOTNULL(process.get())); > > +} > > + > > + > > +Future<Nothing> AufsBackend::provision( > > + const vector<string>& layers, > > + const string& rootfs, > > + const string& backendDir) > > +{ > > + return dispatch( > > + process.get(), > > + &AufsBackendProcess::provision, > > + layers, > > + rootfs, > > + backendDir); > > +} > > + > > +Future<bool> AufsBackend::destroy(const string& rootfs) > > +{ > > + return dispatch(process.get(), &AufsBackendProcess::destroy, rootfs); > > +} > > + > > + > > +Future<Nothing> AufsBackendProcess::provision( > > + const vector<string>& layers, > > + const string& rootfs, > > + const string& backendDir) > > +{ > > + if (layers.size() == 0) { > > + return Failure("No filesystem layer provided"); > > + } > > + > > + Try<Nothing> mkdir = os::mkdir(rootfs); > > + if (mkdir.isError()) { > > + return Failure( > > + "Failed to create container rootfs at '" + > > + rootfs + "': " + mkdir.error()); > > + } > > + > > + const string scratchDirId = Path(rootfs).basename(); > > + const string scratchDir = path::join(backendDir, "scratch", > scratchDirId); > > + > > + // The top writable directory for aufs. > > + const string workdir = path::join(scratchDir, "workdir"); > > + > > + mkdir = os::mkdir(workdir); > > + if (mkdir.isError()) { > > + return Failure( > > + "Failed to create aufs workdir at '" + > > + workdir + "': " + mkdir.error()); > > + } > > + > > + // See http://aufs.sourceforge.net/aufs2/man.html > > + // for the mount syntax for aufs. > > + string options = "dirs=" + workdir + ":"; > > + > > + // For aufs, 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 bottom most layer. > > + options += strings::join(":", adaptor::reverse(layers)); > > + > > + VLOG(1) << "Provisioning image rootfs with aufs: '" << options << "'"; > > + > > + Try<Nothing> mount = fs::mount( > > + "aufs", > > + rootfs, > > + "aufs", > > + 0, > > + options); > > + > > + if (mount.isError()) { > > + return Failure( > > + "Failed to mount rootfs '" + rootfs + > > + "' with aufs: " + mount.error()); > > + } > > + > > + // Mark the mount as shared+slave. > > + mount = fs::mount( > > + None(), > > + rootfs, > > + None(), > > + MS_SLAVE, > > + nullptr); > > + > > + if (mount.isError()) { > > + return Failure( > > + "Failed to mark mount '" + rootfs + > > + "' as a slave mount: " + mount.error()); > > + } > > + > > + mount = fs::mount( > > + None(), > > + rootfs, > > + None(), > > + MS_SHARED, > > + nullptr); > > + > > + if (mount.isError()) { > > + return Failure( > > + "Failed to mark mount '" + rootfs + > > + "' as a shared mount: " + mount.error()); > > + } > > + > > + return Nothing(); > > +} > > + > > + > > +Future<bool> AufsBackendProcess::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 aufs-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/e5358ed1/src/slave/containerizer/mesos/provisioner/backends/aufs.hpp > > ---------------------------------------------------------------------- > > diff --git a/src/slave/containerizer/mesos/provisioner/backends/aufs.hpp > b/src/slave/containerizer/mesos/provisioner/backends/aufs.hpp > > new file mode 100644 > > index 0000000..a5ac13b > > --- /dev/null > > +++ b/src/slave/containerizer/mesos/provisioner/backends/aufs.hpp > > @@ -0,0 +1,70 @@ > > +// 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_AUFS_HPP__ > > +#define __MESOS_PROVISIONER_AUFS_HPP__ > > + > > +#include "slave/containerizer/mesos/provisioner/backend.hpp" > > + > > +namespace mesos { > > +namespace internal { > > +namespace slave { > > + > > +// Forward declaration. > > +class AufsBackendProcess; > > + > > + > > +// This backend mounts the images layers to the rootfs using the aufs > file > > +// system. The directory layout is as follows: > > +// <work_dir> ('--work_dir' flag) > > +// |-- provisioner > > +// |-- containers > > +// |-- <container-id> > > +// |-- backends > > +// |-- aufs > > +// |-- rootfses > > +// |-- <rootfs_id> (the rootfs) > > +// |-- scratch > > +// |-- <rootfs_id> (the scratch space) > > +// |-- workdir > > +class AufsBackend : public Backend > > +{ > > +public: > > + virtual ~AufsBackend(); > > + > > + static Try<process::Owned<Backend>> create(const Flags&); > > + > > + virtual process::Future<Nothing> provision( > > + const std::vector<std::string>& layers, > > + const std::string& rootfs, > > + const std::string& backendDir); > > + > > + virtual process::Future<bool> destroy(const std::string& rootfs); > > + > > +private: > > + explicit AufsBackend(process::Owned<AufsBackendProcess> process); > > + > > + AufsBackend(const AufsBackend&); > > + AufsBackend& operator=(const AufsBackend&); > > + > > + process::Owned<AufsBackendProcess> process; > > +}; > > + > > +} // namespace slave { > > +} // namespace internal { > > +} // namespace mesos { > > + > > +#endif // __MESOS_PROVISIONER_AUFS_HPP__ > > > > > http://git-wip-us.apache.org/repos/asf/mesos/blob/e5358ed1/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 bc04760..f18cc3f 100644 > > --- a/src/tests/containerizer/provisioner_backend_tests.cpp > > +++ b/src/tests/containerizer/provisioner_backend_tests.cpp > > @@ -200,6 +200,57 @@ TEST_F(CopyBackendTest, ROOT_CopyBackend) > > EXPECT_FALSE(os::exists(rootfs)); > > } > > > > + > > +class AufsBackendTest : public MountBackendTest {}; > > + > > + > > +// Provision a rootfs using multiple layers with the aufs backend. > > +TEST_F(AufsBackendTest, ROOT_AUFS_AufsBackend) > > +{ > > + 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("aufs")); > > + > > + AWAIT_READY(backends["aufs"]->provision( > > + {layer1, layer2}, > > + rootfs, > > + sandbox.get())); > > + > > + EXPECT_TRUE(os::exists(path::join(rootfs, "dir1", "1"))); > > + EXPECT_SOME_EQ("1", os::read(path::join(rootfs, "dir1", "1"))); > > + > > + EXPECT_TRUE(os::exists(path::join(rootfs, "dir2", "2"))); > > + EXPECT_SOME_EQ("2", os::read(path::join(rootfs, "dir2", "2"))); > > + > > + // Last layer should overwrite existing file of earlier layers. > > + EXPECT_TRUE(os::exists(path::join(rootfs, "file"))); > > + EXPECT_SOME_EQ("test2", os::read(path::join(rootfs, "file"))); > > + > > + // Rootfs should be writable. > > + ASSERT_SOME(os::write(path::join(rootfs, "file"), "test3")); > > + > > + // Files created in rootfs should shadow the files of lower dirs. > > + EXPECT_SOME_EQ("test3", os::read(path::join(rootfs, "file"))); > > + EXPECT_SOME_EQ("test2", os::read(path::join(layer2, "file"))); > > + > > + AWAIT_READY(backends["aufs"]->destroy(rootfs)); > > + > > + EXPECT_FALSE(os::exists(rootfs)); > > +} > > + > > } // namespace tests { > > } // namespace internal { > > } // namespace mesos { > > > > > http://git-wip-us.apache.org/repos/asf/mesos/blob/e5358ed1/src/tests/environment.cpp > > ---------------------------------------------------------------------- > > diff --git a/src/tests/environment.cpp b/src/tests/environment.cpp > > index 849e9ce..ebfacc3 100644 > > --- a/src/tests/environment.cpp > > +++ b/src/tests/environment.cpp > > @@ -517,6 +517,18 @@ public: > > }; > > > > > > +class AufsFilter : public SupportedFilesystemTestFilter > > +{ > > +public: > > + AufsFilter() : SupportedFilesystemTestFilter("aufs") {} > > + > > + bool disable(const ::testing::TestInfo* test) const > > + { > > + return fsSupportError.isSome() && matches(test, "AUFS_"); > > + } > > +}; > > + > > + > > class OverlayFSFilter : public SupportedFilesystemTestFilter > > { > > public: > > @@ -738,6 +750,7 @@ Environment::Environment(const Flags& _flags) : > flags(_flags) > > > > vector<Owned<TestFilter> > filters; > > > > + filters.push_back(Owned<TestFilter>(new AufsFilter())); > > filters.push_back(Owned<TestFilter>(new BenchmarkFilter())); > > filters.push_back(Owned<TestFilter>(new CfsFilter())); > > filters.push_back(Owned<TestFilter>(new CgroupsFilter())); > > >