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