Repository: mesos
Updated Branches:
  refs/heads/master 13953dc4b -> 50a553dd1


Made the rlimits isolator work with nested containers.

Make the rlimits isolator support nesting. Without this patch rlimits
sets for tasks launched by the DefaultExecutor are silently ignored.

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


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

Branch: refs/heads/master
Commit: 21a3405aaa189328a774f031d55c71df56fd0b4f
Parents: 13953dc
Author: Gastón Kleiman <[email protected]>
Authored: Fri Aug 4 15:29:47 2017 -0700
Committer: Jie Yu <[email protected]>
Committed: Fri Aug 4 15:44:02 2017 -0700

----------------------------------------------------------------------
 .../mesos/isolators/posix/rlimits.cpp           |   6 +
 .../mesos/isolators/posix/rlimits.hpp           |   2 +
 .../posix_rlimits_isolator_tests.cpp            | 178 +++++++++++++++++++
 3 files changed, 186 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/21a3405a/src/slave/containerizer/mesos/isolators/posix/rlimits.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/isolators/posix/rlimits.cpp 
b/src/slave/containerizer/mesos/isolators/posix/rlimits.cpp
index 3fc2b3d..0136652 100644
--- a/src/slave/containerizer/mesos/isolators/posix/rlimits.cpp
+++ b/src/slave/containerizer/mesos/isolators/posix/rlimits.cpp
@@ -28,6 +28,12 @@ namespace mesos {
 namespace internal {
 namespace slave {
 
+bool PosixRLimitsIsolatorProcess::supportsNesting()
+{
+  return true;
+}
+
+
 process::Future<Option<ContainerLaunchInfo>>
 PosixRLimitsIsolatorProcess::prepare(
     const ContainerID& containerId,

http://git-wip-us.apache.org/repos/asf/mesos/blob/21a3405a/src/slave/containerizer/mesos/isolators/posix/rlimits.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/isolators/posix/rlimits.hpp 
b/src/slave/containerizer/mesos/isolators/posix/rlimits.hpp
index ee36a24..0bce083 100644
--- a/src/slave/containerizer/mesos/isolators/posix/rlimits.hpp
+++ b/src/slave/containerizer/mesos/isolators/posix/rlimits.hpp
@@ -32,6 +32,8 @@ class PosixRLimitsIsolatorProcess : public 
MesosIsolatorProcess
 public:
   static Try<mesos::slave::Isolator*> create(const Flags& flags);
 
+  virtual bool supportsNesting();
+
   virtual process::Future<Option<mesos::slave::ContainerLaunchInfo>> prepare(
       const ContainerID& containerId,
       const mesos::slave::ContainerConfig& containerConfig);

http://git-wip-us.apache.org/repos/asf/mesos/blob/21a3405a/src/tests/containerizer/posix_rlimits_isolator_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/posix_rlimits_isolator_tests.cpp 
b/src/tests/containerizer/posix_rlimits_isolator_tests.cpp
index b7ccc7e..3d87a78 100644
--- a/src/tests/containerizer/posix_rlimits_isolator_tests.cpp
+++ b/src/tests/containerizer/posix_rlimits_isolator_tests.cpp
@@ -31,6 +31,8 @@
 #include <mesos/mesos.hpp>
 #include <mesos/scheduler.hpp>
 
+#include <stout/hashmap.hpp>
+
 #include "slave/flags.hpp"
 #include "tests/cluster.hpp"
 #include "tests/environment.hpp"
@@ -338,6 +340,182 @@ TEST_F(PosixRLimitsIsolatorTest, TaskExceedingLimit)
   driver.join();
 }
 
+
+// This test confirms that rlimits are set for nested containers.
+TEST_F(PosixRLimitsIsolatorTest, NestedContainers)
+{
+  Try<Owned<cluster::Master>> master = StartMaster();
+  ASSERT_SOME(master);
+
+  slave::Flags flags = CreateSlaveFlags();
+  flags.isolation = "posix/rlimits";
+
+#ifndef USE_SSL_SOCKET
+  // Disable operator API authentication for the default executor.
+  // Executor authentication currently has SSL as a dependency, so we
+  // cannot require executors to authenticate with the agent operator
+  // API if Mesos was not built with SSL support.
+  flags.authenticate_http_readwrite = false;
+#endif // USE_SSL_SOCKET
+
+  Owned<MasterDetector> detector = master.get()->createDetector();
+
+  Try<Owned<cluster::Slave>> slave = StartSlave(detector.get(), flags);
+  ASSERT_SOME(slave);
+
+  MockScheduler sched;
+
+  MesosSchedulerDriver driver(
+      &sched,
+      DEFAULT_FRAMEWORK_INFO,
+      master.get()->pid,
+      DEFAULT_CREDENTIAL);
+
+  Future<FrameworkID> frameworkId;
+  EXPECT_CALL(sched, registered(&driver, _, _))
+    .WillOnce(FutureArg<1>(&frameworkId));
+
+  Future<vector<Offer>> offers;
+  EXPECT_CALL(sched, resourceOffers(_, _))
+      .WillOnce(FutureArg<1>(&offers))
+      .WillRepeatedly(Return()); // Ignore subsequent offers.
+
+  driver.start();
+
+  AWAIT_READY(frameworkId);
+
+  AWAIT_READY(offers);
+  ASSERT_NE(0u, offers->size());
+
+  Future<TaskStatus> taskStatuses[4];
+
+  {
+    // This variable doesn't have to be used explicitly.
+    testing::InSequence inSequence;
+
+    foreach (Future<TaskStatus>& taskStatus, taskStatuses) {
+      EXPECT_CALL(sched, statusUpdate(&driver, _))
+          .WillOnce(FutureArg<1>(&taskStatus));
+    }
+
+    EXPECT_CALL(sched, statusUpdate(&driver, _))
+        .WillRepeatedly(Return()); // Ignore subsequent updates.
+  }
+
+  Resources resources = Resources::parse("cpus:0.1;mem:32;disk:32").get();
+
+  const Offer& offer = offers->front();
+  const SlaveID& slaveId = offer.slave_id();
+
+  TaskInfo task1 = createTask(
+      slaveId,
+      resources,
+      "ULIMIT=`ulimit -t`;\n"
+      "if [ \"$ULIMIT\" != \"10000\" ]; then\n"
+      "  exit 1;\n"
+      "fi");
+
+  {
+    TaskID taskId;
+    taskId.set_value("task1");
+
+    task1.mutable_task_id()->CopyFrom(taskId);
+
+    ContainerInfo* container = task1.mutable_container();
+    container->set_type(ContainerInfo::MESOS);
+
+    RLimitInfo rlimitInfo;
+    RLimitInfo::RLimit* cpuLimit = rlimitInfo.add_rlimits();
+    cpuLimit->set_type(RLimitInfo::RLimit::RLMT_CPU);
+    cpuLimit->set_soft(10000);
+    cpuLimit->set_hard(10000);
+
+    container->mutable_rlimit_info()->CopyFrom(rlimitInfo);
+  }
+
+  TaskInfo task2 = createTask(
+      slaveId,
+      resources,
+      "ULIMIT=`ulimit -t`;\n"
+      "if [ \"$ULIMIT\" != \"20000\" ]; then\n"
+      "  exit 1;\n"
+      "fi");
+
+  {
+    TaskID taskId;
+    taskId.set_value("task2");
+
+    task2.mutable_task_id()->CopyFrom(taskId);
+
+    ContainerInfo* container = task2.mutable_container();
+    container->set_type(ContainerInfo::MESOS);
+
+    RLimitInfo rlimitInfo;
+    RLimitInfo::RLimit* cpuLimit = rlimitInfo.add_rlimits();
+    cpuLimit->set_type(RLimitInfo::RLimit::RLMT_CPU);
+    cpuLimit->set_soft(20000);
+    cpuLimit->set_hard(20000);
+
+    container->mutable_rlimit_info()->CopyFrom(rlimitInfo);
+  }
+
+  TaskGroupInfo taskGroup = createTaskGroupInfo({task1, task2});
+
+  ExecutorInfo executorInfo;
+  executorInfo.set_type(ExecutorInfo::DEFAULT);
+  executorInfo.mutable_executor_id()->CopyFrom(DEFAULT_EXECUTOR_ID);
+  executorInfo.mutable_framework_id()->CopyFrom(frameworkId.get());
+  executorInfo.mutable_resources()->CopyFrom(resources);
+
+  driver.acceptOffers(
+      {offer.id()},
+      {LAUNCH_GROUP(executorInfo, taskGroup)});
+
+  // We track the status updates of each task separately, to verify
+  // that they transition from TASK_RUNNING to TASK_FINISHED.
+  enum class Stage
+  {
+    INITIAL,
+    RUNNING,
+    FINISHED
+  };
+
+  hashmap<TaskID, Stage> taskStages;
+  taskStages[task1.task_id()] = Stage::INITIAL;
+  taskStages[task2.task_id()] = Stage::INITIAL;
+
+  foreach (const Future<TaskStatus>& taskStatus, taskStatuses) {
+    AWAIT_READY(taskStatus);
+
+    Option<Stage> taskStage = taskStages.get(taskStatus->task_id());
+    ASSERT_SOME(taskStage);
+
+    switch (taskStage.get()) {
+      case Stage::INITIAL: {
+        ASSERT_EQ(TASK_RUNNING, taskStatus->state())
+          << taskStatus->DebugString();
+
+        taskStages[taskStatus->task_id()] = Stage::RUNNING;
+        break;
+      }
+      case Stage::RUNNING: {
+        ASSERT_EQ(TASK_FINISHED, taskStatus->state())
+          << taskStatus->DebugString();
+
+        taskStages[taskStatus->task_id()] = Stage::FINISHED;
+        break;
+      }
+      case Stage::FINISHED: {
+        FAIL() << "Unexpected task update: " << taskStatus->DebugString();
+        break;
+      }
+    }
+  }
+
+  driver.stop();
+  driver.join();
+}
+
 } // namespace tests {
 } // namespace internal {
 } // namespace mesos {

Reply via email to