This is an automated email from the ASF dual-hosted git repository. bennoe pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/mesos.git
commit b6bdc74c896303dc1775c68642023ee4513834b1 Author: Benno Evers <[email protected]> AuthorDate: Fri Nov 8 14:19:22 2019 +0100 Added end-to-end test for operator API reservation updates. Added a new test to verify that reservations can be updated using the operator API. Review: https://reviews.apache.org/r/71725/ --- src/tests/api_tests.cpp | 144 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) diff --git a/src/tests/api_tests.cpp b/src/tests/api_tests.cpp index bd207ea..393f9a2 100644 --- a/src/tests/api_tests.cpp +++ b/src/tests/api_tests.cpp @@ -1439,6 +1439,150 @@ TEST_P(MasterAPITest, ReserveResources) } +// This test verifies that an operator can update existing reservation through +// the `RESERVE_RESOURCES` call. +TEST_P(MasterAPITest, ReservationUpdate) +{ + ContentType contentType = GetParam(); + + TestAllocator<> allocator; + EXPECT_CALL(allocator, initialize(_, _, _)); + + Try<Owned<cluster::Master>> master = StartMaster(&allocator); + ASSERT_SOME(master); + + Future<SlaveID> slaveId; + EXPECT_CALL(allocator, addSlave(_, _, _, _, _, _)) + .WillOnce(DoAll(InvokeAddSlave(&allocator), + FutureArg<0>(&slaveId))); + + Future<Nothing> __recover = FUTURE_DISPATCH(_, &Slave::__recover); + + Owned<MasterDetector> detector = master.get()->createDetector(); + Try<Owned<cluster::Slave>> slave = StartSlave(detector.get()); + ASSERT_SOME(slave); + + // Wait until the agent has finished recovery. + ASSERT_SOME(slave); + AWAIT_READY(__recover); + + // It's actually impossible to construct `Resources` from the contents + // of `state["reserved_resources"]`, so instead we just compare against + // the expected JSON below. + JSON::Value resourcesJson = JSON::parse(R"_( + { + "cpus": 1.0, + "disk": 0.0, + "gpus": 0.0, + "mem": 10.0 + } + )_").get(); + + Resources unreserved = Resources::parse("cpus:1;mem:10").get(); + Resources dynamicallyReserved = + unreserved.pushReservation(createDynamicReservationInfo( + "role", DEFAULT_CREDENTIAL.principal())); + + Resources dynamicallyReservedToFoo = + dynamicallyReserved.pushReservation(createDynamicReservationInfo( + "role/foo", DEFAULT_CREDENTIAL.principal())); + + Resources dynamicallyReservedToBar = + dynamicallyReserved.pushReservation(createDynamicReservationInfo( + "role/bar", DEFAULT_CREDENTIAL.principal())); + + // Helper function to attempt a reservation update from a given `source` + // to a given reservation using an operator API call. + auto attemptReservation = [&master, &slaveId, &contentType]( + const Resources& source, + const Resources& resources, + const std::string& expectedResponseStatus) { + v1::master::Call v1Call; + v1Call.set_type(v1::master::Call::RESERVE_RESOURCES); + v1::master::Call::ReserveResources* reserveResources = + v1Call.mutable_reserve_resources(); + + reserveResources->mutable_agent_id()->CopyFrom(evolve(slaveId.get())); + reserveResources->mutable_source()->CopyFrom(evolve(source)); + reserveResources->mutable_resources()->CopyFrom(evolve(resources)); + + Future<http::Response> response = http::post( + master.get()->pid, + "api/v1", + createBasicAuthHeaders(DEFAULT_CREDENTIAL), + serialize(contentType, v1Call), + stringify(contentType)); + + AWAIT_EXPECT_RESPONSE_STATUS_EQ(expectedResponseStatus, response); + }; + + // Helper function to verify that the resources on the agent are + // indeed reserved to the specified role. + auto verifyReservation = [&slave, &resourcesJson]( + const std::string& intendedRole) { + Future<http::Response> response = http::get( + slave.get()->pid, + "state", + None(), // query + createBasicAuthHeaders(DEFAULT_CREDENTIAL)); + + AWAIT_EXPECT_RESPONSE_STATUS_EQ(http::OK().status, response); + AWAIT_EXPECT_RESPONSE_HEADER_EQ(APPLICATION_JSON, "Content-Type", response); + + Try<JSON::Value> json = JSON::parse(response->body); + ASSERT_SOME(json); + + Result<JSON::Object> reservations = + json->as<JSON::Object>().at<JSON::Object>("reserved_resources"); + + ASSERT_SOME(reservations); + EXPECT_EQ(reservations->values.size(), 1u); + + foreachpair ( + const std::string& role, + const JSON::Value& reservation, + reservations->values) { + EXPECT_EQ(role, intendedRole); + EXPECT_EQ(resourcesJson, reservation); + } + }; + + // Setup done, actual test can start now. + const std::string conflict = http::Conflict().status; + const std::string accepted = http::Accepted().status; + + // Should fail, since there are no resources that are dynamically + // reserved to `role/bar`. + attemptReservation( + dynamicallyReservedToBar, + dynamicallyReservedToFoo, + conflict); + + // Reserve unreserved resources to `role/foo`. + attemptReservation( + unreserved, + dynamicallyReservedToFoo, + accepted); + + verifyReservation("role/foo"); + + // Should fail again, since there are still no resources that are + // dynamically reserved to `role/bar`. + attemptReservation( + dynamicallyReservedToBar, + dynamicallyReservedToFoo, + conflict); + + // Update reservation from `role/foo` to `role/bar`. + attemptReservation( + dynamicallyReservedToFoo, + dynamicallyReservedToBar, + accepted); + + verifyReservation("role/bar"); +} + + // This test verifies that an operator can unreserve reserved resources through // the `UNRESERVE_RESOURCES` call. TEST_P(MasterAPITest, UnreserveResources)
