Repository: mesos Updated Branches: refs/heads/master 91ea75e83 -> f8111469b
Added a test to check shared resources accounting in quota enforcement. This test verifies that when enforcing quota limit, shared resources that are part of a role's reserved-allocated resources are only charged once even when they are offered multiple times. Review: https://reviews.apache.org/r/64466/ Project: http://git-wip-us.apache.org/repos/asf/mesos/repo Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/f8111469 Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/f8111469 Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/f8111469 Branch: refs/heads/master Commit: f8111469b2f7c05703a680c1753df3541a83df94 Parents: 2763792 Author: Meng Zhu <[email protected]> Authored: Fri Dec 15 18:06:07 2017 -0800 Committer: Benjamin Mahler <[email protected]> Committed: Fri Dec 15 18:22:06 2017 -0800 ---------------------------------------------------------------------- src/tests/hierarchical_allocator_tests.cpp | 163 ++++++++++++++++++++++++ 1 file changed, 163 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mesos/blob/f8111469/src/tests/hierarchical_allocator_tests.cpp ---------------------------------------------------------------------- diff --git a/src/tests/hierarchical_allocator_tests.cpp b/src/tests/hierarchical_allocator_tests.cpp index 332d4e1..4127d05 100644 --- a/src/tests/hierarchical_allocator_tests.cpp +++ b/src/tests/hierarchical_allocator_tests.cpp @@ -1526,6 +1526,169 @@ TEST_P(HierarchicalAllocatorTestWithReservations, } +// This test verifies that when enforcing quota limit, shared resources +// that are part of a role's reserved-allocated resources are only +// charged once even when they are offered multiple times. +TEST_P(HierarchicalAllocatorTestWithReservations, + SharedAllocatedResourceQuotaAccounting) +{ + // We test this by creating a quota for 100 disk resources. + // We then create three agents: + // + // (1) One agent is reserved for the quota role with 50 disk. + // On this agent, we create a shared volume using the 50 + // disk, and allocate it twice to the framework. + // + // (2) We create another agent with 50 disk. Since the role + // only has 50 disk allocated to it (two instances of the + // 50 disk shared volume), the disk should be allocated + // towards the role's quota. + // + // (3) We create another agent with 50 disk. This time, the + // agent should not be allocated to the role since now + // the role has 100 disk already allocated to it. + Clock::pause(); + + const string QUOTA_ROLE{"quota-role"}; + + initialize(); + + const Quota quota = createQuota(QUOTA_ROLE, "cpus:3;mem:2048;disk:100"); + allocator->setQuota(QUOTA_ROLE, quota); + + Resource::ReservationInfo reservation; + reservation.set_type(GetParam()); + reservation.set_role(QUOTA_ROLE); + + Resources reserved = Resources::parse("cpus:2;mem:1024;disk:50").get(); + reserved = reserved.pushReservation(reservation); + + SlaveInfo agent1 = createSlaveInfo(reserved); + allocator->addSlave( + agent1.id(), + agent1, + AGENT_CAPABILITIES(), + None(), + agent1.resources(), + {}); + + FrameworkInfo framework = createFrameworkInfo( + {QUOTA_ROLE}, + {FrameworkInfo::Capability::SHARED_RESOURCES}); + + allocator->addFramework(framework.id(), framework, {}, true, {}); + + Allocation expected = Allocation( + framework.id(), + {{QUOTA_ROLE, {{agent1.id(), reserved}}}}); + + Future<Allocation> allocation = allocations.get(); + AWAIT_EXPECT_EQ(expected, allocation); + + // Quota: "cpus:3;mem:2048;disk:100". + // Allocated quota: "cpus:2;mem:1024;disk:50". + + Resource::AllocationInfo allocationInfo; + allocationInfo.set_role(QUOTA_ROLE); + + // Create a shared volume. + Resource volume = createDiskResource( + "50", QUOTA_ROLE, "id1", None(), None(), true); + + // Inject reservation info. + volume.add_reservations()->CopyFrom(reservation); + + Offer::Operation create = CREATE(volume); + protobuf::injectAllocationInfo(&create, allocationInfo); + + Try<vector<ResourceConversion>> conversions = getResourceConversions(create); + ASSERT_SOME(conversions); + + // Ensure the CREATE operation can be applied. + Try<Resources> updated = + allocation->resources.at(QUOTA_ROLE).at(agent1.id()) + .apply(conversions.get()); + + ASSERT_SOME(updated); + + // Update the allocation in the allocator with a CREATE operation. + allocator->updateAllocation( + framework.id(), + agent1.id(), + allocation->resources.at(QUOTA_ROLE).at(agent1.id()), + conversions.get()); + + // Recover part of the resources for the next allocation. + Resources recover = allocatedResources( + Resources::parse("cpus:1;mem:512").get(), + QUOTA_ROLE); + + // Inject reservation info. + recover = recover.pushReservation(reservation); + + allocator->recoverResources( + framework.id(), + agent1.id(), + recover, + None()); + + // Quota: "cpus:3;mem:2048;disk:100". + // Allocated quota: "cpus:1;mem:512;disk:50". + + // Trigger a batch allocation. + Clock::advance(flags.allocation_interval); + + // The remaining resources (cpus:1;mem:512) of agent1 along with + // the shared volume are offered again. + expected = Allocation( + framework.id(), + {{QUOTA_ROLE, {{agent1.id(), recover + create.create().volumes()}}}}); + + AWAIT_EXPECT_EQ(expected, allocations.get()); + + // Quota: "cpus:3;mem:2048;disk:100". + // Allocated quota: "cpus:2;mem:1024;disk:50". + // Note: 50 shared disk is allocated twice but should only count once. + + // Add agent2. This will trigger a batch allocation. + SlaveInfo agent2 = createSlaveInfo("cpus:0.5;mem:512;disk:50"); + allocator->addSlave( + agent2.id(), + agent2, + AGENT_CAPABILITIES(), + None(), + agent2.resources(), + {}); + + // `framework` will get all of agent2's resources. + expected = Allocation( + framework.id(), + {{QUOTA_ROLE, {{agent2.id(), agent2.resources()}}}}); + + AWAIT_EXPECT_EQ(expected, allocations.get()); + + // Quota: "cpus:3;mem:2048;disk:100" + // Allocated quota: "cpus:2.5;mem:1536;disk:100" + // The quota is met (disk limit has been reached). + + // Add agent3. + // This will trigger a batch allocation. + SlaveInfo agent3 = createSlaveInfo("cpus:0.5;mem:512;disk:50"); + allocator->addSlave( + agent3.id(), + agent3, + AGENT_CAPABILITIES(), + None(), + agent3.resources(), + {}); + + Clock::settle(); + + // No more allocations because quota limit has been reached. + EXPECT_TRUE(allocations.get().isPending()); +} + + // Checks that resources on a slave that are statically reserved to // a role are only offered to frameworks in that role. TEST_F(HierarchicalAllocatorTest, Reservations)
