This is an automated email from the ASF dual-hosted git repository.

bmahler pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mesos.git


The following commit(s) were added to refs/heads/master by this push:
     new 0c431dd  Added tests for the V1 UPDATE_FRAMEWORK call.
0c431dd is described below

commit 0c431dd60ae39138cc7e8b099d41ad794c02c9a9
Author: Andrei Sekretenko <[email protected]>
AuthorDate: Sun Jun 9 23:46:18 2019 -0400

    Added tests for the V1 UPDATE_FRAMEWORK call.
    
    Review: https://reviews.apache.org/r/70534/
---
 src/Makefile.am                             |   1 +
 src/tests/CMakeLists.txt                    |   1 +
 src/tests/master/update_framework_tests.cpp | 599 ++++++++++++++++++++++++++++
 3 files changed, 601 insertions(+)

diff --git a/src/Makefile.am b/src/Makefile.am
index a367bf7..761dde1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -2604,6 +2604,7 @@ mesos_tests_SOURCES =                                     
        \
   tests/master_tests.cpp                                       \
   tests/master/mock_master_api_subscriber.cpp                  \
   tests/master/mock_master_api_subscriber.hpp                  \
+  tests/master/update_framework_tests.cpp                      \
   tests/master_validation_tests.cpp                            \
   tests/mesos.cpp                                              \
   tests/mesos.hpp                                              \
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 830942f..3ea8b44 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -110,6 +110,7 @@ set(MESOS_TESTS_SRC
   master_load_tests.cpp
   master_maintenance_tests.cpp
   master_slave_reconciliation_tests.cpp
+  master/update_framework_tests.cpp
   operation_reconciliation_tests.cpp
   operation_status_update_manager_tests.cpp
   partition_tests.cpp
diff --git a/src/tests/master/update_framework_tests.cpp 
b/src/tests/master/update_framework_tests.cpp
new file mode 100644
index 0000000..78304c2
--- /dev/null
+++ b/src/tests/master/update_framework_tests.cpp
@@ -0,0 +1,599 @@
+// 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 <memory>
+#include <string>
+
+#include <gmock/gmock.h>
+
+#include <google/protobuf/util/message_differencer.h>
+
+#include <mesos/executor.hpp>
+
+#include <mesos/v1/mesos.hpp>
+#include <mesos/v1/resources.hpp>
+#include <mesos/v1/scheduler.hpp>
+
+#include <mesos/v1/scheduler/scheduler.hpp>
+
+#include <process/clock.hpp>
+#include <process/future.hpp>
+#include <process/gmock.hpp>
+#include <process/gtest.hpp>
+#include <process/owned.hpp>
+#include <process/pid.hpp>
+
+#include <stout/lambda.hpp>
+#include <stout/try.hpp>
+
+#include "internal/evolve.hpp"
+
+#include "master/allocator/mesos/allocator.hpp"
+#include "master/master.hpp"
+
+#include "tests/mesos.hpp"
+#include "tests/master/mock_master_api_subscriber.hpp"
+
+using mesos::internal::master::Master;
+using mesos::internal::master::allocator::MesosAllocatorProcess;
+using mesos::internal::recordio::Reader;
+using mesos::internal::slave::Containerizer;
+using mesos::internal::slave::Slave;
+
+using mesos::master::detector::MasterDetector;
+
+using mesos::v1::FrameworkInfo;
+using mesos::v1::scheduler::APIResult;
+using mesos::v1::scheduler::Call;
+using mesos::v1::scheduler::Event;
+using mesos::v1::scheduler::Mesos;
+
+using process::Clock;
+using process::Failure;
+using process::Future;
+using process::Owned;
+using process::PID;
+using process::Promise;
+
+using recordio::Decoder;
+
+using std::string;
+
+using testing::_;
+using testing::AtMost;
+using testing::Return;
+
+
+namespace mesos {
+namespace internal {
+namespace tests {
+namespace v1 {
+namespace scheduler {
+
+
+class UpdateFrameworkTest : public MesosTest {};
+
+
+static Future<APIResult> callUpdateFramework(
+    Mesos* mesos,
+    const FrameworkInfo& info)
+{
+  CHECK(info.has_id());
+
+  Call call;
+  call.set_type(Call::UPDATE_FRAMEWORK);
+  *call.mutable_framework_id() = info.id();
+  *call.mutable_update_framework()->mutable_framework_info() = info;
+  return mesos->call(call);
+}
+
+
+static Future<v1::master::Response::GetFrameworks> getFrameworks(
+    const process::PID<Master>& pid)
+{
+  v1::master::Call call;
+  call.set_type(v1::master::Call::GET_FRAMEWORKS);
+
+  process::http::Headers headers = createBasicAuthHeaders(DEFAULT_CREDENTIAL);
+  headers["Accept"] = stringify(ContentType::PROTOBUF);
+
+  return process::http::post(
+           pid,
+           "api/v1",
+           headers,
+           serialize(ContentType::PROTOBUF, call),
+           stringify(ContentType::PROTOBUF))
+    .then([](const process::http::Response& httpResponse)
+        -> Future<v1::master::Response::GetFrameworks> {
+        if (httpResponse.status != process::http::OK().status) {
+          return Failure(
+            "GET_FRAMEWORKS failed with response status " +
+            httpResponse.status);
+        }
+
+        Try<v1::master::Response> response =
+          deserialize<mesos::v1::master::Response>(
+            ContentType::PROTOBUF, httpResponse.body);
+
+        if (response.isError()) {
+          return Failure(response.error());
+        }
+
+        if (!response->has_get_frameworks()) {
+          return Failure("Response to GET_FRAMEWORKS has no 'get_frameworks'");
+        }
+
+        return response->get_frameworks();
+      });
+}
+
+
+static FrameworkInfo changeAllMutableFields(const FrameworkInfo& oldInfo)
+{
+  CHECK_EQ(FrameworkInfo::descriptor()->field_count(), 13)
+    << "After adding a new mutable field to FrameworkInfo, please make sure "
+    << "that this function modifies this field";
+
+  FrameworkInfo newInfo = oldInfo;
+
+  *newInfo.mutable_name() += "_foo";
+  newInfo.set_failover_timeout(newInfo.failover_timeout() + 1000.0);
+  *newInfo.mutable_hostname() += ".foo";
+  *newInfo.mutable_webui_url() += "/foo";
+
+  newInfo.add_capabilities()->set_type(
+      FrameworkInfo::Capability::REGION_AWARE);
+
+  mesos::v1::Label* newLabel = newInfo.mutable_labels()->add_labels();
+  *newLabel->mutable_key() = "UPDATE_FRAMEWORK_KEY";
+  *newLabel->mutable_value() = "UPDATE_FRAMEWORK_VALUE";
+
+  // TODO(asekretenko): Test update of `role` with a non-MULTI_ROLE framework.
+  newInfo.add_roles("new_role");
+
+  CHECK(newInfo.offer_filters().count("new_role") == 0);
+  (*newInfo.mutable_offer_filters())["new_role_without_resources"] =
+    mesos::v1::OfferFilters();
+
+  return newInfo;
+}
+
+
+static Option<string> diff(
+    const FrameworkInfo& lhs, const FrameworkInfo& rhs)
+{
+  const google::protobuf::Descriptor* descriptor = FrameworkInfo::descriptor();
+  google::protobuf::util::MessageDifferencer differencer;
+
+  differencer.TreatAsSet(descriptor->FindFieldByName("capabilities"));
+  differencer.TreatAsSet(descriptor->FindFieldByName("roles"));
+
+  string result;
+  differencer.ReportDifferencesToString(&result);
+
+  if (differencer.Compare(lhs, rhs)) {
+    return None();
+  }
+
+  return result;
+}
+
+
+TEST_F(UpdateFrameworkTest, UserChangeFails)
+{
+  Try<Owned<cluster::Master>> master = StartMaster(CreateMasterFlags());
+  ASSERT_SOME(master);
+
+  auto scheduler = std::make_shared<MockHTTPScheduler>();
+
+  Future<Nothing> connected;
+  EXPECT_CALL(*scheduler, connected(_))
+    .WillOnce(SendSubscribe(DEFAULT_FRAMEWORK_INFO));
+
+  EXPECT_CALL(*scheduler, heartbeat(_))
+    .WillRepeatedly(Return()); // Ignore heartbeats.
+
+  Future<Event::Subscribed> subscribed;
+
+  EXPECT_CALL(*scheduler, subscribed(_, _))
+    .WillOnce(FutureArg<1>(&subscribed));
+
+  TestMesos mesos(master->get()->pid, ContentType::PROTOBUF, scheduler);
+
+  AWAIT_READY(subscribed);
+
+  FrameworkInfo update = changeAllMutableFields(DEFAULT_FRAMEWORK_INFO);
+  *update.mutable_id() = subscribed->framework_id();
+  *update.mutable_user() += "_foo";
+
+  Future<APIResult> result = callUpdateFramework(&mesos, update);
+
+  AWAIT_READY(result);
+  EXPECT_EQ(result->status_code(), 400u);
+  EXPECT_TRUE(strings::contains(
+      result->error(), "Updating 'FrameworkInfo.user' is unsupported"));
+
+  // Check that no partial update occurred.
+  Future<v1::master::Response::GetFrameworks> frameworks =
+    getFrameworks(master->get()->pid);
+  AWAIT_READY(frameworks);
+  ASSERT_EQ(frameworks->frameworks_size(), 1);
+
+  FrameworkInfo expected = DEFAULT_FRAMEWORK_INFO;
+  *expected.mutable_id() = subscribed->framework_id();
+  EXPECT_NONE(diff(frameworks->frameworks(0).framework_info(), expected));
+
+  // Sanity check for diff()
+  EXPECT_SOME(diff(update, expected));
+}
+
+
+TEST_F(UpdateFrameworkTest, PrincipalChangeFails)
+{
+  Try<Owned<cluster::Master>> master = StartMaster(CreateMasterFlags());
+  ASSERT_SOME(master);
+
+  auto scheduler = std::make_shared<MockHTTPScheduler>();
+
+  Future<Nothing> connected;
+  EXPECT_CALL(*scheduler, connected(_))
+    .WillOnce(SendSubscribe(DEFAULT_FRAMEWORK_INFO));
+
+  EXPECT_CALL(*scheduler, heartbeat(_))
+    .WillRepeatedly(Return()); // Ignore heartbeats.
+
+  Future<Event::Subscribed> subscribed;
+
+  EXPECT_CALL(*scheduler, subscribed(_, _))
+    .WillOnce(FutureArg<1>(&subscribed));
+
+  TestMesos mesos(master->get()->pid, ContentType::PROTOBUF, scheduler);
+
+  AWAIT_READY(subscribed);
+
+  FrameworkInfo update = changeAllMutableFields(DEFAULT_FRAMEWORK_INFO);
+  *update.mutable_id() = subscribed->framework_id();
+  *update.mutable_principal() += "_foo";
+
+  Future<APIResult> result = callUpdateFramework(&mesos, update);
+
+  AWAIT_READY(result);
+  EXPECT_EQ(result->status_code(), 400u);
+  EXPECT_TRUE(strings::contains(
+      result->error(), "Changing framework's principal is not allowed"));
+
+  // Check that no partial update occurred.
+  Future<v1::master::Response::GetFrameworks> frameworks =
+    getFrameworks(master->get()->pid);
+  AWAIT_READY(frameworks);
+
+  ASSERT_EQ(frameworks->frameworks_size(), 1);
+
+  FrameworkInfo expected = DEFAULT_FRAMEWORK_INFO;
+  *expected.mutable_id() = subscribed->framework_id();
+  EXPECT_NONE(diff(frameworks->frameworks(0).framework_info(), expected));
+
+  // Sanity check for diff()
+  EXPECT_SOME(diff(update, expected));
+}
+
+
+TEST_F(UpdateFrameworkTest, CheckpointingChangeFails)
+{
+  Try<Owned<cluster::Master>> master = StartMaster(CreateMasterFlags());
+  ASSERT_SOME(master);
+
+  auto scheduler = std::make_shared<MockHTTPScheduler>();
+
+  Future<Nothing> connected;
+  EXPECT_CALL(*scheduler, connected(_))
+    .WillOnce(SendSubscribe(DEFAULT_FRAMEWORK_INFO));
+
+  EXPECT_CALL(*scheduler, heartbeat(_))
+    .WillRepeatedly(Return()); // Ignore heartbeats.
+
+  Future<Event::Subscribed> subscribed;
+
+  EXPECT_CALL(*scheduler, subscribed(_, _))
+    .WillOnce(FutureArg<1>(&subscribed));
+
+  TestMesos mesos(master->get()->pid, ContentType::PROTOBUF, scheduler);
+
+  AWAIT_READY(subscribed);
+
+  FrameworkInfo update = changeAllMutableFields(DEFAULT_FRAMEWORK_INFO);
+  *update.mutable_id() = subscribed->framework_id();
+  update.set_checkpoint(!update.checkpoint());
+
+  Future<APIResult> result = callUpdateFramework(&mesos, update);
+
+  AWAIT_READY(result);
+  EXPECT_EQ(result->status_code(), 400u);
+  EXPECT_TRUE(strings::contains(
+      result->error(), "Updating 'FrameworkInfo.checkpoint' is unsupported"));
+
+  // Check that no partial update occurred.
+  Future<v1::master::Response::GetFrameworks> frameworks =
+    getFrameworks(master->get()->pid);
+
+  AWAIT_READY(frameworks);
+
+  ASSERT_EQ(frameworks->frameworks_size(), 1);
+
+  FrameworkInfo expected = DEFAULT_FRAMEWORK_INFO;
+  *expected.mutable_id() = subscribed->framework_id();
+  EXPECT_NONE(diff(frameworks->frameworks(0).framework_info(), expected));
+
+  // Sanity check for diff()
+  EXPECT_SOME(diff(update, expected));
+}
+
+
+// TODO(asekretenko): Add more tests for invalid updates:
+// - Try to add a nonexisting role.
+// - Try to add a role which the framework is not authorized to use.
+// ....
+
+
+// This test checks that it is possible to update all the mutable fields.
+// It verifies the following:
+// - HTTP status code of the scheduler API response.
+// - FrameworkInfo returned by GetFrameworks API call.
+// - FrameworkInfo sent in UpdateFrameworkMessage to the slave.
+// - FrameworkInfo sent in FRAMEWORK_UPDATED to the API subscribers.
+TEST_F(UpdateFrameworkTest, MutableFieldsUpdateSuccessfully)
+{
+  Try<Owned<cluster::Master>> master = StartMaster(CreateMasterFlags());
+  ASSERT_SOME(master);
+
+  Owned<MasterDetector> detector = master->get()->createDetector();
+
+  // Subscribe to master v1 API.
+  MockMasterAPISubscriber masterAPISubscriber;
+  AWAIT_READY(masterAPISubscriber.subscribe(master.get()->pid));
+
+  Future<Nothing> agentAdded;
+  EXPECT_CALL(masterAPISubscriber, agentAdded(_))
+    .WillOnce(FutureSatisfy(&agentAdded));
+
+  // We need a slave to test the UpdateFrameworkMessage.
+  mesos::internal::slave::Flags slaveFlags = CreateSlaveFlags();
+  Try<Owned<cluster::Slave>> slave = StartSlave(detector.get(), slaveFlags);
+  ASSERT_SOME(slave);
+
+  // To test the UpdateFrameworkMessage, we should wait for the slave
+  // to be added before calling UPDATE_FRAMEWORK.
+  AWAIT_READY(agentAdded);
+
+  // Expect FRAMEWORK_UPDATED event
+  Future<v1::master::Event::FrameworkUpdated> frameworkUpdated;
+  EXPECT_CALL(masterAPISubscriber, frameworkUpdated(_))
+    .WillOnce(FutureArg<0>(&frameworkUpdated));
+
+  // Expect UpdateFrameworkMessage to be sent from master to slave.
+  Future<UpdateFrameworkMessage> updateFrameworkMessage = FUTURE_PROTOBUF(
+      UpdateFrameworkMessage(), master->get()->pid, slave->get()->pid);
+
+  // Start scheduler, wait for connection and subscribe
+  auto scheduler = std::make_shared<MockHTTPScheduler>();
+
+  Future<Nothing> connected;
+  EXPECT_CALL(*scheduler, connected(_))
+    .WillOnce(SendSubscribe(DEFAULT_FRAMEWORK_INFO));
+
+  EXPECT_CALL(*scheduler, heartbeat(_))
+    .WillRepeatedly(Return()); // Ignore heartbeats.
+
+  Future<Event::Subscribed> subscribed;
+
+  EXPECT_CALL(*scheduler, subscribed(_, _))
+    .WillOnce(FutureArg<1>(&subscribed));
+
+  TestMesos mesos(master->get()->pid, ContentType::PROTOBUF, scheduler);
+
+  AWAIT_READY(subscribed);
+
+  FrameworkInfo update = changeAllMutableFields(DEFAULT_FRAMEWORK_INFO);
+  *update.mutable_id() = subscribed->framework_id();
+
+  Future<APIResult> result = callUpdateFramework(&mesos, update);
+
+  AWAIT_READY(result);
+  EXPECT_EQ(result->status_code(), 200u);
+
+  // Check that update occurred.
+  Future<v1::master::Response::GetFrameworks> frameworks =
+    getFrameworks(master->get()->pid);
+  AWAIT_READY(frameworks);
+
+  ASSERT_EQ(frameworks->frameworks_size(), 1);
+  const FrameworkInfo& frameworkInfo =
+    frameworks->frameworks(0).framework_info();
+
+  EXPECT_NONE(diff(frameworkInfo, update));
+
+  AWAIT_READY(updateFrameworkMessage);
+  EXPECT_NONE(diff(evolve(updateFrameworkMessage->framework_info()), update));
+
+  AWAIT_READY(frameworkUpdated);
+  EXPECT_NONE(diff(frameworkUpdated->framework().framework_info(), update));
+};
+
+
+// This tests that adding a role via UPDATE_FRAMEWORK to a framework which had
+// no roles triggers allocation of an offer for that role.
+TEST_F(UpdateFrameworkTest, OffersOnAddingRole)
+{
+  mesos::internal::master::Flags masterFlags = CreateMasterFlags();
+  Try<Owned<cluster::Master>> master = StartMaster(masterFlags);
+  ASSERT_SOME(master);
+
+  // There are at least two distinct cases that one might want to test:
+  // - That adding a role triggers allocation.
+  // - That adding a slave triggers allocation when the framework has roles.
+  //
+  // In this test the intention is to test the first case - and definitely
+  // not to alternate between these two cases from run to run.
+  // Therefore, before making scheduler calls, we need to wait for the slave to
+  // be added. This is done by waiting for an AGENT_ADDED master API event.
+  MockMasterAPISubscriber masterAPISubscriber;
+  AWAIT_READY(masterAPISubscriber.subscribe(master.get()->pid));
+
+  Future<Nothing> agentAdded;
+  EXPECT_CALL(masterAPISubscriber, agentAdded(_))
+    .WillOnce(FutureSatisfy(&agentAdded));
+
+  Owned<MasterDetector> detector = master->get()->createDetector();
+
+  mesos::internal::slave::Flags slaveFlags = CreateSlaveFlags();
+  Try<Owned<cluster::Slave>> slave = StartSlave(detector.get(), slaveFlags);
+  ASSERT_SOME(slave);
+
+  AWAIT_READY(agentAdded);
+
+  auto scheduler = std::make_shared<MockHTTPScheduler>();
+
+  // Initially, the framework subscribes with no roles.
+  FrameworkInfo initialFrameworkInfo = DEFAULT_FRAMEWORK_INFO;
+  initialFrameworkInfo.clear_roles();
+
+  Future<Nothing> connected;
+  EXPECT_CALL(*scheduler, connected(_))
+    .WillOnce(SendSubscribe(initialFrameworkInfo));
+
+  EXPECT_CALL(*scheduler, heartbeat(_))
+    .WillRepeatedly(Return()); // Ignore heartbeats.
+
+  Future<Event::Subscribed> subscribed;
+
+  EXPECT_CALL(*scheduler, subscribed(_, _))
+    .WillOnce(FutureArg<1>(&subscribed));
+
+  // Expect that the framework gets no offers before update.
+  EXPECT_CALL(*scheduler, offers(_, _))
+    .Times(AtMost(0));
+
+  TestMesos mesos(master->get()->pid, ContentType::PROTOBUF, scheduler);
+
+  AWAIT_READY(subscribed);
+
+  // Trigger allocation to ensure that offers are not generated before update.
+  Clock::pause();
+  Clock::settle();
+  Clock::advance(masterFlags.allocation_interval);
+  Clock::settle();
+
+  // Expect an offer after adding a role.
+  Future<Event::Offers> offers;
+  EXPECT_CALL(*scheduler, offers(_, _))
+    .WillOnce(FutureArg<1>(&offers));
+
+  FrameworkInfo update = initialFrameworkInfo;
+  *update.mutable_id() = subscribed->framework_id();
+  update.add_roles("new_role");
+
+  // To spare test running time, we wait for the update response and advance
+  // clock after that.
+  AWAIT_READY(callUpdateFramework(&mesos, update));
+
+  Clock::settle();
+  Clock::advance(masterFlags.allocation_interval);
+  Clock::settle();
+
+  AWAIT_READY(offers);
+
+  ASSERT_EQ(offers->offers().size(), 1);
+  EXPECT_EQ(offers->offers(0).allocation_info().role(), "new_role");
+}
+
+
+// Test that framework's offers are rescinded when a framework is
+// removed from all its roles via UPDATE_FRAMEWORK.
+TEST_F(UpdateFrameworkTest, RescindOnRemovingRoles)
+{
+  mesos::internal::master::Flags masterFlags = CreateMasterFlags();
+  Try<Owned<cluster::Master>> master = StartMaster(masterFlags);
+  ASSERT_SOME(master);
+
+  Owned<MasterDetector> detector = master->get()->createDetector();
+
+  mesos::internal::slave::Flags slaveFlags = CreateSlaveFlags();
+  Try<Owned<cluster::Slave>> slave = StartSlave(detector.get(), slaveFlags);
+  ASSERT_SOME(slave);
+
+  auto scheduler = std::make_shared<MockHTTPScheduler>();
+
+  EXPECT_CALL(*scheduler, connected(_))
+    .WillOnce(SendSubscribe(DEFAULT_FRAMEWORK_INFO));
+
+  EXPECT_CALL(*scheduler, heartbeat(_))
+    .WillRepeatedly(Return()); // Ignore heartbeats.
+
+  Future<Event::Subscribed> subscribed;
+  EXPECT_CALL(*scheduler, subscribed(_, _))
+    .WillOnce(FutureArg<1>(&subscribed));
+
+  // Expect offers. This should happen exactly once (when both
+  // the slave is added and the framework is subscribed).
+  Future<Event::Offers> offers;
+  EXPECT_CALL(*scheduler, offers(_, _))
+    .WillOnce(FutureArg<1>(&offers));
+
+  TestMesos mesos(master->get()->pid, ContentType::PROTOBUF, scheduler);
+
+  AWAIT_READY(offers);
+  ASSERT_EQ(offers->offers().size(), 1);
+
+  // Set up expectations for things that should happen after role removal.
+
+  // The offer for the removed role should be rescinded.
+  Future<Event::Rescind> rescind;
+  EXPECT_CALL(*scheduler, rescind(_, _))
+    .WillOnce(FutureArg<1>(&rescind));
+
+  // recoverResources() should be called.
+  //
+  // TODO(asekretenko): Add a more in-depth check that the
+  // allocator does what it should.
+  Future<Nothing> recoverResources =
+    FUTURE_DISPATCH(_, &MesosAllocatorProcess::recoverResources);
+
+  // Remove the framework from the role via UPDATE_FRAMEWORK.
+  FrameworkInfo update = DEFAULT_FRAMEWORK_INFO;
+  *update.mutable_id() = subscribed->framework_id();
+  update.clear_roles();
+
+  callUpdateFramework(&mesos, update);
+
+  AWAIT_READY(rescind);
+  AWAIT_READY(recoverResources);
+
+  EXPECT_EQ(offers->offers(0).id(), rescind->offer_id());
+
+  // After that, nothing of interest should happen within an allocation
+  // interval: no more offers and no more rescinding.
+  Clock::pause();
+  Clock::settle();
+  Clock::advance(masterFlags.allocation_interval);
+  Clock::settle();
+}
+
+
+} // namespace scheduler {
+} // namespace v1 {
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {

Reply via email to