This is an automated email from the ASF dual-hosted git repository. mzhu pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/mesos.git
commit 0026ea46dc35cbba1f442b8e425c6cbaf81ee8f8 Author: Meng Zhu <[email protected]> AuthorDate: Fri Jul 5 18:05:59 2019 -0700 Implemented `UPDATE_QUOTA` operator call. This patch wires up the master, auth, registar and allocator pieces for `UPDATE_QUOTA` call. This enables the master capability `QUOTA_V2`. The capability implies the quota v2 API is capable of writes (`UPDATE_QUOTA`) and the master is capable of recovering from V2 quota (`QuotaConfig`) in registry. This patch lacks the rescind offer logic. When quota limits and guarantees are configured, it might be necessary to rescind offers on the fly to satisfy new guarantees or be constrained by the new limits. A todo is left and will be tackled in subsequent patches. Also enabled test `MasterQuotaTest.RecoverQuotaEmptyCluster`. Review: https://reviews.apache.org/r/71021 --- src/master/constants.cpp | 1 + src/master/http.cpp | 2 +- src/master/master.hpp | 4 ++++ src/master/quota_handler.cpp | 47 +++++++++++++++++++++++++++++++++++++--- src/tests/master_quota_tests.cpp | 4 +--- src/tests/master_tests.cpp | 2 +- 6 files changed, 52 insertions(+), 8 deletions(-) diff --git a/src/master/constants.cpp b/src/master/constants.cpp index 13b3467..6bd89c6 100644 --- a/src/master/constants.cpp +++ b/src/master/constants.cpp @@ -25,6 +25,7 @@ std::vector<MasterInfo::Capability> MASTER_CAPABILITIES() MasterInfo::Capability::Type types[] = { MasterInfo::Capability::AGENT_UPDATE, MasterInfo::Capability::AGENT_DRAINING, + MasterInfo::Capability::QUOTA_V2, }; std::vector<MasterInfo::Capability> result; diff --git a/src/master/http.cpp b/src/master/http.cpp index 1b3d48d..cd0f40c 100644 --- a/src/master/http.cpp +++ b/src/master/http.cpp @@ -380,7 +380,7 @@ Future<Response> Master::Http::api( return quotaHandler.status(call, principal, acceptType); case mesos::master::Call::UPDATE_QUOTA: - return NotImplemented(); + return quotaHandler.update(call, principal); // TODO(bmahler): Add this to a deprecated call section // at the bottom once deprecated by `UPDATE_QUOTA`. diff --git a/src/master/master.hpp b/src/master/master.hpp index b57483c..9b86155 100644 --- a/src/master/master.hpp +++ b/src/master/master.hpp @@ -1288,6 +1288,10 @@ private: const Option<process::http::authentication::Principal>& principal) const; + process::Future<process::http::Response> _update( + const google::protobuf::RepeatedPtrField<mesos::quota::QuotaConfig>& + quotaConfigs) const; + process::Future<process::http::Response> _set( const mesos::quota::QuotaRequest& quotaRequest, const Option<process::http::authentication::Principal>& diff --git a/src/master/quota_handler.cpp b/src/master/quota_handler.cpp index 9afab28..13789d8 100644 --- a/src/master/quota_handler.cpp +++ b/src/master/quota_handler.cpp @@ -436,8 +436,11 @@ Future<http::Response> Master::QuotaHandler::update( CHECK_EQ(mesos::master::Call::UPDATE_QUOTA, call.type()); CHECK(call.has_update_quota()); + const RepeatedPtrField<QuotaConfig>& configs = + call.update_quota().quota_configs(); + // Validate `QuotaConfig`. - foreach (auto&& config, call.update_quota().quota_configs()) { + foreach (const auto& config, configs) { // Check that the role is on the role whitelist, if it exists. if (!master->isWhitelistedRole(config.role())) { return BadRequest( @@ -478,7 +481,7 @@ Future<http::Response> Master::QuotaHandler::update( quotaTree.update(role, quota); } - foreach (auto&& config, call.update_quota().quota_configs()) { + foreach (const auto& config, configs) { quotaTree.update(config.role(), Quota{config}); } @@ -520,7 +523,45 @@ Future<http::Response> Master::QuotaHandler::update( } } - return NotImplemented(); + // Create a list of authorization actions + // for each quota configuration update. + vector<Future<bool>> authorizedUpdates; + authorizedUpdates.reserve(configs.size()); + foreach (const QuotaConfig& config, configs) { + authorizedUpdates.push_back(authorizeUpdateQuotaConfig(principal, config)); + } + + return process::collect(authorizedUpdates) + .then(defer( + master->self(), + [=](const vector<bool>& authorizations) -> Future<http::Response> { + return std::all_of( + authorizations.begin(), + authorizations.end(), + [](bool authorized) { return authorized; }) + ? _update(configs) + : Forbidden(); + })); +} + + +Future<http::Response> Master::QuotaHandler::_update( + const RepeatedPtrField<QuotaConfig>& configs) const +{ + return master->registrar + ->apply(Owned<RegistryOperation>(new quota::UpdateQuota(configs))) + .then(defer(master->self(), [=](bool result) -> Future<http::Response> { + // Currently, quota registry entry mutation never fails. + CHECK(result); + + foreach (const QuotaConfig& config, configs) { + master->allocator->updateQuota(config.role(), Quota{config}); + } + + // TODO(mzhu): Rescind offers. + + return OK(); + })); } diff --git a/src/tests/master_quota_tests.cpp b/src/tests/master_quota_tests.cpp index 34a6520..4b805e9 100644 --- a/src/tests/master_quota_tests.cpp +++ b/src/tests/master_quota_tests.cpp @@ -1268,9 +1268,7 @@ TEST_F(MasterQuotaTest, AvailableResourcesAfterRescinding) // Checks that quota is recovered correctly after master failover if // the expected size of the cluster is zero. -// -// TODO(mzhu): Enable this once master is QUOTA_V2 capable. -TEST_F(MasterQuotaTest, DISABLED_RecoverQuotaEmptyCluster) +TEST_F(MasterQuotaTest, RecoverQuotaEmptyCluster) { master::Flags masterFlags = CreateMasterFlags(); masterFlags.registry = "replicated_log"; diff --git a/src/tests/master_tests.cpp b/src/tests/master_tests.cpp index f817450..b9ef13c 100644 --- a/src/tests/master_tests.cpp +++ b/src/tests/master_tests.cpp @@ -5036,7 +5036,7 @@ TEST_F(MasterTest, StateEndpoint) // Master should always have these default capabilities. Try<JSON::Value> expectedCapabilities = - JSON::parse("[\"AGENT_UPDATE\", \"AGENT_DRAINING\"]"); + JSON::parse("[\"AGENT_UPDATE\", \"AGENT_DRAINING\", \"QUOTA_V2\"]"); ASSERT_SOME(expectedCapabilities); EXPECT_TRUE(masterCapabilities.contains(expectedCapabilities.get()));
