mesos-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ji...@apache.org
Subject [1/3] mesos git commit: Implement a namespaces/ipc isolator.
Date Mon, 05 Dec 2016 21:24:38 GMT
Repository: mesos
Updated Branches:
  refs/heads/master 5abda76d6 -> bc4a83b49


Implement a namespaces/ipc isolator.

Review: https://reviews.apache.org/r/53688/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/25ac5819
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/25ac5819
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/25ac5819

Branch: refs/heads/master
Commit: 25ac581966eace322bf3062a315bbc6735aaec4c
Parents: 5abda76
Author: James Peach <jpeach@apache.org>
Authored: Mon Dec 5 13:18:14 2016 -0800
Committer: Jie Yu <yujie.jay@gmail.com>
Committed: Mon Dec 5 13:18:14 2016 -0800

----------------------------------------------------------------------
 src/CMakeLists.txt                              |   1 +
 src/Makefile.am                                 |   2 +
 src/slave/containerizer/mesos/containerizer.cpp |   2 +
 .../mesos/isolators/namespaces/ipc.cpp          |  90 +++++++++++++++
 .../mesos/isolators/namespaces/ipc.hpp          |  49 ++++++++
 src/tests/containerizer/isolator_tests.cpp      | 113 +++++++++++++++++++
 6 files changed, 257 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/25ac5819/src/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index ea6e399..4d8658b 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -287,6 +287,7 @@ set(LINUX_SRC
   slave/containerizer/mesos/isolators/gpu/nvml.cpp
   slave/containerizer/mesos/isolators/gpu/volume.cpp
   slave/containerizer/mesos/isolators/linux/capabilities.cpp
+  slave/containerizer/mesos/isolators/namespaces/ipc.cpp
   slave/containerizer/mesos/isolators/namespaces/pid.cpp
   slave/containerizer/mesos/isolators/network/cni/cni.cpp
   slave/containerizer/mesos/isolators/network/cni/plugins/port_mapper/port_mapper.cpp

http://git-wip-us.apache.org/repos/asf/mesos/blob/25ac5819/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index c7f78c1..5736349 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1128,6 +1128,7 @@ MESOS_LINUX_FILES =									\
   slave/containerizer/mesos/isolators/gpu/nvml.cpp					\
   slave/containerizer/mesos/isolators/gpu/volume.cpp					\
   slave/containerizer/mesos/isolators/linux/capabilities.cpp				\
+  slave/containerizer/mesos/isolators/namespaces/ipc.cpp				\
   slave/containerizer/mesos/isolators/namespaces/pid.cpp				\
   slave/containerizer/mesos/isolators/network/cni/cni.cpp				\
   slave/containerizer/mesos/isolators/network/cni/plugins/port_mapper/port_mapper.cpp	\
@@ -1167,6 +1168,7 @@ MESOS_LINUX_FILES +=									\
   slave/containerizer/mesos/isolators/gpu/nvml.hpp					\
   slave/containerizer/mesos/isolators/gpu/volume.hpp					\
   slave/containerizer/mesos/isolators/linux/capabilities.hpp				\
+  slave/containerizer/mesos/isolators/namespaces/ipc.hpp				\
   slave/containerizer/mesos/isolators/namespaces/pid.hpp				\
   slave/containerizer/mesos/isolators/network/cni/cni.hpp				\
   slave/containerizer/mesos/isolators/network/cni/plugins/port_mapper/port_mapper.hpp	\

http://git-wip-us.apache.org/repos/asf/mesos/blob/25ac5819/src/slave/containerizer/mesos/containerizer.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/containerizer.cpp b/src/slave/containerizer/mesos/containerizer.cpp
index 13cf757..cb23122 100644
--- a/src/slave/containerizer/mesos/containerizer.cpp
+++ b/src/slave/containerizer/mesos/containerizer.cpp
@@ -87,6 +87,7 @@
 #include "slave/containerizer/mesos/isolators/filesystem/shared.hpp"
 #include "slave/containerizer/mesos/isolators/gpu/nvidia.hpp"
 #include "slave/containerizer/mesos/isolators/linux/capabilities.hpp"
+#include "slave/containerizer/mesos/isolators/namespaces/ipc.hpp"
 #include "slave/containerizer/mesos/isolators/namespaces/pid.hpp"
 #include "slave/containerizer/mesos/isolators/network/cni/cni.hpp"
 #include "slave/containerizer/mesos/isolators/volume/image.hpp"
@@ -322,6 +323,7 @@ Try<MesosContainerizer*> MesosContainerizer::create(
         return NvidiaGpuIsolatorProcess::create(flags, nvidia.get());
       }},
 
