Added tests for offer filters.

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


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

Branch: refs/heads/master
Commit: ecfb8d53da58cc694ef885c929873042618dc16e
Parents: 447d814
Author: Alexander Rukletsov <[email protected]>
Authored: Thu Jan 21 23:29:06 2016 -0800
Committer: Benjamin Mahler <[email protected]>
Committed: Thu Jan 21 23:59:36 2016 -0800

----------------------------------------------------------------------
 src/tests/hierarchical_allocator_tests.cpp | 233 ++++++++++++++++++++++++
 1 file changed, 233 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/ecfb8d53/src/tests/hierarchical_allocator_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/hierarchical_allocator_tests.cpp 
b/src/tests/hierarchical_allocator_tests.cpp
index 516c5cf..aa364ea 100644
--- a/src/tests/hierarchical_allocator_tests.cpp
+++ b/src/tests/hierarchical_allocator_tests.cpp
@@ -446,6 +446,239 @@ TEST_F(HierarchicalAllocatorTest, ReservedDRF)
 }
 
 
+// This test ensures that an offer filter larger than the
+// allocation interval effectively filters out resources.
+TEST_F(HierarchicalAllocatorTest, OfferFilter)
+{
+  // Pausing the clock is not necessary, but ensures that the test
+  // doesn't rely on the batch allocation in the allocator, which
+  // would slow down the test.
+  Clock::pause();
+
+  // We put both frameworks into the same role, but we could also
+  // have had separate roles; this should not influence the test.
+  const string ROLE{"role"};
+
+  hashmap<FrameworkID, Resources> EMPTY;
+
+  initialize();
+
+  FrameworkInfo framework1 = createFrameworkInfo(ROLE);
+
+  SlaveInfo agent1 = createSlaveInfo("cpus:1;mem:512;disk:0");
+
+  allocator->addFramework(
+      framework1.id(),
+      framework1,
+      hashmap<SlaveID, Resources>());
+
+  allocator->addSlave(
+      agent1.id(),
+      agent1,
+      None(),
+      agent1.resources(),
+      EMPTY);
+
+  // `framework1` will be offered all of `agent1` resources
+  // because it is the only framework in the cluster.
+  Future<Allocation> allocation = allocations.get();
+  AWAIT_READY(allocation);
+  EXPECT_EQ(framework1.id(), allocation.get().frameworkId);
+  EXPECT_EQ(agent1.resources(), Resources::sum(allocation.get().resources));
+
+  // Now `framework1` declines the offer and sets a filter
+  // with the duration greater than the allocation interval.
+  Duration filterTimeout = flags.allocation_interval * 2;
+  Filters offerFilter;
+  offerFilter.set_refuse_seconds(filterTimeout.secs());
+
+  allocator->recoverResources(
+      framework1.id(),
+      agent1.id(),
+      allocation.get().resources.get(agent1.id()).get(),
+      offerFilter);
+
+  // Ensure the offer filter timeout is set before advancing the clock.
+  Clock::settle();
+
+  // Trigger a batch allocation.
+  Clock::advance(flags.allocation_interval);
+  Clock::settle();
+
+  // There should be no allocation due to the offer filter.
+  allocation = allocations.get();
+  ASSERT_TRUE(allocation.isPending());
+
+  // Ensure the offer filter times out (2x the allocation interval)
+  // and the next batch allocation occurs.
+  Clock::advance(flags.allocation_interval);
+  Clock::settle();
+
+  // The next batch allocation should offer resources to `framework1`.
+  AWAIT_READY(allocation);
+  EXPECT_EQ(framework1.id(), allocation.get().frameworkId);
+  EXPECT_EQ(agent1.resources(), Resources::sum(allocation.get().resources));
+}
+
+
+// This test ensures that an offer filter is not removed earlier than
+// the next batch allocation. See MESOS-4302 for more information.
+//
+// NOTE: If we update the code to allocate upon resource recovery
+// (MESOS-3078), this test should still pass in that the small offer
+// filter timeout should lead to the next allocation for the agent
+// applying the filter.
+TEST_F(HierarchicalAllocatorTest, SmallOfferFilterTimeout)
+{
+  // Pausing the clock is not necessary, but ensures that the test
+  // doesn't rely on the batch allocation in the allocator, which
+  // would slow down the test.
+  Clock::pause();
+
+  // We put both frameworks into the same role, but we could also
+  // have had separate roles; this should not influence the test.
+  const string ROLE{"role"};
+
+  hashmap<FrameworkID, Resources> EMPTY;
+
+  // Explicitly set the allocation interval to make sure
+  // it is greater than the offer filter timeout.
+  master::Flags flags_;
+  flags_.allocation_interval = Minutes(1);
+
+  initialize(flags_);
+
+  // We start with the following cluster setup.
+  // Total cluster resources (1 agent): cpus=1, mem=512.
+  // ROLE1 share = 1 (cpus=1, mem=512)
+  //   framework1 share = 1 (cpus=1, mem=512)
+  //   framework2 share = 0
+
+  FrameworkInfo framework1 = createFrameworkInfo(ROLE);
+  FrameworkInfo framework2 = createFrameworkInfo(ROLE);
+
+  SlaveInfo agent1 = createSlaveInfo("cpus:1;mem:512;disk:0");
+
+  allocator->addFramework(
+      framework1.id(),
+      framework1,
+      hashmap<SlaveID, Resources>());
+
+  allocator->addFramework(
+      framework2.id(),
+      framework2,
+      hashmap<SlaveID, Resources>());
+
+  allocator->addSlave(
+      agent1.id(),
+      agent1,
+      None(),
+      agent1.resources(),
+      {std::make_pair(framework1.id(), agent1.resources())});
+
+  // Process all triggered allocation events.
+  // NOTE: No allocations happen because there are no resources to allocate.
+  Clock::settle();
+
+  // Add one more agent with some free resources.
+  SlaveInfo agent2 = createSlaveInfo("cpus:1;mem:512;disk:0");
+  allocator->addSlave(
+      agent2.id(),
+      agent2,
+      None(),
+      agent2.resources(),
+      EMPTY);
+
+  // Process the allocation triggered by the agent addition.
+  Clock::settle();
+
+  // `framework2` will be offered all of `agent2` resources
+  // because its share (0) is smaller than `framework1`.
+  Future<Allocation> allocation = allocations.get();
+  AWAIT_READY(allocation);
+  EXPECT_EQ(framework2.id(), allocation.get().frameworkId);
+  EXPECT_EQ(agent2.resources(), Resources::sum(allocation.get().resources));
+
+  // Total cluster resources (2 agents): cpus=2, mem=1024.
+  // ROLE1 share = 1 (cpus=2, mem=1024)
+  //   framework1 share = 0.5 (cpus=1, mem=512)
+  //   framework2 share = 0.5 (cpus=1, mem=512)
+
+  // Now `framework2` declines the offer and sets a filter
+  // for 1 second, which is less than the allocation interval.
+  Duration filterTimeout = Seconds(1);
+  ASSERT_GT(flags.allocation_interval, filterTimeout);
+
+  Filters offerFilter;
+  offerFilter.set_refuse_seconds(filterTimeout.secs());
+
+  allocator->recoverResources(
+      framework2.id(),
+      agent2.id(),
+      allocation.get().resources.get(agent2.id()).get(),
+      offerFilter);
+
+  // Total cluster resources (2 agents): cpus=2, mem=1024.
+  // ROLE1 share = 0.5 (cpus=1, mem=512)
+  //   framework1 share = 1 (cpus=1, mem=512)
+  //   framework2 share = 0
+
+  // The offer filter times out. Since the allocator ensures that
+  // offer filters are removed after at least one batch allocation
+  // has occurred, we expect that after the timeout elapses, the
+  // filter will remain active for the next allocation and the
+  // resources are allocated to `framework1`.
+  Clock::advance(filterTimeout);
+  Clock::settle();
+
+  // Trigger a batch allocation.
+  Clock::advance(flags.allocation_interval);
+  Clock::settle();
+
+  // Since the filter is applied, resources are offered to `framework1`
+  // even though its share is greater than `framework2`.
+  allocation = allocations.get();
+  AWAIT_READY(allocation);
+  EXPECT_EQ(framework1.id(), allocation.get().frameworkId);
+  EXPECT_EQ(agent2.resources(), Resources::sum(allocation.get().resources));
+
+  // Total cluster resources (2 agents): cpus=2, mem=1024.
+  // ROLE1 share = 1 (cpus=2, mem=1024)
+  //   framework1 share = 1 (cpus=2, mem=1024)
+  //   framework2 share = 0
+
+  // The filter should be removed now than the batch
+  // allocation has occurred!
+
+  // Now `framework1` declines the offer.
+  allocator->recoverResources(
+      framework1.id(),
+      agent2.id(),
+      allocation.get().resources.get(agent2.id()).get(),
+      None());
+
+  // Total cluster resources (2 agents): cpus=2, mem=1024.
+  // ROLE1 share = 0.5 (cpus=1, mem=512)
+  //   framework1 share = 1 (cpus=1, mem=512)
+  //   framework2 share = 0
+
+  // Trigger a batch allocation.
+  Clock::advance(flags.allocation_interval);
+  Clock::settle();
+
+  // Since the filter is removed, resources are offered to `framework2`.
+  allocation = allocations.get();
+  AWAIT_READY(allocation);
+  EXPECT_EQ(framework2.id(), allocation.get().frameworkId);
+  EXPECT_EQ(agent2.resources(), Resources::sum(allocation.get().resources));
+
+  // Total cluster resources (2 agents): cpus=2, mem=1024.
+  // ROLE1 share = 1 (cpus=2, mem=1024)
+  //   framework1 share = 0.5 (cpus=1, mem=512)
+  //   framework2 share = 0.5 (cpus=1, mem=512)
+}
+
+
 // This test ensures that agents which are scheduled for maintenance are
 // properly sent inverse offers after they have accepted or reserved resources.
 TEST_F(HierarchicalAllocatorTest, MaintenanceInverseOffers)

Reply via email to