You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by gi...@apache.org on 2019/07/13 17:18:12 UTC

[mesos] 01/06: Added the test `NamespacesIsolatorTest.ROOT_ShareIPCNamespace`.

This is an automated email from the ASF dual-hosted git repository.

gilbert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mesos.git

commit 6f402d5f193262e854826cac4bc14f29395ca02f
Author: Qian Zhang <zh...@gmail.com>
AuthorDate: Sat Jul 13 10:07:52 2019 -0700

    Added the test `NamespacesIsolatorTest.ROOT_ShareIPCNamespace`.
    
    Review: https://reviews.apache.org/r/70845/
---
 src/tests/containerizer/isolator_tests.cpp | 267 ++++++++++++++++++++++++++++-
 1 file changed, 265 insertions(+), 2 deletions(-)

diff --git a/src/tests/containerizer/isolator_tests.cpp b/src/tests/containerizer/isolator_tests.cpp
index bb2cda4..82081d3 100644
--- a/src/tests/containerizer/isolator_tests.cpp
+++ b/src/tests/containerizer/isolator_tests.cpp
@@ -39,6 +39,7 @@
 #include "slave/containerizer/fetcher.hpp"
 
 #include "slave/containerizer/mesos/containerizer.hpp"
+#include "slave/containerizer/mesos/paths.hpp"
 
 #include "tests/mesos.hpp"
 
@@ -51,6 +52,9 @@ using mesos::internal::slave::Containerizer;
 using mesos::internal::slave::Fetcher;
 using mesos::internal::slave::MesosContainerizer;
 
+using mesos::internal::slave::containerizer::paths::getSandboxPath;
+
+using mesos::slave::ContainerClass;
 using mesos::slave::ContainerTermination;
 
 namespace mesos {
@@ -71,16 +75,26 @@ public:
 
   Try<Owned<MesosContainerizer>> createContainerizer(
       const string& isolation,
-      const Option<bool>& disallowSharingAgentPidNamespace = None())
+      const Option<bool>& disallowSharingAgentPidNamespace = None(),
+      const Option<bool>& disallowSharingAgentIpcNamespace = None(),
+      const Option<Bytes>& defaultShmSize = None())
   {
     slave::Flags flags = CreateSlaveFlags();
-    flags.isolation = isolation;
+    flags.image_providers = "docker";
+    flags.isolation = isolation + ",docker/runtime";
 
     if (disallowSharingAgentPidNamespace.isSome()) {
       flags.disallow_sharing_agent_pid_namespace =
         disallowSharingAgentPidNamespace.get();
     }
 
+    if (disallowSharingAgentIpcNamespace.isSome()) {
+      flags.disallow_sharing_agent_ipc_namespace =
+        disallowSharingAgentIpcNamespace.get();
+    }
+
+    flags.default_shm_size = defaultShmSize;
+
     fetcher.reset(new Fetcher(flags));
 
     Try<MesosContainerizer*> _containerizer =
@@ -321,6 +335,255 @@ TEST_F(NamespacesIsolatorTest, ROOT_IPCNamespace)
   EXPECT_NE(hostShmmax.get(), childShmmax.get());
   EXPECT_EQ(shmmaxValue, childShmmax.get());
 }