+    {"namespaces/ipc", &NamespacesIPCIsolatorProcess::create},
     {"namespaces/pid", &NamespacesPidIsolatorProcess::create},
     {"network/cni", &NetworkCniIsolatorProcess::create},
 #endif // __linux__

http://git-wip-us.apache.org/repos/asf/mesos/blob/25ac5819/src/slave/containerizer/mesos/isolators/namespaces/ipc.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/isolators/namespaces/ipc.cpp b/src/slave/containerizer/mesos/isolators/namespaces/ipc.cpp
new file mode 100644
index 0000000..8675640
--- /dev/null
+++ b/src/slave/containerizer/mesos/isolators/namespaces/ipc.cpp
@@ -0,0 +1,90 @@
+// 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 "linux/ns.hpp"
+
+#include "slave/containerizer/mesos/isolators/namespaces/ipc.hpp"
+
+using process::Future;
+
+using mesos::slave::ContainerConfig;
+using mesos::slave::ContainerLaunchInfo;
+using mesos::slave::Isolator;
+
+namespace mesos {
+namespace internal {
+namespace slave {
+
+Try<Isolator*> NamespacesIPCIsolatorProcess::create(const Flags& flags)
+{
+  // Check for root permission.
+  if (geteuid() != 0) {
+    return Error("The IPC namespace isolator requires root permissions");
+  }
+
+  // Verify that IPC namespaces are available on this kernel.
+  if (ns::namespaces().count("ipc") == 0) {
+    return Error("IPC namespaces are not supported by this kernel");
+  }
+
+  // Make sure the 'linux' launcher is used because only 'linux' launcher
+  // supports cloning namespaces for the container.
+  if (flags.launcher != "linux") {
+    return Error(
+        "The 'linux' launcher must be used to enable the IPC namespace");
+  }
+
+  return new MesosIsolator(process::Owned<MesosIsolatorProcess>(
+      new NamespacesIPCIsolatorProcess()));
+}
+
+
+NamespacesIPCIsolatorProcess::NamespacesIPCIsolatorProcess()
+  : ProcessBase(process::ID::generate("ipc-namespace-isolator")) {}
+
+
+bool NamespacesIPCIsolatorProcess::supportsNesting()
+{
+  return true;
+}
+
+
+// IPC isolation on Linux just requires that a process be placed in an IPC
+// namespace. Neither /proc, nor any of the special SVIPC filesystem need
+// to be remounted for this to work. IPC namespaces are disjoint. That is,
+// once you enter an IPC namespace, IPC objects from the host namespace are
+// no longer visible (and vice versa). Since IPC namespaces do not nest,
+// we always place nested containers into the IPC namespace of the parent
+// container. That is, containers in the same group share an IPC namespace,
+// but groups are isolated from each other.
+Future<Option<ContainerLaunchInfo>> NamespacesIPCIsolatorProcess::prepare(
+    const ContainerID& containerId,
+    const ContainerConfig& containerConfig)
+{
+  ContainerLaunchInfo launchInfo;
+
+  if (containerId.has_parent()) {
+    launchInfo.add_enter_namespaces(CLONE_NEWIPC);
+  } else {
+    launchInfo.add_clone_namespaces(CLONE_NEWIPC);
+  }
+
+  return launchInfo;
+}
+
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/25ac5819/src/slave/containerizer/mesos/isolators/namespaces/ipc.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/isolators/namespaces/ipc.hpp b/src/slave/containerizer/mesos/isolators/namespaces/ipc.hpp
new file mode 100644
index 0000000..9850407
--- /dev/null
+++ b/src/slave/containerizer/mesos/isolators/namespaces/ipc.hpp
@@ -0,0 +1,49 @@
+// 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 __NAMESPACES_IPC_ISOLATOR_HPP__
+#define __NAMESPACES_IPC_ISOLATOR_HPP__
+
+#include "slave/flags.hpp"
+
+#include "slave/containerizer/mesos/isolator.hpp"
+
+namespace mesos {
+namespace internal {
+namespace slave {
+
+class NamespacesIPCIsolatorProcess : public MesosIsolatorProcess
+{
+public:
+  static Try<mesos::slave::Isolator*> create(const Flags& flags);
+
+  virtual ~NamespacesIPCIsolatorProcess() {}
+
+  virtual bool supportsNesting();
+
+  virtual process::Future<Option<mesos::slave::ContainerLaunchInfo>> prepare(
+      const ContainerID& containerId,
+      const mesos::slave::ContainerConfig& containerConfig);
+
+private:
+  NamespacesIPCIsolatorProcess();
+};
+
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
+
+#endif // __NAMESPACES_IPC_ISOLATOR_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/25ac5819/src/tests/containerizer/isolator_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/isolator_tests.cpp b/src/tests/containerizer/isolator_tests.cpp
index da46278..111e1ab 100644
--- a/src/tests/containerizer/isolator_tests.cpp
+++ b/src/tests/containerizer/isolator_tests.cpp
@@ -55,6 +55,50 @@ namespace internal {
 namespace tests {
 
 #ifdef __linux__
+class NamespacesIsolatorTest : public MesosTest
+{
+public:
+  virtual void SetUp()
+  {
+    MesosTest::SetUp();
+
+    directory = os::getcwd(); // We're inside a temporary sandbox.
+    containerId.set_value(UUID::random().toString());
+  }
+
+  Try<Owned<MesosContainerizer>> createContainerizer(const string& isolation)
+  {
+    slave::Flags flags = CreateSlaveFlags();
+    flags.isolation = isolation;
+
+    Try<MesosContainerizer*> _containerizer =
+      MesosContainerizer::create(flags, false, &fetcher);
+
+    if (_containerizer.isError()) {
+      return Error(_containerizer.error());
+    }
+
+    return Owned<MesosContainerizer>(_containerizer.get());
+  }
+
+  // Read a uint64_t value from the given path.
+  Try<uint64_t> readValue(const string& path)
+  {
+    Try<string> value = os::read(path);
+
+    if (value.isError()) {
+      return Error("Failed to read '" + path + "': " + value.error());
+    }
+
+    return numify<uint64_t>(strings::trim(value.get()));
+  }
+
+  string directory;
+  Fetcher fetcher;
+  ContainerID containerId;
+};
+
+
 class NamespacesPidIsolatorTest : public MesosTest {};
 
 
@@ -120,6 +164,75 @@ TEST_F(NamespacesPidIsolatorTest, ROOT_PidNamespace)
 
   EXPECT_TRUE(strings::contains(init.get(), "mesos"));
 }
+
+
+// The IPC namespace has its own copy of the svipc(7) tunables. We verify
+// that we are correctly entering the IPC namespace by verifying that we
+// can set shmmax some different value than that of the host namespace.
+TEST_F(NamespacesIsolatorTest, ROOT_IPCNamespace)
+{
+  Try<Owned<MesosContainerizer>> containerizer =
+    createContainerizer("namespaces/ipc");
+  ASSERT_SOME(containerizer);
+
+  // Value we will set the child namespace shmmax to.
+  uint64_t shmmaxValue = static_cast<uint64_t>(::getpid());
+
+  Try<uint64_t> hostShmmax = readValue("/proc/sys/kernel/shmmax");
+  ASSERT_SOME(hostShmmax);
+
+  // Verify that the host namespace shmmax is different.
+  ASSERT_NE(hostShmmax.get(), shmmaxValue);
+
+  const string command =
+    "stat -c %i /proc/self/ns/ipc > ns;"
+    "echo " + stringify(shmmaxValue) + " > /proc/sys/kernel/shmmax;"
+    "cp /proc/sys/kernel/shmmax shmmax";
+
+  process::Future<bool> launch = containerizer.get()->launch(
+      containerId,
+      None(),
+      createExecutorInfo("executor", command),
+      directory,
+      None(),
+      SlaveID(),
+      std::map<string, string>(),
+      false);
+
+  AWAIT_READY(launch);
+  ASSERT_TRUE(launch.get());
+
+  // Wait on the container.
+  Future<Option<ContainerTermination>> wait =
+    containerizer.get()->wait(containerId);
+
+  AWAIT_READY(wait);
+  ASSERT_SOME(wait.get());
+
+  // Check the executor exited correctly.
+  EXPECT_TRUE(wait->get().has_status());
+  EXPECT_EQ(0, wait->get().status());
+
+  // Check that the command was run in a different IPC namespace.
+  Try<ino_t> testIPCNamespace = ns::getns(::getpid(), "ipc");
+  ASSERT_SOME(testIPCNamespace);
+
+  Try<string> containerIPCNamespace = os::read(path::join(directory, "ns"));
+  ASSERT_SOME(containerIPCNamespace);
+
+  EXPECT_NE(stringify(testIPCNamespace.get()),
+            strings::trim(containerIPCNamespace.get()));
+
+  // Check that we modified the IPC shmmax of the namespace, not the host.
+  Try<uint64_t> childShmmax = readValue("shmmax");
+  ASSERT_SOME(childShmmax);
+
+  // Verify that we didn't modify shmmax in the host namespace.
+  ASSERT_EQ(hostShmmax.get(), readValue("/proc/sys/kernel/shmmax").get());
+
+  EXPECT_NE(hostShmmax.get(), childShmmax.get());
+  EXPECT_EQ(shmmaxValue, childShmmax.get());
+}
 #endif // __linux__
 
 } // namespace tests {


Mime
View raw message