You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by bm...@apache.org on 2017/02/21 23:57:49 UTC

mesos git commit: Added a test to ensure offers with different roles cannot be combined.

Repository: mesos
Updated Branches:
  refs/heads/master f27027d10 -> 807b2343f


Added a test to ensure offers with different roles cannot be combined.

Multi-role framework cannot combine offers allocated to different
roles of that framework in a single launchTask call.

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


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

Branch: refs/heads/master
Commit: 807b2343fac186d4fe5f42362c07a729fcaad80f
Parents: f27027d
Author: Jay Guo <gu...@gmail.com>
Authored: Tue Feb 21 15:51:12 2017 -0800
Committer: Benjamin Mahler <bm...@apache.org>
Committed: Tue Feb 21 15:57:32 2017 -0800

----------------------------------------------------------------------
 src/tests/master_tests.cpp | 186 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 186 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/807b2343/src/tests/master_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/master_tests.cpp b/src/tests/master_tests.cpp
index 4f7c45b..357a9a4 100644
--- a/src/tests/master_tests.cpp
+++ b/src/tests/master_tests.cpp
@@ -1882,6 +1882,192 @@ TEST_F(MasterTest, LaunchDuplicateOfferDropped)
 }
 
 
+// This test ensures that a multi-role framework cannot launch tasks with
+// offers allocated to different roles of that framework in a single
+// launchTasks call. We follow similar pattern in LaunchCombinedOfferTest.
+//
+// We launch a cluster with one master and one slave, and a framework
+// with two roles. Firstly, total resources will be offered to one of
+// the roles (we don't assume that it is deterministic as to which of
+// the two roles are chosen first). We launch a task using half of the
+// total resources. The other half will be returned to master and offered
+// to the other role, since it has a lower share (0). Then we kill the
+// task, half of resources will be offered to first role again, since
+// the first has a lower share (0). At this point, two offers with
+// different roles are outstanding and we can combine them in one
+// `launchTasks` call. A non-partition-aware framework should
+// receive TASK_LOST.
+//
+// TODO(jay_guo): Add tests for other operations as well.
+TEST_F(MasterTest, LaunchDifferentRoleLost)
+{
+  Clock::pause();
+
+  master::Flags masterFlags = CreateMasterFlags();
+  Try<Owned<cluster::Master>> master = StartMaster(masterFlags);
+  ASSERT_SOME(master);
+
+  MockExecutor exec(DEFAULT_EXECUTOR_ID);
+  TestContainerizer containerizer(&exec);
+
+  // The CPU granularity is 1.0 which means that we need slaves
+  // with at least 2 cpus for a combined offer.
+  Resources halfSlave = Resources::parse("cpus:1;mem:512").get();
+  Resources fullSlave = halfSlave + halfSlave;
+
+  slave::Flags flags = CreateSlaveFlags();
+  flags.resources = Option<string>(stringify(fullSlave));
+
+  Owned<MasterDetector> detector = master.get()->createDetector();
+
+  Try<Owned<cluster::Slave>> slave =
+    StartSlave(detector.get(), &containerizer, flags);
+  ASSERT_SOME(slave);
+
+  FrameworkInfo framework = DEFAULT_FRAMEWORK_INFO;
+  framework.clear_role();
+  framework.add_roles("role1");
+  framework.add_roles("role2");
+  framework.add_capabilities()->set_type(
+      FrameworkInfo::Capability::MULTI_ROLE);
+
+  MockScheduler sched;
+  MesosSchedulerDriver driver(
+      &sched, framework, master.get()->pid, DEFAULT_CREDENTIAL);
+
+  EXPECT_CALL(sched, registered(&driver, _, _));
+
+  // Get 1st offer and use half of the resources.
+  Future<vector<Offer>> offers1;
+  EXPECT_CALL(sched, resourceOffers(&driver, _))
+    .WillOnce(FutureArg<1>(&offers1));
+
+  driver.start();
+
+  Clock::settle();
+  Clock::advance(masterFlags.allocation_interval);
+
+  AWAIT_READY(offers1);
+  EXPECT_NE(0u, offers1->size());
+  Resources resources1(offers1.get()[0].resources());
+  EXPECT_EQ(2, resources1.cpus().get());
+  EXPECT_EQ(Megabytes(1024), resources1.mem().get());
+
+  TaskInfo task1;
+  task1.set_name("");
+  task1.mutable_task_id()->set_value("1");
+  task1.mutable_slave_id()->MergeFrom(offers1.get()[0].slave_id());
+  task1.mutable_resources()->MergeFrom(halfSlave);
+  task1.mutable_executor()->MergeFrom(DEFAULT_EXECUTOR_INFO);
+
+  EXPECT_CALL(exec, registered(_, _, _, _));
+
+  EXPECT_CALL(exec, launchTask(_, _))
+    .WillOnce(SendStatusUpdateFromTask(TASK_RUNNING));
+
+  Future<TaskStatus> status1;
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&status1));
+
+  Future<vector<Offer>> offers2;
+  EXPECT_CALL(sched, resourceOffers(&driver, _))
+    .WillOnce(FutureArg<1>(&offers2));
+
+  // We want to be receive an offer for the remainder immediately.
+  Filters filters;
+  filters.set_refuse_seconds(0);
+
+  driver.launchTasks(offers1.get()[0].id(), {task1}, filters);
+
+  AWAIT_READY(status1);
+  EXPECT_EQ(TASK_RUNNING, status1.get().state());
+
+  // Advance the clock and trigger a batch allocation.
+  Clock::settle();
+  Clock::advance(masterFlags.allocation_interval);
+
+  // Await 2nd offer.
+  AWAIT_READY(offers2);
+  EXPECT_NE(0u, offers2->size());
+  ASSERT_TRUE(offers2.get()[0].has_allocation_info());
+
+  Resources resources2(offers2.get()[0].resources());
+  EXPECT_EQ(1, resources2.cpus().get());
+  EXPECT_EQ(Megabytes(512), resources2.mem().get());
+
+  Future<TaskStatus> status2;
+  EXPECT_CALL(exec, killTask(_, _))
+    .WillOnce(SendStatusUpdateFromTaskID(TASK_KILLED));
+
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&status2));
+
+  Future<vector<Offer>> offers3;
+  EXPECT_CALL(sched, resourceOffers(&driver, _))
+    .WillOnce(FutureArg<1>(&offers3))
+    .WillRepeatedly(Return()); // Ignore subsequent offers.
+
+  // Kill 1st task.
+  TaskID taskId1 = task1.task_id();
+  driver.killTask(taskId1);
+
+  AWAIT_READY(status2);
+  EXPECT_EQ(TASK_KILLED, status2.get().state());
+
+  // Advance the clock and trigger a batch allocation.
+  Clock::settle();
+  Clock::advance(masterFlags.allocation_interval);
+
+  // Await 3rd offer - 2nd and 3rd offer to same slave are now ready.
+  AWAIT_READY(offers3);
+  EXPECT_NE(0u, offers3->size());
+  ASSERT_TRUE(offers3.get()[0].has_allocation_info());
+  Resources resources3(offers3.get()[0].resources());
+  EXPECT_EQ(1, resources3.cpus().get());
+  EXPECT_EQ(Megabytes(512), resources3.mem().get());
+
+  // 2nd and 3rd offer should be allocated to different roles.
+  ASSERT_NE(
+      offers2.get()[0].allocation_info().role(),
+      offers3.get()[0].allocation_info().role());
+
+  TaskInfo task2;
+  task2.set_name("");
+  task2.mutable_task_id()->set_value("2");
+  task2.mutable_slave_id()->MergeFrom(offers2.get()[0].slave_id());
+  task2.mutable_resources()->MergeFrom(fullSlave);
+  task2.mutable_executor()->MergeFrom(DEFAULT_EXECUTOR_INFO);
+
+  Future<TaskStatus> status3;
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&status3));
+
+  vector<OfferID> combinedOffers;
+  combinedOffers.push_back(offers2.get()[0].id());
+  combinedOffers.push_back(offers3.get()[0].id());
+
+  Future<Nothing> recoverResources =
+    FUTURE_DISPATCH(_, &MesosAllocatorProcess::recoverResources);
+
+  driver.launchTasks(combinedOffers, {task2});
+
+  Clock::settle();
+
+  AWAIT_READY(status3);
+  EXPECT_EQ(TASK_LOST, status3.get().state());
+  EXPECT_EQ(TaskStatus::REASON_INVALID_OFFERS, status3.get().reason());
+
+  // The resources of the invalid offers should be recovered.
+  AWAIT_READY(recoverResources);
+
+  EXPECT_CALL(exec, shutdown(_))
+    .Times(AtMost(1));
+
+  driver.stop();
+  driver.join();
+}
+
+
 // TODO(vinod): These tests only verify that the master metrics exist
 // but we need tests that verify that these metrics are updated.
 TEST_F(MasterTest, MetricsInMetricsEndpoint)