Repository: mesos Updated Branches: refs/heads/master 7b83081c6 -> fbcc3922d
Created a test abstraction for preparing test rootfs. Review: https://reviews.apache.org/r/36956 Project: http://git-wip-us.apache.org/repos/asf/mesos/repo Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/f077ae66 Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/f077ae66 Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/f077ae66 Branch: refs/heads/master Commit: f077ae6626f04e95e47ee24ffa7ad401ad8ebfae Parents: 7b83081 Author: Jie Yu <[email protected]> Authored: Thu Jul 30 15:07:30 2015 -0700 Committer: Jie Yu <[email protected]> Committed: Fri Aug 7 16:30:36 2015 -0700 ---------------------------------------------------------------------- src/Makefile.am | 1 + src/tests/containerizer/launch_tests.cpp | 132 +++++------------------ src/tests/containerizer/rootfs.hpp | 145 ++++++++++++++++++++++++++ 3 files changed, 173 insertions(+), 105 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mesos/blob/f077ae66/src/Makefile.am ---------------------------------------------------------------------- diff --git a/src/Makefile.am b/src/Makefile.am index 35ebbbd..c213ac7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -665,6 +665,7 @@ libmesos_no_3rdparty_la_SOURCES += \ tests/containerizer/isolator.hpp \ tests/containerizer/launcher.hpp \ tests/containerizer/memory_test_helper.hpp \ + tests/containerizer/rootfs.hpp \ tests/containerizer/setns_test_helper.hpp \ usage/usage.hpp \ watcher/whitelist_watcher.hpp \ http://git-wip-us.apache.org/repos/asf/mesos/blob/f077ae66/src/tests/containerizer/launch_tests.cpp ---------------------------------------------------------------------- diff --git a/src/tests/containerizer/launch_tests.cpp b/src/tests/containerizer/launch_tests.cpp index 73c8c5f..d211fc0 100644 --- a/src/tests/containerizer/launch_tests.cpp +++ b/src/tests/containerizer/launch_tests.cpp @@ -21,25 +21,23 @@ #include <gmock/gmock.h> -#include <stout/foreach.hpp> #include <stout/gtest.hpp> +#include <stout/none.hpp> +#include <stout/option.hpp> #include <stout/os.hpp> #include <stout/try.hpp> #include <process/gtest.hpp> -#include <process/io.hpp> #include <process/reap.hpp> #include <process/subprocess.hpp> -#include "mesos/resources.hpp" - #include "slave/containerizer/mesos/launch.hpp" -#include "linux/fs.hpp" - #include "tests/flags.hpp" #include "tests/utils.hpp" +#include "tests/containerizer/rootfs.hpp" + using namespace process; using std::string; @@ -49,62 +47,15 @@ namespace mesos { namespace internal { namespace tests { -class Chroot -{ -public: - Chroot(const string& _rootfs) - : rootfs(_rootfs) {} - - virtual ~Chroot() {} +// TODO(jieyu): Move this test to mesos_containerizer_tests.cpp once +// we have a filesystem isolator that supports changing rootfs. - virtual Try<Subprocess> run(const string& command) = 0; - - const string rootfs; -}; - - -class BasicLinuxChroot : public Chroot +class MesosContainerizerLaunchTest : public TemporaryDirectoryTest { public: - static Try<Owned<Chroot>> create(const string& rootfs) - { - if (!os::exists(rootfs)) { - return Error("rootfs does not exist"); - } - - if (os::system("cp -r /bin " + rootfs + "/") != 0) { - return ErrnoError("Failed to copy /bin to chroot"); - } - - if (os::system("cp -r /lib " + rootfs + "/") != 0) { - return ErrnoError("Failed to copy /lib to chroot"); - } - - if (os::system("cp -r /lib64 " + rootfs + "/") != 0) { - return ErrnoError("Failed to copy /lib64 to chroot"); - } - - vector<string> directories = {"proc", "sys", "dev", "tmp"}; - foreach (const string& directory, directories) { - Try<Nothing> mkdir = os::mkdir(path::join(rootfs, directory)); - if (mkdir.isError()) { - return Error("Failed to create /" + directory + " in chroot: " + - mkdir.error()); - } - } - - // We need to bind mount the rootfs so we can pivot on it. - Try<Nothing> mount = - fs::mount(rootfs, rootfs, None(), MS_BIND | MS_SLAVE, NULL); - - if (mount.isError()) { - return Error("Failed to bind mount chroot rootfs: " + mount.error()); - } - - return Owned<Chroot>(new BasicLinuxChroot(rootfs)); - } - - virtual Try<Subprocess> run(const string& _command) + Try<Subprocess> run( + const string& _command, + const Option<string>& rootfs = None()) { slave::MesosContainerizerLaunch::Flags launchFlags; @@ -125,23 +76,15 @@ public: path::join(tests::flags.build_dir, "src", "mesos-containerizer"), argv, Subprocess::PATH("/dev/null"), - Subprocess::PIPE(), + Subprocess::FD(STDOUT_FILENO), Subprocess::FD(STDERR_FILENO), launchFlags, None(), None(), lambda::bind(&clone, lambda::_1)); - if (s.isError()) { - close(launchFlags.pipe_read.get()); - close(launchFlags.pipe_write.get()); - } else { - s.get().status().onAny([=]() { - // Close when the subprocess terminates. - close(launchFlags.pipe_read.get()); - close(launchFlags.pipe_write.get()); - }); - } + close(launchFlags.pipe_read.get()); + close(launchFlags.pipe_write.get()); return s; } @@ -165,47 +108,26 @@ private: return (*_f)(); } - - BasicLinuxChroot(const string& rootfs) : Chroot(rootfs) {} - - ~BasicLinuxChroot() - { - // Because the test process has the rootfs as its cwd the umount - // won't actually happen until the - // TemporaryDirectoryTest::TearDown() changes back to the original - // directory. - fs::unmount(rootfs, MNT_DETACH); - } }; -template <typename T> -class LaunchChrootTest : public TemporaryDirectoryTest {}; - - -// TODO(idownes): Add tests for OSX chroots. -typedef ::testing::Types<BasicLinuxChroot> ChrootTypes; - - -TYPED_TEST_CASE(LaunchChrootTest, ChrootTypes); - - -TYPED_TEST(LaunchChrootTest, ROOT_DifferentRoot) +TEST_F(MesosContainerizerLaunchTest, ROOT_ChangeRootfs) { - Try<Owned<Chroot>> chroot = TypeParam::create(os::getcwd()); - ASSERT_SOME(chroot); + Try<Owned<Rootfs>> rootfs = + LinuxRootfs::create(path::join(os::getcwd(), "rootfs")); + + ASSERT_SOME(rootfs); - // Add /usr/bin/stat into the chroot. - const string usrbin = path::join(chroot.get()->rootfs, "usr", "bin"); - ASSERT_SOME(os::mkdir(usrbin)); - ASSERT_EQ(0, os::system("cp /usr/bin/stat " + path::join(usrbin, "stat"))); + // Add /usr/bin/stat into the rootfs. + ASSERT_SOME(rootfs.get()->add("/usr/bin/stat")); Clock::pause(); - Try<Subprocess> s = chroot.get()->run( - "/usr/bin/stat -c %i / >" + path::join("/", "stat.output")); + Try<Subprocess> s = run( + "/usr/bin/stat -c %i / >" + path::join("/", "stat.output"), + rootfs.get()->root); - CHECK_SOME(s); + ASSERT_SOME(s); // Advance time until the internal reaper reaps the subprocess. while (s.get().status().isPending()) { @@ -220,12 +142,12 @@ TYPED_TEST(LaunchChrootTest, ROOT_DifferentRoot) ASSERT_TRUE(WIFEXITED(status)); ASSERT_EQ(0, WEXITSTATUS(status)); - // Check the chroot has a different root by comparing the inodes. + // Check the rootfs has a different root by comparing the inodes. Try<ino_t> self = os::stat::inode("/"); ASSERT_SOME(self); - Try<string> read = os::read(path::join(chroot.get()->rootfs, "stat.output")); - CHECK_SOME(read); + Try<string> read = os::read(path::join(rootfs.get()->root, "stat.output")); + ASSERT_SOME(read); Try<ino_t> other = numify<ino_t>(strings::trim(read.get())); ASSERT_SOME(other); http://git-wip-us.apache.org/repos/asf/mesos/blob/f077ae66/src/tests/containerizer/rootfs.hpp ---------------------------------------------------------------------- diff --git a/src/tests/containerizer/rootfs.hpp b/src/tests/containerizer/rootfs.hpp new file mode 100644 index 0000000..55dd496 --- /dev/null +++ b/src/tests/containerizer/rootfs.hpp @@ -0,0 +1,145 @@ +/** + * 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 __TEST_ROOTFS_HPP__ +#define __TEST_ROOTFS_HPP__ + +#include <string> +#include <vector> + +#include <process/owned.hpp> + +#include <stout/error.hpp> +#include <stout/foreach.hpp> +#include <stout/nothing.hpp> +#include <stout/os.hpp> +#include <stout/strings.hpp> +#include <stout/try.hpp> + +namespace mesos { +namespace internal { +namespace tests { + +class Rootfs { +public: + virtual ~Rootfs() + { + if (os::exists(root)) { + os::rmdir(root); + } + } + + // Add a host directory or file to the root filesystem. Note that + // the host directory or file needs to be an absolute path. + Try<Nothing> add(const std::string& path) + { + if (!os::exists(path)) { + return Error("File or directory not found on the host"); + } + + if (!strings::startsWith(path, "/")) { + return Error("Not an absolute path"); + } + + // TODO(jieyu): Make sure 'path' is not under 'root'. + + if (os::stat::isdir(path)) { + if (os::system("cp -r '" + path + "' '" + root + "'") != 0) { + return ErrnoError("Failed to copy '" + path + "' to rootfs"); + } + } else if (os::stat::isfile(path)) { + std::string dirname = Path(path).dirname(); + std::string target = path::join(root, dirname); + + Try<Nothing> mkdir = os::mkdir(target); + if (mkdir.isError()) { + return Error("Failed to create directory in rootfs: " + mkdir.error()); + } + + if (os::system("cp '" + path + "' '" + target + "'") != 0) { + return ErrnoError("Failed to copy '" + path + "' to rootfs"); + } + } else { + return Error("Unsupported file or directory"); + } + + return Nothing(); + } + + const std::string root; + +protected: + Rootfs(const std::string& _root) : root(_root) {} +}; + + +class LinuxRootfs : public Rootfs +{ +public: + static Try<process::Owned<Rootfs>> create(const std::string& root) + { + process::Owned<Rootfs> rootfs(new LinuxRootfs(root)); + + if (!os::exists(root)) { + Try<Nothing> mkdir = os::mkdir(root); + if (mkdir.isError()) { + return Error("Failed to create root directory: " + mkdir.error()); + } + } + + std::vector<std::string> directories = { + "/bin", + "/lib", + "/lib64" + }; + + foreach (const std::string& directory, directories) { + Try<Nothing> result = rootfs->add(directory); + if (result.isError()) { + return Error("Failed to add '" + directory + + "' to rootfs: " + result.error()); + } + } + + directories = { + "/proc", + "/sys", + "/dev", + "/tmp" + }; + + foreach (const std::string& directory, directories) { + Try<Nothing> mkdir = os::mkdir(path::join(root, directory)); + if (mkdir.isError()) { + return Error("Failed to create '" + directory + + "' in rootfs: " + mkdir.error()); + } + } + + return rootfs; + } + +protected: + LinuxRootfs(const std::string& root) : Rootfs(root) {} +}; + +} // namespace tests { +} // namespace internal { +} // namespace mesos { + +#endif // __TEST_ROOTFS_HPP__