+
+
+// This test verifies that a top-level container with private IPC mode will
+// have its own IPC namespace and /dev/shm, and it can share IPC namespace
+// and /dev/shm with its child container, grandchild container and debug
+// container.
+TEST_F(NamespacesIsolatorTest, ROOT_ShareIPCNamespace)
+{
+  Try<Owned<MesosContainerizer>> containerizer =
+    createContainerizer("filesystem/linux,namespaces/ipc");
+
+  ASSERT_SOME(containerizer);
+
+  // Launch a top-level container with `PRIVATE` IPC mode and 128MB /dev/shm,
+  // check its /dev/shm size is correctly set and its IPC namespace is
+  // different than agent's IPC namespace, write its IPC namespace inode to
+  // a file under /dev/shm.
+  const string command =
+    "df -m /dev/shm | grep -w 128 && "
+    "test `stat -Lc %i /proc/self/ns/ipc` != `stat -Lc %i /proc/1/ns/ipc` && "
+    "stat -Lc %i /proc/self/ns/ipc > /dev/shm/root && "
+    "touch marker && "
+    "sleep 1000";
+
+  mesos::slave::ContainerConfig containerConfig = createContainerConfig(
+      None(),
+      createExecutorInfo("executor", command),
+      directory);
+
+  ContainerInfo* container = containerConfig.mutable_container_info();
+  container->set_type(ContainerInfo::MESOS);
+  container->mutable_linux_info()->set_ipc_mode(LinuxInfo::PRIVATE);
+  container->mutable_linux_info()->set_shm_size(128);
+
+  process::Future<Containerizer::LaunchResult> launch =
+    containerizer.get()->launch(
+        containerId,
+        containerConfig,
+        std::map<string, string>(),
+        None());
+
+  AWAIT_ASSERT_EQ(Containerizer::LaunchResult::SUCCESS, launch);
+
+  // Wait until the marker file is created.
+  Duration waited = Duration::zero();
+
+  do {
+    if (os::exists(path::join(directory, "marker"))) {
+      break;
+    }
+
+    os::sleep(Seconds(1));
+    waited += Seconds(1);
+  } while (waited < process::TEST_AWAIT_TIMEOUT);
+
+  EXPECT_LT(waited, process::TEST_AWAIT_TIMEOUT);
+
+  // The file created by the top-level container should only exist in
+  // its own /dev/shm rather than in agent's /dev/shm.
+  ASSERT_FALSE(os::exists("/dev/shm/root"));
+
+  // Now launch two child containers with `SHARE_PARENT` ipc mode and
+  // 256MB /dev/shm.
+  ContainerID childContainerId1, childContainerId2;
+
+  childContainerId1.mutable_parent()->CopyFrom(containerId);
+  childContainerId1.set_value(id::UUID::random().toString());
+
+  childContainerId2.mutable_parent()->CopyFrom(containerId);
+  childContainerId2.set_value(id::UUID::random().toString());
+
+  ContainerInfo containerInfo;
+  containerInfo.set_type(ContainerInfo::MESOS);
+  containerInfo.mutable_linux_info()->set_ipc_mode(LinuxInfo::SHARE_PARENT);
+  containerInfo.mutable_linux_info()->set_shm_size(256);
+
+  // Launch the first child container, check its /dev/shm size is 128MB
+  // rather than 256MB, it can see the file created by its parent container
+  // in /dev/shm and it is in the same IPC namespace with its parent container,
+  // and then write its IPC namespace inode to a file under /dev/shm.
+  launch = containerizer.get()->launch(
+      childContainerId1,
+      createContainerConfig(
+          createCommandInfo(
+              "df -m /dev/shm | grep -w 128 && "
+              "test `stat -Lc %i /proc/self/ns/ipc` = `cat /dev/shm/root` && "
+              "stat -Lc %i /proc/self/ns/ipc > /dev/shm/child1 && "
+              "touch marker && "
+              "sleep 1000"),
+          containerInfo),
+      std::map<string, string>(),
+      None());
+
+  AWAIT_ASSERT_EQ(Containerizer::LaunchResult::SUCCESS, launch);
+
+  // Wait until the marker file is created.
+  waited = Duration::zero();
+  const string childSandboxPath1 = getSandboxPath(directory, childContainerId1);
+
+  do {
+    if (os::exists(path::join(childSandboxPath1, "marker"))) {
+      break;
+    }
+
+    os::sleep(Seconds(1));
+    waited += Seconds(1);
+  } while (waited < process::TEST_AWAIT_TIMEOUT);
+
+  EXPECT_LT(waited, process::TEST_AWAIT_TIMEOUT);
+
+  // Launch the second child container with its own rootfs, check its /dev/shm
+  // size is 128MB rather than 256MB, it can see the files created by its parent
+  // container and the first child container in /dev/shm and it is in the same
+  // IPC namespace with its parent container and the first child container. and
+  // then write its IPC namespace inode to a file under /dev/shm.
+  mesos::Image image;
+  image.set_type(mesos::Image::DOCKER);
+  image.mutable_docker()->set_name("alpine");
+
+  containerInfo.mutable_mesos()->mutable_image()->CopyFrom(image);
+
+  launch = containerizer.get()->launch(
+      childContainerId2,
+      createContainerConfig(
+          createCommandInfo(
+              "df -m /dev/shm | grep -w 128 && "
+              "test `stat -Lc %i /proc/self/ns/ipc` = `cat /dev/shm/root` && "
+              "test `stat -Lc %i /proc/self/ns/ipc` = `cat /dev/shm/child1` && "
+              "stat -Lc %i /proc/self/ns/ipc > /dev/shm/child2 && "
+              "touch marker && "
+              "sleep 1000"),
+          containerInfo),
+      std::map<string, string>(),
+      None());
+
+  AWAIT_ASSERT_EQ(Containerizer::LaunchResult::SUCCESS, launch);
+
+  // Wait until the marker file is created.
+  waited = Duration::zero();
+  const string childSandboxPath2 = getSandboxPath(directory, childContainerId2);
+
+  do {
+    if (os::exists(path::join(childSandboxPath2, "marker"))) {
+      break;
+    }
+
+    os::sleep(Seconds(1));
+    waited += Seconds(1);
+  } while (waited < process::TEST_AWAIT_TIMEOUT);
+
+  EXPECT_LT(waited, process::TEST_AWAIT_TIMEOUT);
+
+  // Launch a grandchild container with `SHARE_PARENT` ipc mode and
+  // 256MB /dev/shm under the first child container, check its /dev/shm
+  // size is 128MB rather than 256MB, it can see the files created by
+  // its parent and grandparent containers and it is in the same IPC
+  // namespace with its parent and grandparent containers.
+  ContainerID grandchildContainerId;
+  grandchildContainerId.mutable_parent()->CopyFrom(childContainerId1);
+  grandchildContainerId.set_value(id::UUID::random().toString());
+
+  launch = containerizer.get()->launch(
+      grandchildContainerId,
+      createContainerConfig(
+          createCommandInfo(
+              "df -m /dev/shm | grep -w 128 && "
+              "test `stat -Lc %i /proc/self/ns/ipc` = `cat /dev/shm/child1` && "
+              "test `stat -Lc %i /proc/self/ns/ipc` = `cat /dev/shm/root`"),
+          containerInfo),
+      std::map<string, string>(),
+      None());
+
+  AWAIT_ASSERT_EQ(Containerizer::LaunchResult::SUCCESS, launch);
+
+  Future<Option<ContainerTermination>> wait =
+    containerizer.get()->wait(grandchildContainerId);
+
+  AWAIT_READY(wait);
+  ASSERT_SOME(wait.get());
+  ASSERT_TRUE(wait.get()->has_status());
+  EXPECT_WEXITSTATUS_EQ(0, wait.get()->status());
+
+  // Launch a debug container with `PRIVATE` ipc mode and 256MB /dev/shm
+  // under the first child container, check its /dev/shm size is 128MB
+  // rather than 256MB and it is in the same IPC namespace with its parent
+  // container even its ipc mode is `PRIVATE`.
+  ContainerID debugContainerId1;
+  debugContainerId1.mutable_parent()->CopyFrom(childContainerId1);
+  debugContainerId1.set_value(id::UUID::random().toString());
+
+  containerInfo.clear_mesos();
+  containerInfo.mutable_linux_info()->set_ipc_mode(LinuxInfo::PRIVATE);
+
+  launch = containerizer.get()->launch(
+      debugContainerId1,
+      createContainerConfig(
+          createCommandInfo(
+              "df -m /dev/shm | grep -w 128 && "
+              "test `stat -Lc %i /proc/self/ns/ipc` = `cat /dev/shm/child1`"),
+          containerInfo,
+          ContainerClass::DEBUG),
+      std::map<string, string>(),
+      None());
+
+  AWAIT_ASSERT_EQ(Containerizer::LaunchResult::SUCCESS, launch);
+
+  wait = containerizer.get()->wait(debugContainerId1);
+  AWAIT_READY(wait);
+  ASSERT_SOME(wait.get());
+  ASSERT_TRUE(wait.get()->has_status());
+  EXPECT_WEXITSTATUS_EQ(0, wait.get()->status());
+
+  // Launch a debug container with `PRIVATE` ipc mode and 256MB /dev/shm
+  // under the second child container, check its /dev/shm size is 128MB
+  // rather than 256MB and it is in the same IPC namespace with its parent
+  // container even its ipc mode is `PRIVATE`.
+  ContainerID debugContainerId2;
+  debugContainerId2.mutable_parent()->CopyFrom(childContainerId2);
+  debugContainerId2.set_value(id::UUID::random().toString());
+
+  containerInfo.mutable_linux_info()->set_ipc_mode(LinuxInfo::PRIVATE);
+
+  launch = containerizer.get()->launch(
+      debugContainerId2,
+      createContainerConfig(
+          createCommandInfo(
+              "df -m /dev/shm | grep -w 128 && "
+              "test `stat -Lc %i /proc/self/ns/ipc` = `cat /dev/shm/child2`"),
+          containerInfo,
+          ContainerClass::DEBUG),
+      std::map<string, string>(),
+      None());
+
+  AWAIT_ASSERT_EQ(Containerizer::LaunchResult::SUCCESS, launch);
+
+  wait = containerizer.get()->wait(debugContainerId2);
+  AWAIT_READY(wait);
+  ASSERT_SOME(wait.get());
+  ASSERT_TRUE(wait.get()->has_status());
+  EXPECT_WEXITSTATUS_EQ(0, wait.get()->status());
+
+  Future<Option<ContainerTermination>> termination =
+    containerizer.get()->destroy(containerId);
+
+  AWAIT_READY(termination);
+  ASSERT_SOME(termination.get());
+  ASSERT_TRUE(termination.get()->has_status());
+  EXPECT_WTERMSIG_EQ(SIGKILL, termination.get()->status());
+}
 #endif // __linux__
 
 } // namespace tests {