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__

Reply via email to