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 2014/12/11 23:55:38 UTC

[01/11] mesos git commit: Simplified AllocatorTest.Whitelist by adding a unit test.

Repository: mesos
Updated Branches:
  refs/heads/master 3ea7e9a45 -> e2239a02c


Simplified AllocatorTest.Whitelist by adding a unit test.

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


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

Branch: refs/heads/master
Commit: feedbfd5c28c129959ba10255bb718daef981467
Parents: cc70b0e
Author: Benjamin Mahler <be...@gmail.com>
Authored: Sun Dec 7 21:08:32 2014 -0800
Committer: Benjamin Mahler <be...@gmail.com>
Committed: Thu Dec 11 14:40:30 2014 -0800

----------------------------------------------------------------------
 src/tests/hierarchical_allocator_tests.cpp |  48 +++++++++++
 src/tests/master_allocator_tests.cpp       | 103 +++++++-----------------
 2 files changed, 75 insertions(+), 76 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/feedbfd5/src/tests/hierarchical_allocator_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/hierarchical_allocator_tests.cpp b/src/tests/hierarchical_allocator_tests.cpp
index 4156769..7e3dcd5 100644
--- a/src/tests/hierarchical_allocator_tests.cpp
+++ b/src/tests/hierarchical_allocator_tests.cpp
@@ -28,6 +28,8 @@
 #include <process/queue.hpp>
 
 #include <stout/hashmap.hpp>
+#include <stout/hashset.hpp>
+#include <stout/utils.hpp>
 
 #include "master/allocator.hpp"
 #include "master/flags.hpp"
@@ -520,3 +522,49 @@ TEST_F(HierarchicalAllocatorTest, RecoverResources)
   EXPECT_TRUE(allocation.get().resources.contains(slave.id()));
   EXPECT_EQ(unreserved, sum(allocation.get().resources.values()));
 }
+
+
+// Checks that a slave that is not whitelisted will not have its
+// resources get offered, and that if the whitelist is updated so
+// that it is whitelisted, its resources will then be offered.
+TEST_F(HierarchicalAllocatorTest, Whitelist)
+{
+  Clock::pause();
+
+  initialize({"role1"});
+
+  hashset<string> whitelist;
+  whitelist.insert("dummy-slave");
+
+  allocator->updateWhitelist(whitelist);
+
+  hashmap<FrameworkID, Resources> EMPTY;
+
+  SlaveInfo slave = createSlaveInfo("cpus:2;mem:1024");
+  allocator->addSlave(slave.id(), slave, slave.resources(), EMPTY);
+
+  FrameworkInfo framework = createFrameworkInfo("*");
+  allocator->addFramework(framework.id(), framework, Resources());
+
+  Future<Allocation> allocation = queue.get();
+
+  // Ensure a batch allocation is triggered.
+  Clock::advance(flags.allocation_interval);
+  Clock::settle();
+
+  // There should be no allocation!
+  ASSERT_TRUE(allocation.isPending());
+
+  // Updating the whitelist to include the slave should
+  // trigger an allocation in the next batch.
+  whitelist.insert(slave.hostname());
+  allocator->updateWhitelist(whitelist);
+
+  Clock::advance(flags.allocation_interval);
+
+  AWAIT_READY(allocation);
+  EXPECT_EQ(framework.id(), allocation.get().frameworkId);
+  EXPECT_EQ(1u, allocation.get().resources.size());
+  EXPECT_TRUE(allocation.get().resources.contains(slave.id()));
+  EXPECT_EQ(slave.resources(), sum(allocation.get().resources.values()));
+}

http://git-wip-us.apache.org/repos/asf/mesos/blob/feedbfd5/src/tests/master_allocator_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/master_allocator_tests.cpp b/src/tests/master_allocator_tests.cpp
index ff99c72..0e2e5b5 100644
--- a/src/tests/master_allocator_tests.cpp
+++ b/src/tests/master_allocator_tests.cpp
@@ -31,8 +31,10 @@
 #include <process/pid.hpp>
 
 #include <stout/some.hpp>
+#include <stout/strings.hpp>
 
 #include "master/allocator.hpp"
+#include "master/constants.hpp"
 #include "master/detector.hpp"
 #include "master/hierarchical_allocator_process.hpp"
 #include "master/master.hpp"
@@ -1174,104 +1176,53 @@ TYPED_TEST(MasterAllocatorTest, MemoryOnlyOfferedAndTaskLaunched)
 }
 
 
