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)
