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()));

Reply via email to