-// Checks that a slave that is not whitelisted will not have its
-// resources get offered, and that if the whitelist is updated so
-// that it is whitelisted, its resources will then be offered.
-TYPED_TEST(MasterAllocatorTest, WhitelistSlave)
+// Checks that changes to the whitelist are sent to the allocator.
+// The allocator whitelisting is tested in the allocator unit tests.
+// TODO(bmahler): Move this to a whitelist unit test.
+TYPED_TEST(MasterAllocatorTest, Whitelist)
 {
+  Clock::pause();
+
   // Create a dummy whitelist, so that no resources will get allocated.
-  string hosts = "dummy-slave";
-  string path = "whitelist.txt";
-  ASSERT_SOME(os::write(path, hosts)) << "Error writing whitelist";
+  hashset<string> hosts;
+  hosts.insert("dummy-slave1");
+
+  const string path = "whitelist.txt";
+  ASSERT_SOME(os::write(path, strings::join("\n", hosts)));
 
   master::Flags masterFlags = this->CreateMasterFlags();
-  masterFlags.whitelist = "file://" + path; // TODO(benh): Put in /tmp.
+  masterFlags.whitelist = "file://" + path;
 
   EXPECT_CALL(this->allocator, initialize(_, _, _));
 
   Future<Nothing> updateWhitelist1;
-  EXPECT_CALL(this->allocator, updateWhitelist(_))
+  EXPECT_CALL(this->allocator, updateWhitelist(Option<hashset<string>>(hosts)))
     .WillOnce(DoAll(InvokeUpdateWhitelist(&this->allocator),
                     FutureSatisfy(&updateWhitelist1)));
 
   Try<PID<Master> > master = this->StartMaster(&this->allocator, masterFlags);
   ASSERT_SOME(master);
 
-  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
-
-  slave::Flags flags = this->CreateSlaveFlags();
-  flags.resources = Some("cpus:2;mem:1024");
-
-  Try<string> hostname = net::hostname();
-  ASSERT_SOME(hostname);
-  flags.hostname = hostname.get();
-
-  Try<PID<Slave> > slave = this->StartSlave(flags);
-  ASSERT_SOME(slave);
-
-  MockScheduler sched;
-  MesosSchedulerDriver driver(
-      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(this->allocator, addFramework(_, _, _));
-
-  EXPECT_CALL(sched, registered(_, _, _));
-
-  // Once the slave gets whitelisted, all of its resources should be
-  // offered to the one framework running.
-  Future<Nothing> resourceOffers;
-  EXPECT_CALL(sched, resourceOffers(_, OfferEq(2, 1024)))
-    .WillOnce(FutureSatisfy(&resourceOffers));
-
-  // Make sure the allocator has been given the original, empty
-  // whitelist.
+  // Make sure the allocator has been given the initial whitelist.
   AWAIT_READY(updateWhitelist1);
 
-  driver.start();
-
-  // Give the allocator some time to confirm that it doesn't
-  // make an allocation.
-  Clock::pause();
-  Clock::advance(Seconds(1));
-  Clock::settle();
-
-  EXPECT_FALSE(resourceOffers.isReady());
-
-  // Update the whitelist to include the slave, so that
-  // the allocator will start making allocations.
-  hosts = hostname.get() + "\n" + "dummy-slave";
-
-  EXPECT_CALL(this->allocator, updateWhitelist(_));
+  // Update the whitelist to ensure that the change is sent
+  // to the allocator.
+  hosts.insert("dummy-slave2");
 
-  ASSERT_SOME(os::write(path, hosts)) << "Error writing whitelist";
-
-  // Give the WhitelistWatcher some time to notice that
-  // the whitelist has changed.
-  while (resourceOffers.isPending()) {
-    Clock::advance(Seconds(1));
-    Clock::settle();
-  }
-  Clock::resume();
-
-  // Shut everything down.
-  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
-    .WillRepeatedly(DoDefault());
+  Future<Nothing> updateWhitelist2;
+  EXPECT_CALL(this->allocator, updateWhitelist(Option<hashset<string>>(hosts)))
+    .WillOnce(DoAll(InvokeUpdateWhitelist(&this->allocator),
+                    FutureSatisfy(&updateWhitelist2)));
 
-  EXPECT_CALL(this->allocator, deactivateFramework(_))
-    .Times(AtMost(1));
+  ASSERT_SOME(os::write(path, strings::join("\n", hosts)));
 
-  EXPECT_CALL(this->allocator, removeFramework(_))
-    .Times(AtMost(1));
+  Clock::advance(mesos::internal::master::WHITELIST_WATCH_INTERVAL);
 
-  driver.stop();
-  driver.join();
-
-  EXPECT_CALL(this->allocator, removeSlave(_))
-    .Times(AtMost(1));
+  // Make sure the allocator has been given the updated whitelist.
+  AWAIT_READY(updateWhitelist2);
 
   this->Shutdown();
-
-  os::rm(path);
 }
 
 


[03/11] mesos git commit: Converted an initial DRF integration test to a unit test.

Posted by bm...@apache.org.
http://git-wip-us.apache.org/repos/asf/mesos/blob/6cf1b016/src/tests/master_allocator_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/master_allocator_tests.cpp b/src/tests/master_allocator_tests.cpp
new file mode 100644
index 0000000..de0d7e8
--- /dev/null
+++ b/src/tests/master_allocator_tests.cpp
@@ -0,0 +1,2179 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <mesos/executor.hpp>
+#include <mesos/scheduler.hpp>
+
+#include <process/clock.hpp>
+#include <process/future.hpp>
+#include <process/gmock.hpp>
+#include <process/pid.hpp>
+
+#include <stout/some.hpp>
+
+#include "master/allocator.hpp"
+#include "master/detector.hpp"
+#include "master/hierarchical_allocator_process.hpp"
+#include "master/master.hpp"
+
+#include "tests/containerizer.hpp"
+#include "tests/mesos.hpp"
+
+using namespace mesos;
+using namespace mesos::internal;
+using namespace mesos::internal::tests;
+
+using mesos::internal::master::allocator::Allocator;
+using mesos::internal::master::allocator::AllocatorProcess;
+using mesos::internal::master::allocator::HierarchicalDRFAllocatorProcess;
+
+using mesos::internal::master::Master;
+
+using mesos::internal::slave::Slave;
+
+using process::Clock;
+using process::Future;
+using process::PID;
+
+using std::map;
+using std::string;
+using std::vector;
+
+using testing::_;
+using testing::AtMost;
+using testing::DoAll;
+using testing::DoDefault;
+using testing::Eq;
+using testing::SaveArg;
+
+// TODO(bmahler): Move the remainder of the DRFAllocatorTests to unit
+// tests. This file should only be testing the integration between the
+// master and the allocator.
+class DRFAllocatorTest : public MesosTest {};
+
+// This test ensures that allocation is done per slave. This is done
+// by having 2 slaves and 2 frameworks and making sure each framework
+// gets only one slave's resources during an allocation.
+TEST_F(DRFAllocatorTest, PerSlaveAllocation)
+{
+  MockAllocatorProcess<HierarchicalDRFAllocatorProcess> allocator;
+
+  EXPECT_CALL(allocator, initialize(_, _, _));
+
+  // Start the master.
+  // NOTE: We set a high allocation interval, so that allocator does
+  // allocations only based on events (framework added, slave added)
+  // but not due to allocation interval. This lets us tightly control
+  // the test expectations.
+  master::Flags masterFlags = CreateMasterFlags();
+  masterFlags.roles = Some("role1,role2");
+  masterFlags.allocation_interval = Days(1);
+  Try<PID<Master> > master = StartMaster(&allocator, masterFlags);
+  ASSERT_SOME(master);
+
+  // Start slave 1.
+  slave::Flags flags1 = CreateSlaveFlags();
+  flags1.resources = Some("cpus:2;mem:1024;disk:0");
+
+  Future<Nothing> addSlave1;
+  EXPECT_CALL(allocator, addSlave(_, _, _, _))
+    .WillOnce(DoAll(InvokeSlaveAdded(&allocator),
+                    FutureSatisfy(&addSlave1)));
+
+  Try<PID<Slave> > slave1 = StartSlave(flags1);
+  ASSERT_SOME(slave1);
+
+  AWAIT_READY(addSlave1);
+
+  // Start slave 2.
+  slave::Flags flags2 = CreateSlaveFlags();
+  flags2.resources = Some("cpus:2;mem:1024;disk:0");
+
+  Future<Nothing> addSlave2;
+  EXPECT_CALL(allocator, addSlave(_, _, _, _))
+    .WillOnce(DoAll(InvokeSlaveAdded(&allocator),
+                    FutureSatisfy(&addSlave2)));
+
+  Try<PID<Slave> > slave2 = StartSlave(flags2);
+  ASSERT_SOME(slave2);
+
+  AWAIT_READY(addSlave2);
+
+  // Start framework 1.
+  FrameworkInfo frameworkInfo1; // Bug in gcc 4.1.*, must assign on next line.
+  frameworkInfo1 = DEFAULT_FRAMEWORK_INFO;
+  frameworkInfo1.set_name("framework1");
+  frameworkInfo1.set_user("user1");
+  frameworkInfo1.set_role("role1");
+
+  MockScheduler sched1;
+  MesosSchedulerDriver driver1(
+      &sched1, frameworkInfo1, master.get(), DEFAULT_CREDENTIAL);
+
+  EXPECT_CALL(allocator, addFramework(_, _, _));
+
+  EXPECT_CALL(sched1, registered(_, _, _));
+
+  Future<Nothing> recoverResources1;
+  Future<Nothing> recoverResources2;
+  EXPECT_CALL(allocator, recoverResources(_, _, _, _))
+    .WillOnce(DoAll(InvokeResourcesRecovered(&allocator),
+                    FutureSatisfy(&recoverResources1)))
+    .WillOnce(DoAll(InvokeResourcesRecovered(&allocator),
+                    FutureSatisfy(&recoverResources2)));
+
+  // Decline the offers immediately so that resources for both slaves
+  // are eligible for allocation to this and other frameworks.
+  Filters filters;
+  filters.set_refuse_seconds(0);
+  EXPECT_CALL(sched1, resourceOffers(_, _))
+    .WillOnce(DeclineOffers(filters));
+
+  driver1.start();
+
+  // Wait until the resources are returned to the allocator.
+  // NOTE: No allocations will be made after this point until a new
+  // framework registers because
+  // 1) 'recoverResources' does not trigger an allocation and
+  // 2) 'flags.allocation_interval' is set to a very high value.
+  AWAIT_READY(recoverResources1);
+  AWAIT_READY(recoverResources2);
+
+  // Start framework 2.
+  FrameworkInfo frameworkInfo2; // Bug in gcc 4.1.*, must assign on next line.
+  frameworkInfo2 = DEFAULT_FRAMEWORK_INFO;
+  frameworkInfo2.set_name("framework2");
+  frameworkInfo2.set_user("user2");
+  frameworkInfo2.set_role("role2");
+
+  MockScheduler sched2;
+  MesosSchedulerDriver driver2(
+      &sched2, frameworkInfo2, master.get(), DEFAULT_CREDENTIAL);
+
+  EXPECT_CALL(allocator, addFramework(_, _, _));
+
+  EXPECT_CALL(sched2, registered(_, _, _));
+
+  // Offers to framework 1.
+  Future<vector<Offer> > offers1;
+  EXPECT_CALL(sched1, resourceOffers(_, _))
+    .WillOnce(FutureArg<1>(&offers1));
+
+  // Offers to framework 2.
+  Future<vector<Offer> > offers2;
+  EXPECT_CALL(sched2, resourceOffers(_, _))
+    .WillOnce(FutureArg<1>(&offers2));
+
+  driver2.start();
+
+  // Now each framework should receive offers for one slave each.
+  AWAIT_READY(offers1);
+  EXPECT_THAT(offers1.get(), OfferEq(2, 1024));
+
+  AWAIT_READY(offers2);
+  EXPECT_THAT(offers2.get(), OfferEq(2, 1024));
+
+  // Shut everything down.
+  EXPECT_CALL(allocator, recoverResources(_, _, _, _))
+    .WillRepeatedly(DoDefault());
+
+  EXPECT_CALL(allocator, deactivateFramework(_))
+    .WillRepeatedly(DoDefault());
+
+  EXPECT_CALL(allocator, removeFramework(_))
+    .WillRepeatedly(DoDefault());
+
+  driver1.stop();
+  driver1.join();
+
+  driver2.stop();
+  driver2.join();
+
+  EXPECT_CALL(allocator, removeSlave(_))
+    .WillRepeatedly(DoDefault());
+
+  Shutdown();
+}
+
+
+// Helper that simply increments the value by reference.
+ACTION_P(Increment, value) { *value += 1; }
+
+
+// This test ensures that frameworks that have the same share get an
+// equal number of allocations over time (rather than the same
+// framework getting all the allocations because it's name is
+// lexicographically ordered first).
+TEST_F(DRFAllocatorTest, SameShareAllocations)
+{
+  MockAllocatorProcess<HierarchicalDRFAllocatorProcess> allocator;
+
+  EXPECT_CALL(allocator, initialize(_, _, _));
+
+  master::Flags masterFlags = CreateMasterFlags();
+  Try<PID<Master> > master = StartMaster(&allocator, masterFlags);
+  ASSERT_SOME(master);
+
+  // Start the first scheduler.
+  FrameworkInfo frameworkInfo1; // Bug in gcc 4.1.*, must assign on next line.
+  frameworkInfo1 = DEFAULT_FRAMEWORK_INFO;
+  frameworkInfo1.set_name("framework1");
+
+  MockScheduler sched1;
+  MesosSchedulerDriver driver1(
+      &sched1, frameworkInfo1, master.get(), DEFAULT_CREDENTIAL);
+
+  EXPECT_CALL(allocator, addFramework(_, _, _));
+
+  Future<Nothing> registered1;
+  EXPECT_CALL(sched1, registered(_, _, _))
+    .WillOnce(FutureSatisfy(&registered1));
+
+  driver1.start();
+
+  AWAIT_READY(registered1);
+
+  // Start the second scheduler.
+  FrameworkInfo frameworkInfo2; // Bug in gcc 4.1.*, must assign on next line.
+  frameworkInfo2 = DEFAULT_FRAMEWORK_INFO;
+  frameworkInfo2.set_name("framework2");
+
+  MockScheduler sched2;
+  MesosSchedulerDriver driver2(
+      &sched2, frameworkInfo2, master.get(), DEFAULT_CREDENTIAL);
+
+  // We need to retire this expectation on the first match because
+  // framework1 can match this expectation first in which case
+  // framework2 should be able to match the expectation above.
+  EXPECT_CALL(allocator, addFramework(_, _, _))
+    .RetiresOnSaturation();
+
+  Future<Nothing> registered2;
+  EXPECT_CALL(sched2, registered(_, _, _))
+    .WillOnce(FutureSatisfy(&registered2));
+
+  driver2.start();
+
+  AWAIT_READY(registered2);
+
+  // Set filter timeout to 0 so that both frameworks are eligible
+  // for allocation during every allocation interval.
+  Filters filters;
+  filters.set_refuse_seconds(0);
+
+  int allocations1 = 0;
+  EXPECT_CALL(sched1, resourceOffers(_, _))
+    .WillRepeatedly(DoAll(Increment(&allocations1),
+                          DeclineOffers(filters)));
+
+  int allocations2 = 0;
+  EXPECT_CALL(sched2, resourceOffers(_, _))
+    .WillRepeatedly(DoAll(Increment(&allocations2),
+                          DeclineOffers(filters)));
+
+  EXPECT_CALL(allocator, recoverResources(_, _, _, _))
+    .WillRepeatedly(DoDefault());
+
+  // Start the slave.
+  EXPECT_CALL(allocator, addSlave(_, _, _, _));
+
+  Try<PID<Slave> > slave = StartSlave();
+  ASSERT_SOME(slave);
+
+  // Continuously do allocations.
+  Clock::pause();
+  while(allocations1 + allocations2 < 10) {
+    Clock::advance(masterFlags.allocation_interval);
+    Clock::settle();
+  }
+
+  // Each framework should get equal number of allocations.
+  ASSERT_EQ(allocations1, allocations2);
+
+  Clock::resume();
+
+  driver1.stop();
+  driver1.join();
+
+  driver2.stop();
+  driver2.join();
+
+  Shutdown();
+}
+
+
+class ReservationAllocatorTest : public MesosTest {};
+
+
+// Checks that resources on a slave that are statically reserved to
+// a role are only offered to frameworks in that role.
+TEST_F(ReservationAllocatorTest, ReservedResources)
+{
+  MockAllocatorProcess<HierarchicalDRFAllocatorProcess> allocator;
+
+  EXPECT_CALL(allocator, initialize(_, _, _));
+
+  master::Flags masterFlags = CreateMasterFlags();
+  masterFlags.roles = Some("role1,role2,role3");
+  Try<PID<Master> > master = StartMaster(&allocator, masterFlags);
+
+  ASSERT_SOME(master);
+
+  Future<Nothing> addSlave;
+  EXPECT_CALL(allocator, addSlave(_, _, _, _))
+    .WillOnce(DoDefault())
+    .WillOnce(DoDefault())
+    .WillOnce(DoDefault())
+    .WillOnce(DoAll(InvokeSlaveAdded(&allocator),
+                    FutureSatisfy(&addSlave)));
+
+  slave::Flags flags1 = CreateSlaveFlags();
+  flags1.default_role = "role1";
+  flags1.resources = Some("cpus:2;mem:1024;disk:0");
+  Try<PID<Slave> > slave1 = StartSlave(flags1);
+  ASSERT_SOME(slave1);
+
+  slave::Flags flags2 = CreateSlaveFlags();
+  flags2.resources =
+    Some("cpus(role2):2;mem(role2):1024;cpus:1;mem:1024;disk:0");
+  Try<PID<Slave> > slave2 = StartSlave(flags2);
+  ASSERT_SOME(slave2);
+
+  slave::Flags flags3 = CreateSlaveFlags();
+  flags3.default_role = "role3";
+  flags3.resources = Some("cpus:4;mem:4096;disk:0");
+  Try<PID<Slave> > slave3 = StartSlave(flags3);
+  ASSERT_SOME(slave3);
+
+  // This slave's resources should never be allocated,
+  // since there is no framework for role4.
+  slave::Flags flags4 = CreateSlaveFlags();
+  flags4.default_role = "role4";
+  flags4.resources = Some("cpus:1;mem:1024;disk:0");
+  Try<PID<Slave> > slave4 = StartSlave(flags4);
+  ASSERT_SOME(slave4);
+
+  AWAIT_READY(addSlave);
+
+  FrameworkInfo frameworkInfo1; // Bug in gcc 4.1.*, must assign on next line.
+  frameworkInfo1 = DEFAULT_FRAMEWORK_INFO;
+  frameworkInfo1.set_user("user1");
+  frameworkInfo1.set_name("framework1");
+  frameworkInfo1.set_role("role1");
+  MockScheduler sched1;
+  MesosSchedulerDriver driver1(
+      &sched1, frameworkInfo1, master.get(), DEFAULT_CREDENTIAL);
+
+  EXPECT_CALL(allocator, addFramework(_, _, _));
+
+  EXPECT_CALL(sched1, registered(_, _, _));
+
+  Future<Nothing> resourceOffers1;
+  EXPECT_CALL(sched1, resourceOffers(_, OfferEq(3, 2048)))
+    .WillOnce(FutureSatisfy(&resourceOffers1));
+
+  driver1.start();
+
+  // framework1 gets all the resources from slave1, plus the
+  // unreserved resources on slave2.
+  AWAIT_READY(resourceOffers1);
+
+  FrameworkInfo frameworkInfo2; // Bug in gcc 4.1.*, must assign on next line.
+  frameworkInfo2 = DEFAULT_FRAMEWORK_INFO;
+  frameworkInfo2.set_user("user2");
+  frameworkInfo2.set_name("framework2");
+  frameworkInfo2.set_role("role2");
+  MockScheduler sched2;
+  MesosSchedulerDriver driver2(
+      &sched2, frameworkInfo2, master.get(), DEFAULT_CREDENTIAL);
+
+  EXPECT_CALL(allocator, addFramework(_, _, _));
+
+  EXPECT_CALL(sched2, registered(_, _, _));
+
+  Future<Nothing> resourceOffers2;
+  EXPECT_CALL(sched2, resourceOffers(_, OfferEq(2, 1024)))
+    .WillOnce(FutureSatisfy(&resourceOffers2));
+
+  driver2.start();
+
+  // framework2 gets all of its reserved resources on slave2.
+  AWAIT_READY(resourceOffers2);
+
+  FrameworkInfo frameworkInfo3; // Bug in gcc 4.1.*, must assign on next line.
+  frameworkInfo3 = DEFAULT_FRAMEWORK_INFO;
+  frameworkInfo3.set_user("user2");
+  frameworkInfo3.set_name("framework3");
+  frameworkInfo3.set_role("role3");
+  MockScheduler sched3;
+  MesosSchedulerDriver driver3(
+      &sched3, frameworkInfo3, master.get(), DEFAULT_CREDENTIAL);
+
+  EXPECT_CALL(allocator, addFramework(_, _, _));
+
+  EXPECT_CALL(sched3, registered(_, _, _));
+
+  Future<Nothing> resourceOffers3;
+  EXPECT_CALL(sched3, resourceOffers(_, OfferEq(4, 4096)))
+    .WillOnce(FutureSatisfy(&resourceOffers3));
+
+  driver3.start();
+
+  // framework3 gets all the resources from slave3.
+  AWAIT_READY(resourceOffers3);
+
+  slave::Flags flags5 = CreateSlaveFlags();
+  flags5.default_role = "role1";
+  flags5.resources = Some("cpus:1;mem:512;disk:0");
+
+  EXPECT_CALL(allocator, addSlave(_, _, _, _));
+
+  Future<Nothing> resourceOffers4;
+  EXPECT_CALL(sched1, resourceOffers(_, OfferEq(1, 512)))
+    .WillOnce(FutureSatisfy(&resourceOffers4));
+
+  Try<PID<Slave> > slave5 = StartSlave(flags5);
+  ASSERT_SOME(slave5);
+
+  // framework1 gets all the resources from slave5.
+  AWAIT_READY(resourceOffers4);
+
+  // Shut everything down.
+  EXPECT_CALL(allocator, recoverResources(_, _, _, _))
+    .WillRepeatedly(DoDefault());
+
+  EXPECT_CALL(allocator, deactivateFramework(_))
+    .Times(AtMost(3));
+
+  EXPECT_CALL(allocator, removeFramework(_))
+    .Times(AtMost(3));
+
+  driver3.stop();
+  driver2.stop();
+  driver1.stop();
+
+  EXPECT_CALL(allocator, removeSlave(_))
+    .Times(AtMost(5));
+
+  this->Shutdown();
+}
+
+
+// Checks that statically allocated resources that are returned
+// either unused or after a task finishes are statically reallocated
+// appropriately.
+TEST_F(ReservationAllocatorTest, ResourcesReturned)
+{
+  MockAllocatorProcess<HierarchicalDRFAllocatorProcess> allocator;
+
+  EXPECT_CALL(allocator, initialize(_, _, _));
+
+  master::Flags masterFlags = CreateMasterFlags();
+  masterFlags.roles = Some("role1,role2");
+  masterFlags.allocation_interval = Milliseconds(50);
+  Try<PID<Master> > master = StartMaster(&allocator, masterFlags);
+
+  ASSERT_SOME(master);
+
+  MockExecutor exec(DEFAULT_EXECUTOR_ID);
+
+  EXPECT_CALL(allocator, addSlave(_, _, _, _))
+    .Times(2);
+
+  Future<Nothing> addSlave1 = FUTURE_DISPATCH(
+      allocator.real, &AllocatorProcess::addSlave);
+
+  slave::Flags flags1 = CreateSlaveFlags();
+  flags1.resources = Some("cpus(role1):1;mem(role1):200;cpus(role2):2;"
+                          "mem(role2):600;cpus:1;mem:200;disk:0");
+  Try<PID<Slave> > slave1 = StartSlave(&exec, flags1);
+  ASSERT_SOME(slave1);
+
+  // Wait until allocator has added slave1.
+  AWAIT_READY(addSlave1);
+
+  Future<Nothing> addSlave2 = FUTURE_DISPATCH(
+      allocator.real, &AllocatorProcess::addSlave);
+
+  // This slave's resources will never be offered to anyone,
+  // because there is no framework with role3.
+  slave::Flags flags2 = CreateSlaveFlags();
+  flags2.resources = Some("cpus(role3):4;mem(role3):1024;disk:0");
+  Try<PID<Slave> > slave2 = StartSlave(flags2);
+  ASSERT_SOME(slave2);
+
+  // Wait until allocator has added slave2.
+  AWAIT_READY(addSlave2);
+
+  FrameworkInfo frameworkInfo1; // Bug in gcc 4.1.*, must assign on next line.
+  frameworkInfo1 = DEFAULT_FRAMEWORK_INFO;
+  frameworkInfo1.set_user("user1");
+  frameworkInfo1.set_name("framework1");
+  frameworkInfo1.set_role("role1");
+  FrameworkID frameworkId1;
+
+  MockScheduler sched1;
+  MesosSchedulerDriver driver1(
+      &sched1, frameworkInfo1, master.get(), DEFAULT_CREDENTIAL);
+
+  EXPECT_CALL(allocator, addFramework(_, _, _));
+
+  EXPECT_CALL(sched1, registered(_, _, _));
+
+  // Initially, framework1 should be offered all of the resources on
+  // slave1 that aren't reserved to role2.
+  EXPECT_CALL(sched1, resourceOffers(_, OfferEq(2, 400)))
+    .WillOnce(LaunchTasks(DEFAULT_EXECUTOR_INFO, 1, 1, 100, "role1"));
+
+  EXPECT_CALL(allocator, recoverResources(_, _, _, _))
+    .WillOnce(InvokeResourcesRecoveredWithFilters(&allocator, 0));
+
+  EXPECT_CALL(exec, registered(_, _, _, _));
+
+  ExecutorDriver* execDriver;
+  TaskInfo taskInfo;
+  Future<Nothing> launchTask;
+  EXPECT_CALL(exec, launchTask(_, _))
+    .WillOnce(DoAll(SaveArg<0>(&execDriver),
+                    SaveArg<1>(&taskInfo),
+                    SendStatusUpdateFromTask(TASK_RUNNING),
+                    FutureSatisfy(&launchTask)));
+
+  EXPECT_CALL(sched1, statusUpdate(_, _))
+    .WillRepeatedly(DoDefault());
+
+  // After framework1's task launches, it should be offered all resources
+  // not dedicatd to role2 and not used by its task.
+  Future<Nothing> resourceOffers1;
+  EXPECT_CALL(sched1, resourceOffers(_, OfferEq(1, 300)))
+    .WillOnce(FutureSatisfy(&resourceOffers1));
+
+  driver1.start();
+
+  AWAIT_READY(launchTask);
+
+  AWAIT_READY(resourceOffers1);
+
+  FrameworkInfo frameworkInfo2; // Bug in gcc 4.1.*, must assign on next line.
+  frameworkInfo2 = DEFAULT_FRAMEWORK_INFO;
+  frameworkInfo2.set_user("user2");
+  frameworkInfo2.set_name("framework2");
+  frameworkInfo2.set_role("role2");
+  FrameworkID frameworkId2;
+
+  MockScheduler sched2;
+  MesosSchedulerDriver driver2(
+      &sched2, frameworkInfo2, master.get(), DEFAULT_CREDENTIAL);
+
+  EXPECT_CALL(allocator, addFramework(_, _, _));
+
+  EXPECT_CALL(sched2, registered(_, _, _));
+
+  // The first time framework2 is allocated to, it should be offered
+  // all of the resources on slave1 that are reserved to role2.
+  Future<Nothing> resourceOffers2;
+  EXPECT_CALL(sched2, resourceOffers(_, OfferEq(2, 600)))
+    .WillOnce(FutureSatisfy(&resourceOffers2));
+
+  driver2.start();
+
+  AWAIT_READY(resourceOffers2);
+
+  TaskStatus status;
+  status.mutable_task_id()->MergeFrom(taskInfo.task_id());
+  status.set_state(TASK_FINISHED);
+
+  EXPECT_CALL(allocator, recoverResources(_, _, _, _));
+
+  // After the task finishes, its resources should be reoffered to
+  // framework1.
+  Future<Nothing> resourceOffers3;
+  EXPECT_CALL(sched1, resourceOffers(_, OfferEq(1, 100)))
+    .WillOnce(FutureSatisfy(&resourceOffers3));
+
+  execDriver->sendStatusUpdate(status);
+
+  AWAIT_READY(resourceOffers3);
+
+  // Shut everything down.
+  EXPECT_CALL(allocator, recoverResources(_, _, _, _))
+    .WillRepeatedly(DoDefault());
+
+  EXPECT_CALL(allocator, deactivateFramework(_))
+    .Times(AtMost(2));
+
+  EXPECT_CALL(allocator, removeFramework(_))
+    .Times(AtMost(2));
+
+  Future<Nothing> shutdown;
+  EXPECT_CALL(exec, shutdown(_))
+    .WillOnce(FutureSatisfy(&shutdown));
+
+  driver2.stop();
+  driver1.stop();
+
+  AWAIT_READY(shutdown); // Ensures MockExecutor can be deallocated.
+
+  EXPECT_CALL(allocator, removeSlave(_))
+    .Times(AtMost(2));
+
+  this->Shutdown();
+}
+
+
+template <typename T>
+class MasterAllocatorTest : public MesosTest
+{
+protected:
+  void StopAllocator()
+  {
+    process::terminate(allocator.real);
+    process::wait(allocator.real);
+  }
+
+  MockAllocatorProcess<T> allocator;
+};
+
+
+// Causes all TYPED_TEST(MasterAllocatorTest, ...) to be run for
+// each of the specified Allocator classes.
+TYPED_TEST_CASE(MasterAllocatorTest, AllocatorTypes);
+
+
+// Checks that in a cluster with one slave and one framework, all of
+// the slave's resources are offered to the framework.
+TYPED_TEST(MasterAllocatorTest, MockAllocator)
+{
+  EXPECT_CALL(this->allocator, initialize(_, _, _));
+
+  Try<PID<Master> > master = this->StartMaster(&this->allocator);
+  ASSERT_SOME(master);
+
+  slave::Flags flags = this->CreateSlaveFlags();
+  flags.resources = Some("cpus:2;mem:1024;disk:0");
+
+  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
+
+  Try<PID<Slave> > slave = this->StartSlave(flags);
+  ASSERT_SOME(slave);
+
+  MockScheduler sched;
+  MesosSchedulerDriver driver(
+      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+  EXPECT_CALL(this->allocator, addFramework(_, _, _));
+
+  EXPECT_CALL(sched, registered(_, _, _));
+
+  // The framework should be offered all of the resources on the slave
+  // since it is the only framework in the cluster.
+  Future<Nothing> resourceOffers;
+  EXPECT_CALL(sched, resourceOffers(_, OfferEq(2, 1024)))
+    .WillOnce(FutureSatisfy(&resourceOffers));
+
+  driver.start();
+
+  AWAIT_READY(resourceOffers);
+
+  // Shut everything down.
+  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
+    .WillRepeatedly(DoDefault());
+
+  EXPECT_CALL(this->allocator, deactivateFramework(_))
+    .Times(AtMost(1));
+
+  EXPECT_CALL(this->allocator, removeFramework(_))
+    .Times(AtMost(1));
+
+  driver.stop();
+  driver.join();
+
+  EXPECT_CALL(this->allocator, removeSlave(_))
+    .Times(AtMost(1));
+
+  this->Shutdown();
+}
+
+
+// Checks that when a task is launched with fewer resources than what
+// the offer was for, the resources that are returned unused are
+// reoffered appropriately.
+TYPED_TEST(MasterAllocatorTest, ResourcesUnused)
+{
+  EXPECT_CALL(this->allocator, initialize(_, _, _));
+
+  Try<PID<Master> > master = this->StartMaster(&this->allocator);
+  ASSERT_SOME(master);
+
+  MockExecutor exec(DEFAULT_EXECUTOR_ID);
+
+  slave::Flags flags1 = this->CreateSlaveFlags();
+  flags1.resources = Some("cpus:2;mem:1024");
+
+  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
+
+  Try<PID<Slave> > slave1 = this->StartSlave(&exec, flags1);
+  ASSERT_SOME(slave1);
+
+  MockScheduler sched1;
+  MesosSchedulerDriver driver1(
+      &sched1, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+  EXPECT_CALL(this->allocator, addFramework(_, _, _));
+
+  EXPECT_CALL(sched1, registered(_, _, _));
+
+  // We decline offers that we aren't expecting so that the resources
+  // get aggregated. Note that we need to do this _first_ and
+  // _separate_ from the expectation below so that this expectation is
+  // checked last and matches all possible offers.
+  EXPECT_CALL(sched1, resourceOffers(_, _))
+    .WillRepeatedly(DeclineOffers());
+
+  // The first offer will contain all of the slave's resources, since
+  // this is the only framework running so far. Launch a task that
+  // uses less than that to leave some resources unused.
+  EXPECT_CALL(sched1, resourceOffers(_, OfferEq(2, 1024)))
+    .WillOnce(LaunchTasks(DEFAULT_EXECUTOR_INFO, 1, 1, 512, "*"));
+
+  Future<Nothing> recoverResources;
+  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
+    .WillOnce(DoAll(InvokeResourcesRecovered(&this->allocator),
+                    FutureSatisfy(&recoverResources)));
+
+  EXPECT_CALL(exec, registered(_, _, _, _));
+
+  Future<Nothing> launchTask;
+  EXPECT_CALL(exec, launchTask(_, _))
+    .WillOnce(FutureSatisfy(&launchTask));
+
+  driver1.start();
+
+  AWAIT_READY(launchTask);
+
+  // We need to wait until the allocator knows about the unused
+  // resources to start the second framework so that we get the
+  // expected offer.
+  AWAIT_READY(recoverResources);
+
+  FrameworkInfo frameworkInfo2; // Bug in gcc 4.1.*, must assign on next line.
+  frameworkInfo2 = DEFAULT_FRAMEWORK_INFO;
+  frameworkInfo2.set_user("user2");
+  frameworkInfo2.set_name("framework2");
+
+  MockScheduler sched2;
+  MesosSchedulerDriver driver2(
+      &sched2, frameworkInfo2, master.get(), DEFAULT_CREDENTIAL);
+
+  EXPECT_CALL(this->allocator, addFramework(_, _, _));
+
+  EXPECT_CALL(sched2, registered(_, _, _));
+
+  // We should expect that framework2 gets offered all of the
+  // resources on the slave not being used by the launched task.
+  Future<Nothing> resourceOffers;
+  EXPECT_CALL(sched2, resourceOffers(_, OfferEq(1, 512)))
+    .WillOnce(FutureSatisfy(&resourceOffers));
+
+  driver2.start();
+
+  AWAIT_READY(resourceOffers);
+
+  // Shut everything down.
+  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
+    .WillRepeatedly(DoDefault());
+
+  EXPECT_CALL(this->allocator, deactivateFramework(_))
+    .Times(AtMost(2));
+
+  EXPECT_CALL(this->allocator, removeFramework(_))
+    .Times(AtMost(2));
+
+  Future<Nothing> shutdown;
+  EXPECT_CALL(exec, shutdown(_))
+    .WillOnce(FutureSatisfy(&shutdown));
+
+  driver1.stop();
+  driver1.join();
+
+  driver2.stop();
+  driver2.join();
+
+  AWAIT_READY(shutdown); // Ensures MockExecutor can be deallocated.
+
+  EXPECT_CALL(this->allocator, removeSlave(_))
+    .Times(AtMost(1));
+
+  this->Shutdown();
+}
+
+
+// Tests the situation where a removeFramework call is dispatched
+// while we're doing an allocation to that framework, so that
+// recoverResources is called for an already removed framework.
+TYPED_TEST(MasterAllocatorTest, OutOfOrderDispatch)
+{
+  EXPECT_CALL(this->allocator, initialize(_, _, _));
+
+  Try<PID<Master> > master = this->StartMaster(&this->allocator);
+  ASSERT_SOME(master);
+
+  slave::Flags flags1 = this->CreateSlaveFlags();
+  flags1.resources = Some("cpus:2;mem:1024");
+
+  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
+
+  Try<PID<Slave> > slave1 = this->StartSlave(flags1);
+  ASSERT_SOME(slave1);
+
+  FrameworkInfo frameworkInfo1; // Bug in gcc 4.1.*, must assign on next line.
+  frameworkInfo1 = DEFAULT_FRAMEWORK_INFO;
+  frameworkInfo1.set_user("user1");
+  frameworkInfo1.set_name("framework1");
+
+  MockScheduler sched1;
+  MesosSchedulerDriver driver1(
+      &sched1, frameworkInfo1, master.get(), DEFAULT_CREDENTIAL);
+
+  EXPECT_CALL(this->allocator, addFramework(_, Eq(frameworkInfo1), _))
+    .WillOnce(InvokeFrameworkAdded(&this->allocator));
+
+  FrameworkID frameworkId1;
+  EXPECT_CALL(sched1, registered(_, _, _))
+    .WillOnce(SaveArg<1>(&frameworkId1));
+
+  // All of the slave's resources should be offered to start.
+  Future<Nothing> resourceOffers;
+  EXPECT_CALL(sched1, resourceOffers(_, OfferEq(2, 1024)))
+    .WillOnce(FutureSatisfy(&resourceOffers));
+
+  driver1.start();
+
+  AWAIT_READY(resourceOffers);
+
+  // TODO(benh): I don't see why we want to "catch" (i.e., block) this
+  // recoverResources call. It seems like we want this one to
+  // properly be executed and later we want to _inject_ a
+  // recoverResources to simulate the code in Master::offer after a
+  // framework has terminated or is inactive.
+  FrameworkID frameworkId;
+  SlaveID slaveId;
+  Resources savedResources;
+  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
+    // "Catches" the recoverResources call from the master, so
+    // that it doesn't get processed until we redispatch it after
+    // the removeFramework trigger.
+    .WillOnce(DoAll(SaveArg<0>(&frameworkId),
+                    SaveArg<1>(&slaveId),
+                    SaveArg<2>(&savedResources)));
+
+  EXPECT_CALL(this->allocator, deactivateFramework(_));
+
+  Future<Nothing> removeFramework;
+  EXPECT_CALL(this->allocator, removeFramework(Eq(frameworkId1)))
+    .WillOnce(DoAll(InvokeFrameworkRemoved(&this->allocator),
+                    FutureSatisfy(&removeFramework)));
+
+  driver1.stop();
+  driver1.join();
+
+  AWAIT_READY(removeFramework);
+
+  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
+    .WillOnce(DoDefault());
+
+  // Re-dispatch the recoverResources call which we "caught"
+  // earlier now that the framework has been removed, to test
+  // that recovering resources from a removed framework works.
+  this->allocator.recoverResources(
+      frameworkId,
+      slaveId,
+      savedResources,
+      None());
+
+  // TODO(benh): Seems like we should wait for the above
+  // recoverResources to be executed.
+
+  FrameworkInfo frameworkInfo2; // Bug in gcc 4.1.*, must assign on next line.
+  frameworkInfo2 = DEFAULT_FRAMEWORK_INFO;
+  frameworkInfo2.set_user("user2");
+  frameworkInfo2.set_name("framework2");
+
+  MockScheduler sched2;
+  MesosSchedulerDriver driver2(
+      &sched2, frameworkInfo2, master.get(), DEFAULT_CREDENTIAL);
+
+  EXPECT_CALL(this->allocator, addFramework(_, Eq(frameworkInfo2), _))
+    .WillOnce(InvokeFrameworkAdded(&this->allocator));
+
+  FrameworkID frameworkId2;
+  EXPECT_CALL(sched2, registered(_, _, _))
+    .WillOnce(SaveArg<1>(&frameworkId2));
+
+  // All of the slave's resources should be offered since no other
+  // frameworks should be running.
+  EXPECT_CALL(sched2, resourceOffers(_, OfferEq(2, 1024)))
+    .WillOnce(FutureSatisfy(&resourceOffers));
+
+  driver2.start();
+
+  AWAIT_READY(resourceOffers);
+
+  // Shut everything down.
+  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
+    .WillRepeatedly(DoDefault());
+
+  EXPECT_CALL(this->allocator, deactivateFramework(_))
+    .Times(AtMost(1));
+
+  EXPECT_CALL(this->allocator, removeFramework(Eq(frameworkId2)))
+    .Times(AtMost(1));
+
+  driver2.stop();
+  driver2.join();
+
+  EXPECT_CALL(this->allocator, removeSlave(_))
+    .Times(AtMost(1));
+
+  this->Shutdown();
+}
+
+
+// Checks that if a framework launches a task and then fails over to a
+// new scheduler, the task's resources are not reoffered as long as it
+// is running.
+TYPED_TEST(MasterAllocatorTest, SchedulerFailover)
+{
+  EXPECT_CALL(this->allocator, initialize(_, _, _));
+
+  Try<PID<Master> > master = this->StartMaster(&this->allocator);
+  ASSERT_SOME(master);
+
+  MockExecutor exec(DEFAULT_EXECUTOR_ID);
+
+  slave::Flags flags = this->CreateSlaveFlags();
+  flags.resources = Some("cpus:3;mem:1024");
+
+  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
+
+  Try<PID<Slave> > slave = this->StartSlave(&exec, flags);
+  ASSERT_SOME(slave);
+
+  FrameworkInfo frameworkInfo1; // Bug in gcc 4.1.*, must assign on next line.
+  frameworkInfo1 = DEFAULT_FRAMEWORK_INFO;
+  frameworkInfo1.set_name("framework1");
+  frameworkInfo1.set_user("user1");
+  frameworkInfo1.set_failover_timeout(10);
+
+  // Launch the first (i.e., failing) scheduler.
+  MockScheduler sched1;
+  MesosSchedulerDriver driver1(
+      &sched1, frameworkInfo1, master.get(), DEFAULT_CREDENTIAL);
+
+  EXPECT_CALL(this->allocator, addFramework(_, _, _));
+
+  FrameworkID frameworkId;
+  EXPECT_CALL(sched1, registered(&driver1, _, _))
+    .WillOnce(SaveArg<1>(&frameworkId));
+
+  // We decline offers that we aren't expecting so that the resources
+  // get aggregated. Note that we need to do this _first_ and
+  // _separate_ from the expectation below so that this expectation is
+  // checked last and matches all possible offers.
+  EXPECT_CALL(sched1, resourceOffers(_, _))
+    .WillRepeatedly(DeclineOffers()); // For subsequent offers.
+
+  // Initially, all of slave1's resources are avaliable.
+  EXPECT_CALL(sched1, resourceOffers(_, OfferEq(3, 1024)))
+    .WillOnce(LaunchTasks(DEFAULT_EXECUTOR_INFO, 1, 1, 256, "*"));
+
+  // We don't filter the unused resources to make sure that
+  // they get offered to the framework as soon as it fails over.
+  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
+    .WillOnce(InvokeResourcesRecoveredWithFilters(&this->allocator, 0))
+    // For subsequent offers.
+    .WillRepeatedly(InvokeResourcesRecoveredWithFilters(&this->allocator, 0));
+
+  EXPECT_CALL(exec, registered(_, _, _, _));
+
+  Future<Nothing> launchTask;
+  EXPECT_CALL(exec, launchTask(_, _))
+    .WillOnce(FutureSatisfy(&launchTask));
+
+  driver1.start();
+
+  // Ensures that the task has been completely launched
+  // before we have the framework fail over.
+  AWAIT_READY(launchTask);
+
+  // When we shut down the first framework, we don't want it to tell
+  // the master it's shutting down so that the master will wait to see
+  // if it fails over.
+  DROP_PROTOBUFS(UnregisterFrameworkMessage(), _, _);
+
+  Future<Nothing> deactivateFramework;
+  EXPECT_CALL(this->allocator, deactivateFramework(_))
+    .WillOnce(DoAll(InvokeFrameworkDeactivated(&this->allocator),
+                    FutureSatisfy(&deactivateFramework)));
+
+  driver1.stop();
+
+  AWAIT_READY(deactivateFramework);
+
+  FrameworkInfo frameworkInfo2; // Bug in gcc 4.1.*, must assign on next line.
+  frameworkInfo2 = DEFAULT_FRAMEWORK_INFO;
+  frameworkInfo2.mutable_id()->MergeFrom(frameworkId);
+
+  // Now launch the second (i.e., failover) scheduler using the
+  // framework id recorded from the first scheduler.
+  MockScheduler sched2;
+  MesosSchedulerDriver driver2(
+      &sched2, frameworkInfo2, master.get(), DEFAULT_CREDENTIAL);
+
+  EXPECT_CALL(this->allocator, activateFramework(_));
+
+  EXPECT_CALL(sched2, registered(_, frameworkId, _));
+
+  // Even though the scheduler failed over, the 1 cpu, 256 mem
+  // task that it launched earlier should still be running, so
+  // only 2 cpus and 768 mem are available.
+  Future<Nothing> resourceOffers;
+  EXPECT_CALL(sched2, resourceOffers(_, OfferEq(2, 768)))
+    .WillOnce(FutureSatisfy(&resourceOffers));
+
+  driver2.start();
+
+  AWAIT_READY(resourceOffers);
+
+  // Shut everything down.
+  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
+    .WillRepeatedly(DoDefault());
+
+  EXPECT_CALL(this->allocator, deactivateFramework(_))
+    .Times(AtMost(1));
+
+  EXPECT_CALL(this->allocator, removeFramework(_))
+    .Times(AtMost(1));
+
+  EXPECT_CALL(exec, shutdown(_))
+    .Times(AtMost(1));
+
+  driver2.stop();
+  driver2.join();
+
+  EXPECT_CALL(this->allocator, removeSlave(_))
+    .Times(AtMost(1));
+
+  this->Shutdown();
+}
+
+
+// Checks that if a framework launches a task and then the framework
+// is killed, the tasks resources are returned and reoffered correctly.
+TYPED_TEST(MasterAllocatorTest, FrameworkExited)
+{
+  EXPECT_CALL(this->allocator, initialize(_, _, _));
+
+  master::Flags masterFlags = this->CreateMasterFlags();
+  masterFlags.allocation_interval = Milliseconds(50);
+  Try<PID<Master> > master = this->StartMaster(&this->allocator, masterFlags);
+  ASSERT_SOME(master);
+
+  ExecutorInfo executor1; // Bug in gcc 4.1.*, must assign on next line.
+  executor1 = CREATE_EXECUTOR_INFO("executor-1", "exit 1");
+
+  ExecutorInfo executor2; // Bug in gcc 4.1.*, must assign on next line.
+  executor2 = CREATE_EXECUTOR_INFO("executor-2", "exit 1");
+
+  MockExecutor exec1(executor1.executor_id());
+  MockExecutor exec2(executor2.executor_id());
+
+  hashmap<ExecutorID, Executor*> execs;
+  execs[executor1.executor_id()] = &exec1;
+  execs[executor2.executor_id()] = &exec2;
+
+  TestContainerizer containerizer(execs);
+
+  slave::Flags flags = this->CreateSlaveFlags();
+
+  flags.resources = Some("cpus:3;mem:1024");
+
+  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
+
+  Try<PID<Slave> > slave = this->StartSlave(&containerizer, flags);
+  ASSERT_SOME(slave);
+
+  MockScheduler sched1;
+  MesosSchedulerDriver driver1(
+      &sched1, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+  EXPECT_CALL(this->allocator, addFramework(_, _, _));
+
+  EXPECT_CALL(sched1, registered(_, _, _));
+
+  // We decline offers that we aren't expecting so that the resources
+  // get aggregated. Note that we need to do this _first_ and
+  // _separate_ from the expectation below so that this expectation is
+  // checked last and matches all possible offers.
+  EXPECT_CALL(sched1, resourceOffers(_, _))
+    .WillRepeatedly(DeclineOffers());
+
+  // The first time the framework is offered resources, all of the
+  // cluster's resources should be avaliable.
+  EXPECT_CALL(sched1, resourceOffers(_, OfferEq(3, 1024)))
+    .WillOnce(LaunchTasks(executor1, 1, 2, 512, "*"));
+
+  // The framework does not use all the resources.
+  Future<Nothing> recoverResources;
+  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
+    .WillOnce(DoAll(InvokeResourcesRecovered(&this->allocator),
+                    FutureSatisfy(&recoverResources)));
+
+  EXPECT_CALL(exec1, registered(_, _, _, _));
+
+  Future<Nothing> launchTask;
+  EXPECT_CALL(exec1, launchTask(_, _))
+    .WillOnce(FutureSatisfy(&launchTask));
+
+  driver1.start();
+
+  // Ensures that framework 1's task is completely launched
+  // before we kill the framework to test if its resources
+  // are recovered correctly.
+  AWAIT_READY(launchTask);
+
+  // We need to wait until the allocator knows about the unused
+  // resources to start the second framework so that we get the
+  // expected offer.
+  AWAIT_READY(recoverResources);
+
+  MockScheduler sched2;
+  MesosSchedulerDriver driver2(
+      &sched2, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+  EXPECT_CALL(this->allocator, addFramework(_, _, _));
+
+  EXPECT_CALL(sched2, registered(_, _, _));
+
+  // We decline offers that we aren't expecting so that the resources
+  // get aggregated. Note that we need to do this _first_ and
+  // _separate_ from the expectation below so that this expectation is
+  // checked last and matches all possible offers.
+  EXPECT_CALL(sched2, resourceOffers(_, _))
+    .WillRepeatedly(DeclineOffers());
+
+  // The first time sched2 gets an offer, framework 1 has a task
+  // running with 2 cpus and 512 mem, leaving 1 cpu and 512 mem.
+  EXPECT_CALL(sched2, resourceOffers(_, OfferEq(1, 512)))
+    .WillOnce(LaunchTasks(executor2, 1, 1, 256, "*"));
+
+  // The framework 2 does not use all the resources.
+  Future<Nothing> recoverResources2;
+  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
+    .WillOnce(DoAll(InvokeResourcesRecovered(&this->allocator),
+                    FutureSatisfy(&recoverResources2)));
+
+  EXPECT_CALL(exec2, registered(_, _, _, _));
+
+  EXPECT_CALL(exec2, launchTask(_, _))
+    .WillOnce(FutureSatisfy(&launchTask));
+
+  driver2.start();
+
+  AWAIT_READY(launchTask);
+
+  AWAIT_READY(recoverResources2);
+
+  // Shut everything down but check that framework 2 gets the
+  // resources from framework 1 after it is shutdown.
+  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
+    .WillRepeatedly(DoDefault());
+
+  EXPECT_CALL(this->allocator, deactivateFramework(_))
+    .Times(AtMost(2)); // Once for each framework.
+
+  EXPECT_CALL(this->allocator, removeFramework(_))
+    .Times(AtMost(2)); // Once for each framework.
+
+  // After we stop framework 1, all of it's resources should
+  // have been returned, but framework 2 should still have a
+  // task with 1 cpu and 256 mem, leaving 2 cpus and 768 mem.
+  Future<Nothing> resourceOffers;
+  EXPECT_CALL(sched2, resourceOffers(_, OfferEq(2, 768)))
+    .WillOnce(FutureSatisfy(&resourceOffers));
+
+  EXPECT_CALL(exec1, shutdown(_))
+    .Times(AtMost(1));
+
+  driver1.stop();
+  driver1.join();
+
+  AWAIT_READY(resourceOffers);
+
+  EXPECT_CALL(exec2, shutdown(_))
+    .Times(AtMost(1));
+
+  driver2.stop();
+  driver2.join();
+
+  EXPECT_CALL(this->allocator, removeSlave(_))
+    .Times(AtMost(1));
+
+  this->Shutdown();
+}
+
+
+// Checks that if a framework launches a task and then the slave the
+// task was running on gets killed, the task's resources are properly
+// recovered and, along with the rest of the resources from the killed
+// slave, never offered again.
+TYPED_TEST(MasterAllocatorTest, SlaveLost)
+{
+  EXPECT_CALL(this->allocator, initialize(_, _, _));
+
+  Try<PID<Master> > master = this->StartMaster(&this->allocator);
+  ASSERT_SOME(master);
+
+  MockExecutor exec(DEFAULT_EXECUTOR_ID);
+
+  slave::Flags flags1 = this->CreateSlaveFlags();
+  flags1.resources = Some("cpus:2;mem:1024");
+
+  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
+
+  Try<PID<Slave> > slave1 = this->StartSlave(&exec, flags1);
+  ASSERT_SOME(slave1);
+
+  MockScheduler sched;
+  MesosSchedulerDriver driver(
+      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+  EXPECT_CALL(this->allocator, addFramework(_, _, _));
+
+  EXPECT_CALL(sched, registered(_, _, _));
+
+  // Initially, all of slave1's resources are available.
+  EXPECT_CALL(sched, resourceOffers(_, OfferEq(2, 1024)))
+    .WillOnce(LaunchTasks(DEFAULT_EXECUTOR_INFO, 1, 2, 512, "*"));
+
+  Future<Nothing> recoverResources;
+  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
+    .WillOnce(DoAll(InvokeResourcesRecovered(&this->allocator),
+                    FutureSatisfy(&recoverResources)));
+
+  EXPECT_CALL(exec, registered(_, _, _, _));
+
+  Future<Nothing> launchTask;
+  EXPECT_CALL(exec, launchTask(_, _))
+    .WillOnce(DoAll(SendStatusUpdateFromTask(TASK_RUNNING),
+                    FutureSatisfy(&launchTask)));
+
+  EXPECT_CALL(sched, statusUpdate(_, _))
+    .WillRepeatedly(DoDefault());
+
+  driver.start();
+
+  // Ensures the task is completely launched before we kill the
+  // slave, to test that the task's and executor's resources are
+  // recovered correctly (i.e. never reallocated since the slave
+  // is killed).
+  AWAIT_READY(launchTask);
+
+  // Framework does not use all the resources.
+  AWAIT_READY(recoverResources);
+
+  // 'recoverResources' should be called twice, once for the task
+  // and once for the executor.
+  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
+    .Times(2);
+
+  Future<Nothing> removeSlave;
+  EXPECT_CALL(this->allocator, removeSlave(_))
+    .WillOnce(DoAll(InvokeSlaveRemoved(&this->allocator),
+                    FutureSatisfy(&removeSlave)));
+
+  EXPECT_CALL(exec, shutdown(_))
+    .Times(AtMost(1));
+
+  EXPECT_CALL(sched, slaveLost(_, _));
+
+  this->ShutdownSlaves();
+
+  AWAIT_READY(removeSlave);
+
+  slave::Flags flags2 = this->CreateSlaveFlags();
+  flags2.resources = string("cpus:3;mem:256;disk:1024;ports:[31000-32000]");
+
+  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
+
+  // Eventually after slave2 is launched, we should get
+  // an offer that contains all of slave2's resources
+  // and none of slave1's resources.
+  Future<vector<Offer> > resourceOffers;
+  EXPECT_CALL(sched, resourceOffers(_, OfferEq(3, 256)))
+    .WillOnce(FutureArg<1>(&resourceOffers));
+
+  Try<PID<Slave> > slave2 = this->StartSlave(flags2);
+  ASSERT_SOME(slave2);
+
+  AWAIT_READY(resourceOffers);
+
+  EXPECT_EQ(Resources(resourceOffers.get()[0].resources()),
+            Resources::parse(flags2.resources.get()).get());
+
+  // Shut everything down.
+  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
+    .WillRepeatedly(DoDefault());
+
+  EXPECT_CALL(this->allocator, deactivateFramework(_))
+    .Times(AtMost(1));
+
+  EXPECT_CALL(this->allocator, removeFramework(_))
+    .Times(AtMost(1));
+
+  driver.stop();
+  driver.join();
+
+  EXPECT_CALL(this->allocator, removeSlave(_))
+    .Times(AtMost(1));
+
+  this->Shutdown();
+}
+
+
+// Checks that if a slave is added after some allocations have already
+// occurred, its resources are added to the available pool of
+// resources and offered appropriately.
+TYPED_TEST(MasterAllocatorTest, SlaveAdded)
+{
+  EXPECT_CALL(this->allocator, initialize(_, _, _));
+
+  master::Flags masterFlags = this->CreateMasterFlags();
+  masterFlags.allocation_interval = Milliseconds(50);
+  Try<PID<Master> > master = this->StartMaster(&this->allocator, masterFlags);
+  ASSERT_SOME(master);
+
+  MockExecutor exec(DEFAULT_EXECUTOR_ID);
+
+  slave::Flags flags1 = this->CreateSlaveFlags();
+  flags1.resources = Some("cpus:3;mem:1024");
+
+  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
+
+  Try<PID<Slave> > slave1 = this->StartSlave(&exec, flags1);
+  ASSERT_SOME(slave1);
+
+  MockScheduler sched;
+  MesosSchedulerDriver driver(
+      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+  EXPECT_CALL(this->allocator, addFramework(_, _, _));
+
+  EXPECT_CALL(sched, registered(_, _, _));
+
+  // We decline offers that we aren't expecting so that the resources
+  // get aggregated. Note that we need to do this _first_ and
+  // _separate_ from the expectation below so that this expectation is
+  // checked last and matches all possible offers.
+  EXPECT_CALL(sched, resourceOffers(_, _))
+    .WillRepeatedly(DeclineOffers());
+
+  // Initially, all of slave1's resources are avaliable.
+  EXPECT_CALL(sched, resourceOffers(_, OfferEq(3, 1024)))
+    .WillOnce(LaunchTasks(DEFAULT_EXECUTOR_INFO, 1, 2, 512, "*"));
+
+  // We filter the first time so that the unused resources
+  // on slave1 from the task launch won't get reoffered
+  // immediately and will get combined with slave2's
+  // resources for a single offer.
+  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
+    .WillOnce(InvokeResourcesRecoveredWithFilters(&this->allocator, 0.1))
+    .WillRepeatedly(InvokeResourcesRecoveredWithFilters(&this->allocator, 0));
+
+  EXPECT_CALL(exec, registered(_, _, _, _));
+
+  Future<Nothing> launchTask;
+  EXPECT_CALL(exec, launchTask(_, _))
+    .WillOnce(DoAll(SendStatusUpdateFromTask(TASK_RUNNING),
+                    FutureSatisfy(&launchTask)));
+
+  EXPECT_CALL(sched, statusUpdate(_, _))
+    .WillRepeatedly(DoDefault());
+
+  driver.start();
+
+  AWAIT_READY(launchTask);
+
+  slave::Flags flags2 = this->CreateSlaveFlags();
+  flags2.resources = Some("cpus:4;mem:2048");
+
+  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
+
+  // After slave2 launches, all of its resources are combined with the
+  // resources on slave1 that the task isn't using.
+  Future<Nothing> resourceOffers;
+  EXPECT_CALL(sched, resourceOffers(_, OfferEq(5, 2560)))
+    .WillOnce(FutureSatisfy(&resourceOffers));
+
+  Try<PID<Slave> > slave2 = this->StartSlave(flags2);
+  ASSERT_SOME(slave2);
+
+  AWAIT_READY(resourceOffers);
+
+  // Shut everything down.
+  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
+    .WillRepeatedly(DoDefault());
+
+  EXPECT_CALL(this->allocator, deactivateFramework(_))
+    .Times(AtMost(1));
+
+  EXPECT_CALL(this->allocator, removeFramework(_))
+    .Times(AtMost(1));
+
+  EXPECT_CALL(exec, shutdown(_))
+    .Times(AtMost(1));
+
+  driver.stop();
+  driver.join();
+
+  EXPECT_CALL(this->allocator, removeSlave(_))
+    .Times(AtMost(2));
+
+  this->Shutdown();
+}
+
+
+// Checks that if a task is launched and then finishes normally, its
+// resources are recovered and reoffered correctly.
+TYPED_TEST(MasterAllocatorTest, TaskFinished)
+{
+  EXPECT_CALL(this->allocator, initialize(_, _, _));
+
+  master::Flags masterFlags = this->CreateMasterFlags();
+  masterFlags.allocation_interval = Milliseconds(50);
+  Try<PID<Master> > master = this->StartMaster(&this->allocator, masterFlags);
+  ASSERT_SOME(master);
+
+  MockExecutor exec(DEFAULT_EXECUTOR_ID);
+
+  slave::Flags flags = this->CreateSlaveFlags();
+  flags.resources = Some("cpus:3;mem:1024");
+
+  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
+
+  Try<PID<Slave> > slave = this->StartSlave(&exec, flags);
+  ASSERT_SOME(slave);
+
+  MockScheduler sched;
+  MesosSchedulerDriver driver(
+      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+  EXPECT_CALL(this->allocator, addFramework(_, _, _));
+
+  EXPECT_CALL(sched, registered(_, _, _));
+
+  // We decline offers that we aren't expecting so that the resources
+  // get aggregated. Note that we need to do this _first_ and
+  // _separate_ from the expectation below so that this expectation is
+  // checked last and matches all possible offers.
+  EXPECT_CALL(sched, resourceOffers(_, _))
+    .WillRepeatedly(DeclineOffers());
+
+  // Initially, all of the slave's resources.
+  EXPECT_CALL(sched, resourceOffers(_, OfferEq(3, 1024)))
+    .WillOnce(LaunchTasks(DEFAULT_EXECUTOR_INFO, 2, 1, 256, "*"));
+
+  // Some resources will be unused and we need to make sure that we
+  // don't send the TASK_FINISHED status update below until after the
+  // allocator knows about the unused resources so that it can
+  // aggregate them with the resources from the finished task.
+  Future<Nothing> recoverResources;
+  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
+    .WillRepeatedly(DoAll(InvokeResourcesRecovered(&this->allocator),
+                          FutureSatisfy(&recoverResources)));
+
+  EXPECT_CALL(exec, registered(_, _, _, _));
+
+  ExecutorDriver* execDriver;
+  TaskInfo taskInfo;
+  Future<Nothing> launchTask;
+  EXPECT_CALL(exec, launchTask(_, _))
+    .WillOnce(DoAll(SaveArg<0>(&execDriver),
+                    SaveArg<1>(&taskInfo),
+                    SendStatusUpdateFromTask(TASK_RUNNING),
+                    FutureSatisfy(&launchTask)))
+    .WillOnce(SendStatusUpdateFromTask(TASK_RUNNING));
+
+  EXPECT_CALL(sched, statusUpdate(_, _))
+    .WillRepeatedly(DoDefault());
+
+  driver.start();
+
+  AWAIT_READY(launchTask);
+
+  AWAIT_READY(recoverResources);
+
+  TaskStatus status;
+  status.mutable_task_id()->MergeFrom(taskInfo.task_id());
+  status.set_state(TASK_FINISHED);
+
+  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _));
+
+  // After the first task gets killed.
+  Future<Nothing> resourceOffers;
+  EXPECT_CALL(sched, resourceOffers(_, OfferEq(2, 768)))
+    .WillOnce(FutureSatisfy(&resourceOffers));
+
+  execDriver->sendStatusUpdate(status);
+
+  AWAIT_READY(resourceOffers);
+
+  // Shut everything down.
+  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
+    .WillRepeatedly(DoDefault());
+
+  EXPECT_CALL(this->allocator, deactivateFramework(_))
+    .Times(AtMost(1));
+
+  EXPECT_CALL(this->allocator, removeFramework(_))
+    .Times(AtMost(1));
+
+  EXPECT_CALL(exec, shutdown(_))
+    .Times(AtMost(1));
+
+  driver.stop();
+  driver.join();
+
+  EXPECT_CALL(this->allocator, removeSlave(_))
+    .Times(AtMost(1));
+
+  this->Shutdown();
+}
+
+
+// Checks that cpus only resources are offered
+// and tasks using only cpus are launched.
+TYPED_TEST(MasterAllocatorTest, CpusOnlyOfferedAndTaskLaunched)
+{
+  EXPECT_CALL(this->allocator, initialize(_, _, _));
+
+  master::Flags masterFlags = this->CreateMasterFlags();
+  masterFlags.allocation_interval = Milliseconds(50);
+  Try<PID<Master> > master = this->StartMaster(&this->allocator, masterFlags);
+  ASSERT_SOME(master);
+
+  MockExecutor exec(DEFAULT_EXECUTOR_ID);
+
+  // Start a slave with cpus only resources.
+  slave::Flags flags = this->CreateSlaveFlags();
+  flags.resources = Some("cpus:2;mem:0");
+
+  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
+
+  Try<PID<Slave> > slave = this->StartSlave(&exec, flags);
+  ASSERT_SOME(slave);
+
+  MockScheduler sched;
+  MesosSchedulerDriver driver(
+      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+  EXPECT_CALL(this->allocator, addFramework(_, _, _));
+
+  EXPECT_CALL(sched, registered(_, _, _));
+
+  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
+    .WillRepeatedly(DoDefault());
+
+  // Launch a cpus only task.
+  EXPECT_CALL(sched, resourceOffers(_, OfferEq(2, 0)))
+    .WillOnce(LaunchTasks(DEFAULT_EXECUTOR_INFO, 1, 2, 0, "*"));
+
+  EXPECT_CALL(exec, registered(_, _, _, _));
+
+  ExecutorDriver* execDriver;
+  TaskInfo taskInfo;
+  Future<Nothing> launchTask;
+  EXPECT_CALL(exec, launchTask(_, _))
+    .WillOnce(DoAll(SaveArg<0>(&execDriver),
+                    SaveArg<1>(&taskInfo),
+                    SendStatusUpdateFromTask(TASK_RUNNING),
+                    FutureSatisfy(&launchTask)));
+
+  EXPECT_CALL(sched, statusUpdate(_, _))
+    .WillRepeatedly(DoDefault());
+
+  driver.start();
+
+  AWAIT_READY(launchTask);
+
+  TaskStatus status;
+  status.mutable_task_id()->MergeFrom(taskInfo.task_id());
+  status.set_state(TASK_FINISHED);
+
+  // Check that cpus resources of finished task are offered again.
+  Future<Nothing> resourceOffers;
+  EXPECT_CALL(sched, resourceOffers(_, OfferEq(2, 0)))
+    .WillOnce(FutureSatisfy(&resourceOffers));
+
+  execDriver->sendStatusUpdate(status);
+
+  AWAIT_READY(resourceOffers);
+
+  // Shut everything down.
+  EXPECT_CALL(this->allocator, deactivateFramework(_))
+    .Times(AtMost(1));
+
+  EXPECT_CALL(this->allocator, removeFramework(_))
+    .Times(AtMost(1));
+
+  EXPECT_CALL(exec, shutdown(_))
+    .Times(AtMost(1));
+
+  driver.stop();
+  driver.join();
+
+  EXPECT_CALL(this->allocator, removeSlave(_))
+    .Times(AtMost(1));
+
+  this->Shutdown();
+}
+
+
+// Checks that memory only resources are offered
+// and tasks using only memory are launched.
+TYPED_TEST(MasterAllocatorTest, MemoryOnlyOfferedAndTaskLaunched)
+{
+  EXPECT_CALL(this->allocator, initialize(_, _, _));
+
+  master::Flags masterFlags = this->CreateMasterFlags();
+  masterFlags.allocation_interval = Milliseconds(50);
+  Try<PID<Master> > master = this->StartMaster(&this->allocator, masterFlags);
+  ASSERT_SOME(master);
+
+  MockExecutor exec(DEFAULT_EXECUTOR_ID);
+
+  // Start a slave with memory only resources.
+  slave::Flags flags = this->CreateSlaveFlags();
+  flags.resources = Some("cpus:0;mem:200");
+
+  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
+
+  Try<PID<Slave> > slave = this->StartSlave(&exec, flags);
+  ASSERT_SOME(slave);
+
+  MockScheduler sched;
+  MesosSchedulerDriver driver(
+      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+  EXPECT_CALL(this->allocator, addFramework(_, _, _));
+
+  EXPECT_CALL(sched, registered(_, _, _));
+
+  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
+    .WillRepeatedly(DoDefault());
+
+  // Launch a memory only task.
+  EXPECT_CALL(sched, resourceOffers(_, OfferEq(0, 200)))
+    .WillOnce(LaunchTasks(DEFAULT_EXECUTOR_INFO, 1, 0, 200, "*"));
+
+  EXPECT_CALL(exec, registered(_, _, _, _));
+
+  ExecutorDriver* execDriver;
+  TaskInfo taskInfo;
+  Future<Nothing> launchTask;
+  EXPECT_CALL(exec, launchTask(_, _))
+    .WillOnce(DoAll(SaveArg<0>(&execDriver),
+                    SaveArg<1>(&taskInfo),
+                    SendStatusUpdateFromTask(TASK_RUNNING),
+                    FutureSatisfy(&launchTask)));
+
+  EXPECT_CALL(sched, statusUpdate(_, _))
+    .WillRepeatedly(DoDefault());
+
+  driver.start();
+
+  AWAIT_READY(launchTask);
+
+  TaskStatus status;
+  status.mutable_task_id()->MergeFrom(taskInfo.task_id());
+  status.set_state(TASK_FINISHED);
+
+  // Check that mem resources of finished task are offered again.
+  Future<Nothing> resourceOffers;
+  EXPECT_CALL(sched, resourceOffers(_, OfferEq(0, 200)))
+    .WillOnce(FutureSatisfy(&resourceOffers));
+
+  execDriver->sendStatusUpdate(status);
+
+  AWAIT_READY(resourceOffers);
+
+  // Shut everything down.
+  EXPECT_CALL(this->allocator, deactivateFramework(_))
+    .Times(AtMost(1));
+
+  EXPECT_CALL(this->allocator, removeFramework(_))
+    .Times(AtMost(1));
+
+  EXPECT_CALL(exec, shutdown(_))
+    .Times(AtMost(1));
+
+  driver.stop();
+  driver.join();
+
+  EXPECT_CALL(this->allocator, removeSlave(_))
+    .Times(AtMost(1));
+
+  this->Shutdown();
+}
+
+
+// Checks that a slave that is not whitelisted will not have its
+// resources get offered, and that if the whitelist is updated so
+// that it is whitelisted, its resources will then be offered.
+TYPED_TEST(MasterAllocatorTest, WhitelistSlave)
+{
+  // Create a dummy whitelist, so that no resources will get allocated.
+  string hosts = "dummy-slave";
+  string path = "whitelist.txt";
+  ASSERT_SOME(os::write(path, hosts)) << "Error writing whitelist";
+
+  master::Flags masterFlags = this->CreateMasterFlags();
+  masterFlags.whitelist = "file://" + path; // TODO(benh): Put in /tmp.
+
+  EXPECT_CALL(this->allocator, initialize(_, _, _));
+
+  Future<Nothing> updateWhitelist1;
+  EXPECT_CALL(this->allocator, updateWhitelist(_))
+    .WillOnce(DoAll(InvokeUpdateWhitelist(&this->allocator),
+                    FutureSatisfy(&updateWhitelist1)));
+
+  Try<PID<Master> > master = this->StartMaster(&this->allocator, masterFlags);
+  ASSERT_SOME(master);
+
+  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
+
+  slave::Flags flags = this->CreateSlaveFlags();
+  flags.resources = Some("cpus:2;mem:1024");
+
+  Try<string> hostname = net::hostname();
+  ASSERT_SOME(hostname);
+  flags.hostname = hostname.get();
+
+  Try<PID<Slave> > slave = this->StartSlave(flags);
+  ASSERT_SOME(slave);
+
+  MockScheduler sched;
+  MesosSchedulerDriver driver(
+      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+  EXPECT_CALL(this->allocator, addFramework(_, _, _));
+
+  EXPECT_CALL(sched, registered(_, _, _));
+
+  // Once the slave gets whitelisted, all of its resources should be
+  // offered to the one framework running.
+  Future<Nothing> resourceOffers;
+  EXPECT_CALL(sched, resourceOffers(_, OfferEq(2, 1024)))
+    .WillOnce(FutureSatisfy(&resourceOffers));
+
+  // Make sure the allocator has been given the original, empty
+  // whitelist.
+  AWAIT_READY(updateWhitelist1);
+
+  driver.start();
+
+  // Give the allocator some time to confirm that it doesn't
+  // make an allocation.
+  Clock::pause();
+  Clock::advance(Seconds(1));
+  Clock::settle();
+
+  EXPECT_FALSE(resourceOffers.isReady());
+
+  // Update the whitelist to include the slave, so that
+  // the allocator will start making allocations.
+  hosts = hostname.get() + "\n" + "dummy-slave";
+
+  EXPECT_CALL(this->allocator, updateWhitelist(_));
+
+  ASSERT_SOME(os::write(path, hosts)) << "Error writing whitelist";
+
+  // Give the WhitelistWatcher some time to notice that
+  // the whitelist has changed.
+  while (resourceOffers.isPending()) {
+    Clock::advance(Seconds(1));
+    Clock::settle();
+  }
+  Clock::resume();
+
+  // Shut everything down.
+  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
+    .WillRepeatedly(DoDefault());
+
+  EXPECT_CALL(this->allocator, deactivateFramework(_))
+    .Times(AtMost(1));
+
+  EXPECT_CALL(this->allocator, removeFramework(_))
+    .Times(AtMost(1));
+
+  driver.stop();
+  driver.join();
+
+  EXPECT_CALL(this->allocator, removeSlave(_))
+    .Times(AtMost(1));
+
+  this->Shutdown();
+
+  os::rm(path);
+}
+
+
+// Checks that a framework attempting to register with an invalid role
+// will receive an error message and that roles can be added through the
+// master's command line flags.
+TYPED_TEST(MasterAllocatorTest, RoleTest)
+{
+  EXPECT_CALL(this->allocator, initialize(_, _, _));
+
+  master::Flags masterFlags = this->CreateMasterFlags();
+  masterFlags.roles = Some("role2");
+  Try<PID<Master> > master = this->StartMaster(&this->allocator, masterFlags);
+  ASSERT_SOME(master);
+
+  // Launch a framework with a role that doesn't exist to see that it
+  // receives an error message.
+  FrameworkInfo frameworkInfo1; // Bug in gcc 4.1.*, must assign on next line.
+  frameworkInfo1 = DEFAULT_FRAMEWORK_INFO;
+  frameworkInfo1.set_name("framework1");
+  frameworkInfo1.set_user("user1");
+  frameworkInfo1.set_role("role1");
+
+  MockScheduler sched1;
+  MesosSchedulerDriver driver1(
+      &sched1, frameworkInfo1, master.get(), DEFAULT_CREDENTIAL);
+
+  Future<FrameworkErrorMessage> errorMessage =
+    FUTURE_PROTOBUF(FrameworkErrorMessage(), _, _);
+
+  EXPECT_CALL(sched1, error(_, _));
+
+  driver1.start();
+
+  AWAIT_READY(errorMessage);
+
+  // Launch a framework under an existing role to see that it registers.
+  FrameworkInfo frameworkInfo2; // Bug in gcc 4.1.*, must assign on next line.
+  frameworkInfo2 = DEFAULT_FRAMEWORK_INFO;
+  frameworkInfo2.set_name("framework2");
+  frameworkInfo2.set_user("user2");
+  frameworkInfo2.set_role("role2");
+
+  MockScheduler sched2;
+  MesosSchedulerDriver driver2(
+      &sched2, frameworkInfo2, master.get(), DEFAULT_CREDENTIAL);
+
+  Future<Nothing> registered2;
+  EXPECT_CALL(sched2, registered(_, _, _))
+    .WillOnce(FutureSatisfy(&registered2));
+
+  Future<Nothing> addFramework;
+  EXPECT_CALL(this->allocator, addFramework(_, _, _))
+    .WillOnce(FutureSatisfy(&addFramework));
+
+  driver2.start();
+
+  AWAIT_READY(registered2);
+  AWAIT_READY(addFramework);
+
+  // Shut everything down.
+  Future<Nothing> deactivateFramework;
+  EXPECT_CALL(this->allocator, deactivateFramework(_))
+    .WillOnce(FutureSatisfy(&deactivateFramework));
+
+  Future<Nothing> removeFramework;
+  EXPECT_CALL(this->allocator, removeFramework(_))
+    .WillOnce(FutureSatisfy(&removeFramework));
+
+  driver2.stop();
+  driver2.join();
+
+  AWAIT_READY(deactivateFramework);
+  AWAIT_READY(removeFramework);
+
+  driver1.stop();
+  driver1.join();
+
+  this->Shutdown();
+}
+
+
+// Checks that in the event of a master failure and the election of a
+// new master, if a framework reregisters before a slave that it has
+// resources on reregisters, all used and unused resources are
+// accounted for correctly.
+TYPED_TEST(MasterAllocatorTest, FrameworkReregistersFirst)
+{
+  EXPECT_CALL(this->allocator, initialize(_, _, _));
+
+  Try<PID<Master> > master = this->StartMaster(&this->allocator);
+  ASSERT_SOME(master);
+
+  MockExecutor exec(DEFAULT_EXECUTOR_ID);
+
+  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
+
+  StandaloneMasterDetector slaveDetector(master.get());
+
+  slave::Flags flags = this->CreateSlaveFlags();
+  flags.resources = Some("cpus:2;mem:1024");
+
+  Try<PID<Slave> > slave = this->StartSlave(&exec, &slaveDetector, flags);
+  ASSERT_SOME(slave);
+
+  EXPECT_CALL(this->allocator, addFramework(_, _, _));
+
+  MockScheduler sched;
+  StandaloneMasterDetector schedulerDetector(master.get());
+  TestingMesosSchedulerDriver driver(&sched, &schedulerDetector);
+
+  EXPECT_CALL(sched, registered(&driver, _, _));
+
+  // The framework should be offered all of the resources on the slave
+  // since it is the only framework running.
+  EXPECT_CALL(sched, resourceOffers(&driver, OfferEq(2, 1024)))
+    .WillOnce(LaunchTasks(DEFAULT_EXECUTOR_INFO, 1, 1, 500, "*"))
+    .WillRepeatedly(DeclineOffers());
+
+  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _));
+
+  EXPECT_CALL(exec, registered(_, _, _, _));
+
+  EXPECT_CALL(exec, launchTask(_, _))
+    .WillOnce(SendStatusUpdateFromTask(TASK_RUNNING));
+
+  Future<TaskStatus> status;
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&status));
+
+  Future<Nothing> _statusUpdateAcknowledgement =
+    FUTURE_DISPATCH(_, &Slave::_statusUpdateAcknowledgement);
+
+  driver.start();
+
+  AWAIT_READY(status);
+
+  EXPECT_EQ(TASK_RUNNING, status.get().state());
+
+  // Make sure the slave handles status update acknowledgement so that
+  // it doesn't try to retry the update after master failover.
+  AWAIT_READY(_statusUpdateAcknowledgement);
+
+  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
+    .WillRepeatedly(DoDefault());
+
+  this->ShutdownMasters();
+  this->StopAllocator();
+
+  MockAllocatorProcess<TypeParam> allocator2;
+
+  EXPECT_CALL(allocator2, initialize(_, _, _));
+
+  Future<Nothing> addFramework;
+  EXPECT_CALL(allocator2, addFramework(_, _, _))
+    .WillOnce(DoAll(InvokeFrameworkAdded(&allocator2),
+                    FutureSatisfy(&addFramework)));
+
+  EXPECT_CALL(sched, registered(&driver, _, _));
+
+  Try<PID<Master> > master2 = this->StartMaster(&allocator2);
+  ASSERT_SOME(master2);
+
+  EXPECT_CALL(sched, disconnected(_));
+
+  // Inform the scheduler about the new master.
+  schedulerDetector.appoint(master2.get());
+
+  AWAIT_READY(addFramework);
+
+  EXPECT_CALL(allocator2, addSlave(_, _, _, _));
+
+  Future<vector<Offer> > resourceOffers2;
+  EXPECT_CALL(sched, resourceOffers(&driver, _))
+    .WillOnce(FutureArg<1>(&resourceOffers2));
+
+  // Inform the slave about the new master.
+  slaveDetector.appoint(master2.get());
+
+  AWAIT_READY(resourceOffers2);
+
+  // Since the task is still running on the slave, the framework
+  // should only be offered the resources not being used by the task.
+  EXPECT_THAT(resourceOffers2.get(), OfferEq(1, 524));
+
+  // Shut everything down.
+  EXPECT_CALL(allocator2, recoverResources(_, _, _, _))
+    .WillRepeatedly(DoDefault());
+
+  EXPECT_CALL(allocator2, deactivateFramework(_))
+    .Times(AtMost(1));
+
+  EXPECT_CALL(allocator2, removeFramework(_))
+    .Times(AtMost(1));
+
+  EXPECT_CALL(exec, shutdown(_))
+    .Times(AtMost(1));
+
+  driver.stop();
+  driver.join();
+
+  EXPECT_CALL(allocator2, removeSlave(_))
+    .Times(AtMost(1));
+
+  this->Shutdown();
+}
+
+
+// Checks that in the event of a master failure and the election of a
+// new master, if a slave reregisters before a framework that has
+// resources on reregisters, all used and unused resources are
+// accounted for correctly.
+TYPED_TEST(MasterAllocatorTest, SlaveReregistersFirst)
+{
+  EXPECT_CALL(this->allocator, initialize(_, _, _));
+
+  Try<PID<Master> > master = this->StartMaster(&this->allocator);
+  ASSERT_SOME(master);
+
+  MockExecutor exec(DEFAULT_EXECUTOR_ID);
+  StandaloneMasterDetector slaveDetector(master.get());
+
+  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
+
+  slave::Flags flags = this->CreateSlaveFlags();
+  flags.resources = Some("cpus:2;mem:1024");
+
+  Try<PID<Slave> > slave = this->StartSlave(&exec, &slaveDetector, flags);
+  ASSERT_SOME(slave);
+
+  MockScheduler sched;
+  StandaloneMasterDetector schedulerDetector(master.get());
+  TestingMesosSchedulerDriver driver(&sched, &schedulerDetector);
+
+  EXPECT_CALL(this->allocator, addFramework(_, _, _));
+
+  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _));
+
+  EXPECT_CALL(sched, registered(&driver, _, _));
+
+  // The framework should be offered all of the resources on the slave
+  // since it is the only framework running.
+  EXPECT_CALL(sched, resourceOffers(&driver, OfferEq(2, 1024)))
+    .WillOnce(LaunchTasks(DEFAULT_EXECUTOR_INFO, 1, 1, 500, "*"))
+    .WillRepeatedly(DeclineOffers());
+
+  EXPECT_CALL(exec, registered(_, _, _, _));
+
+  EXPECT_CALL(exec, launchTask(_, _))
+    .WillOnce(SendStatusUpdateFromTask(TASK_RUNNING));
+
+  Future<TaskStatus> status;
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&status));
+
+  Future<Nothing> _statusUpdateAcknowledgement =
+    FUTURE_DISPATCH(_, &Slave::_statusUpdateAcknowledgement);
+
+  driver.start();
+
+  AWAIT_READY(status);
+
+  EXPECT_EQ(TASK_RUNNING, status.get().state());
+
+  // Make sure the slave handles status update acknowledgement so that
+  // it doesn't try to retry the update after master failover.
+  AWAIT_READY(_statusUpdateAcknowledgement);
+
+  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
+    .WillRepeatedly(DoDefault());
+
+  this->ShutdownMasters();
+  this->StopAllocator();
+
+  MockAllocatorProcess<TypeParam> allocator2;
+
+  EXPECT_CALL(allocator2, initialize(_, _, _));
+
+  Future<Nothing> addSlave;
+  EXPECT_CALL(allocator2, addSlave(_, _, _, _))
+    .WillOnce(DoAll(InvokeSlaveAdded(&allocator2),
+                    FutureSatisfy(&addSlave)));
+
+  Try<PID<Master> > master2 = this->StartMaster(&allocator2);
+  ASSERT_SOME(master2);
+
+  // Inform the slave about the new master.
+  slaveDetector.appoint(master2.get());
+
+  AWAIT_READY(addSlave);
+
+  EXPECT_CALL(sched, disconnected(_));
+
+  EXPECT_CALL(sched, registered(&driver, _, _));
+
+  EXPECT_CALL(allocator2, addFramework(_, _, _));
+
+  Future<vector<Offer> > resourceOffers2;
+  EXPECT_CALL(sched, resourceOffers(&driver, _))
+    .WillOnce(FutureArg<1>(&resourceOffers2));
+
+  // Inform the scheduler about the new master.
+  schedulerDetector.appoint(master2.get());
+
+  AWAIT_READY(resourceOffers2);
+
+  // Since the task is still running on the slave, the framework
+  // should only be offered the resources not being used by the task.
+  EXPECT_THAT(resourceOffers2.get(), OfferEq(1, 524));
+
+  // Shut everything down.
+  EXPECT_CALL(allocator2, recoverResources(_, _, _, _))
+    .WillRepeatedly(DoDefault());
+
+  EXPECT_CALL(allocator2, deactivateFramework(_))
+    .Times(AtMost(1));
+
+  EXPECT_CALL(allocator2, removeFramework(_))
+    .Times(AtMost(1));
+
+  EXPECT_CALL(exec, shutdown(_))
+    .Times(AtMost(1));
+
+  driver.stop();
+  driver.join();
+
+  EXPECT_CALL(allocator2, removeSlave(_))
+    .Times(AtMost(1));
+
+  this->Shutdown();
+}


[05/11] mesos git commit: Added a TODO in the master for an allocator bug.

Posted by bm...@apache.org.
Added a TODO in the master for an allocator bug.

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


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

Branch: refs/heads/master
Commit: c1421aacd2188f121ef4c34b684671f8a87a16f5
Parents: 3ea7e9a
Author: Benjamin Mahler <be...@gmail.com>
Authored: Thu Dec 4 14:27:22 2014 -0800
Committer: Benjamin Mahler <be...@gmail.com>
Committed: Thu Dec 11 14:40:30 2014 -0800

----------------------------------------------------------------------
 src/master/master.cpp | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/c1421aac/src/master/master.cpp
----------------------------------------------------------------------
diff --git a/src/master/master.cpp b/src/master/master.cpp
index 1cf2074..9936980 100644
--- a/src/master/master.cpp
+++ b/src/master/master.cpp
@@ -4531,8 +4531,14 @@ void Master::removeSlave(Slave* slave)
 
   LOG(INFO) << "Removing slave " << *slave;
 
-  // We do this first, to make sure any of the resources recovered
-  // below (e.g., removeTask()) are ignored by the allocator.
+  // We want to remove the slave first, to avoid the allocator
+  // re-allocating the recovered resources.
+  //
+  // NOTE: Removing the slave is not sufficient for recovering the
+  // resources in the allocator, because the "Sorters" are updated
+  // only within recoverResources() (see MESOS-621). The calls to
+  // recoverResources() below are therefore required, even though
+  // the slave is already removed.
   allocator->removeSlave(slave->id);
 
   // Transition the tasks to lost and remove them, BUT do not send


[04/11] mesos git commit: Converted an initial DRF integration test to a unit test.

Posted by bm...@apache.org.
Converted an initial DRF integration test to a unit test.

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


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

Branch: refs/heads/master
Commit: 6cf1b016dae90292759dc2dc182fc25a39c58f91
Parents: e72037f
Author: Benjamin Mahler <be...@gmail.com>
Authored: Fri Dec 5 18:38:09 2014 -0800
Committer: Benjamin Mahler <be...@gmail.com>
Committed: Thu Dec 11 14:40:30 2014 -0800

----------------------------------------------------------------------
 src/Makefile.am                            |    7 +-
 src/tests/allocator_tests.cpp              | 2432 -----------------------
 src/tests/hierarchical_allocator_tests.cpp |  282 +++
 src/tests/master_allocator_tests.cpp       | 2179 ++++++++++++++++++++
 4 files changed, 2465 insertions(+), 2435 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/6cf1b016/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index 86161fe..6f132b5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1207,11 +1207,10 @@ libtestauthentication_la_CPPFLAGS = $(MESOS_CPPFLAGS)
 libtestauthentication_la_LDFLAGS = -release $(PACKAGE_VERSION) -shared
 
 mesos_tests_SOURCES =				\
-  tests/allocator_tests.cpp			\
   tests/attributes_tests.cpp			\
   tests/authentication_tests.cpp		\
   tests/authorization_tests.cpp		        \
-  tests/common/http_tests.cpp							\
+  tests/common/http_tests.cpp			\
   tests/composing_containerizer_tests.cpp       \
   tests/containerizer.cpp			\
   tests/containerizer_tests.cpp			\
@@ -1229,16 +1228,18 @@ mesos_tests_SOURCES =				\
   tests/files_tests.cpp				\
   tests/flags.cpp				\
   tests/gc_tests.cpp				\
+  tests/hierarchical_allocator_tests.cpp	\
   tests/isolator_tests.cpp			\
   tests/log_tests.cpp				\
   tests/logging_tests.cpp			\
   tests/main.cpp				\
+  tests/master_allocator_tests.cpp		\
   tests/master_authorization_tests.cpp		\
   tests/master_contender_detector_tests.cpp	\
   tests/master_slave_reconciliation_tests.cpp	\
   tests/master_tests.cpp			\
   tests/mesos.cpp				\
-  tests/metrics_tests.cpp				\
+  tests/metrics_tests.cpp			\
   tests/module.cpp				\
   tests/module_tests.cpp			\
   tests/monitor_tests.cpp			\

http://git-wip-us.apache.org/repos/asf/mesos/blob/6cf1b016/src/tests/allocator_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/allocator_tests.cpp b/src/tests/allocator_tests.cpp
deleted file mode 100644
index 8626362..0000000
--- a/src/tests/allocator_tests.cpp
+++ /dev/null
@@ -1,2432 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gmock/gmock.h>
-
-#include <map>
-#include <string>
-#include <vector>
-
-#include <mesos/executor.hpp>
-#include <mesos/scheduler.hpp>
-
-#include <process/clock.hpp>
-#include <process/future.hpp>
-#include <process/gmock.hpp>
-#include <process/pid.hpp>
-
-#include <stout/some.hpp>
-
-#include "master/allocator.hpp"
-#include "master/detector.hpp"
-#include "master/hierarchical_allocator_process.hpp"
-#include "master/master.hpp"
-
-#include "tests/containerizer.hpp"
-#include "tests/mesos.hpp"
-
-using namespace mesos;
-using namespace mesos::internal;
-using namespace mesos::internal::tests;
-
-using mesos::internal::master::allocator::Allocator;
-using mesos::internal::master::allocator::AllocatorProcess;
-using mesos::internal::master::allocator::HierarchicalDRFAllocatorProcess;
-
-using mesos::internal::master::Master;
-
-using mesos::internal::slave::Slave;
-
-using process::Clock;
-using process::Future;
-using process::PID;
-
-using std::map;
-using std::string;
-using std::vector;
-
-using testing::_;
-using testing::AtMost;
-using testing::DoAll;
-using testing::DoDefault;
-using testing::Eq;
-using testing::SaveArg;
-
-
-class DRFAllocatorTest : public MesosTest {};
-
-
-// Checks that the DRF allocator implements the DRF algorithm
-// correctly. The test accomplishes this by adding frameworks and
-// slaves one at a time to the allocator, making sure that each time
-// a new slave is added all of its resources are offered to whichever
-// framework currently has the smallest share. Checking for proper DRF
-// logic when resources are returned, frameworks exit, etc. is handled
-// by SorterTest.DRFSorter.
-TEST_F(DRFAllocatorTest, DRFAllocatorProcess)
-{
-  MockAllocatorProcess<HierarchicalDRFAllocatorProcess> allocator;
-
-  EXPECT_CALL(allocator, initialize(_, _, _));
-
-  master::Flags masterFlags = CreateMasterFlags();
-  masterFlags.roles = Some("role1,role2");
-  Try<PID<Master> > master = StartMaster(&allocator, masterFlags);
-  ASSERT_SOME(master);
-
-  slave::Flags flags1 = CreateSlaveFlags();
-  flags1.resources = Some("cpus:2;mem:1024;disk:0");
-
-  EXPECT_CALL(allocator, addSlave(_, _, _, _));
-
-  Try<PID<Slave> > slave1 = StartSlave(flags1);
-  ASSERT_SOME(slave1);
-  // Total cluster resources now cpus=2, mem=1024.
-
-  FrameworkInfo frameworkInfo1; // Bug in gcc 4.1.*, must assign on next line.
-  frameworkInfo1 = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo1.set_name("framework1");
-  frameworkInfo1.set_user("user1");
-  frameworkInfo1.set_role("role1");
-
-  MockScheduler sched1;
-  MesosSchedulerDriver driver1(
-      &sched1, frameworkInfo1, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(allocator, addFramework(_, _, _));
-
-  EXPECT_CALL(sched1, registered(_, _, _));
-
-  Future<vector<Offer> > offers1;
-  EXPECT_CALL(sched1, resourceOffers(_, _))
-    .WillOnce(FutureArg<1>(&offers1));
-
-  driver1.start();
-
-  AWAIT_READY(offers1);
-
-  // framework1 will be offered all of slave1's resources since it is
-  // the only framework running so far.
-  EXPECT_THAT(offers1.get(), OfferEq(2, 1024));
-  // user1 share = 1 (cpus=2, mem=1024)
-  //   framework1 share = 1
-
-  FrameworkInfo frameworkInfo2; // Bug in gcc 4.1.*, must assign on next line.
-  frameworkInfo2 = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo2.set_name("framework2");
-  frameworkInfo2.set_user("user2");
-  frameworkInfo2.set_role("role2");
-
-  MockScheduler sched2;
-  MesosSchedulerDriver driver2(
-      &sched2, frameworkInfo2, master.get(), DEFAULT_CREDENTIAL);
-
-  Future<Nothing> addFramework2;
-  EXPECT_CALL(allocator, addFramework(_, _, _))
-    .WillOnce(DoAll(InvokeFrameworkAdded(&allocator),
-      FutureSatisfy(&addFramework2)));
-
-  EXPECT_CALL(sched2, registered(_, _, _));
-
-  driver2.start();
-
-  AWAIT_READY(addFramework2);
-
-  slave::Flags flags2 = CreateSlaveFlags();
-  flags2.resources = Some("cpus:1;mem:512;disk:0");
-
-  EXPECT_CALL(allocator, addSlave(_, _, _, _));
-
-  Future<vector<Offer> > offers2;
-  EXPECT_CALL(sched2, resourceOffers(_, _))
-    .WillOnce(FutureArg<1>(&offers2));
-
-  Try<PID<Slave> > slave2 = StartSlave(flags2);
-  ASSERT_SOME(slave2);
-  // Total cluster resources now cpus=3, mem=1536.
-  // user1 share = 0.66 (cpus=2, mem=1024)
-  //   framework1 share = 1
-  // user2 share = 0
-  //   framework2 share = 0
-
-  AWAIT_READY(offers2);
-
-  // framework2 will be offered all of slave2's resources since user2
-  // has the lowest user share, and framework2 is its only framework.
-  EXPECT_THAT(offers2.get(), OfferEq(1, 512));
-  // user1 share = 0.67 (cpus=2, mem=1024)
-  //   framework1 share = 1
-  // user2 share = 0.33 (cpus=1, mem=512)
-  //   framework2 share = 1
-
-  slave::Flags flags3 = CreateSlaveFlags();
-  flags3.resources = Some("cpus:3;mem:2048;disk:0");
-
-  EXPECT_CALL(allocator, addSlave(_, _, _, _));
-
-  Future<vector<Offer> > offers3;
-  EXPECT_CALL(sched2, resourceOffers(_, _))
-    .WillOnce(FutureArg<1>(&offers3));
-
-  Try<PID<Slave> > slave3 = StartSlave(flags3);
-  ASSERT_SOME(slave3);
-  // Total cluster resources now cpus=6, mem=3584.
-  // user1 share = 0.33 (cpus=2, mem=1024)
-  //   framework1 share = 1
-  // user2 share = 0.16 (cpus=1, mem=512)
-  //   framework2 share = 1
-
-  AWAIT_READY(offers3);
-
-  // framework2 will be offered all of slave3's resources since user2
-  // has the lowest share.
-  EXPECT_THAT(offers3.get(), OfferEq(3, 2048));
-  // user1 share = 0.33 (cpus=2, mem=1024)
-  //   framework1 share = 1
-  // user2 share = 0.71 (cpus=4, mem=2560)
-  //   framework2 share = 1
-
-  FrameworkInfo frameworkInfo3; // Bug in gcc 4.1.*, must assign on next line.
-  frameworkInfo3 = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo3.set_name("framework3");
-  frameworkInfo3.set_user("user3");
-  frameworkInfo3.set_role("role1");
-
-  MockScheduler sched3;
-  MesosSchedulerDriver driver3(
-      &sched3, frameworkInfo3, master.get(), DEFAULT_CREDENTIAL);
-
-  Future<Nothing> addFramework3;
-  EXPECT_CALL(allocator, addFramework(_, _, _))
-    .WillOnce(DoAll(InvokeFrameworkAdded(&allocator),
-      FutureSatisfy(&addFramework3)));
-
-  EXPECT_CALL(sched3, registered(_, _, _));
-
-  driver3.start();
-
-  AWAIT_READY(addFramework3);
-
-  slave::Flags flags4 = CreateSlaveFlags();
-  flags4.resources = Some("cpus:4;mem:4096;disk:0");
-
-  EXPECT_CALL(allocator, addSlave(_, _, _, _));
-
-  Future<vector<Offer> > offers4;
-  EXPECT_CALL(sched3, resourceOffers(_, _))
-    .WillOnce(FutureArg<1>(&offers4));
-
-  Try<PID<Slave> > slave4 = StartSlave(flags4);
-  ASSERT_SOME(slave4);
-  // Total cluster resources now cpus=10, mem=7680.
-  // user1 share = 0.2 (cpus=2, mem=1024)
-  //   framework1 share = 1
-  //   framework3 share = 0
-  // user2 share = 0.4 (cpus=4, mem=2560)
-  //   framework2 share = 1
-
-  AWAIT_READY(offers4);
-
-  // framework3 will be offered all of slave4's resources since user1
-  // has the lowest user share, and framework3 has the lowest share of
-  // user1's frameworks.
-  EXPECT_THAT(offers4.get(), OfferEq(4, 4096));
-  // user1 share = 0.67 (cpus=6, mem=5120)
-  //   framework1 share = 0.33 (cpus=2, mem=1024)
-  //   framework3 share = 0.8 (cpus=4, mem=4096)
-  // user2 share = 0.4 (cpus=4, mem=2560)
-  //   framework2 share = 1
-
-  FrameworkInfo frameworkInfo4; // Bug in gcc 4.1.*, must assign on next line.
-  frameworkInfo4 = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo4.set_name("framework4");
-  frameworkInfo4.set_user("user1");
-  frameworkInfo4.set_role("role1");
-  MockScheduler sched4;
-  MesosSchedulerDriver driver4(
-      &sched4, frameworkInfo4, master.get(), DEFAULT_CREDENTIAL);
-
-  Future<Nothing> addFramework4;
-  EXPECT_CALL(allocator, addFramework(_, _, _))
-    .WillOnce(DoAll(InvokeFrameworkAdded(&allocator),
-                    FutureSatisfy(&addFramework4)));
-
-  EXPECT_CALL(sched4, registered(_, _, _));
-
-  driver4.start();
-
-  AWAIT_READY(addFramework4);
-
-  slave::Flags flags5 = CreateSlaveFlags();
-  flags5.resources = Some("cpus:1;mem:512;disk:0");
-
-  EXPECT_CALL(allocator, addSlave(_, _, _, _));
-
-  Future<vector<Offer> > offers5;
-  EXPECT_CALL(sched2, resourceOffers(_, _))
-    .WillOnce(FutureArg<1>(&offers5));
-
-  Try<PID<Slave> > slave5 = StartSlave(flags5);
-  ASSERT_SOME(slave5);
-  // Total cluster resources now cpus=11, mem=8192
-  // user1 share = 0.63 (cpus=6, mem=5120)
-  //   framework1 share = 0.33 (cpus=2, mem=1024)
-  //   framework3 share = 0.8 (cpus=4, mem=4096)
-  //   framework4 share = 0
-  // user2 share = 0.36 (cpus=4, mem=2560)
-  //   framework2 share = 1
-  AWAIT_READY(offers5);
-
-  // Even though framework4 doesn't have any resources, user2 has a
-  // lower share than user1, so framework2 receives slave4's resources.
-  EXPECT_THAT(offers5.get(), OfferEq(1, 512));
-
-  // Shut everything down.
-  EXPECT_CALL(allocator, recoverResources(_, _, _, _))
-    .WillRepeatedly(DoDefault());
-
-  EXPECT_CALL(allocator, deactivateFramework(_))
-    .Times(AtMost(4));
-
-  EXPECT_CALL(allocator, removeFramework(_))
-    .Times(AtMost(4));
-
-  driver1.stop();
-  driver1.join();
-
-  driver2.stop();
-  driver2.join();
-
-  driver3.stop();
-  driver3.join();
-
-  driver4.stop();
-  driver4.join();
-
-  EXPECT_CALL(allocator, removeSlave(_))
-    .Times(AtMost(5));
-
-  Shutdown();
-}
-
-
-// This test ensures that allocation is done per slave. This is done
-// by having 2 slaves and 2 frameworks and making sure each framework
-// gets only one slave's resources during an allocation.
-TEST_F(DRFAllocatorTest, PerSlaveAllocation)
-{
-  MockAllocatorProcess<HierarchicalDRFAllocatorProcess> allocator;
-
-  EXPECT_CALL(allocator, initialize(_, _, _));
-
-  // Start the master.
-  // NOTE: We set a high allocation interval, so that allocator does
-  // allocations only based on events (framework added, slave added)
-  // but not due to allocation interval. This lets us tightly control
-  // the test expectations.
-  master::Flags masterFlags = CreateMasterFlags();
-  masterFlags.roles = Some("role1,role2");
-  masterFlags.allocation_interval = Days(1);
-  Try<PID<Master> > master = StartMaster(&allocator, masterFlags);
-  ASSERT_SOME(master);
-
-  // Start slave 1.
-  slave::Flags flags1 = CreateSlaveFlags();
-  flags1.resources = Some("cpus:2;mem:1024;disk:0");
-
-  Future<Nothing> addSlave1;
-  EXPECT_CALL(allocator, addSlave(_, _, _, _))
-    .WillOnce(DoAll(InvokeSlaveAdded(&allocator),
-                    FutureSatisfy(&addSlave1)));
-
-  Try<PID<Slave> > slave1 = StartSlave(flags1);
-  ASSERT_SOME(slave1);
-
-  AWAIT_READY(addSlave1);
-
-  // Start slave 2.
-  slave::Flags flags2 = CreateSlaveFlags();
-  flags2.resources = Some("cpus:2;mem:1024;disk:0");
-
-  Future<Nothing> addSlave2;
-  EXPECT_CALL(allocator, addSlave(_, _, _, _))
-    .WillOnce(DoAll(InvokeSlaveAdded(&allocator),
-                    FutureSatisfy(&addSlave2)));
-
-  Try<PID<Slave> > slave2 = StartSlave(flags2);
-  ASSERT_SOME(slave2);
-
-  AWAIT_READY(addSlave2);
-
-  // Start framework 1.
-  FrameworkInfo frameworkInfo1; // Bug in gcc 4.1.*, must assign on next line.
-  frameworkInfo1 = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo1.set_name("framework1");
-  frameworkInfo1.set_user("user1");
-  frameworkInfo1.set_role("role1");
-
-  MockScheduler sched1;
-  MesosSchedulerDriver driver1(
-      &sched1, frameworkInfo1, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(allocator, addFramework(_, _, _));
-
-  EXPECT_CALL(sched1, registered(_, _, _));
-
-  Future<Nothing> recoverResources1;
-  Future<Nothing> recoverResources2;
-  EXPECT_CALL(allocator, recoverResources(_, _, _, _))
-    .WillOnce(DoAll(InvokeResourcesRecovered(&allocator),
-                    FutureSatisfy(&recoverResources1)))
-    .WillOnce(DoAll(InvokeResourcesRecovered(&allocator),
-                    FutureSatisfy(&recoverResources2)));
-
-  // Decline the offers immediately so that resources for both slaves
-  // are eligible for allocation to this and other frameworks.
-  Filters filters;
-  filters.set_refuse_seconds(0);
-  EXPECT_CALL(sched1, resourceOffers(_, _))
-    .WillOnce(DeclineOffers(filters));
-
-  driver1.start();
-
-  // Wait until the resources are returned to the allocator.
-  // NOTE: No allocations will be made after this point until a new
-  // framework registers because
-  // 1) 'recoverResources' does not trigger an allocation and
-  // 2) 'flags.allocation_interval' is set to a very high value.
-  AWAIT_READY(recoverResources1);
-  AWAIT_READY(recoverResources2);
-
-  // Start framework 2.
-  FrameworkInfo frameworkInfo2; // Bug in gcc 4.1.*, must assign on next line.
-  frameworkInfo2 = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo2.set_name("framework2");
-  frameworkInfo2.set_user("user2");
-  frameworkInfo2.set_role("role2");
-
-  MockScheduler sched2;
-  MesosSchedulerDriver driver2(
-      &sched2, frameworkInfo2, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(allocator, addFramework(_, _, _));
-
-  EXPECT_CALL(sched2, registered(_, _, _));
-
-  // Offers to framework 1.
-  Future<vector<Offer> > offers1;
-  EXPECT_CALL(sched1, resourceOffers(_, _))
-    .WillOnce(FutureArg<1>(&offers1));
-
-  // Offers to framework 2.
-  Future<vector<Offer> > offers2;
-  EXPECT_CALL(sched2, resourceOffers(_, _))
-    .WillOnce(FutureArg<1>(&offers2));
-
-  driver2.start();
-
-  // Now each framework should receive offers for one slave each.
-  AWAIT_READY(offers1);
-  EXPECT_THAT(offers1.get(), OfferEq(2, 1024));
-
-  AWAIT_READY(offers2);
-  EXPECT_THAT(offers2.get(), OfferEq(2, 1024));
-
-  // Shut everything down.
-  EXPECT_CALL(allocator, recoverResources(_, _, _, _))
-    .WillRepeatedly(DoDefault());
-
-  EXPECT_CALL(allocator, deactivateFramework(_))
-    .WillRepeatedly(DoDefault());
-
-  EXPECT_CALL(allocator, removeFramework(_))
-    .WillRepeatedly(DoDefault());
-
-  driver1.stop();
-  driver1.join();
-
-  driver2.stop();
-  driver2.join();
-
-  EXPECT_CALL(allocator, removeSlave(_))
-    .WillRepeatedly(DoDefault());
-
-  Shutdown();
-}
-
-
-// Helper that simply increments the value by reference.
-ACTION_P(Increment, value) { *value += 1; }
-
-
-// This test ensures that frameworks that have the same share get an
-// equal number of allocations over time (rather than the same
-// framework getting all the allocations because it's name is
-// lexicographically ordered first).
-TEST_F(DRFAllocatorTest, SameShareAllocations)
-{
-  MockAllocatorProcess<HierarchicalDRFAllocatorProcess> allocator;
-
-  EXPECT_CALL(allocator, initialize(_, _, _));
-
-  master::Flags masterFlags = CreateMasterFlags();
-  Try<PID<Master> > master = StartMaster(&allocator, masterFlags);
-  ASSERT_SOME(master);
-
-  // Start the first scheduler.
-  FrameworkInfo frameworkInfo1; // Bug in gcc 4.1.*, must assign on next line.
-  frameworkInfo1 = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo1.set_name("framework1");
-
-  MockScheduler sched1;
-  MesosSchedulerDriver driver1(
-      &sched1, frameworkInfo1, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(allocator, addFramework(_, _, _));
-
-  Future<Nothing> registered1;
-  EXPECT_CALL(sched1, registered(_, _, _))
-    .WillOnce(FutureSatisfy(&registered1));
-
-  driver1.start();
-
-  AWAIT_READY(registered1);
-
-  // Start the second scheduler.
-  FrameworkInfo frameworkInfo2; // Bug in gcc 4.1.*, must assign on next line.
-  frameworkInfo2 = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo2.set_name("framework2");
-
-  MockScheduler sched2;
-  MesosSchedulerDriver driver2(
-      &sched2, frameworkInfo2, master.get(), DEFAULT_CREDENTIAL);
-
-  // We need to retire this expectation on the first match because
-  // framework1 can match this expectation first in which case
-  // framework2 should be able to match the expectation above.
-  EXPECT_CALL(allocator, addFramework(_, _, _))
-    .RetiresOnSaturation();
-
-  Future<Nothing> registered2;
-  EXPECT_CALL(sched2, registered(_, _, _))
-    .WillOnce(FutureSatisfy(&registered2));
-
-  driver2.start();
-
-  AWAIT_READY(registered2);
-
-  // Set filter timeout to 0 so that both frameworks are eligible
-  // for allocation during every allocation interval.
-  Filters filters;
-  filters.set_refuse_seconds(0);
-
-  int allocations1 = 0;
-  EXPECT_CALL(sched1, resourceOffers(_, _))
-    .WillRepeatedly(DoAll(Increment(&allocations1),
-                          DeclineOffers(filters)));
-
-  int allocations2 = 0;
-  EXPECT_CALL(sched2, resourceOffers(_, _))
-    .WillRepeatedly(DoAll(Increment(&allocations2),
-                          DeclineOffers(filters)));
-
-  EXPECT_CALL(allocator, recoverResources(_, _, _, _))
-    .WillRepeatedly(DoDefault());
-
-  // Start the slave.
-  EXPECT_CALL(allocator, addSlave(_, _, _, _));
-
-  Try<PID<Slave> > slave = StartSlave();
-  ASSERT_SOME(slave);
-
-  // Continuously do allocations.
-  Clock::pause();
-  while(allocations1 + allocations2 < 10) {
-    Clock::advance(masterFlags.allocation_interval);
-    Clock::settle();
-  }
-
-  // Each framework should get equal number of allocations.
-  ASSERT_EQ(allocations1, allocations2);
-
-  Clock::resume();
-
-  driver1.stop();
-  driver1.join();
-
-  driver2.stop();
-  driver2.join();
-
-  Shutdown();
-}
-
-
-class ReservationAllocatorTest : public MesosTest {};
-
-
-// Checks that resources on a slave that are statically reserved to
-// a role are only offered to frameworks in that role.
-TEST_F(ReservationAllocatorTest, ReservedResources)
-{
-  MockAllocatorProcess<HierarchicalDRFAllocatorProcess> allocator;
-
-  EXPECT_CALL(allocator, initialize(_, _, _));
-
-  master::Flags masterFlags = CreateMasterFlags();
-  masterFlags.roles = Some("role1,role2,role3");
-  Try<PID<Master> > master = StartMaster(&allocator, masterFlags);
-
-  ASSERT_SOME(master);
-
-  Future<Nothing> addSlave;
-  EXPECT_CALL(allocator, addSlave(_, _, _, _))
-    .WillOnce(DoDefault())
-    .WillOnce(DoDefault())
-    .WillOnce(DoDefault())
-    .WillOnce(DoAll(InvokeSlaveAdded(&allocator),
-                    FutureSatisfy(&addSlave)));
-
-  slave::Flags flags1 = CreateSlaveFlags();
-  flags1.default_role = "role1";
-  flags1.resources = Some("cpus:2;mem:1024;disk:0");
-  Try<PID<Slave> > slave1 = StartSlave(flags1);
-  ASSERT_SOME(slave1);
-
-  slave::Flags flags2 = CreateSlaveFlags();
-  flags2.resources =
-    Some("cpus(role2):2;mem(role2):1024;cpus:1;mem:1024;disk:0");
-  Try<PID<Slave> > slave2 = StartSlave(flags2);
-  ASSERT_SOME(slave2);
-
-  slave::Flags flags3 = CreateSlaveFlags();
-  flags3.default_role = "role3";
-  flags3.resources = Some("cpus:4;mem:4096;disk:0");
-  Try<PID<Slave> > slave3 = StartSlave(flags3);
-  ASSERT_SOME(slave3);
-
-  // This slave's resources should never be allocated,
-  // since there is no framework for role4.
-  slave::Flags flags4 = CreateSlaveFlags();
-  flags4.default_role = "role4";
-  flags4.resources = Some("cpus:1;mem:1024;disk:0");
-  Try<PID<Slave> > slave4 = StartSlave(flags4);
-  ASSERT_SOME(slave4);
-
-  AWAIT_READY(addSlave);
-
-  FrameworkInfo frameworkInfo1; // Bug in gcc 4.1.*, must assign on next line.
-  frameworkInfo1 = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo1.set_user("user1");
-  frameworkInfo1.set_name("framework1");
-  frameworkInfo1.set_role("role1");
-  MockScheduler sched1;
-  MesosSchedulerDriver driver1(
-      &sched1, frameworkInfo1, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(allocator, addFramework(_, _, _));
-
-  EXPECT_CALL(sched1, registered(_, _, _));
-
-  Future<Nothing> resourceOffers1;
-  EXPECT_CALL(sched1, resourceOffers(_, OfferEq(3, 2048)))
-    .WillOnce(FutureSatisfy(&resourceOffers1));
-
-  driver1.start();
-
-  // framework1 gets all the resources from slave1, plus the
-  // unreserved resources on slave2.
-  AWAIT_READY(resourceOffers1);
-
-  FrameworkInfo frameworkInfo2; // Bug in gcc 4.1.*, must assign on next line.
-  frameworkInfo2 = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo2.set_user("user2");
-  frameworkInfo2.set_name("framework2");
-  frameworkInfo2.set_role("role2");
-  MockScheduler sched2;
-  MesosSchedulerDriver driver2(
-      &sched2, frameworkInfo2, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(allocator, addFramework(_, _, _));
-
-  EXPECT_CALL(sched2, registered(_, _, _));
-
-  Future<Nothing> resourceOffers2;
-  EXPECT_CALL(sched2, resourceOffers(_, OfferEq(2, 1024)))
-    .WillOnce(FutureSatisfy(&resourceOffers2));
-
-  driver2.start();
-
-  // framework2 gets all of its reserved resources on slave2.
-  AWAIT_READY(resourceOffers2);
-
-  FrameworkInfo frameworkInfo3; // Bug in gcc 4.1.*, must assign on next line.
-  frameworkInfo3 = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo3.set_user("user2");
-  frameworkInfo3.set_name("framework3");
-  frameworkInfo3.set_role("role3");
-  MockScheduler sched3;
-  MesosSchedulerDriver driver3(
-      &sched3, frameworkInfo3, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(allocator, addFramework(_, _, _));
-
-  EXPECT_CALL(sched3, registered(_, _, _));
-
-  Future<Nothing> resourceOffers3;
-  EXPECT_CALL(sched3, resourceOffers(_, OfferEq(4, 4096)))
-    .WillOnce(FutureSatisfy(&resourceOffers3));
-
-  driver3.start();
-
-  // framework3 gets all the resources from slave3.
-  AWAIT_READY(resourceOffers3);
-
-  slave::Flags flags5 = CreateSlaveFlags();
-  flags5.default_role = "role1";
-  flags5.resources = Some("cpus:1;mem:512;disk:0");
-
-  EXPECT_CALL(allocator, addSlave(_, _, _, _));
-
-  Future<Nothing> resourceOffers4;
-  EXPECT_CALL(sched1, resourceOffers(_, OfferEq(1, 512)))
-    .WillOnce(FutureSatisfy(&resourceOffers4));
-
-  Try<PID<Slave> > slave5 = StartSlave(flags5);
-  ASSERT_SOME(slave5);
-
-  // framework1 gets all the resources from slave5.
-  AWAIT_READY(resourceOffers4);
-
-  // Shut everything down.
-  EXPECT_CALL(allocator, recoverResources(_, _, _, _))
-    .WillRepeatedly(DoDefault());
-
-  EXPECT_CALL(allocator, deactivateFramework(_))
-    .Times(AtMost(3));
-
-  EXPECT_CALL(allocator, removeFramework(_))
-    .Times(AtMost(3));
-
-  driver3.stop();
-  driver2.stop();
-  driver1.stop();
-
-  EXPECT_CALL(allocator, removeSlave(_))
-    .Times(AtMost(5));
-
-  this->Shutdown();
-}
-
-
-// Checks that statically allocated resources that are returned
-// either unused or after a task finishes are statically reallocated
-// appropriately.
-TEST_F(ReservationAllocatorTest, ResourcesReturned)
-{
-  MockAllocatorProcess<HierarchicalDRFAllocatorProcess> allocator;
-
-  EXPECT_CALL(allocator, initialize(_, _, _));
-
-  master::Flags masterFlags = CreateMasterFlags();
-  masterFlags.roles = Some("role1,role2");
-  masterFlags.allocation_interval = Milliseconds(50);
-  Try<PID<Master> > master = StartMaster(&allocator, masterFlags);
-
-  ASSERT_SOME(master);
-
-  MockExecutor exec(DEFAULT_EXECUTOR_ID);
-
-  EXPECT_CALL(allocator, addSlave(_, _, _, _))
-    .Times(2);
-
-  Future<Nothing> addSlave1 = FUTURE_DISPATCH(
-      allocator.real, &AllocatorProcess::addSlave);
-
-  slave::Flags flags1 = CreateSlaveFlags();
-  flags1.resources = Some("cpus(role1):1;mem(role1):200;cpus(role2):2;"
-                          "mem(role2):600;cpus:1;mem:200;disk:0");
-  Try<PID<Slave> > slave1 = StartSlave(&exec, flags1);
-  ASSERT_SOME(slave1);
-
-  // Wait until allocator has added slave1.
-  AWAIT_READY(addSlave1);
-
-  Future<Nothing> addSlave2 = FUTURE_DISPATCH(
-      allocator.real, &AllocatorProcess::addSlave);
-
-  // This slave's resources will never be offered to anyone,
-  // because there is no framework with role3.
-  slave::Flags flags2 = CreateSlaveFlags();
-  flags2.resources = Some("cpus(role3):4;mem(role3):1024;disk:0");
-  Try<PID<Slave> > slave2 = StartSlave(flags2);
-  ASSERT_SOME(slave2);
-
-  // Wait until allocator has added slave2.
-  AWAIT_READY(addSlave2);
-
-  FrameworkInfo frameworkInfo1; // Bug in gcc 4.1.*, must assign on next line.
-  frameworkInfo1 = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo1.set_user("user1");
-  frameworkInfo1.set_name("framework1");
-  frameworkInfo1.set_role("role1");
-  FrameworkID frameworkId1;
-
-  MockScheduler sched1;
-  MesosSchedulerDriver driver1(
-      &sched1, frameworkInfo1, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(allocator, addFramework(_, _, _));
-
-  EXPECT_CALL(sched1, registered(_, _, _));
-
-  // Initially, framework1 should be offered all of the resources on
-  // slave1 that aren't reserved to role2.
-  EXPECT_CALL(sched1, resourceOffers(_, OfferEq(2, 400)))
-    .WillOnce(LaunchTasks(DEFAULT_EXECUTOR_INFO, 1, 1, 100, "role1"));
-
-  EXPECT_CALL(allocator, recoverResources(_, _, _, _))
-    .WillOnce(InvokeResourcesRecoveredWithFilters(&allocator, 0));
-
-  EXPECT_CALL(exec, registered(_, _, _, _));
-
-  ExecutorDriver* execDriver;
-  TaskInfo taskInfo;
-  Future<Nothing> launchTask;
-  EXPECT_CALL(exec, launchTask(_, _))
-    .WillOnce(DoAll(SaveArg<0>(&execDriver),
-                    SaveArg<1>(&taskInfo),
-                    SendStatusUpdateFromTask(TASK_RUNNING),
-                    FutureSatisfy(&launchTask)));
-
-  EXPECT_CALL(sched1, statusUpdate(_, _))
-    .WillRepeatedly(DoDefault());
-
-  // After framework1's task launches, it should be offered all resources
-  // not dedicatd to role2 and not used by its task.
-  Future<Nothing> resourceOffers1;
-  EXPECT_CALL(sched1, resourceOffers(_, OfferEq(1, 300)))
-    .WillOnce(FutureSatisfy(&resourceOffers1));
-
-  driver1.start();
-
-  AWAIT_READY(launchTask);
-
-  AWAIT_READY(resourceOffers1);
-
-  FrameworkInfo frameworkInfo2; // Bug in gcc 4.1.*, must assign on next line.
-  frameworkInfo2 = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo2.set_user("user2");
-  frameworkInfo2.set_name("framework2");
-  frameworkInfo2.set_role("role2");
-  FrameworkID frameworkId2;
-
-  MockScheduler sched2;
-  MesosSchedulerDriver driver2(
-      &sched2, frameworkInfo2, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(allocator, addFramework(_, _, _));
-
-  EXPECT_CALL(sched2, registered(_, _, _));
-
-  // The first time framework2 is allocated to, it should be offered
-  // all of the resources on slave1 that are reserved to role2.
-  Future<Nothing> resourceOffers2;
-  EXPECT_CALL(sched2, resourceOffers(_, OfferEq(2, 600)))
-    .WillOnce(FutureSatisfy(&resourceOffers2));
-
-  driver2.start();
-
-  AWAIT_READY(resourceOffers2);
-
-  TaskStatus status;
-  status.mutable_task_id()->MergeFrom(taskInfo.task_id());
-  status.set_state(TASK_FINISHED);
-
-  EXPECT_CALL(allocator, recoverResources(_, _, _, _));
-
-  // After the task finishes, its resources should be reoffered to
-  // framework1.
-  Future<Nothing> resourceOffers3;
-  EXPECT_CALL(sched1, resourceOffers(_, OfferEq(1, 100)))
-    .WillOnce(FutureSatisfy(&resourceOffers3));
-
-  execDriver->sendStatusUpdate(status);
-
-  AWAIT_READY(resourceOffers3);
-
-  // Shut everything down.
-  EXPECT_CALL(allocator, recoverResources(_, _, _, _))
-    .WillRepeatedly(DoDefault());
-
-  EXPECT_CALL(allocator, deactivateFramework(_))
-    .Times(AtMost(2));
-
-  EXPECT_CALL(allocator, removeFramework(_))
-    .Times(AtMost(2));
-
-  Future<Nothing> shutdown;
-  EXPECT_CALL(exec, shutdown(_))
-    .WillOnce(FutureSatisfy(&shutdown));
-
-  driver2.stop();
-  driver1.stop();
-
-  AWAIT_READY(shutdown); // Ensures MockExecutor can be deallocated.
-
-  EXPECT_CALL(allocator, removeSlave(_))
-    .Times(AtMost(2));
-
-  this->Shutdown();
-}
-
-
-template <typename T>
-class AllocatorTest : public MesosTest
-{
-protected:
-  void StopAllocator()
-  {
-    process::terminate(allocator.real);
-    process::wait(allocator.real);
-  }
-
-  MockAllocatorProcess<T> allocator;
-};
-
-
-// Causes all TYPED_TEST(AllocatorTest, ...) to be run for
-// each of the specified Allocator classes.
-TYPED_TEST_CASE(AllocatorTest, AllocatorTypes);
-
-
-// Checks that in a cluster with one slave and one framework, all of
-// the slave's resources are offered to the framework.
-TYPED_TEST(AllocatorTest, MockAllocator)
-{
-  EXPECT_CALL(this->allocator, initialize(_, _, _));
-
-  Try<PID<Master> > master = this->StartMaster(&this->allocator);
-  ASSERT_SOME(master);
-
-  slave::Flags flags = this->CreateSlaveFlags();
-  flags.resources = Some("cpus:2;mem:1024;disk:0");
-
-  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
-
-  Try<PID<Slave> > slave = this->StartSlave(flags);
-  ASSERT_SOME(slave);
-
-  MockScheduler sched;
-  MesosSchedulerDriver driver(
-      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(this->allocator, addFramework(_, _, _));
-
-  EXPECT_CALL(sched, registered(_, _, _));
-
-  // The framework should be offered all of the resources on the slave
-  // since it is the only framework in the cluster.
-  Future<Nothing> resourceOffers;
-  EXPECT_CALL(sched, resourceOffers(_, OfferEq(2, 1024)))
-    .WillOnce(FutureSatisfy(&resourceOffers));
-
-  driver.start();
-
-  AWAIT_READY(resourceOffers);
-
-  // Shut everything down.
-  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
-    .WillRepeatedly(DoDefault());
-
-  EXPECT_CALL(this->allocator, deactivateFramework(_))
-    .Times(AtMost(1));
-
-  EXPECT_CALL(this->allocator, removeFramework(_))
-    .Times(AtMost(1));
-
-  driver.stop();
-  driver.join();
-
-  EXPECT_CALL(this->allocator, removeSlave(_))
-    .Times(AtMost(1));
-
-  this->Shutdown();
-}
-
-
-// Checks that when a task is launched with fewer resources than what
-// the offer was for, the resources that are returned unused are
-// reoffered appropriately.
-TYPED_TEST(AllocatorTest, ResourcesUnused)
-{
-  EXPECT_CALL(this->allocator, initialize(_, _, _));
-
-  Try<PID<Master> > master = this->StartMaster(&this->allocator);
-  ASSERT_SOME(master);
-
-  MockExecutor exec(DEFAULT_EXECUTOR_ID);
-
-  slave::Flags flags1 = this->CreateSlaveFlags();
-  flags1.resources = Some("cpus:2;mem:1024");
-
-  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
-
-  Try<PID<Slave> > slave1 = this->StartSlave(&exec, flags1);
-  ASSERT_SOME(slave1);
-
-  MockScheduler sched1;
-  MesosSchedulerDriver driver1(
-      &sched1, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(this->allocator, addFramework(_, _, _));
-
-  EXPECT_CALL(sched1, registered(_, _, _));
-
-  // We decline offers that we aren't expecting so that the resources
-  // get aggregated. Note that we need to do this _first_ and
-  // _separate_ from the expectation below so that this expectation is
-  // checked last and matches all possible offers.
-  EXPECT_CALL(sched1, resourceOffers(_, _))
-    .WillRepeatedly(DeclineOffers());
-
-  // The first offer will contain all of the slave's resources, since
-  // this is the only framework running so far. Launch a task that
-  // uses less than that to leave some resources unused.
-  EXPECT_CALL(sched1, resourceOffers(_, OfferEq(2, 1024)))
-    .WillOnce(LaunchTasks(DEFAULT_EXECUTOR_INFO, 1, 1, 512, "*"));
-
-  Future<Nothing> recoverResources;
-  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
-    .WillOnce(DoAll(InvokeResourcesRecovered(&this->allocator),
-                    FutureSatisfy(&recoverResources)));
-
-  EXPECT_CALL(exec, registered(_, _, _, _));
-
-  Future<Nothing> launchTask;
-  EXPECT_CALL(exec, launchTask(_, _))
-    .WillOnce(FutureSatisfy(&launchTask));
-
-  driver1.start();
-
-  AWAIT_READY(launchTask);
-
-  // We need to wait until the allocator knows about the unused
-  // resources to start the second framework so that we get the
-  // expected offer.
-  AWAIT_READY(recoverResources);
-
-  FrameworkInfo frameworkInfo2; // Bug in gcc 4.1.*, must assign on next line.
-  frameworkInfo2 = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo2.set_user("user2");
-  frameworkInfo2.set_name("framework2");
-
-  MockScheduler sched2;
-  MesosSchedulerDriver driver2(
-      &sched2, frameworkInfo2, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(this->allocator, addFramework(_, _, _));
-
-  EXPECT_CALL(sched2, registered(_, _, _));
-
-  // We should expect that framework2 gets offered all of the
-  // resources on the slave not being used by the launched task.
-  Future<Nothing> resourceOffers;
-  EXPECT_CALL(sched2, resourceOffers(_, OfferEq(1, 512)))
-    .WillOnce(FutureSatisfy(&resourceOffers));
-
-  driver2.start();
-
-  AWAIT_READY(resourceOffers);
-
-  // Shut everything down.
-  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
-    .WillRepeatedly(DoDefault());
-
-  EXPECT_CALL(this->allocator, deactivateFramework(_))
-    .Times(AtMost(2));
-
-  EXPECT_CALL(this->allocator, removeFramework(_))
-    .Times(AtMost(2));
-
-  Future<Nothing> shutdown;
-  EXPECT_CALL(exec, shutdown(_))
-    .WillOnce(FutureSatisfy(&shutdown));
-
-  driver1.stop();
-  driver1.join();
-
-  driver2.stop();
-  driver2.join();
-
-  AWAIT_READY(shutdown); // Ensures MockExecutor can be deallocated.
-
-  EXPECT_CALL(this->allocator, removeSlave(_))
-    .Times(AtMost(1));
-
-  this->Shutdown();
-}
-
-
-// Tests the situation where a removeFramework call is dispatched
-// while we're doing an allocation to that framework, so that
-// recoverResources is called for an already removed framework.
-TYPED_TEST(AllocatorTest, OutOfOrderDispatch)
-{
-  EXPECT_CALL(this->allocator, initialize(_, _, _));
-
-  Try<PID<Master> > master = this->StartMaster(&this->allocator);
-  ASSERT_SOME(master);
-
-  slave::Flags flags1 = this->CreateSlaveFlags();
-  flags1.resources = Some("cpus:2;mem:1024");
-
-  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
-
-  Try<PID<Slave> > slave1 = this->StartSlave(flags1);
-  ASSERT_SOME(slave1);
-
-  FrameworkInfo frameworkInfo1; // Bug in gcc 4.1.*, must assign on next line.
-  frameworkInfo1 = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo1.set_user("user1");
-  frameworkInfo1.set_name("framework1");
-
-  MockScheduler sched1;
-  MesosSchedulerDriver driver1(
-      &sched1, frameworkInfo1, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(this->allocator, addFramework(_, Eq(frameworkInfo1), _))
-    .WillOnce(InvokeFrameworkAdded(&this->allocator));
-
-  FrameworkID frameworkId1;
-  EXPECT_CALL(sched1, registered(_, _, _))
-    .WillOnce(SaveArg<1>(&frameworkId1));
-
-  // All of the slave's resources should be offered to start.
-  Future<Nothing> resourceOffers;
-  EXPECT_CALL(sched1, resourceOffers(_, OfferEq(2, 1024)))
-    .WillOnce(FutureSatisfy(&resourceOffers));
-
-  driver1.start();
-
-  AWAIT_READY(resourceOffers);
-
-  // TODO(benh): I don't see why we want to "catch" (i.e., block) this
-  // recoverResources call. It seems like we want this one to
-  // properly be executed and later we want to _inject_ a
-  // recoverResources to simulate the code in Master::offer after a
-  // framework has terminated or is inactive.
-  FrameworkID frameworkId;
-  SlaveID slaveId;
-  Resources savedResources;
-  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
-    // "Catches" the recoverResources call from the master, so
-    // that it doesn't get processed until we redispatch it after
-    // the removeFramework trigger.
-    .WillOnce(DoAll(SaveArg<0>(&frameworkId),
-                    SaveArg<1>(&slaveId),
-                    SaveArg<2>(&savedResources)));
-
-  EXPECT_CALL(this->allocator, deactivateFramework(_));
-
-  Future<Nothing> removeFramework;
-  EXPECT_CALL(this->allocator, removeFramework(Eq(frameworkId1)))
-    .WillOnce(DoAll(InvokeFrameworkRemoved(&this->allocator),
-                    FutureSatisfy(&removeFramework)));
-
-  driver1.stop();
-  driver1.join();
-
-  AWAIT_READY(removeFramework);
-
-  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
-    .WillOnce(DoDefault());
-
-  // Re-dispatch the recoverResources call which we "caught"
-  // earlier now that the framework has been removed, to test
-  // that recovering resources from a removed framework works.
-  this->allocator.recoverResources(
-      frameworkId,
-      slaveId,
-      savedResources,
-      None());
-
-  // TODO(benh): Seems like we should wait for the above
-  // recoverResources to be executed.
-
-  FrameworkInfo frameworkInfo2; // Bug in gcc 4.1.*, must assign on next line.
-  frameworkInfo2 = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo2.set_user("user2");
-  frameworkInfo2.set_name("framework2");
-
-  MockScheduler sched2;
-  MesosSchedulerDriver driver2(
-      &sched2, frameworkInfo2, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(this->allocator, addFramework(_, Eq(frameworkInfo2), _))
-    .WillOnce(InvokeFrameworkAdded(&this->allocator));
-
-  FrameworkID frameworkId2;
-  EXPECT_CALL(sched2, registered(_, _, _))
-    .WillOnce(SaveArg<1>(&frameworkId2));
-
-  // All of the slave's resources should be offered since no other
-  // frameworks should be running.
-  EXPECT_CALL(sched2, resourceOffers(_, OfferEq(2, 1024)))
-    .WillOnce(FutureSatisfy(&resourceOffers));
-
-  driver2.start();
-
-  AWAIT_READY(resourceOffers);
-
-  // Shut everything down.
-  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
-    .WillRepeatedly(DoDefault());
-
-  EXPECT_CALL(this->allocator, deactivateFramework(_))
-    .Times(AtMost(1));
-
-  EXPECT_CALL(this->allocator, removeFramework(Eq(frameworkId2)))
-    .Times(AtMost(1));
-
-  driver2.stop();
-  driver2.join();
-
-  EXPECT_CALL(this->allocator, removeSlave(_))
-    .Times(AtMost(1));
-
-  this->Shutdown();
-}
-
-
-// Checks that if a framework launches a task and then fails over to a
-// new scheduler, the task's resources are not reoffered as long as it
-// is running.
-TYPED_TEST(AllocatorTest, SchedulerFailover)
-{
-  EXPECT_CALL(this->allocator, initialize(_, _, _));
-
-  Try<PID<Master> > master = this->StartMaster(&this->allocator);
-  ASSERT_SOME(master);
-
-  MockExecutor exec(DEFAULT_EXECUTOR_ID);
-
-  slave::Flags flags = this->CreateSlaveFlags();
-  flags.resources = Some("cpus:3;mem:1024");
-
-  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
-
-  Try<PID<Slave> > slave = this->StartSlave(&exec, flags);
-  ASSERT_SOME(slave);
-
-  FrameworkInfo frameworkInfo1; // Bug in gcc 4.1.*, must assign on next line.
-  frameworkInfo1 = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo1.set_name("framework1");
-  frameworkInfo1.set_user("user1");
-  frameworkInfo1.set_failover_timeout(10);
-
-  // Launch the first (i.e., failing) scheduler.
-  MockScheduler sched1;
-  MesosSchedulerDriver driver1(
-      &sched1, frameworkInfo1, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(this->allocator, addFramework(_, _, _));
-
-  FrameworkID frameworkId;
-  EXPECT_CALL(sched1, registered(&driver1, _, _))
-    .WillOnce(SaveArg<1>(&frameworkId));
-
-  // We decline offers that we aren't expecting so that the resources
-  // get aggregated. Note that we need to do this _first_ and
-  // _separate_ from the expectation below so that this expectation is
-  // checked last and matches all possible offers.
-  EXPECT_CALL(sched1, resourceOffers(_, _))
-    .WillRepeatedly(DeclineOffers()); // For subsequent offers.
-
-  // Initially, all of slave1's resources are avaliable.
-  EXPECT_CALL(sched1, resourceOffers(_, OfferEq(3, 1024)))
-    .WillOnce(LaunchTasks(DEFAULT_EXECUTOR_INFO, 1, 1, 256, "*"));
-
-  // We don't filter the unused resources to make sure that
-  // they get offered to the framework as soon as it fails over.
-  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
-    .WillOnce(InvokeResourcesRecoveredWithFilters(&this->allocator, 0))
-    // For subsequent offers.
-    .WillRepeatedly(InvokeResourcesRecoveredWithFilters(&this->allocator, 0));
-
-  EXPECT_CALL(exec, registered(_, _, _, _));
-
-  Future<Nothing> launchTask;
-  EXPECT_CALL(exec, launchTask(_, _))
-    .WillOnce(FutureSatisfy(&launchTask));
-
-  driver1.start();
-
-  // Ensures that the task has been completely launched
-  // before we have the framework fail over.
-  AWAIT_READY(launchTask);
-
-  // When we shut down the first framework, we don't want it to tell
-  // the master it's shutting down so that the master will wait to see
-  // if it fails over.
-  DROP_PROTOBUFS(UnregisterFrameworkMessage(), _, _);
-
-  Future<Nothing> deactivateFramework;
-  EXPECT_CALL(this->allocator, deactivateFramework(_))
-    .WillOnce(DoAll(InvokeFrameworkDeactivated(&this->allocator),
-                    FutureSatisfy(&deactivateFramework)));
-
-  driver1.stop();
-
-  AWAIT_READY(deactivateFramework);
-
-  FrameworkInfo frameworkInfo2; // Bug in gcc 4.1.*, must assign on next line.
-  frameworkInfo2 = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo2.mutable_id()->MergeFrom(frameworkId);
-
-  // Now launch the second (i.e., failover) scheduler using the
-  // framework id recorded from the first scheduler.
-  MockScheduler sched2;
-  MesosSchedulerDriver driver2(
-      &sched2, frameworkInfo2, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(this->allocator, activateFramework(_));
-
-  EXPECT_CALL(sched2, registered(_, frameworkId, _));
-
-  // Even though the scheduler failed over, the 1 cpu, 256 mem
-  // task that it launched earlier should still be running, so
-  // only 2 cpus and 768 mem are available.
-  Future<Nothing> resourceOffers;
-  EXPECT_CALL(sched2, resourceOffers(_, OfferEq(2, 768)))
-    .WillOnce(FutureSatisfy(&resourceOffers));
-
-  driver2.start();
-
-  AWAIT_READY(resourceOffers);
-
-  // Shut everything down.
-  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
-    .WillRepeatedly(DoDefault());
-
-  EXPECT_CALL(this->allocator, deactivateFramework(_))
-    .Times(AtMost(1));
-
-  EXPECT_CALL(this->allocator, removeFramework(_))
-    .Times(AtMost(1));
-
-  EXPECT_CALL(exec, shutdown(_))
-    .Times(AtMost(1));
-
-  driver2.stop();
-  driver2.join();
-
-  EXPECT_CALL(this->allocator, removeSlave(_))
-    .Times(AtMost(1));
-
-  this->Shutdown();
-}
-
-
-// Checks that if a framework launches a task and then the framework
-// is killed, the tasks resources are returned and reoffered correctly.
-TYPED_TEST(AllocatorTest, FrameworkExited)
-{
-  EXPECT_CALL(this->allocator, initialize(_, _, _));
-
-  master::Flags masterFlags = this->CreateMasterFlags();
-  masterFlags.allocation_interval = Milliseconds(50);
-  Try<PID<Master> > master = this->StartMaster(&this->allocator, masterFlags);
-  ASSERT_SOME(master);
-
-  ExecutorInfo executor1; // Bug in gcc 4.1.*, must assign on next line.
-  executor1 = CREATE_EXECUTOR_INFO("executor-1", "exit 1");
-
-  ExecutorInfo executor2; // Bug in gcc 4.1.*, must assign on next line.
-  executor2 = CREATE_EXECUTOR_INFO("executor-2", "exit 1");
-
-  MockExecutor exec1(executor1.executor_id());
-  MockExecutor exec2(executor2.executor_id());
-
-  hashmap<ExecutorID, Executor*> execs;
-  execs[executor1.executor_id()] = &exec1;
-  execs[executor2.executor_id()] = &exec2;
-
-  TestContainerizer containerizer(execs);
-
-  slave::Flags flags = this->CreateSlaveFlags();
-
-  flags.resources = Some("cpus:3;mem:1024");
-
-  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
-
-  Try<PID<Slave> > slave = this->StartSlave(&containerizer, flags);
-  ASSERT_SOME(slave);
-
-  MockScheduler sched1;
-  MesosSchedulerDriver driver1(
-      &sched1, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(this->allocator, addFramework(_, _, _));
-
-  EXPECT_CALL(sched1, registered(_, _, _));
-
-  // We decline offers that we aren't expecting so that the resources
-  // get aggregated. Note that we need to do this _first_ and
-  // _separate_ from the expectation below so that this expectation is
-  // checked last and matches all possible offers.
-  EXPECT_CALL(sched1, resourceOffers(_, _))
-    .WillRepeatedly(DeclineOffers());
-
-  // The first time the framework is offered resources, all of the
-  // cluster's resources should be avaliable.
-  EXPECT_CALL(sched1, resourceOffers(_, OfferEq(3, 1024)))
-    .WillOnce(LaunchTasks(executor1, 1, 2, 512, "*"));
-
-  // The framework does not use all the resources.
-  Future<Nothing> recoverResources;
-  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
-    .WillOnce(DoAll(InvokeResourcesRecovered(&this->allocator),
-                    FutureSatisfy(&recoverResources)));
-
-  EXPECT_CALL(exec1, registered(_, _, _, _));
-
-  Future<Nothing> launchTask;
-  EXPECT_CALL(exec1, launchTask(_, _))
-    .WillOnce(FutureSatisfy(&launchTask));
-
-  driver1.start();
-
-  // Ensures that framework 1's task is completely launched
-  // before we kill the framework to test if its resources
-  // are recovered correctly.
-  AWAIT_READY(launchTask);
-
-  // We need to wait until the allocator knows about the unused
-  // resources to start the second framework so that we get the
-  // expected offer.
-  AWAIT_READY(recoverResources);
-
-  MockScheduler sched2;
-  MesosSchedulerDriver driver2(
-      &sched2, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(this->allocator, addFramework(_, _, _));
-
-  EXPECT_CALL(sched2, registered(_, _, _));
-
-  // We decline offers that we aren't expecting so that the resources
-  // get aggregated. Note that we need to do this _first_ and
-  // _separate_ from the expectation below so that this expectation is
-  // checked last and matches all possible offers.
-  EXPECT_CALL(sched2, resourceOffers(_, _))
-    .WillRepeatedly(DeclineOffers());
-
-  // The first time sched2 gets an offer, framework 1 has a task
-  // running with 2 cpus and 512 mem, leaving 1 cpu and 512 mem.
-  EXPECT_CALL(sched2, resourceOffers(_, OfferEq(1, 512)))
-    .WillOnce(LaunchTasks(executor2, 1, 1, 256, "*"));
-
-  // The framework 2 does not use all the resources.
-  Future<Nothing> recoverResources2;
-  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
-    .WillOnce(DoAll(InvokeResourcesRecovered(&this->allocator),
-                    FutureSatisfy(&recoverResources2)));
-
-  EXPECT_CALL(exec2, registered(_, _, _, _));
-
-  EXPECT_CALL(exec2, launchTask(_, _))
-    .WillOnce(FutureSatisfy(&launchTask));
-
-  driver2.start();
-
-  AWAIT_READY(launchTask);
-
-  AWAIT_READY(recoverResources2);
-
-  // Shut everything down but check that framework 2 gets the
-  // resources from framework 1 after it is shutdown.
-  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
-    .WillRepeatedly(DoDefault());
-
-  EXPECT_CALL(this->allocator, deactivateFramework(_))
-    .Times(AtMost(2)); // Once for each framework.
-
-  EXPECT_CALL(this->allocator, removeFramework(_))
-    .Times(AtMost(2)); // Once for each framework.
-
-  // After we stop framework 1, all of it's resources should
-  // have been returned, but framework 2 should still have a
-  // task with 1 cpu and 256 mem, leaving 2 cpus and 768 mem.
-  Future<Nothing> resourceOffers;
-  EXPECT_CALL(sched2, resourceOffers(_, OfferEq(2, 768)))
-    .WillOnce(FutureSatisfy(&resourceOffers));
-
-  EXPECT_CALL(exec1, shutdown(_))
-    .Times(AtMost(1));
-
-  driver1.stop();
-  driver1.join();
-
-  AWAIT_READY(resourceOffers);
-
-  EXPECT_CALL(exec2, shutdown(_))
-    .Times(AtMost(1));
-
-  driver2.stop();
-  driver2.join();
-
-  EXPECT_CALL(this->allocator, removeSlave(_))
-    .Times(AtMost(1));
-
-  this->Shutdown();
-}
-
-
-// Checks that if a framework launches a task and then the slave the
-// task was running on gets killed, the task's resources are properly
-// recovered and, along with the rest of the resources from the killed
-// slave, never offered again.
-TYPED_TEST(AllocatorTest, SlaveLost)
-{
-  EXPECT_CALL(this->allocator, initialize(_, _, _));
-
-  Try<PID<Master> > master = this->StartMaster(&this->allocator);
-  ASSERT_SOME(master);
-
-  MockExecutor exec(DEFAULT_EXECUTOR_ID);
-
-  slave::Flags flags1 = this->CreateSlaveFlags();
-  flags1.resources = Some("cpus:2;mem:1024");
-
-  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
-
-  Try<PID<Slave> > slave1 = this->StartSlave(&exec, flags1);
-  ASSERT_SOME(slave1);
-
-  MockScheduler sched;
-  MesosSchedulerDriver driver(
-      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(this->allocator, addFramework(_, _, _));
-
-  EXPECT_CALL(sched, registered(_, _, _));
-
-  // Initially, all of slave1's resources are available.
-  EXPECT_CALL(sched, resourceOffers(_, OfferEq(2, 1024)))
-    .WillOnce(LaunchTasks(DEFAULT_EXECUTOR_INFO, 1, 2, 512, "*"));
-
-  Future<Nothing> recoverResources;
-  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
-    .WillOnce(DoAll(InvokeResourcesRecovered(&this->allocator),
-                    FutureSatisfy(&recoverResources)));
-
-  EXPECT_CALL(exec, registered(_, _, _, _));
-
-  Future<Nothing> launchTask;
-  EXPECT_CALL(exec, launchTask(_, _))
-    .WillOnce(DoAll(SendStatusUpdateFromTask(TASK_RUNNING),
-                    FutureSatisfy(&launchTask)));
-
-  EXPECT_CALL(sched, statusUpdate(_, _))
-    .WillRepeatedly(DoDefault());
-
-  driver.start();
-
-  // Ensures the task is completely launched before we kill the
-  // slave, to test that the task's and executor's resources are
-  // recovered correctly (i.e. never reallocated since the slave
-  // is killed).
-  AWAIT_READY(launchTask);
-
-  // Framework does not use all the resources.
-  AWAIT_READY(recoverResources);
-
-  // 'recoverResources' should be called twice, once for the task
-  // and once for the executor.
-  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
-    .Times(2);
-
-  Future<Nothing> removeSlave;
-  EXPECT_CALL(this->allocator, removeSlave(_))
-    .WillOnce(DoAll(InvokeSlaveRemoved(&this->allocator),
-                    FutureSatisfy(&removeSlave)));
-
-  EXPECT_CALL(exec, shutdown(_))
-    .Times(AtMost(1));
-
-  EXPECT_CALL(sched, slaveLost(_, _));
-
-  this->ShutdownSlaves();
-
-  AWAIT_READY(removeSlave);
-
-  slave::Flags flags2 = this->CreateSlaveFlags();
-  flags2.resources = string("cpus:3;mem:256;disk:1024;ports:[31000-32000]");
-
-  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
-
-  // Eventually after slave2 is launched, we should get
-  // an offer that contains all of slave2's resources
-  // and none of slave1's resources.
-  Future<vector<Offer> > resourceOffers;
-  EXPECT_CALL(sched, resourceOffers(_, OfferEq(3, 256)))
-    .WillOnce(FutureArg<1>(&resourceOffers));
-
-  Try<PID<Slave> > slave2 = this->StartSlave(flags2);
-  ASSERT_SOME(slave2);
-
-  AWAIT_READY(resourceOffers);
-
-  EXPECT_EQ(Resources(resourceOffers.get()[0].resources()),
-            Resources::parse(flags2.resources.get()).get());
-
-  // Shut everything down.
-  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
-    .WillRepeatedly(DoDefault());
-
-  EXPECT_CALL(this->allocator, deactivateFramework(_))
-    .Times(AtMost(1));
-
-  EXPECT_CALL(this->allocator, removeFramework(_))
-    .Times(AtMost(1));
-
-  driver.stop();
-  driver.join();
-
-  EXPECT_CALL(this->allocator, removeSlave(_))
-    .Times(AtMost(1));
-
-  this->Shutdown();
-}
-
-
-// Checks that if a slave is added after some allocations have already
-// occurred, its resources are added to the available pool of
-// resources and offered appropriately.
-TYPED_TEST(AllocatorTest, SlaveAdded)
-{
-  EXPECT_CALL(this->allocator, initialize(_, _, _));
-
-  master::Flags masterFlags = this->CreateMasterFlags();
-  masterFlags.allocation_interval = Milliseconds(50);
-  Try<PID<Master> > master = this->StartMaster(&this->allocator, masterFlags);
-  ASSERT_SOME(master);
-
-  MockExecutor exec(DEFAULT_EXECUTOR_ID);
-
-  slave::Flags flags1 = this->CreateSlaveFlags();
-  flags1.resources = Some("cpus:3;mem:1024");
-
-  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
-
-  Try<PID<Slave> > slave1 = this->StartSlave(&exec, flags1);
-  ASSERT_SOME(slave1);
-
-  MockScheduler sched;
-  MesosSchedulerDriver driver(
-      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(this->allocator, addFramework(_, _, _));
-
-  EXPECT_CALL(sched, registered(_, _, _));
-
-  // We decline offers that we aren't expecting so that the resources
-  // get aggregated. Note that we need to do this _first_ and
-  // _separate_ from the expectation below so that this expectation is
-  // checked last and matches all possible offers.
-  EXPECT_CALL(sched, resourceOffers(_, _))
-    .WillRepeatedly(DeclineOffers());
-
-  // Initially, all of slave1's resources are avaliable.
-  EXPECT_CALL(sched, resourceOffers(_, OfferEq(3, 1024)))
-    .WillOnce(LaunchTasks(DEFAULT_EXECUTOR_INFO, 1, 2, 512, "*"));
-
-  // We filter the first time so that the unused resources
-  // on slave1 from the task launch won't get reoffered
-  // immediately and will get combined with slave2's
-  // resources for a single offer.
-  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
-    .WillOnce(InvokeResourcesRecoveredWithFilters(&this->allocator, 0.1))
-    .WillRepeatedly(InvokeResourcesRecoveredWithFilters(&this->allocator, 0));
-
-  EXPECT_CALL(exec, registered(_, _, _, _));
-
-  Future<Nothing> launchTask;
-  EXPECT_CALL(exec, launchTask(_, _))
-    .WillOnce(DoAll(SendStatusUpdateFromTask(TASK_RUNNING),
-                    FutureSatisfy(&launchTask)));
-
-  EXPECT_CALL(sched, statusUpdate(_, _))
-    .WillRepeatedly(DoDefault());
-
-  driver.start();
-
-  AWAIT_READY(launchTask);
-
-  slave::Flags flags2 = this->CreateSlaveFlags();
-  flags2.resources = Some("cpus:4;mem:2048");
-
-  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
-
-  // After slave2 launches, all of its resources are combined with the
-  // resources on slave1 that the task isn't using.
-  Future<Nothing> resourceOffers;
-  EXPECT_CALL(sched, resourceOffers(_, OfferEq(5, 2560)))
-    .WillOnce(FutureSatisfy(&resourceOffers));
-
-  Try<PID<Slave> > slave2 = this->StartSlave(flags2);
-  ASSERT_SOME(slave2);
-
-  AWAIT_READY(resourceOffers);
-
-  // Shut everything down.
-  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
-    .WillRepeatedly(DoDefault());
-
-  EXPECT_CALL(this->allocator, deactivateFramework(_))
-    .Times(AtMost(1));
-
-  EXPECT_CALL(this->allocator, removeFramework(_))
-    .Times(AtMost(1));
-
-  EXPECT_CALL(exec, shutdown(_))
-    .Times(AtMost(1));
-
-  driver.stop();
-  driver.join();
-
-  EXPECT_CALL(this->allocator, removeSlave(_))
-    .Times(AtMost(2));
-
-  this->Shutdown();
-}
-
-
-// Checks that if a task is launched and then finishes normally, its
-// resources are recovered and reoffered correctly.
-TYPED_TEST(AllocatorTest, TaskFinished)
-{
-  EXPECT_CALL(this->allocator, initialize(_, _, _));
-
-  master::Flags masterFlags = this->CreateMasterFlags();
-  masterFlags.allocation_interval = Milliseconds(50);
-  Try<PID<Master> > master = this->StartMaster(&this->allocator, masterFlags);
-  ASSERT_SOME(master);
-
-  MockExecutor exec(DEFAULT_EXECUTOR_ID);
-
-  slave::Flags flags = this->CreateSlaveFlags();
-  flags.resources = Some("cpus:3;mem:1024");
-
-  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
-
-  Try<PID<Slave> > slave = this->StartSlave(&exec, flags);
-  ASSERT_SOME(slave);
-
-  MockScheduler sched;
-  MesosSchedulerDriver driver(
-      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(this->allocator, addFramework(_, _, _));
-
-  EXPECT_CALL(sched, registered(_, _, _));
-
-  // We decline offers that we aren't expecting so that the resources
-  // get aggregated. Note that we need to do this _first_ and
-  // _separate_ from the expectation below so that this expectation is
-  // checked last and matches all possible offers.
-  EXPECT_CALL(sched, resourceOffers(_, _))
-    .WillRepeatedly(DeclineOffers());
-
-  // Initially, all of the slave's resources.
-  EXPECT_CALL(sched, resourceOffers(_, OfferEq(3, 1024)))
-    .WillOnce(LaunchTasks(DEFAULT_EXECUTOR_INFO, 2, 1, 256, "*"));
-
-  // Some resources will be unused and we need to make sure that we
-  // don't send the TASK_FINISHED status update below until after the
-  // allocator knows about the unused resources so that it can
-  // aggregate them with the resources from the finished task.
-  Future<Nothing> recoverResources;
-  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
-    .WillRepeatedly(DoAll(InvokeResourcesRecovered(&this->allocator),
-                          FutureSatisfy(&recoverResources)));
-
-  EXPECT_CALL(exec, registered(_, _, _, _));
-
-  ExecutorDriver* execDriver;
-  TaskInfo taskInfo;
-  Future<Nothing> launchTask;
-  EXPECT_CALL(exec, launchTask(_, _))
-    .WillOnce(DoAll(SaveArg<0>(&execDriver),
-                    SaveArg<1>(&taskInfo),
-                    SendStatusUpdateFromTask(TASK_RUNNING),
-                    FutureSatisfy(&launchTask)))
-    .WillOnce(SendStatusUpdateFromTask(TASK_RUNNING));
-
-  EXPECT_CALL(sched, statusUpdate(_, _))
-    .WillRepeatedly(DoDefault());
-
-  driver.start();
-
-  AWAIT_READY(launchTask);
-
-  AWAIT_READY(recoverResources);
-
-  TaskStatus status;
-  status.mutable_task_id()->MergeFrom(taskInfo.task_id());
-  status.set_state(TASK_FINISHED);
-
-  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _));
-
-  // After the first task gets killed.
-  Future<Nothing> resourceOffers;
-  EXPECT_CALL(sched, resourceOffers(_, OfferEq(2, 768)))
-    .WillOnce(FutureSatisfy(&resourceOffers));
-
-  execDriver->sendStatusUpdate(status);
-
-  AWAIT_READY(resourceOffers);
-
-  // Shut everything down.
-  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
-    .WillRepeatedly(DoDefault());
-
-  EXPECT_CALL(this->allocator, deactivateFramework(_))
-    .Times(AtMost(1));
-
-  EXPECT_CALL(this->allocator, removeFramework(_))
-    .Times(AtMost(1));
-
-  EXPECT_CALL(exec, shutdown(_))
-    .Times(AtMost(1));
-
-  driver.stop();
-  driver.join();
-
-  EXPECT_CALL(this->allocator, removeSlave(_))
-    .Times(AtMost(1));
-
-  this->Shutdown();
-}
-
-
-// Checks that cpus only resources are offered
-// and tasks using only cpus are launched.
-TYPED_TEST(AllocatorTest, CpusOnlyOfferedAndTaskLaunched)
-{
-  EXPECT_CALL(this->allocator, initialize(_, _, _));
-
-  master::Flags masterFlags = this->CreateMasterFlags();
-  masterFlags.allocation_interval = Milliseconds(50);
-  Try<PID<Master> > master = this->StartMaster(&this->allocator, masterFlags);
-  ASSERT_SOME(master);
-
-  MockExecutor exec(DEFAULT_EXECUTOR_ID);
-
-  // Start a slave with cpus only resources.
-  slave::Flags flags = this->CreateSlaveFlags();
-  flags.resources = Some("cpus:2;mem:0");
-
-  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
-
-  Try<PID<Slave> > slave = this->StartSlave(&exec, flags);
-  ASSERT_SOME(slave);
-
-  MockScheduler sched;
-  MesosSchedulerDriver driver(
-      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(this->allocator, addFramework(_, _, _));
-
-  EXPECT_CALL(sched, registered(_, _, _));
-
-  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
-    .WillRepeatedly(DoDefault());
-
-  // Launch a cpus only task.
-  EXPECT_CALL(sched, resourceOffers(_, OfferEq(2, 0)))
-    .WillOnce(LaunchTasks(DEFAULT_EXECUTOR_INFO, 1, 2, 0, "*"));
-
-  EXPECT_CALL(exec, registered(_, _, _, _));
-
-  ExecutorDriver* execDriver;
-  TaskInfo taskInfo;
-  Future<Nothing> launchTask;
-  EXPECT_CALL(exec, launchTask(_, _))
-    .WillOnce(DoAll(SaveArg<0>(&execDriver),
-                    SaveArg<1>(&taskInfo),
-                    SendStatusUpdateFromTask(TASK_RUNNING),
-                    FutureSatisfy(&launchTask)));
-
-  EXPECT_CALL(sched, statusUpdate(_, _))
-    .WillRepeatedly(DoDefault());
-
-  driver.start();
-
-  AWAIT_READY(launchTask);
-
-  TaskStatus status;
-  status.mutable_task_id()->MergeFrom(taskInfo.task_id());
-  status.set_state(TASK_FINISHED);
-
-  // Check that cpus resources of finished task are offered again.
-  Future<Nothing> resourceOffers;
-  EXPECT_CALL(sched, resourceOffers(_, OfferEq(2, 0)))
-    .WillOnce(FutureSatisfy(&resourceOffers));
-
-  execDriver->sendStatusUpdate(status);
-
-  AWAIT_READY(resourceOffers);
-
-  // Shut everything down.
-  EXPECT_CALL(this->allocator, deactivateFramework(_))
-    .Times(AtMost(1));
-
-  EXPECT_CALL(this->allocator, removeFramework(_))
-    .Times(AtMost(1));
-
-  EXPECT_CALL(exec, shutdown(_))
-    .Times(AtMost(1));
-
-  driver.stop();
-  driver.join();
-
-  EXPECT_CALL(this->allocator, removeSlave(_))
-    .Times(AtMost(1));
-
-  this->Shutdown();
-}
-
-
-// Checks that memory only resources are offered
-// and tasks using only memory are launched.
-TYPED_TEST(AllocatorTest, MemoryOnlyOfferedAndTaskLaunched)
-{
-  EXPECT_CALL(this->allocator, initialize(_, _, _));
-
-  master::Flags masterFlags = this->CreateMasterFlags();
-  masterFlags.allocation_interval = Milliseconds(50);
-  Try<PID<Master> > master = this->StartMaster(&this->allocator, masterFlags);
-  ASSERT_SOME(master);
-
-  MockExecutor exec(DEFAULT_EXECUTOR_ID);
-
-  // Start a slave with memory only resources.
-  slave::Flags flags = this->CreateSlaveFlags();
-  flags.resources = Some("cpus:0;mem:200");
-
-  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
-
-  Try<PID<Slave> > slave = this->StartSlave(&exec, flags);
-  ASSERT_SOME(slave);
-
-  MockScheduler sched;
-  MesosSchedulerDriver driver(
-      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(this->allocator, addFramework(_, _, _));
-
-  EXPECT_CALL(sched, registered(_, _, _));
-
-  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
-    .WillRepeatedly(DoDefault());
-
-  // Launch a memory only task.
-  EXPECT_CALL(sched, resourceOffers(_, OfferEq(0, 200)))
-    .WillOnce(LaunchTasks(DEFAULT_EXECUTOR_INFO, 1, 0, 200, "*"));
-
-  EXPECT_CALL(exec, registered(_, _, _, _));
-
-  ExecutorDriver* execDriver;
-  TaskInfo taskInfo;
-  Future<Nothing> launchTask;
-  EXPECT_CALL(exec, launchTask(_, _))
-    .WillOnce(DoAll(SaveArg<0>(&execDriver),
-                    SaveArg<1>(&taskInfo),
-                    SendStatusUpdateFromTask(TASK_RUNNING),
-                    FutureSatisfy(&launchTask)));
-
-  EXPECT_CALL(sched, statusUpdate(_, _))
-    .WillRepeatedly(DoDefault());
-
-  driver.start();
-
-  AWAIT_READY(launchTask);
-
-  TaskStatus status;
-  status.mutable_task_id()->MergeFrom(taskInfo.task_id());
-  status.set_state(TASK_FINISHED);
-
-  // Check that mem resources of finished task are offered again.
-  Future<Nothing> resourceOffers;
-  EXPECT_CALL(sched, resourceOffers(_, OfferEq(0, 200)))
-    .WillOnce(FutureSatisfy(&resourceOffers));
-
-  execDriver->sendStatusUpdate(status);
-
-  AWAIT_READY(resourceOffers);
-
-  // Shut everything down.
-  EXPECT_CALL(this->allocator, deactivateFramework(_))
-    .Times(AtMost(1));
-
-  EXPECT_CALL(this->allocator, removeFramework(_))
-    .Times(AtMost(1));
-
-  EXPECT_CALL(exec, shutdown(_))
-    .Times(AtMost(1));
-
-  driver.stop();
-  driver.join();
-
-  EXPECT_CALL(this->allocator, removeSlave(_))
-    .Times(AtMost(1));
-
-  this->Shutdown();
-}
-
-
-// Checks that a slave that is not whitelisted will not have its
-// resources get offered, and that if the whitelist is updated so
-// that it is whitelisted, its resources will then be offered.
-TYPED_TEST(AllocatorTest, WhitelistSlave)
-{
-  // Create a dummy whitelist, so that no resources will get allocated.
-  string hosts = "dummy-slave";
-  string path = "whitelist.txt";
-  ASSERT_SOME(os::write(path, hosts)) << "Error writing whitelist";
-
-  master::Flags masterFlags = this->CreateMasterFlags();
-  masterFlags.whitelist = "file://" + path; // TODO(benh): Put in /tmp.
-
-  EXPECT_CALL(this->allocator, initialize(_, _, _));
-
-  Future<Nothing> updateWhitelist1;
-  EXPECT_CALL(this->allocator, updateWhitelist(_))
-    .WillOnce(DoAll(InvokeUpdateWhitelist(&this->allocator),
-                    FutureSatisfy(&updateWhitelist1)));
-
-  Try<PID<Master> > master = this->StartMaster(&this->allocator, masterFlags);
-  ASSERT_SOME(master);
-
-  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
-
-  slave::Flags flags = this->CreateSlaveFlags();
-  flags.resources = Some("cpus:2;mem:1024");
-
-  Try<string> hostname = net::hostname();
-  ASSERT_SOME(hostname);
-  flags.hostname = hostname.get();
-
-  Try<PID<Slave> > slave = this->StartSlave(flags);
-  ASSERT_SOME(slave);
-
-  MockScheduler sched;
-  MesosSchedulerDriver driver(
-      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(this->allocator, addFramework(_, _, _));
-
-  EXPECT_CALL(sched, registered(_, _, _));
-
-  // Once the slave gets whitelisted, all of its resources should be
-  // offered to the one framework running.
-  Future<Nothing> resourceOffers;
-  EXPECT_CALL(sched, resourceOffers(_, OfferEq(2, 1024)))
-    .WillOnce(FutureSatisfy(&resourceOffers));
-
-  // Make sure the allocator has been given the original, empty
-  // whitelist.
-  AWAIT_READY(updateWhitelist1);
-
-  driver.start();
-
-  // Give the allocator some time to confirm that it doesn't
-  // make an allocation.
-  Clock::pause();
-  Clock::advance(Seconds(1));
-  Clock::settle();
-
-  EXPECT_FALSE(resourceOffers.isReady());
-
-  // Update the whitelist to include the slave, so that
-  // the allocator will start making allocations.
-  hosts = hostname.get() + "\n" + "dummy-slave";
-
-  EXPECT_CALL(this->allocator, updateWhitelist(_));
-
-  ASSERT_SOME(os::write(path, hosts)) << "Error writing whitelist";
-
-  // Give the WhitelistWatcher some time to notice that
-  // the whitelist has changed.
-  while (resourceOffers.isPending()) {
-    Clock::advance(Seconds(1));
-    Clock::settle();
-  }
-  Clock::resume();
-
-  // Shut everything down.
-  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
-    .WillRepeatedly(DoDefault());
-
-  EXPECT_CALL(this->allocator, deactivateFramework(_))
-    .Times(AtMost(1));
-
-  EXPECT_CALL(this->allocator, removeFramework(_))
-    .Times(AtMost(1));
-
-  driver.stop();
-  driver.join();
-
-  EXPECT_CALL(this->allocator, removeSlave(_))
-    .Times(AtMost(1));
-
-  this->Shutdown();
-
-  os::rm(path);
-}
-
-
-// Checks that a framework attempting to register with an invalid role
-// will receive an error message and that roles can be added through the
-// master's command line flags.
-TYPED_TEST(AllocatorTest, RoleTest)
-{
-  EXPECT_CALL(this->allocator, initialize(_, _, _));
-
-  master::Flags masterFlags = this->CreateMasterFlags();
-  masterFlags.roles = Some("role2");
-  Try<PID<Master> > master = this->StartMaster(&this->allocator, masterFlags);
-  ASSERT_SOME(master);
-
-  // Launch a framework with a role that doesn't exist to see that it
-  // receives an error message.
-  FrameworkInfo frameworkInfo1; // Bug in gcc 4.1.*, must assign on next line.
-  frameworkInfo1 = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo1.set_name("framework1");
-  frameworkInfo1.set_user("user1");
-  frameworkInfo1.set_role("role1");
-
-  MockScheduler sched1;
-  MesosSchedulerDriver driver1(
-      &sched1, frameworkInfo1, master.get(), DEFAULT_CREDENTIAL);
-
-  Future<FrameworkErrorMessage> errorMessage =
-    FUTURE_PROTOBUF(FrameworkErrorMessage(), _, _);
-
-  EXPECT_CALL(sched1, error(_, _));
-
-  driver1.start();
-
-  AWAIT_READY(errorMessage);
-
-  // Launch a framework under an existing role to see that it registers.
-  FrameworkInfo frameworkInfo2; // Bug in gcc 4.1.*, must assign on next line.
-  frameworkInfo2 = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo2.set_name("framework2");
-  frameworkInfo2.set_user("user2");
-  frameworkInfo2.set_role("role2");
-
-  MockScheduler sched2;
-  MesosSchedulerDriver driver2(
-      &sched2, frameworkInfo2, master.get(), DEFAULT_CREDENTIAL);
-
-  Future<Nothing> registered2;
-  EXPECT_CALL(sched2, registered(_, _, _))
-    .WillOnce(FutureSatisfy(&registered2));
-
-  Future<Nothing> addFramework;
-  EXPECT_CALL(this->allocator, addFramework(_, _, _))
-    .WillOnce(FutureSatisfy(&addFramework));
-
-  driver2.start();
-
-  AWAIT_READY(registered2);
-  AWAIT_READY(addFramework);
-
-  // Shut everything down.
-  Future<Nothing> deactivateFramework;
-  EXPECT_CALL(this->allocator, deactivateFramework(_))
-    .WillOnce(FutureSatisfy(&deactivateFramework));
-
-  Future<Nothing> removeFramework;
-  EXPECT_CALL(this->allocator, removeFramework(_))
-    .WillOnce(FutureSatisfy(&removeFramework));
-
-  driver2.stop();
-  driver2.join();
-
-  AWAIT_READY(deactivateFramework);
-  AWAIT_READY(removeFramework);
-
-  driver1.stop();
-  driver1.join();
-
-  this->Shutdown();
-}
-
-
-// Checks that in the event of a master failure and the election of a
-// new master, if a framework reregisters before a slave that it has
-// resources on reregisters, all used and unused resources are
-// accounted for correctly.
-TYPED_TEST(AllocatorTest, FrameworkReregistersFirst)
-{
-  EXPECT_CALL(this->allocator, initialize(_, _, _));
-
-  Try<PID<Master> > master = this->StartMaster(&this->allocator);
-  ASSERT_SOME(master);
-
-  MockExecutor exec(DEFAULT_EXECUTOR_ID);
-
-  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
-
-  StandaloneMasterDetector slaveDetector(master.get());
-
-  slave::Flags flags = this->CreateSlaveFlags();
-  flags.resources = Some("cpus:2;mem:1024");
-
-  Try<PID<Slave> > slave = this->StartSlave(&exec, &slaveDetector, flags);
-  ASSERT_SOME(slave);
-
-  EXPECT_CALL(this->allocator, addFramework(_, _, _));
-
-  MockScheduler sched;
-  StandaloneMasterDetector schedulerDetector(master.get());
-  TestingMesosSchedulerDriver driver(&sched, &schedulerDetector);
-
-  EXPECT_CALL(sched, registered(&driver, _, _));
-
-  // The framework should be offered all of the resources on the slave
-  // since it is the only framework running.
-  EXPECT_CALL(sched, resourceOffers(&driver, OfferEq(2, 1024)))
-    .WillOnce(LaunchTasks(DEFAULT_EXECUTOR_INFO, 1, 1, 500, "*"))
-    .WillRepeatedly(DeclineOffers());
-
-  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _));
-
-  EXPECT_CALL(exec, registered(_, _, _, _));
-
-  EXPECT_CALL(exec, launchTask(_, _))
-    .WillOnce(SendStatusUpdateFromTask(TASK_RUNNING));
-
-  Future<TaskStatus> status;
-  EXPECT_CALL(sched, statusUpdate(&driver, _))
-    .WillOnce(FutureArg<1>(&status));
-
-  Future<Nothing> _statusUpdateAcknowledgement =
-    FUTURE_DISPATCH(_, &Slave::_statusUpdateAcknowledgement);
-
-  driver.start();
-
-  AWAIT_READY(status);
-
-  EXPECT_EQ(TASK_RUNNING, status.get().state());
-
-  // Make sure the slave handles status update acknowledgement so that
-  // it doesn't try to retry the update after master failover.
-  AWAIT_READY(_statusUpdateAcknowledgement);
-
-  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
-    .WillRepeatedly(DoDefault());
-
-  this->ShutdownMasters();
-  this->StopAllocator();
-
-  MockAllocatorProcess<TypeParam> allocator2;
-
-  EXPECT_CALL(allocator2, initialize(_, _, _));
-
-  Future<Nothing> addFramework;
-  EXPECT_CALL(allocator2, addFramework(_, _, _))
-    .WillOnce(DoAll(InvokeFrameworkAdded(&allocator2),
-                    FutureSatisfy(&addFramework)));
-
-  EXPECT_CALL(sched, registered(&driver, _, _));
-
-  Try<PID<Master> > master2 = this->StartMaster(&allocator2);
-  ASSERT_SOME(master2);
-
-  EXPECT_CALL(sched, disconnected(_));
-
-  // Inform the scheduler about the new master.
-  schedulerDetector.appoint(master2.get());
-
-  AWAIT_READY(addFramework);
-
-  EXPECT_CALL(allocator2, addSlave(_, _, _, _));
-
-  Future<vector<Offer> > resourceOffers2;
-  EXPECT_CALL(sched, resourceOffers(&driver, _))
-    .WillOnce(FutureArg<1>(&resourceOffers2));
-
-  // Inform the slave about the new master.
-  slaveDetector.appoint(master2.get());
-
-  AWAIT_READY(resourceOffers2);
-
-  // Since the task is still running on the slave, the framework
-  // should only be offered the resources not being used by the task.
-  EXPECT_THAT(resourceOffers2.get(), OfferEq(1, 524));
-
-  // Shut everything down.
-  EXPECT_CALL(allocator2, recoverResources(_, _, _, _))
-    .WillRepeatedly(DoDefault());
-
-  EXPECT_CALL(allocator2, deactivateFramework(_))
-    .Times(AtMost(1));
-
-  EXPECT_CALL(allocator2, removeFramework(_))
-    .Times(AtMost(1));
-
-  EXPECT_CALL(exec, shutdown(_))
-    .Times(AtMost(1));
-
-  driver.stop();
-  driver.join();
-
-  EXPECT_CALL(allocator2, removeSlave(_))
-    .Times(AtMost(1));
-
-  this->Shutdown();
-}
-
-
-// Checks that in the event of a master failure and the election of a
-// new master, if a slave reregisters before a framework that has
-// resources on reregisters, all used and unused resources are
-// accounted for correctly.
-TYPED_TEST(AllocatorTest, SlaveReregistersFirst)
-{
-  EXPECT_CALL(this->allocator, initialize(_, _, _));
-
-  Try<PID<Master> > master = this->StartMaster(&this->allocator);
-  ASSERT_SOME(master);
-
-  MockExecutor exec(DEFAULT_EXECUTOR_ID);
-  StandaloneMasterDetector slaveDetector(master.get());
-
-  EXPECT_CALL(this->allocator, addSlave(_, _, _, _));
-
-  slave::Flags flags = this->CreateSlaveFlags();
-  flags.resources = Some("cpus:2;mem:1024");
-
-  Try<PID<Slave> > slave = this->StartSlave(&exec, &slaveDetector, flags);
-  ASSERT_SOME(slave);
-
-  MockScheduler sched;
-  StandaloneMasterDetector schedulerDetector(master.get());
-  TestingMesosSchedulerDriver driver(&sched, &schedulerDetector);
-
-  EXPECT_CALL(this->allocator, addFramework(_, _, _));
-
-  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _));
-
-  EXPECT_CALL(sched, registered(&driver, _, _));
-
-  // The framework should be offered all of the resources on the slave
-  // since it is the only framework running.
-  EXPECT_CALL(sched, resourceOffers(&driver, OfferEq(2, 1024)))
-    .WillOnce(LaunchTasks(DEFAULT_EXECUTOR_INFO, 1, 1, 500, "*"))
-    .WillRepeatedly(DeclineOffers());
-
-  EXPECT_CALL(exec, registered(_, _, _, _));
-
-  EXPECT_CALL(exec, launchTask(_, _))
-    .WillOnce(SendStatusUpdateFromTask(TASK_RUNNING));
-
-  Future<TaskStatus> status;
-  EXPECT_CALL(sched, statusUpdate(&driver, _))
-    .WillOnce(FutureArg<1>(&status));
-
-  Future<Nothing> _statusUpdateAcknowledgement =
-    FUTURE_DISPATCH(_, &Slave::_statusUpdateAcknowledgement);
-
-  driver.start();
-
-  AWAIT_READY(status);
-
-  EXPECT_EQ(TASK_RUNNING, status.get().state());
-
-  // Make sure the slave handles status update acknowledgement so that
-  // it doesn't try to retry the update after master failover.
-  AWAIT_READY(_statusUpdateAcknowledgement);
-
-  EXPECT_CALL(this->allocator, recoverResources(_, _, _, _))
-    .WillRepeatedly(DoDefault());
-
-  this->ShutdownMasters();
-  this->StopAllocator();
-
-  MockAllocatorProcess<TypeParam> allocator2;
-
-  EXPECT_CALL(allocator2, initialize(_, _, _));
-
-  Future<Nothing> addSlave;
-  EXPECT_CALL(allocator2, addSlave(_, _, _, _))
-    .WillOnce(DoAll(InvokeSlaveAdded(&allocator2),
-                    FutureSatisfy(&addSlave)));
-
-  Try<PID<Master> > master2 = this->StartMaster(&allocator2);
-  ASSERT_SOME(master2);
-
-  // Inform the slave about the new master.
-  slaveDetector.appoint(master2.get());
-
-  AWAIT_READY(addSlave);
-
-  EXPECT_CALL(sched, disconnected(_));
-
-  EXPECT_CALL(sched, registered(&driver, _, _));
-
-  EXPECT_CALL(allocator2, addFramework(_, _, _));
-
-  Future<vector<Offer> > resourceOffers2;
-  EXPECT_CALL(sched, resourceOffers(&driver, _))
-    .WillOnce(FutureArg<1>(&resourceOffers2));
-
-  // Inform the scheduler about the new master.
-  schedulerDetector.appoint(master2.get());
-
-  AWAIT_READY(resourceOffers2);
-
-  // Since the task is still running on the slave, the framework
-  // should only be offered the resources not being used by the task.
-  EXPECT_THAT(resourceOffers2.get(), OfferEq(1, 524));
-
-  // Shut everything down.
-  EXPECT_CALL(allocator2, recoverResources(_, _, _, _))
-    .WillRepeatedly(DoDefault());
-
-  EXPECT_CALL(allocator2, deactivateFramework(_))
-    .Times(AtMost(1));
-
-  EXPECT_CALL(allocator2, removeFramework(_))
-    .Times(AtMost(1));
-
-  EXPECT_CALL(exec, shutdown(_))
-    .Times(AtMost(1));
-
-  driver.stop();
-  driver.join();
-
-  EXPECT_CALL(allocator2, removeSlave(_))
-    .Times(AtMost(1));
-
-  this->Shutdown();
-}

http://git-wip-us.apache.org/repos/asf/mesos/blob/6cf1b016/src/tests/hierarchical_allocator_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/hierarchical_allocator_tests.cpp b/src/tests/hierarchical_allocator_tests.cpp
new file mode 100644
index 0000000..9ecbd0f
--- /dev/null
+++ b/src/tests/hierarchical_allocator_tests.cpp
@@ -0,0 +1,282 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+
+#include <string>
+#include <queue>
+#include <vector>
+
+#include <process/clock.hpp>
+#include <process/future.hpp>
+#include <process/gtest.hpp>
+#include <process/queue.hpp>
+
+#include <stout/hashmap.hpp>
+
+#include "master/allocator.hpp"
+#include "master/flags.hpp"
+#include "master/hierarchical_allocator_process.hpp"
+
+using namespace mesos;
+using namespace mesos::internal;
+
+using mesos::internal::master::allocator::Allocator;
+using mesos::internal::master::allocator::AllocatorProcess;
+using mesos::internal::master::allocator::HierarchicalDRFAllocatorProcess;
+
+using process::Clock;
+using process::Future;
+
+using std::queue;
+using std::string;
+using std::vector;
+
+
+struct Allocation
+{
+  FrameworkID frameworkId;
+  hashmap<SlaveID, Resources> resources;
+};
+
+
+class HierarchicalAllocatorTest : public ::testing::Test
+{
+protected:
+  HierarchicalAllocatorTest()
+    : allocatorProcess(new HierarchicalDRFAllocatorProcess()),
+      allocator(new Allocator(allocatorProcess)),
+      nextSlaveId(1),
+      nextFrameworkId(1) {}
+
+  ~HierarchicalAllocatorTest()
+  {
+    delete allocator;
+    delete allocatorProcess;
+  }
+
+  void initialize(
+      const vector<string>& _roles,
+      const master::Flags& _flags = master::Flags())
+  {
+    flags = _flags;
+
+    // NOTE: The master always adds this default role.
+    RoleInfo info;
+    info.set_name("*");
+    roles["*"] = info;
+
+    foreach (const string& role, _roles) {
+      info.set_name(role);
+      roles[role] = info;
+    }
+
+    allocator->initialize(
+        flags,
+        lambda::bind(&put, &queue, lambda::_1, lambda::_2),
+        roles);
+  }
+
+  SlaveInfo createSlaveInfo(const string& resources)
+  {
+    SlaveID slaveId;
+    slaveId.set_value("slave" + stringify(nextSlaveId++));
+
+    SlaveInfo slave;
+    *(slave.mutable_resources()) = Resources::parse(resources).get();
+    *(slave.mutable_id()) = slaveId;
+    slave.set_hostname(slaveId.value());
+
+    return slave;
+  }
+
+  FrameworkInfo createFrameworkInfo(const string& role)
+  {
+    FrameworkInfo frameworkInfo;
+    frameworkInfo.set_user("user");
+    frameworkInfo.set_name("framework" + stringify(nextFrameworkId++));
+    frameworkInfo.mutable_id()->set_value(frameworkInfo.name());
+    frameworkInfo.set_role(role);
+
+    return frameworkInfo;
+  }
+
+private:
+  static void put(
+      process::Queue<Allocation>* queue,
+      const FrameworkID& frameworkId,
+      const hashmap<SlaveID, Resources>& resources)
+  {
+    Allocation allocation;
+    allocation.frameworkId = frameworkId;
+    allocation.resources = resources;
+
+    queue->put(allocation);
+  }
+
+protected:
+  master::Flags flags;
+
+  AllocatorProcess* allocatorProcess;
+  Allocator* allocator;
+
+  process::Queue<Allocation> queue;
+
+  hashmap<string, RoleInfo> roles;
+
+private:
+  int nextSlaveId;
+  int nextFrameworkId;
+};
+
+
+template <typename Iterable>
+Resources sum(const Iterable& iterable)
+{
+  Resources total;
+  foreach (const Resources& resources, iterable) {
+    total += resources;
+  }
+  return total;
+}
+
+
+// Checks that the DRF allocator implements the DRF algorithm
+// correctly. The test accomplishes this by adding frameworks and
+// slaves one at a time to the allocator, making sure that each time
+// a new slave is added all of its resources are offered to whichever
+// framework currently has the smallest share. Checking for proper DRF
+// logic when resources are returned, frameworks exit, etc. is handled
+// by SorterTest.DRFSorter.
+TEST_F(HierarchicalAllocatorTest, DRF)
+{
+  // Pausing the clock is not necessary, but ensures that the test
+  // doesn't rely on the periodic allocation in the allocator, which
+  // would slow down the test.
+  Clock::pause();
+
+  initialize({"role1", "role2"});
+
+  hashmap<FrameworkID, Resources> EMPTY;
+
+  // Total cluster resources will become cpus=2, mem=1024.
+  SlaveInfo slave1 = createSlaveInfo("cpus:2;mem:1024;disk:0");
+  allocator->addSlave(slave1.id(), slave1, slave1.resources(), EMPTY);
+
+  // framework1 will be offered all of slave1's resources since it is
+  // the only framework running so far.
+  FrameworkInfo framework1 = createFrameworkInfo("role1");
+  allocator->addFramework(framework1.id(), framework1, Resources());
+
+  Future<Allocation> allocation = queue.get();
+  AWAIT_READY(allocation);
+  EXPECT_EQ(framework1.id(), allocation.get().frameworkId);
+  EXPECT_EQ(slave1.resources(), sum(allocation.get().resources.values()));
+
+  // role1 share = 1 (cpus=2, mem=1024)
+  //   framework1 share = 1
+
+  FrameworkInfo framework2 = createFrameworkInfo("role2");
+  allocator->addFramework(framework2.id(), framework2, Resources());
+
+  // Total cluster resources will become cpus=3, mem=1536:
+  // role1 share = 0.66 (cpus=2, mem=1024)
+  //   framework1 share = 1
+  // role2 share = 0
+  //   framework2 share = 0
+  SlaveInfo slave2 = createSlaveInfo("cpus:1;mem:512;disk:0");
+  allocator->addSlave(slave2.id(), slave2, slave2.resources(), EMPTY);
+
+  // framework2 will be offered all of slave2's resources since role2
+  // has the lowest user share, and framework2 is its only framework.
+  allocation = queue.get();
+  AWAIT_READY(allocation);
+  EXPECT_EQ(framework2.id(), allocation.get().frameworkId);
+  EXPECT_EQ(slave2.resources(), sum(allocation.get().resources.values()));
+
+  // role1 share = 0.67 (cpus=2, mem=1024)
+  //   framework1 share = 1
+  // role2 share = 0.33 (cpus=1, mem=512)
+  //   framework2 share = 1
+
+  // Total cluster resources will become cpus=6, mem=3584:
+  // role1 share = 0.33 (cpus=2, mem=1024)
+  //   framework1 share = 1
+  // role2 share = 0.16 (cpus=1, mem=512)
+  //   framework2 share = 1
+  SlaveInfo slave3 = createSlaveInfo("cpus:3;mem:2048;disk:0");
+  allocator->addSlave(slave3.id(), slave3, slave3.resources(), EMPTY);
+
+  // framework2 will be offered all of slave3's resources since role2
+  // has the lowest share.
+  allocation = queue.get();
+  AWAIT_READY(allocation);
+  EXPECT_EQ(framework2.id(), allocation.get().frameworkId);
+  EXPECT_EQ(slave3.resources(), sum(allocation.get().resources.values()));
+
+  // role1 share = 0.33 (cpus=2, mem=1024)
+  //   framework1 share = 1
+  // role2 share = 0.71 (cpus=4, mem=2560)
+  //   framework2 share = 1
+
+  FrameworkInfo framework3 = createFrameworkInfo("role1");
+  allocator->addFramework(framework3.id(), framework3, Resources());
+
+  // Total cluster resources will become cpus=10, mem=7680:
+  // role1 share = 0.2 (cpus=2, mem=1024)
+  //   framework1 share = 1
+  //   framework3 share = 0
+  // role2 share = 0.4 (cpus=4, mem=2560)
+  //   framework2 share = 1
+  SlaveInfo slave4 = createSlaveInfo("cpus:4;mem:4096;disk:0");
+  allocator->addSlave(slave4.id(), slave4, slave4.resources(), EMPTY);
+
+  // framework3 will be offered all of slave4's resources since role1
+  // has the lowest user share, and framework3 has the lowest share of
+  // role1's frameworks.
+  allocation = queue.get();
+  AWAIT_READY(allocation);
+  EXPECT_EQ(framework3.id(), allocation.get().frameworkId);
+  EXPECT_EQ(slave4.resources(), sum(allocation.get().resources.values()));
+
+  // role1 share = 0.67 (cpus=6, mem=5120)
+  //   framework1 share = 0.33 (cpus=2, mem=1024)
+  //   framework3 share = 0.8 (cpus=4, mem=4096)
+  // role2 share = 0.4 (cpus=4, mem=2560)
+  //   framework2 share = 1
+
+  FrameworkInfo framework4 = createFrameworkInfo("role1");
+  allocator->addFramework(framework4.id(), framework4, Resources());
+
+  // Total cluster resources will become cpus=11, mem=8192
+  // role1 share = 0.63 (cpus=6, mem=5120)
+  //   framework1 share = 0.33 (cpus=2, mem=1024)
+  //   framework3 share = 0.8 (cpus=4, mem=4096)
+  //   framework4 share = 0
+  // role2 share = 0.36 (cpus=4, mem=2560)
+  //   framework2 share = 1
+  SlaveInfo slave5 = createSlaveInfo("cpus:1;mem:512;disk:0");
+  allocator->addSlave(slave5.id(), slave5, slave5.resources(), EMPTY);
+
+  // Even though framework4 doesn't have any resources, role2 has a
+  // lower share than role1, so framework2 receives slave5's resources.
+  allocation = queue.get();
+  AWAIT_READY(allocation);
+  EXPECT_EQ(framework2.id(), allocation.get().frameworkId);
+  EXPECT_EQ(slave5.resources(), sum(allocation.get().resources.values()));
+}


[02/11] mesos git commit: Moved ReservationAllocatorTest.ResourcesReturned to a unit test.

Posted by bm...@apache.org.
Moved ReservationAllocatorTest.ResourcesReturned to a unit test.

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


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

Branch: refs/heads/master
Commit: cc70b0eb2bff93fa03e163aaa2362bf276122eb5
Parents: a56c579
Author: Benjamin Mahler <be...@gmail.com>
Authored: Sun Dec 7 17:29:35 2014 -0800
Committer: Benjamin Mahler <be...@gmail.com>
Committed: Thu Dec 11 14:40:30 2014 -0800

----------------------------------------------------------------------
 src/tests/hierarchical_allocator_tests.cpp |  69 ++++++++++
 src/tests/master_allocator_tests.cpp       | 165 ------------------------
 2 files changed, 69 insertions(+), 165 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/cc70b0eb/src/tests/hierarchical_allocator_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/hierarchical_allocator_tests.cpp b/src/tests/hierarchical_allocator_tests.cpp
index f20be8c..4156769 100644
--- a/src/tests/hierarchical_allocator_tests.cpp
+++ b/src/tests/hierarchical_allocator_tests.cpp
@@ -155,6 +155,12 @@ Resources sum(const Iterable& iterable)
   return total;
 }
 
+// TODO(bmahler): These tests were transformed directly from
+// integration tests into unit tests. However, these tests
+// should be simplified even further to each test a single
+// expected behavor, at which point we can have more tests
+// that are each very small.
+
 
 // Checks that the DRF allocator implements the DRF algorithm
 // correctly. The test accomplishes this by adding frameworks and
@@ -451,3 +457,66 @@ TEST_F(HierarchicalAllocatorTest, Reservations)
   EXPECT_EQ(Resources(slave2.resources()).extract("role2"),
             sum(allocation.get().resources.values()));
 }
+
+
+// Checks that recovered resources are re-allocated correctly.
+TEST_F(HierarchicalAllocatorTest, RecoverResources)
+{
+  Clock::pause();
+
+  initialize({"role1"});
+
+  hashmap<FrameworkID, Resources> EMPTY;
+
+  SlaveInfo slave = createSlaveInfo(
+      "cpus(role1):1;mem(role1):200;"
+      "cpus:1;mem:200;disk:0");
+  allocator->addSlave(slave.id(), slave, slave.resources(), EMPTY);
+
+  // Initially, all the resources are allocated.
+  FrameworkInfo framework1 = createFrameworkInfo("role1");
+  allocator->addFramework(framework1.id(), framework1, Resources());
+
+  Future<Allocation> allocation = queue.get();
+  AWAIT_READY(allocation);
+  EXPECT_EQ(framework1.id(), allocation.get().frameworkId);
+  EXPECT_EQ(1u, allocation.get().resources.size());
+  EXPECT_TRUE(allocation.get().resources.contains(slave.id()));
+  EXPECT_EQ(slave.resources(), sum(allocation.get().resources.values()));
+
+  // Recover the reserved resources, expect them to be re-offered.
+  Resources reserved = Resources(slave.resources()).extract("role1");
+
+  allocator->recoverResources(
+      allocation.get().frameworkId,
+      slave.id(),
+      reserved,
+      None());
+
+  Clock::advance(flags.allocation_interval);
+
+  allocation = queue.get();
+  AWAIT_READY(allocation);
+  EXPECT_EQ(framework1.id(), allocation.get().frameworkId);
+  EXPECT_EQ(1u, allocation.get().resources.size());
+  EXPECT_TRUE(allocation.get().resources.contains(slave.id()));
+  EXPECT_EQ(reserved, sum(allocation.get().resources.values()));
+
+  // Recover the unreserved resources, expect them to be re-offered.
+  Resources unreserved = Resources(slave.resources()).extract("*");
+
+  allocator->recoverResources(
+      allocation.get().frameworkId,
+      slave.id(),
+      unreserved,
+      None());
+
+  Clock::advance(flags.allocation_interval);
+
+  allocation = queue.get();
+  AWAIT_READY(allocation);
+  EXPECT_EQ(framework1.id(), allocation.get().frameworkId);
+  EXPECT_EQ(1u, allocation.get().resources.size());
+  EXPECT_TRUE(allocation.get().resources.contains(slave.id()));
+  EXPECT_EQ(unreserved, sum(allocation.get().resources.values()));
+}

http://git-wip-us.apache.org/repos/asf/mesos/blob/cc70b0eb/src/tests/master_allocator_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/master_allocator_tests.cpp b/src/tests/master_allocator_tests.cpp
index 61a5f5c..ff99c72 100644
--- a/src/tests/master_allocator_tests.cpp
+++ b/src/tests/master_allocator_tests.cpp
@@ -68,171 +68,6 @@ using testing::Eq;
 using testing::SaveArg;
 
 
-class ReservationAllocatorTest : public MesosTest {};
-
-
-// Checks that statically allocated resources that are returned
-// either unused or after a task finishes are statically reallocated
-// appropriately.
-TEST_F(ReservationAllocatorTest, ResourcesReturned)
-{
-  MockAllocatorProcess<HierarchicalDRFAllocatorProcess> allocator;
-
-  EXPECT_CALL(allocator, initialize(_, _, _));
-
-  master::Flags masterFlags = CreateMasterFlags();
-  masterFlags.roles = Some("role1,role2");
-  masterFlags.allocation_interval = Milliseconds(50);
-  Try<PID<Master> > master = StartMaster(&allocator, masterFlags);
-
-  ASSERT_SOME(master);
-
-  MockExecutor exec(DEFAULT_EXECUTOR_ID);
-
-  EXPECT_CALL(allocator, addSlave(_, _, _, _))
-    .Times(2);
-
-  Future<Nothing> addSlave1 = FUTURE_DISPATCH(
-      allocator.real, &AllocatorProcess::addSlave);
-
-  slave::Flags flags1 = CreateSlaveFlags();
-  flags1.resources = Some("cpus(role1):1;mem(role1):200;cpus(role2):2;"
-                          "mem(role2):600;cpus:1;mem:200;disk:0");
-  Try<PID<Slave> > slave1 = StartSlave(&exec, flags1);
-  ASSERT_SOME(slave1);
-
-  // Wait until allocator has added slave1.
-  AWAIT_READY(addSlave1);
-
-  Future<Nothing> addSlave2 = FUTURE_DISPATCH(
-      allocator.real, &AllocatorProcess::addSlave);
-
-  // This slave's resources will never be offered to anyone,
-  // because there is no framework with role3.
-  slave::Flags flags2 = CreateSlaveFlags();
-  flags2.resources = Some("cpus(role3):4;mem(role3):1024;disk:0");
-  Try<PID<Slave> > slave2 = StartSlave(flags2);
-  ASSERT_SOME(slave2);
-
-  // Wait until allocator has added slave2.
-  AWAIT_READY(addSlave2);
-
-  FrameworkInfo frameworkInfo1; // Bug in gcc 4.1.*, must assign on next line.
-  frameworkInfo1 = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo1.set_user("user1");
-  frameworkInfo1.set_name("framework1");
-  frameworkInfo1.set_role("role1");
-  FrameworkID frameworkId1;
-
-  MockScheduler sched1;
-  MesosSchedulerDriver driver1(
-      &sched1, frameworkInfo1, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(allocator, addFramework(_, _, _));
-
-  EXPECT_CALL(sched1, registered(_, _, _));
-
-  // Initially, framework1 should be offered all of the resources on
-  // slave1 that aren't reserved to role2.
-  EXPECT_CALL(sched1, resourceOffers(_, OfferEq(2, 400)))
-    .WillOnce(LaunchTasks(DEFAULT_EXECUTOR_INFO, 1, 1, 100, "role1"));
-
-  EXPECT_CALL(allocator, recoverResources(_, _, _, _))
-    .WillOnce(InvokeResourcesRecoveredWithFilters(&allocator, 0));
-
-  EXPECT_CALL(exec, registered(_, _, _, _));
-
-  ExecutorDriver* execDriver;
-  TaskInfo taskInfo;
-  Future<Nothing> launchTask;
-  EXPECT_CALL(exec, launchTask(_, _))
-    .WillOnce(DoAll(SaveArg<0>(&execDriver),
-                    SaveArg<1>(&taskInfo),
-                    SendStatusUpdateFromTask(TASK_RUNNING),
-                    FutureSatisfy(&launchTask)));
-
-  EXPECT_CALL(sched1, statusUpdate(_, _))
-    .WillRepeatedly(DoDefault());
-
-  // After framework1's task launches, it should be offered all resources
-  // not dedicatd to role2 and not used by its task.
-  Future<Nothing> resourceOffers1;
-  EXPECT_CALL(sched1, resourceOffers(_, OfferEq(1, 300)))
-    .WillOnce(FutureSatisfy(&resourceOffers1));
-
-  driver1.start();
-
-  AWAIT_READY(launchTask);
-
-  AWAIT_READY(resourceOffers1);
-
-  FrameworkInfo frameworkInfo2; // Bug in gcc 4.1.*, must assign on next line.
-  frameworkInfo2 = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo2.set_user("user2");
-  frameworkInfo2.set_name("framework2");
-  frameworkInfo2.set_role("role2");
-  FrameworkID frameworkId2;
-
-  MockScheduler sched2;
-  MesosSchedulerDriver driver2(
-      &sched2, frameworkInfo2, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(allocator, addFramework(_, _, _));
-
-  EXPECT_CALL(sched2, registered(_, _, _));
-
-  // The first time framework2 is allocated to, it should be offered
-  // all of the resources on slave1 that are reserved to role2.
-  Future<Nothing> resourceOffers2;
-  EXPECT_CALL(sched2, resourceOffers(_, OfferEq(2, 600)))
-    .WillOnce(FutureSatisfy(&resourceOffers2));
-
-  driver2.start();
-
-  AWAIT_READY(resourceOffers2);
-
-  TaskStatus status;
-  status.mutable_task_id()->MergeFrom(taskInfo.task_id());
-  status.set_state(TASK_FINISHED);
-
-  EXPECT_CALL(allocator, recoverResources(_, _, _, _));
-
-  // After the task finishes, its resources should be reoffered to
-  // framework1.
-  Future<Nothing> resourceOffers3;
-  EXPECT_CALL(sched1, resourceOffers(_, OfferEq(1, 100)))
-    .WillOnce(FutureSatisfy(&resourceOffers3));
-
-  execDriver->sendStatusUpdate(status);
-
-  AWAIT_READY(resourceOffers3);
-
-  // Shut everything down.
-  EXPECT_CALL(allocator, recoverResources(_, _, _, _))
-    .WillRepeatedly(DoDefault());
-
-  EXPECT_CALL(allocator, deactivateFramework(_))
-    .Times(AtMost(2));
-
-  EXPECT_CALL(allocator, removeFramework(_))
-    .Times(AtMost(2));
-
-  Future<Nothing> shutdown;
-  EXPECT_CALL(exec, shutdown(_))
-    .WillOnce(FutureSatisfy(&shutdown));
-
-  driver2.stop();
-  driver1.stop();
-
-  AWAIT_READY(shutdown); // Ensures MockExecutor can be deallocated.
-
-  EXPECT_CALL(allocator, removeSlave(_))
-    .Times(AtMost(2));
-
-  this->Shutdown();
-}
-
-
 template <typename T>
 class MasterAllocatorTest : public MesosTest
 {


[07/11] mesos git commit: Moved DRFAllocatorTest.PerSlaveAllocation to a unit test.

Posted by bm...@apache.org.
Moved DRFAllocatorTest.PerSlaveAllocation to a unit test.

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


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

Branch: refs/heads/master
Commit: 3513c21aa0a3c524a59c73f576bd9ec1fbc749ca
Parents: 6cf1b01
Author: Benjamin Mahler <be...@gmail.com>
Authored: Sat Dec 6 18:54:04 2014 -0800
Committer: Benjamin Mahler <be...@gmail.com>
Committed: Thu Dec 11 14:40:30 2014 -0800

----------------------------------------------------------------------
 src/tests/hierarchical_allocator_tests.cpp |  72 ++++++++++++
 src/tests/master_allocator_tests.cpp       | 144 ------------------------
 2 files changed, 72 insertions(+), 144 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/3513c21a/src/tests/hierarchical_allocator_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/hierarchical_allocator_tests.cpp b/src/tests/hierarchical_allocator_tests.cpp
index 9ecbd0f..9f25167 100644
--- a/src/tests/hierarchical_allocator_tests.cpp
+++ b/src/tests/hierarchical_allocator_tests.cpp
@@ -280,3 +280,75 @@ TEST_F(HierarchicalAllocatorTest, DRF)
   EXPECT_EQ(framework2.id(), allocation.get().frameworkId);
   EXPECT_EQ(slave5.resources(), sum(allocation.get().resources.values()));
 }
+
+
+// This test ensures that allocation is done per slave. This is done
+// by having 2 slaves and 2 frameworks and making sure each framework
+// gets only one slave's resources during an allocation.
+TEST_F(HierarchicalAllocatorTest, CoarseGrained)
+{
+  // Pausing the clock ensures that the batch allocation does not
+  // influence this test.
+  Clock::pause();
+
+  initialize({"role1", "role2"});
+
+  hashmap<FrameworkID, Resources> EMPTY;
+
+  SlaveInfo slave1 = createSlaveInfo("cpus:2;mem:1024;disk:0");
+  allocator->addSlave(slave1.id(), slave1, slave1.resources(), EMPTY);
+
+  SlaveInfo slave2 = createSlaveInfo("cpus:2;mem:1024;disk:0");
+  allocator->addSlave(slave2.id(), slave2, slave2.resources(), EMPTY);
+
+  // Once framework1 is added, an allocation will occur. Return the
+  // resources so that we can test what happens when there are 2
+  // frameworks and 2 slaves to consider during allocation.
+  FrameworkInfo framework1 = createFrameworkInfo("role1");
+  allocator->addFramework(framework1.id(), framework1, Resources());
+
+  Future<Allocation> allocation = queue.get();
+  AWAIT_READY(allocation);
+  EXPECT_EQ(framework1.id(), allocation.get().frameworkId);
+  EXPECT_EQ(slave1.resources() + slave2.resources(),
+            sum(allocation.get().resources.values()));
+
+  allocator->recoverResources(
+      framework1.id(),
+      slave1.id(),
+      allocation.get().resources.get(slave1.id()).get(),
+      None());
+  allocator->recoverResources(
+      framework1.id(),
+      slave2.id(),
+      allocation.get().resources.get(slave2.id()).get(),
+      None());
+
+  // Now add the second framework, we expect there to be 2 subsequent
+  // allocations, each framework being allocated a full slave.
+  FrameworkInfo framework2 = createFrameworkInfo("role2");
+  allocator->addFramework(framework2.id(), framework2, Resources());
+
+  hashmap<FrameworkID, Allocation> allocations;
+
+  allocation = queue.get();
+  AWAIT_READY(allocation);
+  allocations[allocation.get().frameworkId] = allocation.get();
+
+  allocation = queue.get();
+  AWAIT_READY(allocation);
+  allocations[allocation.get().frameworkId] = allocation.get();
+
+  // Note that slave1 and slave2 have the same resources, we don't
+  // care which framework received which slave.. only that they each
+  // received one.
+  ASSERT_TRUE(allocations.contains(framework1.id()));
+  ASSERT_EQ(1u, allocations[framework1.id()].resources.size());
+  EXPECT_EQ(slave1.resources(),
+            sum(allocations[framework1.id()].resources.values()));
+
+  ASSERT_TRUE(allocations.contains(framework2.id()));
+  ASSERT_EQ(1u, allocations[framework1.id()].resources.size());
+  EXPECT_EQ(slave2.resources(),
+            sum(allocations[framework1.id()].resources.values()));
+}

http://git-wip-us.apache.org/repos/asf/mesos/blob/3513c21a/src/tests/master_allocator_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/master_allocator_tests.cpp b/src/tests/master_allocator_tests.cpp
index de0d7e8..8de1d2a 100644
--- a/src/tests/master_allocator_tests.cpp
+++ b/src/tests/master_allocator_tests.cpp
@@ -72,150 +72,6 @@ using testing::SaveArg;
 // master and the allocator.
 class DRFAllocatorTest : public MesosTest {};
 
-// This test ensures that allocation is done per slave. This is done
-// by having 2 slaves and 2 frameworks and making sure each framework
-// gets only one slave's resources during an allocation.
-TEST_F(DRFAllocatorTest, PerSlaveAllocation)
-{
-  MockAllocatorProcess<HierarchicalDRFAllocatorProcess> allocator;
-
-  EXPECT_CALL(allocator, initialize(_, _, _));
-
-  // Start the master.
-  // NOTE: We set a high allocation interval, so that allocator does
-  // allocations only based on events (framework added, slave added)
-  // but not due to allocation interval. This lets us tightly control
-  // the test expectations.
-  master::Flags masterFlags = CreateMasterFlags();
-  masterFlags.roles = Some("role1,role2");
-  masterFlags.allocation_interval = Days(1);
-  Try<PID<Master> > master = StartMaster(&allocator, masterFlags);
-  ASSERT_SOME(master);
-
-  // Start slave 1.
-  slave::Flags flags1 = CreateSlaveFlags();
-  flags1.resources = Some("cpus:2;mem:1024;disk:0");
-
-  Future<Nothing> addSlave1;
-  EXPECT_CALL(allocator, addSlave(_, _, _, _))
-    .WillOnce(DoAll(InvokeSlaveAdded(&allocator),
-                    FutureSatisfy(&addSlave1)));
-
-  Try<PID<Slave> > slave1 = StartSlave(flags1);
-  ASSERT_SOME(slave1);
-
-  AWAIT_READY(addSlave1);
-
-  // Start slave 2.
-  slave::Flags flags2 = CreateSlaveFlags();
-  flags2.resources = Some("cpus:2;mem:1024;disk:0");
-
-  Future<Nothing> addSlave2;
-  EXPECT_CALL(allocator, addSlave(_, _, _, _))
-    .WillOnce(DoAll(InvokeSlaveAdded(&allocator),
-                    FutureSatisfy(&addSlave2)));
-
-  Try<PID<Slave> > slave2 = StartSlave(flags2);
-  ASSERT_SOME(slave2);
-
-  AWAIT_READY(addSlave2);
-
-  // Start framework 1.
-  FrameworkInfo frameworkInfo1; // Bug in gcc 4.1.*, must assign on next line.
-  frameworkInfo1 = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo1.set_name("framework1");
-  frameworkInfo1.set_user("user1");
-  frameworkInfo1.set_role("role1");
-
-  MockScheduler sched1;
-  MesosSchedulerDriver driver1(
-      &sched1, frameworkInfo1, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(allocator, addFramework(_, _, _));
-
-  EXPECT_CALL(sched1, registered(_, _, _));
-
-  Future<Nothing> recoverResources1;
-  Future<Nothing> recoverResources2;
-  EXPECT_CALL(allocator, recoverResources(_, _, _, _))
-    .WillOnce(DoAll(InvokeResourcesRecovered(&allocator),
-                    FutureSatisfy(&recoverResources1)))
-    .WillOnce(DoAll(InvokeResourcesRecovered(&allocator),
-                    FutureSatisfy(&recoverResources2)));
-
-  // Decline the offers immediately so that resources for both slaves
-  // are eligible for allocation to this and other frameworks.
-  Filters filters;
-  filters.set_refuse_seconds(0);
-  EXPECT_CALL(sched1, resourceOffers(_, _))
-    .WillOnce(DeclineOffers(filters));
-
-  driver1.start();
-
-  // Wait until the resources are returned to the allocator.
-  // NOTE: No allocations will be made after this point until a new
-  // framework registers because
-  // 1) 'recoverResources' does not trigger an allocation and
-  // 2) 'flags.allocation_interval' is set to a very high value.
-  AWAIT_READY(recoverResources1);
-  AWAIT_READY(recoverResources2);
-
-  // Start framework 2.
-  FrameworkInfo frameworkInfo2; // Bug in gcc 4.1.*, must assign on next line.
-  frameworkInfo2 = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo2.set_name("framework2");
-  frameworkInfo2.set_user("user2");
-  frameworkInfo2.set_role("role2");
-
-  MockScheduler sched2;
-  MesosSchedulerDriver driver2(
-      &sched2, frameworkInfo2, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(allocator, addFramework(_, _, _));
-
-  EXPECT_CALL(sched2, registered(_, _, _));
-
-  // Offers to framework 1.
-  Future<vector<Offer> > offers1;
-  EXPECT_CALL(sched1, resourceOffers(_, _))
-    .WillOnce(FutureArg<1>(&offers1));
-
-  // Offers to framework 2.
-  Future<vector<Offer> > offers2;
-  EXPECT_CALL(sched2, resourceOffers(_, _))
-    .WillOnce(FutureArg<1>(&offers2));
-
-  driver2.start();
-
-  // Now each framework should receive offers for one slave each.
-  AWAIT_READY(offers1);
-  EXPECT_THAT(offers1.get(), OfferEq(2, 1024));
-
-  AWAIT_READY(offers2);
-  EXPECT_THAT(offers2.get(), OfferEq(2, 1024));
-
-  // Shut everything down.
-  EXPECT_CALL(allocator, recoverResources(_, _, _, _))
-    .WillRepeatedly(DoDefault());
-
-  EXPECT_CALL(allocator, deactivateFramework(_))
-    .WillRepeatedly(DoDefault());
-
-  EXPECT_CALL(allocator, removeFramework(_))
-    .WillRepeatedly(DoDefault());
-
-  driver1.stop();
-  driver1.join();
-
-  driver2.stop();
-  driver2.join();
-
-  EXPECT_CALL(allocator, removeSlave(_))
-    .WillRepeatedly(DoDefault());
-
-  Shutdown();
-}
-
 
 // Helper that simply increments the value by reference.
 ACTION_P(Increment, value) { *value += 1; }


[10/11] mesos git commit: Moved DRFAllocatorTest.SameShareAllocations to a unit test.

Posted by bm...@apache.org.
Moved DRFAllocatorTest.SameShareAllocations to a unit test.

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


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

Branch: refs/heads/master
Commit: 7cfd1d4611eb4f951e82ac90bc9d9abbecf27277
Parents: 3513c21
Author: Benjamin Mahler <be...@gmail.com>
Authored: Sat Dec 6 19:29:04 2014 -0800
Committer: Benjamin Mahler <be...@gmail.com>
Committed: Thu Dec 11 14:40:30 2014 -0800

----------------------------------------------------------------------
 src/tests/hierarchical_allocator_tests.cpp |  47 ++++++++++
 src/tests/master_allocator_tests.cpp       | 111 ------------------------
 2 files changed, 47 insertions(+), 111 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/7cfd1d46/src/tests/hierarchical_allocator_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/hierarchical_allocator_tests.cpp b/src/tests/hierarchical_allocator_tests.cpp
index 9f25167..b813d0c 100644
--- a/src/tests/hierarchical_allocator_tests.cpp
+++ b/src/tests/hierarchical_allocator_tests.cpp
@@ -352,3 +352,50 @@ TEST_F(HierarchicalAllocatorTest, CoarseGrained)
   EXPECT_EQ(slave2.resources(),
             sum(allocations[framework1.id()].resources.values()));
 }
+
+
+// This test ensures that frameworks that have the same share get an
+// equal number of allocations over time (rather than the same
+// framework getting all the allocations because it's name is
+// lexicographically ordered first).
+TEST_F(HierarchicalAllocatorTest, SameShareFairness)
+{
+  Clock::pause();
+
+  initialize({});
+
+  hashmap<FrameworkID, Resources> EMPTY;
+
+  FrameworkInfo framework1 = createFrameworkInfo("*");
+  allocator->addFramework(framework1.id(), framework1, Resources());
+
+  FrameworkInfo framework2 = createFrameworkInfo("*");
+  allocator->addFramework(framework2.id(), framework2, Resources());
+
+  SlaveInfo slave = createSlaveInfo("cpus:2;mem:1024;disk:0");
+  allocator->addSlave(slave.id(), slave, slave.resources(), EMPTY);
+
+  // Ensure that the slave's resources are alternated between both
+  // frameworks.
+  hashmap<FrameworkID, size_t> counts;
+
+  for (int i = 0; i < 10; i++) {
+    Future<Allocation> allocation = queue.get();
+    AWAIT_READY(allocation);
+    counts[allocation.get().frameworkId]++;
+
+    ASSERT_EQ(1u, allocation.get().resources.size());
+    EXPECT_EQ(slave.resources(), sum(allocation.get().resources.values()));
+
+    allocator->recoverResources(
+        allocation.get().frameworkId,
+        slave.id(),
+        allocation.get().resources.get(slave.id()).get(),
+        None());
+
+    Clock::advance(flags.allocation_interval);
+  }
+
+  EXPECT_EQ(5u, counts[framework1.id()]);
+  EXPECT_EQ(5u, counts[framework2.id()]);
+}

http://git-wip-us.apache.org/repos/asf/mesos/blob/7cfd1d46/src/tests/master_allocator_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/master_allocator_tests.cpp b/src/tests/master_allocator_tests.cpp
index 8de1d2a..da32c39 100644
--- a/src/tests/master_allocator_tests.cpp
+++ b/src/tests/master_allocator_tests.cpp
@@ -67,117 +67,6 @@ using testing::DoDefault;
 using testing::Eq;
 using testing::SaveArg;
 
-// TODO(bmahler): Move the remainder of the DRFAllocatorTests to unit
-// tests. This file should only be testing the integration between the
-// master and the allocator.
-class DRFAllocatorTest : public MesosTest {};
-
-
-// Helper that simply increments the value by reference.
-ACTION_P(Increment, value) { *value += 1; }
-
-
-// This test ensures that frameworks that have the same share get an
-// equal number of allocations over time (rather than the same
-// framework getting all the allocations because it's name is
-// lexicographically ordered first).
-TEST_F(DRFAllocatorTest, SameShareAllocations)
-{
-  MockAllocatorProcess<HierarchicalDRFAllocatorProcess> allocator;
-
-  EXPECT_CALL(allocator, initialize(_, _, _));
-
-  master::Flags masterFlags = CreateMasterFlags();
-  Try<PID<Master> > master = StartMaster(&allocator, masterFlags);
-  ASSERT_SOME(master);
-
-  // Start the first scheduler.
-  FrameworkInfo frameworkInfo1; // Bug in gcc 4.1.*, must assign on next line.
-  frameworkInfo1 = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo1.set_name("framework1");
-
-  MockScheduler sched1;
-  MesosSchedulerDriver driver1(
-      &sched1, frameworkInfo1, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(allocator, addFramework(_, _, _));
-
-  Future<Nothing> registered1;
-  EXPECT_CALL(sched1, registered(_, _, _))
-    .WillOnce(FutureSatisfy(&registered1));
-
-  driver1.start();
-
-  AWAIT_READY(registered1);
-
-  // Start the second scheduler.
-  FrameworkInfo frameworkInfo2; // Bug in gcc 4.1.*, must assign on next line.
-  frameworkInfo2 = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo2.set_name("framework2");
-
-  MockScheduler sched2;
-  MesosSchedulerDriver driver2(
-      &sched2, frameworkInfo2, master.get(), DEFAULT_CREDENTIAL);
-
-  // We need to retire this expectation on the first match because
-  // framework1 can match this expectation first in which case
-  // framework2 should be able to match the expectation above.
-  EXPECT_CALL(allocator, addFramework(_, _, _))
-    .RetiresOnSaturation();
-
-  Future<Nothing> registered2;
-  EXPECT_CALL(sched2, registered(_, _, _))
-    .WillOnce(FutureSatisfy(&registered2));
-
-  driver2.start();
-
-  AWAIT_READY(registered2);
-
-  // Set filter timeout to 0 so that both frameworks are eligible
-  // for allocation during every allocation interval.
-  Filters filters;
-  filters.set_refuse_seconds(0);
-
-  int allocations1 = 0;
-  EXPECT_CALL(sched1, resourceOffers(_, _))
-    .WillRepeatedly(DoAll(Increment(&allocations1),
-                          DeclineOffers(filters)));
-
-  int allocations2 = 0;
-  EXPECT_CALL(sched2, resourceOffers(_, _))
-    .WillRepeatedly(DoAll(Increment(&allocations2),
-                          DeclineOffers(filters)));
-
-  EXPECT_CALL(allocator, recoverResources(_, _, _, _))
-    .WillRepeatedly(DoDefault());
-
-  // Start the slave.
-  EXPECT_CALL(allocator, addSlave(_, _, _, _));
-
-  Try<PID<Slave> > slave = StartSlave();
-  ASSERT_SOME(slave);
-
-  // Continuously do allocations.
-  Clock::pause();
-  while(allocations1 + allocations2 < 10) {
-    Clock::advance(masterFlags.allocation_interval);
-    Clock::settle();
-  }
-
-  // Each framework should get equal number of allocations.
-  ASSERT_EQ(allocations1, allocations2);
-
-  Clock::resume();
-
-  driver1.stop();
-  driver1.join();
-
-  driver2.stop();
-  driver2.join();
-
-  Shutdown();
-}
-
 
 class ReservationAllocatorTest : public MesosTest {};
 


[09/11] mesos git commit: Updated the allocator interface to enable unit testing.

Posted by bm...@apache.org.
Updated the allocator interface to enable unit testing.

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


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

Branch: refs/heads/master
Commit: e72037f3459d40412a5f677f7c3d5edc2bb7a033
Parents: 300327f
Author: Benjamin Mahler <be...@gmail.com>
Authored: Fri Dec 5 12:38:15 2014 -0800
Committer: Benjamin Mahler <be...@gmail.com>
Committed: Thu Dec 11 14:40:30 2014 -0800

----------------------------------------------------------------------
 src/master/allocator.hpp                      | 15 +++++++++++----
 src/master/hierarchical_allocator_process.hpp | 22 ++++++++++++++--------
 src/master/master.cpp                         |  5 ++++-
 src/tests/mesos.hpp                           |  4 +++-
 4 files changed, 32 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/e72037f3/src/master/allocator.hpp
----------------------------------------------------------------------
diff --git a/src/master/allocator.hpp b/src/master/allocator.hpp
index 0849ac8..f4068aa 100644
--- a/src/master/allocator.hpp
+++ b/src/master/allocator.hpp
@@ -30,6 +30,7 @@
 
 #include <stout/hashmap.hpp>
 #include <stout/hashset.hpp>
+#include <stout/lambda.hpp>
 #include <stout/option.hpp>
 
 #include "master/flags.hpp"
@@ -66,7 +67,9 @@ public:
 
   void initialize(
       const Flags& flags,
-      const process::PID<Master>& master,
+      const lambda::function<
+          void(const FrameworkID&,
+               const hashmap<SlaveID, Resources>&)>& offerCallback,
       const hashmap<std::string, RoleInfo>& roles);
 
   void addFramework(
@@ -146,7 +149,9 @@ public:
 
   virtual void initialize(
       const Flags& flags,
-      const process::PID<Master>& master,
+      const lambda::function<
+          void(const FrameworkID&,
+               const hashmap<SlaveID, Resources>&)>& offerCallback,
       const hashmap<std::string, RoleInfo>& roles) = 0;
 
   virtual void addFramework(
@@ -212,14 +217,16 @@ inline Allocator::~Allocator()
 
 inline void Allocator::initialize(
     const Flags& flags,
-    const process::PID<Master>& master,
+    const lambda::function<
+        void(const FrameworkID&,
+             const hashmap<SlaveID, Resources>&)>& offerCallback,
     const hashmap<std::string, RoleInfo>& roles)
 {
   process::dispatch(
       process,
       &AllocatorProcess::initialize,
       flags,
-      master,
+      offerCallback,
       roles);
 }
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/e72037f3/src/master/hierarchical_allocator_process.hpp
----------------------------------------------------------------------
diff --git a/src/master/hierarchical_allocator_process.hpp b/src/master/hierarchical_allocator_process.hpp
index f18346f..70970be 100644
--- a/src/master/hierarchical_allocator_process.hpp
+++ b/src/master/hierarchical_allocator_process.hpp
@@ -71,8 +71,10 @@ public:
 
   void initialize(
       const Flags& flags,
-      const process::PID<Master>& _master,
-      const hashmap<std::string, RoleInfo>& _roles);
+      const lambda::function<
+          void(const FrameworkID&,
+               const hashmap<SlaveID, Resources>&)>& offerCallback,
+      const hashmap<std::string, RoleInfo>& roles);
 
   void addFramework(
       const FrameworkID& frameworkId,
@@ -154,7 +156,10 @@ protected:
   bool initialized;
 
   Flags flags;
-  process::PID<Master> master;
+
+  lambda::function<
+      void(const FrameworkID&,
+           const hashmap<SlaveID, Resources>&)> offerCallback;
 
   struct Framework
   {
@@ -248,11 +253,13 @@ template <class RoleSorter, class FrameworkSorter>
 void
 HierarchicalAllocatorProcess<RoleSorter, FrameworkSorter>::initialize(
     const Flags& _flags,
-    const process::PID<Master>& _master,
+    const lambda::function<
+        void(const FrameworkID&,
+             const hashmap<SlaveID, Resources>&)>& _offerCallback,
     const hashmap<std::string, RoleInfo>& _roles)
 {
   flags = _flags;
-  master = _master;
+  offerCallback = _offerCallback;
   roles = _roles;
   initialized = true;
 
@@ -262,8 +269,7 @@ HierarchicalAllocatorProcess<RoleSorter, FrameworkSorter>::initialize(
     sorters[name] = new FrameworkSorter();
   }
 
-  VLOG(1) << "Initializing hierarchical allocator process "
-          << "with master : " << master;
+  VLOG(1) << "Initializing hierarchical allocator process";
 
   delay(flags.allocation_interval, self(), &Self::batch);
 }
@@ -734,7 +740,7 @@ HierarchicalAllocatorProcess<RoleSorter, FrameworkSorter>::allocate(
 
   // Now offer the resources to each framework.
   foreachkey (const FrameworkID& frameworkId, offerable) {
-    dispatch(master, &Master::offer, frameworkId, offerable[frameworkId]);
+    offerCallback(frameworkId, offerable[frameworkId]);
   }
 }
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/e72037f3/src/master/master.cpp
----------------------------------------------------------------------
diff --git a/src/master/master.cpp b/src/master/master.cpp
index 9936980..0f55a5c 100644
--- a/src/master/master.cpp
+++ b/src/master/master.cpp
@@ -456,7 +456,10 @@ void Master::initialize()
   }
 
   // Initialize the allocator.
-  allocator->initialize(flags, self(), roleInfos);
+  allocator->initialize(
+      flags,
+      defer(self(), &Master::offer, lambda::_1, lambda::_2),
+      roleInfos);
 
   // Parse the whitelist. Passing allocator::updateWhitelist()
   // callback is safe because we shut down the whitelistWatcher in

http://git-wip-us.apache.org/repos/asf/mesos/blob/e72037f3/src/tests/mesos.hpp
----------------------------------------------------------------------
diff --git a/src/tests/mesos.hpp b/src/tests/mesos.hpp
index 90c575e..bb24222 100644
--- a/src/tests/mesos.hpp
+++ b/src/tests/mesos.hpp
@@ -734,7 +734,9 @@ public:
 
   MOCK_METHOD3(initialize, void(
       const master::Flags&,
-      const process::PID<master::Master>&,
+      const lambda::function<
+          void(const FrameworkID&,
+               const hashmap<SlaveID, Resources>&)>&,
       const hashmap<std::string, RoleInfo>&));
 
   MOCK_METHOD3(addFramework, void(


[11/11] mesos git commit: Documented the expected allocator sorting semantics.

Posted by bm...@apache.org.
Documented the expected allocator sorting semantics.

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


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

Branch: refs/heads/master
Commit: e2239a02c9ea7c541eae5808807c49a2a5676d8a
Parents: feedbfd
Author: Benjamin Mahler <be...@gmail.com>
Authored: Wed Dec 3 17:22:56 2014 -0800
Committer: Benjamin Mahler <be...@gmail.com>
Committed: Thu Dec 11 14:40:31 2014 -0800

----------------------------------------------------------------------
 src/master/hierarchical_allocator_process.hpp | 60 ++++++++++++----------
 1 file changed, 34 insertions(+), 26 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/e2239a02/src/master/hierarchical_allocator_process.hpp
----------------------------------------------------------------------
diff --git a/src/master/hierarchical_allocator_process.hpp b/src/master/hierarchical_allocator_process.hpp
index 70970be..610fcd9 100644
--- a/src/master/hierarchical_allocator_process.hpp
+++ b/src/master/hierarchical_allocator_process.hpp
@@ -171,10 +171,6 @@ protected:
 
   hashmap<FrameworkID, Framework> frameworks;
 
-  // Maps role names to the Sorter object which contains
-  // all of that role's frameworks.
-  hashmap<std::string, FrameworkSorter*> sorters;
-
   struct Slave
   {
     Resources total;
@@ -193,8 +189,18 @@ protected:
   // Slaves to send offers for.
   Option<hashset<std::string> > whitelist;
 
-  // Sorter containing all active roles.
+  // There are two levels of sorting, hence "hierarchical".
+  // Level 1 sorts across roles:
+  //   Reserved resources are excluded from fairness calculation,
+  //   since they are forcibly pinned to a role.
+  // Level 2 sorts across frameworks within a particular role:
+  //   Both reserved resources and unreserved resources are used
+  //   in the fairness calculation. This is because reserved
+  //   resources can be allocated to any framework in the role.
+  // TODO(bmahler): The code is currently inconsistent in following
+  // this specification, see MESOS-2176.
   RoleSorter* roleSorter;
+  hashmap<std::string, FrameworkSorter*> frameworkSorters;
 };
 
 
@@ -266,7 +272,7 @@ HierarchicalAllocatorProcess<RoleSorter, FrameworkSorter>::initialize(
   roleSorter = new RoleSorter();
   foreachpair (const std::string& name, const RoleInfo& roleInfo, roles) {
     roleSorter->add(name, roleInfo.weight());
-    sorters[name] = new FrameworkSorter();
+    frameworkSorters[name] = new FrameworkSorter();
   }
 
   VLOG(1) << "Initializing hierarchical allocator process";
@@ -288,13 +294,13 @@ HierarchicalAllocatorProcess<RoleSorter, FrameworkSorter>::addFramework(
 
   CHECK(roles.contains(role));
 
-  CHECK(!sorters[role]->contains(frameworkId.value()));
-  sorters[role]->add(frameworkId.value());
+  CHECK(!frameworkSorters[role]->contains(frameworkId.value()));
+  frameworkSorters[role]->add(frameworkId.value());
 
   // Update the allocation to this framework.
   roleSorter->allocated(role, used);
-  sorters[role]->add(used);
-  sorters[role]->allocated(frameworkId.value(), used);
+  frameworkSorters[role]->add(used);
+  frameworkSorters[role]->allocated(frameworkId.value(), used);
 
   frameworks[frameworkId] = Framework();
   frameworks[frameworkId].role = frameworkInfo.role();
@@ -316,13 +322,14 @@ HierarchicalAllocatorProcess<RoleSorter, FrameworkSorter>::removeFramework(
   CHECK(frameworks.contains(frameworkId));
   const std::string& role = frameworks[frameworkId].role;
 
-  // Might not be in 'sorters[role]' because it was previously
+  // Might not be in 'frameworkSorters[role]' because it was previously
   // deactivated and never re-added.
-  if (sorters[role]->contains(frameworkId.value())) {
-    Resources allocation = sorters[role]->allocation(frameworkId.value());
+  if (frameworkSorters[role]->contains(frameworkId.value())) {
+    Resources allocation =
+      frameworkSorters[role]->allocation(frameworkId.value());
     roleSorter->unallocated(role, allocation);
-    sorters[role]->remove(allocation);
-    sorters[role]->remove(frameworkId.value());
+    frameworkSorters[role]->remove(allocation);
+    frameworkSorters[role]->remove(frameworkId.value());
   }
 
   // Do not delete the filters contained in this
@@ -345,7 +352,7 @@ HierarchicalAllocatorProcess<RoleSorter, FrameworkSorter>::activateFramework(
   CHECK(frameworks.contains(frameworkId));
   const std::string& role = frameworks[frameworkId].role;
 
-  sorters[role]->activate(frameworkId.value());
+  frameworkSorters[role]->activate(frameworkId.value());
 
   LOG(INFO) << "Activated framework " << frameworkId;
 
@@ -363,7 +370,7 @@ HierarchicalAllocatorProcess<RoleSorter, FrameworkSorter>::deactivateFramework(
   CHECK(frameworks.contains(frameworkId));
   const std::string& role = frameworks[frameworkId].role;
 
-  sorters[role]->deactivate(frameworkId.value());
+  frameworkSorters[role]->deactivate(frameworkId.value());
 
   // Note that the Sorter *does not* remove the resources allocated
   // to this framework. For now, this is important because if the
@@ -415,8 +422,8 @@ HierarchicalAllocatorProcess<RoleSorter, FrameworkSorter>::addSlave(
     if (frameworks.contains(frameworkId)) {
       const std::string& role = frameworks[frameworkId].role;
 
-      sorters[role]->add(resources);
-      sorters[role]->allocated(frameworkId.value(), resources);
+      frameworkSorters[role]->add(resources);
+      frameworkSorters[role]->allocated(frameworkId.value(), resources);
       roleSorter->allocated(role, resources);
     }
   }
@@ -540,11 +547,11 @@ HierarchicalAllocatorProcess<RoleSorter, FrameworkSorter>::recoverResources(
   if (frameworks.contains(frameworkId)) {
     const std::string& role = frameworks[frameworkId].role;
 
-    CHECK(sorters.contains(role));
+    CHECK(frameworkSorters.contains(role));
 
-    if (sorters[role]->contains(frameworkId.value())) {
-      sorters[role]->unallocated(frameworkId.value(), resources);
-      sorters[role]->remove(resources);
+    if (frameworkSorters[role]->contains(frameworkId.value())) {
+      frameworkSorters[role]->unallocated(frameworkId.value(), resources);
+      frameworkSorters[role]->remove(resources);
       roleSorter->unallocated(role, resources);
     }
   }
@@ -699,7 +706,8 @@ HierarchicalAllocatorProcess<RoleSorter, FrameworkSorter>::allocate(
     }
 
     foreach (const std::string& role, roleSorter->sort()) {
-      foreach (const std::string& frameworkIdValue, sorters[role]->sort()) {
+      foreach (const std::string& frameworkIdValue,
+               frameworkSorters[role]->sort()) {
         FrameworkID frameworkId;
         frameworkId.set_value(frameworkIdValue);
 
@@ -731,8 +739,8 @@ HierarchicalAllocatorProcess<RoleSorter, FrameworkSorter>::allocate(
         // Update the sorters.
         // We only count resources not reserved for this role
         // in the share the sorter considers.
-        sorters[role]->add(unreserved);
-        sorters[role]->allocated(frameworkIdValue, unreserved);
+        frameworkSorters[role]->add(unreserved);
+        frameworkSorters[role]->allocated(frameworkIdValue, unreserved);
         roleSorter->allocated(role, unreserved);
       }
     }


[06/11] mesos git commit: Added missing includes to sorter.hpp.

Posted by bm...@apache.org.
Added missing includes to sorter.hpp.

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


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

Branch: refs/heads/master
Commit: 300327f098045ea818ea4cd6b8a17bc621e210ac
Parents: c1421aa
Author: Benjamin Mahler <be...@gmail.com>
Authored: Thu Dec 4 14:40:53 2014 -0800
Committer: Benjamin Mahler <be...@gmail.com>
Committed: Thu Dec 11 14:40:30 2014 -0800

----------------------------------------------------------------------
 src/master/sorter.hpp | 2 ++
 1 file changed, 2 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/300327f0/src/master/sorter.hpp
----------------------------------------------------------------------
diff --git a/src/master/sorter.hpp b/src/master/sorter.hpp
index aabdd0d..818967f 100644
--- a/src/master/sorter.hpp
+++ b/src/master/sorter.hpp
@@ -20,7 +20,9 @@
 #define __SORTER_HPP__
 
 #include <list>
+#include <string>
 
+#include <mesos/resources.hpp>
 
 namespace mesos {
 namespace internal {


[08/11] mesos git commit: Moved ReservationAllocatorTest.ReservedResources to a unit test.

Posted by bm...@apache.org.
Moved ReservationAllocatorTest.ReservedResources to a unit test.

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


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

Branch: refs/heads/master
Commit: a56c57993e6c847243ee228d131fb48a248aa6a8
Parents: 7cfd1d4
Author: Benjamin Mahler <be...@gmail.com>
Authored: Sun Dec 7 16:57:54 2014 -0800
Committer: Benjamin Mahler <be...@gmail.com>
Committed: Thu Dec 11 14:40:30 2014 -0800

----------------------------------------------------------------------
 src/tests/hierarchical_allocator_tests.cpp |  52 ++++++++
 src/tests/master_allocator_tests.cpp       | 154 ------------------------
 2 files changed, 52 insertions(+), 154 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/a56c5799/src/tests/hierarchical_allocator_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/hierarchical_allocator_tests.cpp b/src/tests/hierarchical_allocator_tests.cpp
index b813d0c..f20be8c 100644
--- a/src/tests/hierarchical_allocator_tests.cpp
+++ b/src/tests/hierarchical_allocator_tests.cpp
@@ -399,3 +399,55 @@ TEST_F(HierarchicalAllocatorTest, SameShareFairness)
   EXPECT_EQ(5u, counts[framework1.id()]);
   EXPECT_EQ(5u, counts[framework2.id()]);
 }
+
+
+// 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)
+{
+  Clock::pause();
+
+  initialize({"role1", "role2", "role3"});
+
+  hashmap<FrameworkID, Resources> EMPTY;
+
+  SlaveInfo slave1 = createSlaveInfo(
+      "cpus(role1):2;mem(role1):1024;disk(role1):0");
+  allocator->addSlave(slave1.id(), slave1, slave1.resources(), EMPTY);
+
+  SlaveInfo slave2 = createSlaveInfo(
+      "cpus(role2):2;mem(role2):1024;cpus:1;mem:1024;disk:0");
+  allocator->addSlave(slave2.id(), slave2, slave2.resources(), EMPTY);
+
+  // This slave's resources should never be allocated, since there
+  // is no framework for role3.
+  SlaveInfo slave3 = createSlaveInfo(
+      "cpus(role3):1;mem(role3):1024;disk(role3):0");
+  allocator->addSlave(slave3.id(), slave3, slave3.resources(), EMPTY);
+
+  // framework1 should get all the resources from slave1, and the
+  // unreserved resources from slave2.
+  FrameworkInfo framework1 = createFrameworkInfo("role1");
+  allocator->addFramework(framework1.id(), framework1, Resources());
+
+  Future<Allocation> allocation = queue.get();
+  AWAIT_READY(allocation);
+  EXPECT_EQ(framework1.id(), allocation.get().frameworkId);
+  EXPECT_EQ(2u, allocation.get().resources.size());
+  EXPECT_TRUE(allocation.get().resources.contains(slave1.id()));
+  EXPECT_TRUE(allocation.get().resources.contains(slave2.id()));
+  EXPECT_EQ(slave1.resources() + Resources(slave2.resources()).extract("*"),
+            sum(allocation.get().resources.values()));
+
+  // framework2 should get all of its reserved resources on slave2.
+  FrameworkInfo framework2 = createFrameworkInfo("role2");
+  allocator->addFramework(framework2.id(), framework2, Resources());
+
+  allocation = queue.get();
+  AWAIT_READY(allocation);
+  EXPECT_EQ(framework2.id(), allocation.get().frameworkId);
+  EXPECT_EQ(1u, allocation.get().resources.size());
+  EXPECT_TRUE(allocation.get().resources.contains(slave2.id()));
+  EXPECT_EQ(Resources(slave2.resources()).extract("role2"),
+            sum(allocation.get().resources.values()));
+}

http://git-wip-us.apache.org/repos/asf/mesos/blob/a56c5799/src/tests/master_allocator_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/master_allocator_tests.cpp b/src/tests/master_allocator_tests.cpp
index da32c39..61a5f5c 100644
--- a/src/tests/master_allocator_tests.cpp
+++ b/src/tests/master_allocator_tests.cpp
@@ -71,160 +71,6 @@ using testing::SaveArg;
 class ReservationAllocatorTest : public MesosTest {};
 
 
-// Checks that resources on a slave that are statically reserved to
-// a role are only offered to frameworks in that role.
-TEST_F(ReservationAllocatorTest, ReservedResources)
-{
-  MockAllocatorProcess<HierarchicalDRFAllocatorProcess> allocator;
-
-  EXPECT_CALL(allocator, initialize(_, _, _));
-
-  master::Flags masterFlags = CreateMasterFlags();
-  masterFlags.roles = Some("role1,role2,role3");
-  Try<PID<Master> > master = StartMaster(&allocator, masterFlags);
-
-  ASSERT_SOME(master);
-
-  Future<Nothing> addSlave;
-  EXPECT_CALL(allocator, addSlave(_, _, _, _))
-    .WillOnce(DoDefault())
-    .WillOnce(DoDefault())
-    .WillOnce(DoDefault())
-    .WillOnce(DoAll(InvokeSlaveAdded(&allocator),
-                    FutureSatisfy(&addSlave)));
-
-  slave::Flags flags1 = CreateSlaveFlags();
-  flags1.default_role = "role1";
-  flags1.resources = Some("cpus:2;mem:1024;disk:0");
-  Try<PID<Slave> > slave1 = StartSlave(flags1);
-  ASSERT_SOME(slave1);
-
-  slave::Flags flags2 = CreateSlaveFlags();
-  flags2.resources =
-    Some("cpus(role2):2;mem(role2):1024;cpus:1;mem:1024;disk:0");
-  Try<PID<Slave> > slave2 = StartSlave(flags2);
-  ASSERT_SOME(slave2);
-
-  slave::Flags flags3 = CreateSlaveFlags();
-  flags3.default_role = "role3";
-  flags3.resources = Some("cpus:4;mem:4096;disk:0");
-  Try<PID<Slave> > slave3 = StartSlave(flags3);
-  ASSERT_SOME(slave3);
-
-  // This slave's resources should never be allocated,
-  // since there is no framework for role4.
-  slave::Flags flags4 = CreateSlaveFlags();
-  flags4.default_role = "role4";
-  flags4.resources = Some("cpus:1;mem:1024;disk:0");
-  Try<PID<Slave> > slave4 = StartSlave(flags4);
-  ASSERT_SOME(slave4);
-
-  AWAIT_READY(addSlave);
-
-  FrameworkInfo frameworkInfo1; // Bug in gcc 4.1.*, must assign on next line.
-  frameworkInfo1 = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo1.set_user("user1");
-  frameworkInfo1.set_name("framework1");
-  frameworkInfo1.set_role("role1");
-  MockScheduler sched1;
-  MesosSchedulerDriver driver1(
-      &sched1, frameworkInfo1, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(allocator, addFramework(_, _, _));
-
-  EXPECT_CALL(sched1, registered(_, _, _));
-
-  Future<Nothing> resourceOffers1;
-  EXPECT_CALL(sched1, resourceOffers(_, OfferEq(3, 2048)))
-    .WillOnce(FutureSatisfy(&resourceOffers1));
-
-  driver1.start();
-
-  // framework1 gets all the resources from slave1, plus the
-  // unreserved resources on slave2.
-  AWAIT_READY(resourceOffers1);
-
-  FrameworkInfo frameworkInfo2; // Bug in gcc 4.1.*, must assign on next line.
-  frameworkInfo2 = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo2.set_user("user2");
-  frameworkInfo2.set_name("framework2");
-  frameworkInfo2.set_role("role2");
-  MockScheduler sched2;
-  MesosSchedulerDriver driver2(
-      &sched2, frameworkInfo2, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(allocator, addFramework(_, _, _));
-
-  EXPECT_CALL(sched2, registered(_, _, _));
-
-  Future<Nothing> resourceOffers2;
-  EXPECT_CALL(sched2, resourceOffers(_, OfferEq(2, 1024)))
-    .WillOnce(FutureSatisfy(&resourceOffers2));
-
-  driver2.start();
-
-  // framework2 gets all of its reserved resources on slave2.
-  AWAIT_READY(resourceOffers2);
-
-  FrameworkInfo frameworkInfo3; // Bug in gcc 4.1.*, must assign on next line.
-  frameworkInfo3 = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo3.set_user("user2");
-  frameworkInfo3.set_name("framework3");
-  frameworkInfo3.set_role("role3");
-  MockScheduler sched3;
-  MesosSchedulerDriver driver3(
-      &sched3, frameworkInfo3, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(allocator, addFramework(_, _, _));
-
-  EXPECT_CALL(sched3, registered(_, _, _));
-
-  Future<Nothing> resourceOffers3;
-  EXPECT_CALL(sched3, resourceOffers(_, OfferEq(4, 4096)))
-    .WillOnce(FutureSatisfy(&resourceOffers3));
-
-  driver3.start();
-
-  // framework3 gets all the resources from slave3.
-  AWAIT_READY(resourceOffers3);
-
-  slave::Flags flags5 = CreateSlaveFlags();
-  flags5.default_role = "role1";
-  flags5.resources = Some("cpus:1;mem:512;disk:0");
-
-  EXPECT_CALL(allocator, addSlave(_, _, _, _));
-
-  Future<Nothing> resourceOffers4;
-  EXPECT_CALL(sched1, resourceOffers(_, OfferEq(1, 512)))
-    .WillOnce(FutureSatisfy(&resourceOffers4));
-
-  Try<PID<Slave> > slave5 = StartSlave(flags5);
-  ASSERT_SOME(slave5);
-
-  // framework1 gets all the resources from slave5.
-  AWAIT_READY(resourceOffers4);
-
-  // Shut everything down.
-  EXPECT_CALL(allocator, recoverResources(_, _, _, _))
-    .WillRepeatedly(DoDefault());
-
-  EXPECT_CALL(allocator, deactivateFramework(_))
-    .Times(AtMost(3));
-
-  EXPECT_CALL(allocator, removeFramework(_))
-    .Times(AtMost(3));
-
-  driver3.stop();
-  driver2.stop();
-  driver1.stop();
-
-  EXPECT_CALL(allocator, removeSlave(_))
-    .Times(AtMost(5));
-
-  this->Shutdown();
-}
-
-
 // Checks that statically allocated resources that are returned
 // either unused or after a task finishes are statically reallocated
 // appropriately.