You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by ch...@apache.org on 2018/05/04 01:18:32 UTC
[09/13] mesos git commit: Added test for authorization actions for
`RESIZE_VOLUME`.
Added test for authorization actions for `RESIZE_VOLUME`.
Review: https://reviews.apache.org/r/66532/
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/360ae2f9
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/360ae2f9
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/360ae2f9
Branch: refs/heads/master
Commit: 360ae2f95ef4850018843e88fd8adc1e847f6ce0
Parents: 9656329
Author: Zhitao Li <zh...@gmail.com>
Authored: Thu May 3 17:04:57 2018 -0700
Committer: Chun-Hung Hsiao <ch...@mesosphere.io>
Committed: Thu May 3 17:04:57 2018 -0700
----------------------------------------------------------------------
src/tests/authorization_tests.cpp | 294 +++++++++++++++++++++++
src/tests/persistent_volume_tests.cpp | 366 +++++++++++++++++++++++++++++
2 files changed, 660 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/360ae2f9/src/tests/authorization_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/authorization_tests.cpp b/src/tests/authorization_tests.cpp
index a76ad18..f6f7769 100644
--- a/src/tests/authorization_tests.cpp
+++ b/src/tests/authorization_tests.cpp
@@ -1977,6 +1977,300 @@ TYPED_TEST(AuthorizationTest, DestroyVolume)
}
+// Tests the authorization of ACLs used for resizing volumes.
+TYPED_TEST(AuthorizationTest, ResizeVolume)
+{
+ ACLs acls;
+
+ {
+ // Principal "foo" can resize volumes for any role.
+ mesos::ACL::ResizeVolume* acl = acls.add_resize_volumes();
+ acl->mutable_principals()->add_values("foo");
+ acl->mutable_roles()->set_type(mesos::ACL::Entity::ANY);
+ }
+
+ {
+ // Principal "bar" can only resize volumes for the "panda" role.
+ mesos::ACL::ResizeVolume* acl = acls.add_resize_volumes();
+ acl->mutable_principals()->add_values("bar");
+ acl->mutable_roles()->add_values("panda");
+ }
+
+ {
+ // Principal "baz" cannot resize volumes.
+ mesos::ACL::ResizeVolume* acl = acls.add_resize_volumes();
+ acl->mutable_principals()->add_values("baz");
+ acl->mutable_roles()->set_type(mesos::ACL::Entity::NONE);
+ }
+
+ {
+ // Principal "elizabeth-ii" can resize volumes for the "king" role and its
+ // nested ones.
+ mesos::ACL::ResizeVolume* acl = acls.add_resize_volumes();
+ acl->mutable_principals()->add_values("elizabeth-ii");
+ acl->mutable_roles()->add_values("king/%");
+ acl->mutable_roles()->add_values("king");
+ }
+
+ {
+ // Principal "charles" can resize volumes for any role below the "king/"
+ // role. Not in "king" itself.
+ mesos::ACL::ResizeVolume* acl = acls.add_resize_volumes();
+ acl->mutable_principals()->add_values("charles");
+ acl->mutable_roles()->add_values("king/%");
+ }
+
+ {
+ // Principal "j-welby" can resize volumes only for the "king" role but
+ // not in any nested one.
+ mesos::ACL::ResizeVolume* acl = acls.add_resize_volumes();
+ acl->mutable_principals()->add_values("j-welby");
+ acl->mutable_roles()->add_values("king");
+ }
+
+ {
+ // No other principals can resize volumes.
+ mesos::ACL::ResizeVolume* acl = acls.add_resize_volumes();
+ acl->mutable_principals()->set_type(mesos::ACL::Entity::ANY);
+ acl->mutable_roles()->set_type(mesos::ACL::Entity::NONE);
+ }
+
+ {
+ // No other principals can resize volumes.
+ mesos::ACL::ResizeVolume* acl = acls.add_resize_volumes();
+ acl->mutable_principals()->set_type(mesos::ACL::Entity::ANY);
+ acl->mutable_roles()->set_type(mesos::ACL::Entity::NONE);
+ }
+
+ // Create an `Authorizer` with the ACLs.
+ Try<Authorizer*> create = TypeParam::create(parameterize(acls));
+ ASSERT_SOME(create);
+ Owned<Authorizer> authorizer(create.get());
+
+ // Principal "foo" can create volumes for any role, so this request will pass.
+ {
+ authorization::Request request;
+ request.set_action(authorization::RESIZE_VOLUME);
+ request.mutable_subject()->set_value("foo");
+ request.mutable_object()->set_value("awesome_role");
+ AWAIT_EXPECT_TRUE(authorizer->authorized(request));
+ }
+ {
+ authorization::Request request;
+ request.set_action(authorization::RESIZE_VOLUME);
+ request.mutable_subject()->set_value("foo");
+ request.mutable_object()
+ ->mutable_resource()
+ ->mutable_reservations()
+ ->Add()
+ ->set_role("awesome_role");
+ AWAIT_EXPECT_TRUE(authorizer->authorized(request));
+ }
+
+ // Principal "bar" can create volumes for the "panda" role,
+ // so this request will pass.
+ {
+ authorization::Request request;
+ request.set_action(authorization::RESIZE_VOLUME);
+ request.mutable_subject()->set_value("bar");
+ request.mutable_object()->set_value("panda");
+ AWAIT_EXPECT_TRUE(authorizer->authorized(request));
+ }
+ {
+ authorization::Request request;
+ request.set_action(authorization::RESIZE_VOLUME);
+ request.mutable_subject()->set_value("bar");
+ request.mutable_object()
+ ->mutable_resource()
+ ->mutable_reservations()
+ ->Add()
+ ->set_role("panda");
+ AWAIT_EXPECT_TRUE(authorizer->authorized(request));
+ }
+
+ // Principal "bar" cannot resize volumes for the "giraffe" role,
+ // so this request will fail.
+ {
+ authorization::Request request;
+ request.set_action(authorization::RESIZE_VOLUME);
+ request.mutable_subject()->set_value("bar");
+ request.mutable_object()->set_value("giraffe");
+ AWAIT_EXPECT_FALSE(authorizer->authorized(request));
+ }
+ {
+ authorization::Request request;
+ request.set_action(authorization::RESIZE_VOLUME);
+ request.mutable_subject()->set_value("bar");
+ request.mutable_object()
+ ->mutable_resource()
+ ->mutable_reservations()
+ ->Add()
+ ->set_role("giraffe");
+ AWAIT_EXPECT_FALSE(authorizer->authorized(request));
+ }
+
+ // Principal "baz" cannot resize volumes for any role,
+ // so this request will fail.
+ {
+ authorization::Request request;
+ request.set_action(authorization::RESIZE_VOLUME);
+ request.mutable_subject()->set_value("baz");
+ request.mutable_object()->set_value("panda");
+ AWAIT_EXPECT_FALSE(authorizer->authorized(request));
+ }
+ {
+ authorization::Request request;
+ request.set_action(authorization::RESIZE_VOLUME);
+ request.mutable_subject()->set_value("baz");
+ request.mutable_object()
+ ->mutable_resource()
+ ->mutable_reservations()
+ ->Add()
+ ->set_role("panda");
+ AWAIT_EXPECT_FALSE(authorizer->authorized(request));
+ }
+
+ // Principal "zelda" is not mentioned in the ACLs of the Authorizer, so it
+ // will be caught by the final ACL, which provides a default case that denies
+ // access for all other principals. This request will fail.
+ {
+ authorization::Request request;
+ request.set_action(authorization::RESIZE_VOLUME);
+ request.mutable_subject()->set_value("zelda");
+ request.mutable_object()->set_value("panda");
+ AWAIT_EXPECT_FALSE(authorizer->authorized(request));
+ }
+ {
+ authorization::Request request;
+ request.set_action(authorization::RESIZE_VOLUME);
+ request.mutable_subject()->set_value("zelda");
+ request.mutable_object()
+ ->mutable_resource()
+ ->mutable_reservations()
+ ->Add()
+ ->set_role("panda");
+ AWAIT_EXPECT_FALSE(authorizer->authorized(request));
+ }
+
+ // "elizabeth-ii" has full permissions for the "king" role as well as all
+ // its nested roles. She should be able to resize volumes in the next
+ // three blocks.
+ {
+ authorization::Request request;
+ request.set_action(authorization::RESIZE_VOLUME);
+ request.mutable_subject()->set_value("elizabeth-ii");
+ request.mutable_object()
+ ->mutable_resource()
+ ->mutable_reservations()
+ ->Add()
+ ->set_role("king");
+ AWAIT_EXPECT_TRUE(authorizer->authorized(request));
+ }
+
+ {
+ authorization::Request request;
+ request.set_action(authorization::RESIZE_VOLUME);
+ request.mutable_subject()->set_value("elizabeth-ii");
+ request.mutable_object()
+ ->mutable_resource()
+ ->mutable_reservations()
+ ->Add()
+ ->set_role("king/prince");
+ AWAIT_EXPECT_TRUE(authorizer->authorized(request));
+ }
+
+ {
+ authorization::Request request;
+ request.set_action(authorization::RESIZE_VOLUME);
+ request.mutable_subject()->set_value("elizabeth-ii");
+ request.mutable_object()
+ ->mutable_resource()
+ ->mutable_reservations()
+ ->Add()
+ ->set_role("king/prince/duke");
+ AWAIT_EXPECT_TRUE(authorizer->authorized(request));
+ }
+
+ // "charles" doesn't have permissions for the "king" role, so the first
+ // test should fail. However he has permissions for "king"'s nested roles
+ // so the next two tests should succeed.
+ {
+ authorization::Request request;
+ request.set_action(authorization::RESIZE_VOLUME);
+ request.mutable_subject()->set_value("charles");
+ request.mutable_object()
+ ->mutable_resource()
+ ->mutable_reservations()
+ ->Add()
+ ->set_role("king");
+ AWAIT_EXPECT_FALSE(authorizer->authorized(request));
+ }
+
+ {
+ authorization::Request request;
+ request.set_action(authorization::RESIZE_VOLUME);
+ request.mutable_subject()->set_value("charles");
+ request.mutable_object()
+ ->mutable_resource()
+ ->mutable_reservations()
+ ->Add()
+ ->set_role("king/prince");
+ AWAIT_EXPECT_TRUE(authorizer->authorized(request));
+ }
+
+ {
+ authorization::Request request;
+ request.set_action(authorization::RESIZE_VOLUME);
+ request.mutable_subject()->set_value("charles");
+ request.mutable_object()
+ ->mutable_resource()
+ ->mutable_reservations()
+ ->Add()
+ ->set_role("king/prince/duke");
+ AWAIT_EXPECT_TRUE(authorizer->authorized(request));
+ }
+
+ // "j-welby" only has permissions for the role "king" itself, but not
+ // for its nested roles, therefore only the first of the following three
+ // tests should succeed.
+ {
+ authorization::Request request;
+ request.set_action(authorization::RESIZE_VOLUME);
+ request.mutable_subject()->set_value("j-welby");
+ request.mutable_object()
+ ->mutable_resource()
+ ->mutable_reservations()
+ ->Add()
+ ->set_role("king");
+ AWAIT_EXPECT_TRUE(authorizer->authorized(request));
+ }
+
+ {
+ authorization::Request request;
+ request.set_action(authorization::RESIZE_VOLUME);
+ request.mutable_subject()->set_value("j-welby");
+ request.mutable_object()
+ ->mutable_resource()
+ ->mutable_reservations()
+ ->Add()
+ ->set_role("king/prince");
+ AWAIT_EXPECT_FALSE(authorizer->authorized(request));
+ }
+
+ {
+ authorization::Request request;
+ request.set_action(authorization::RESIZE_VOLUME);
+ request.mutable_subject()->set_value("j-welby");
+ request.mutable_object()
+ ->mutable_resource()
+ ->mutable_reservations()
+ ->Add()
+ ->set_role("king/prince/duke");
+ AWAIT_EXPECT_FALSE(authorizer->authorized(request));
+ }
+}
+
+
// This tests the authorization of requests to update quotas.
TYPED_TEST(AuthorizationTest, UpdateQuota)
{
http://git-wip-us.apache.org/repos/asf/mesos/blob/360ae2f9/src/tests/persistent_volume_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/persistent_volume_tests.cpp b/src/tests/persistent_volume_tests.cpp
index 5b2a23c..477e6e2 100644
--- a/src/tests/persistent_volume_tests.cpp
+++ b/src/tests/persistent_volume_tests.cpp
@@ -985,6 +985,372 @@ TEST_P(PersistentVolumeTest, NonSpeculativeShrinkAndLaunch)
}
+// This test verifies that grow and shrink operations can complete
+// successfully when authorization succeeds.
+TEST_P(PersistentVolumeTest, GoodACLGrowThenShrink)
+{
+ if (GetParam() == MOUNT) {
+ // It is not possible to have a valid `GrowVolume` on a MOUNT disk because
+ // the volume must use up all disk space at `Create` and no space will be
+ // left for `addition`. Therefore we skip this test.
+ // TODO(zhitao): Make MOUNT a meaningful parameter value for this test, or
+ // create a new fixture to avoid testing against it.
+ return;
+ }
+
+ Clock::pause();
+
+ ACLs acls;
+
+ // This ACL declares that the principal of `DEFAULT_CREDENTIAL`
+ // can resize persistent volumes for DEFAULT_TEST_ROLE.
+ mesos::ACL::ResizeVolume* resize = acls.add_resize_volumes();
+ resize->mutable_principals()->add_values(DEFAULT_CREDENTIAL.principal());
+ resize->mutable_roles()->add_values(DEFAULT_TEST_ROLE);
+
+ // Create a master.
+ master::Flags masterFlags = CreateMasterFlags();
+ masterFlags.acls = acls;
+
+ Try<Owned<cluster::Master>> master = StartMaster(masterFlags);
+ ASSERT_SOME(master);
+
+ slave::Flags slaveFlags = CreateSlaveFlags();
+ slaveFlags.resources = getSlaveResources();
+
+ Owned<MasterDetector> detector = master.get()->createDetector();
+ Try<Owned<cluster::Slave>> slave = StartSlave(detector.get(), slaveFlags);
+ ASSERT_SOME(slave);
+
+ FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO;
+ frameworkInfo.set_roles(0, DEFAULT_TEST_ROLE);
+
+ MockScheduler sched;
+ MesosSchedulerDriver driver(
+ &sched, frameworkInfo, master.get()->pid, DEFAULT_CREDENTIAL);
+
+ EXPECT_CALL(sched, registered(&driver, _, _));
+
+ Future<vector<Offer>> offersBeforeCreate;
+ EXPECT_CALL(sched, resourceOffers(&driver, _))
+ .WillOnce(FutureArg<1>(&offersBeforeCreate));
+
+ driver.start();
+
+ Clock::advance(masterFlags.allocation_interval);
+
+ AWAIT_READY(offersBeforeCreate);
+ ASSERT_FALSE(offersBeforeCreate->empty());
+
+ Offer offer = offersBeforeCreate->at(0);
+
+ // The disk spaces will be merged if the fixture parameter is `NONE`.
+ Bytes totalBytes = GetParam() == NONE ? Megabytes(4096) : Megabytes(2048);
+
+ Bytes bytesDifference = Megabytes(512);
+
+ // Construct a persistent volume which do not use up all disk resources.
+ Resource volume = createPersistentVolume(
+ getDiskResource(totalBytes - bytesDifference, 1),
+ "id1",
+ "path1",
+ None(),
+ frameworkInfo.principal());
+
+ Resource difference = getDiskResource(bytesDifference, 1);
+
+ Resource grownVolume = createPersistentVolume(
+ getDiskResource(totalBytes, 1),
+ "id1",
+ "path1",
+ None(),
+ frameworkInfo.principal());
+
+ Future<vector<Offer>> offersAfterGrow;
+
+ EXPECT_CALL(sched, resourceOffers(&driver, _))
+ .WillOnce(FutureArg<1>(&offersAfterGrow));
+
+ // We use the filter explicitly here so that the resources will not
+ // be filtered for 5 seconds (the default).
+ Filters filters;
+ filters.set_refuse_seconds(0);
+
+ // Create a persistent volume then grow it.
+ driver.acceptOffers(
+ {offer.id()},
+ {CREATE(volume), GROW_VOLUME(volume, difference)},
+ filters);
+
+ Clock::settle();
+ Clock::advance(masterFlags.allocation_interval);
+
+ AWAIT_READY(offersAfterGrow);
+ ASSERT_FALSE(offersAfterGrow->empty());
+
+ offer = offersAfterGrow->at(0);
+
+ EXPECT_EQ(
+ allocatedResources(Resources(grownVolume), frameworkInfo.roles(0)),
+ Resources(offer.resources()).persistentVolumes());
+
+ EXPECT_FALSE(
+ Resources(offer.resources()).contains(
+ allocatedResources(difference, frameworkInfo.roles(0))));
+
+ Future<vector<Offer>> offersAfterShrink;
+
+ EXPECT_CALL(sched, resourceOffers(&driver, _))
+ .WillOnce(FutureArg<1>(&offersAfterShrink));
+
+ // Shrink the volume back to original size.
+ driver.acceptOffers(
+ {offer.id()},
+ {SHRINK_VOLUME(grownVolume, difference.scalar())},
+ filters);
+
+ Clock::settle();
+ Clock::advance(masterFlags.allocation_interval);
+
+ AWAIT_READY(offersAfterShrink);
+ ASSERT_FALSE(offersAfterShrink->empty());
+ offer = offersAfterShrink->at(0);
+
+ EXPECT_EQ(
+ allocatedResources(Resources(volume), frameworkInfo.roles(0)),
+ Resources(offer.resources()).persistentVolumes());
+
+ EXPECT_TRUE(
+ Resources(offer.resources()).contains(
+ allocatedResources(difference, frameworkInfo.roles(0))));
+
+ Clock::resume();
+
+ driver.stop();
+ driver.join();
+}
+
+// This test verifies that grow and shrink operations get dropped if
+// authorization fails and no principal is supplied.
+TEST_P(PersistentVolumeTest, BadACLDropGrowAndShrink)
+{
+ if (GetParam() == MOUNT) {
+ // It is not possible to have a valid `GrowVolume` on a MOUNT disk because
+ // the volume must use up all disk space at `Create` and no space will be
+ // left for `addition`. Therefore we skip this test.
+ // TODO(zhitao): Make MOUNT a meaningful parameter value for this test, or
+ // create a new fixture to avoid testing against it.
+ return;
+ }
+
+ Clock::pause();
+
+ ACLs acls;
+
+ // This ACL declares that no principal can resize any volume.
+ mesos::ACL::ResizeVolume* resize = acls.add_resize_volumes();
+ resize->mutable_principals()->set_type(mesos::ACL::Entity::ANY);
+ resize->mutable_roles()->set_type(mesos::ACL::Entity::NONE);
+
+ // Create a master.
+ master::Flags masterFlags = CreateMasterFlags();
+ masterFlags.acls = acls;
+
+ Try<Owned<cluster::Master>> master = StartMaster(masterFlags);
+ ASSERT_SOME(master);
+
+ slave::Flags slaveFlags = CreateSlaveFlags();
+ slaveFlags.resources = getSlaveResources();
+
+ Owned<MasterDetector> detector = master.get()->createDetector();
+ Try<Owned<cluster::Slave>> slave = StartSlave(detector.get(), slaveFlags);
+ ASSERT_SOME(slave);
+
+ // DEFAULT_FRAMEWORK_INFO uses DEFAULT_CREDENTIAL.
+ FrameworkInfo frameworkInfo1 = DEFAULT_FRAMEWORK_INFO;
+ frameworkInfo1.set_roles(0, DEFAULT_TEST_ROLE);
+
+ MockScheduler sched1;
+ MesosSchedulerDriver driver1(
+ &sched1, frameworkInfo1, master.get()->pid, DEFAULT_CREDENTIAL);
+
+ EXPECT_CALL(sched1, registered(&driver1, _, _));
+
+ Future<vector<Offer>> offersBeforeCreate;
+ EXPECT_CALL(sched1, resourceOffers(&driver1, _))
+ .WillOnce(FutureArg<1>(&offersBeforeCreate));
+
+ driver1.start();
+
+ Clock::advance(masterFlags.allocation_interval);
+
+ AWAIT_READY(offersBeforeCreate);
+ ASSERT_FALSE(offersBeforeCreate->empty());
+
+ Offer offer = offersBeforeCreate->at(0);
+
+ // Disk spaces will be merged if fixture parameter is `NONE`.
+ Bytes totalBytes = GetParam() == NONE ? Megabytes(4096) : Megabytes(2048);
+
+ Bytes bytesDifference = Megabytes(512);
+
+ // Construct a persistent volume which does not use up all disk resources.
+ Resource volume = createPersistentVolume(
+ getDiskResource(totalBytes - bytesDifference, 1),
+ "id1",
+ "path1",
+ None(),
+ frameworkInfo1.principal());
+
+ Resource difference = getDiskResource(bytesDifference, 1);
+
+ Resource grownVolume = createPersistentVolume(
+ getDiskResource(totalBytes, 1),
+ "id1",
+ "path1",
+ None(),
+ frameworkInfo1.principal());
+
+ Future<vector<Offer>> offersAfterGrow1;
+
+ EXPECT_CALL(sched1, resourceOffers(&driver1, _))
+ .WillOnce(FutureArg<1>(&offersAfterGrow1));
+
+ // We use the filter explicitly here so that the resources will not
+ // be filtered for 5 seconds (the default).
+ Filters filters;
+ filters.set_refuse_seconds(0);
+
+ // Creating the persistent volume will succeed, but growing will fail due to
+ // ACL.
+ driver1.acceptOffers(
+ {offer.id()},
+ {CREATE(volume), GROW_VOLUME(volume, difference)},
+ filters);
+
+ Clock::settle();
+ Clock::advance(masterFlags.allocation_interval);
+
+ AWAIT_READY(offersAfterGrow1);
+ ASSERT_FALSE(offersAfterGrow1->empty());
+
+ offer = offersAfterGrow1->at(0);
+
+ EXPECT_EQ(
+ allocatedResources(Resources(volume), DEFAULT_TEST_ROLE),
+ Resources(offer.resources()).persistentVolumes());
+
+ EXPECT_TRUE(
+ Resources(offer.resources()).contains(
+ allocatedResources(difference, DEFAULT_TEST_ROLE)));
+
+ Future<vector<Offer>> offersAfterShrink1;
+
+ EXPECT_CALL(sched1, resourceOffers(&driver1, _))
+ .WillOnce(FutureArg<1>(&offersAfterShrink1));
+
+ driver1.acceptOffers(
+ {offer.id()},
+ {SHRINK_VOLUME(volume, difference.scalar())},
+ filters);
+
+ Clock::settle();
+ Clock::advance(masterFlags.allocation_interval);
+
+ AWAIT_READY(offersAfterShrink1);
+ ASSERT_FALSE(offersAfterShrink1->empty());
+
+ offer = offersAfterShrink1->at(0);
+
+ EXPECT_EQ(
+ allocatedResources(Resources(volume), DEFAULT_TEST_ROLE),
+ Resources(offer.resources()).persistentVolumes());
+
+ driver1.stop();
+ driver1.join();
+
+ // Start the second framework with no principal.
+ FrameworkInfo frameworkInfo2 = DEFAULT_FRAMEWORK_INFO;
+ frameworkInfo2.clear_principal();
+ frameworkInfo2.set_roles(0, DEFAULT_TEST_ROLE);
+
+ MockScheduler sched2;
+ MesosSchedulerDriver driver2(
+ &sched2, frameworkInfo2, master.get()->pid, DEFAULT_CREDENTIAL);
+
+ EXPECT_CALL(sched2, registered(&driver2, _, _));
+
+ Future<vector<Offer>> offersBeforeGrow2;
+ EXPECT_CALL(sched2, resourceOffers(&driver2, _))
+ .WillOnce(FutureArg<1>(&offersBeforeGrow2));
+
+ driver2.start();
+
+ Clock::settle();
+ Clock::advance(masterFlags.allocation_interval);
+
+ AWAIT_READY(offersBeforeGrow2);
+ ASSERT_FALSE(offersBeforeGrow2->empty());
+
+ offer = offersBeforeGrow2->at(0);
+
+ EXPECT_EQ(
+ allocatedResources(Resources(volume), DEFAULT_TEST_ROLE),
+ Resources(offer.resources()).persistentVolumes());
+
+ EXPECT_TRUE(
+ Resources(offer.resources()).contains(
+ allocatedResources(difference, DEFAULT_TEST_ROLE)));
+
+ Future<vector<Offer>> offersAfterGrow2;
+
+ EXPECT_CALL(sched2, resourceOffers(&driver2, _))
+ .WillOnce(FutureArg<1>(&offersAfterGrow2));
+
+ driver2.acceptOffers(
+ {offer.id()},
+ {GROW_VOLUME(volume, difference)},
+ filters);
+
+ Clock::settle();
+ Clock::advance(masterFlags.allocation_interval);
+
+ AWAIT_READY(offersAfterGrow2);
+ ASSERT_FALSE(offersAfterGrow2->empty());
+ offer = offersAfterGrow2->at(0);
+
+ EXPECT_EQ(
+ allocatedResources(Resources(volume), DEFAULT_TEST_ROLE),
+ Resources(offer.resources()).persistentVolumes());
+
+ Future<vector<Offer>> offersAfterShrink2;
+
+ EXPECT_CALL(sched2, resourceOffers(&driver2, _))
+ .WillOnce(FutureArg<1>(&offersAfterShrink2));
+
+ driver2.acceptOffers(
+ {offer.id()},
+ {SHRINK_VOLUME(volume, difference.scalar())},
+ filters);
+
+ Clock::settle();
+ Clock::advance(masterFlags.allocation_interval);
+
+ AWAIT_READY(offersAfterShrink2);
+ ASSERT_FALSE(offersAfterShrink2->empty());
+ offer = offersAfterShrink2->at(0);
+
+ EXPECT_EQ(
+ allocatedResources(Resources(volume), DEFAULT_TEST_ROLE),
+ Resources(offer.resources()).persistentVolumes());
+
+ driver2.stop();
+ driver2.join();
+
+ Clock::resume();
+}
+
+
// This test verifies that the slave checkpoints the resources for
// persistent volumes to the disk, recovers them upon restart, and
// sends them to the master during re-registration.