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 {
