You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by mz...@apache.org on 2019/07/10 23:53:39 UTC

[mesos] branch master updated: Added tests for `UPDATE_QUOTA`.

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


The following commit(s) were added to refs/heads/master by this push:
     new dcd7343  Added tests for `UPDATE_QUOTA`.
dcd7343 is described below

commit dcd73437549413790751d1ff127989dbb29bd753
Author: Meng Zhu <mz...@mesosphere.io>
AuthorDate: Sun Jul 7 14:27:14 2019 -0700

    Added tests for `UPDATE_QUOTA`.
    
    These tests reuse the existing tests for `SET_QUOTA` and
    `REMOVE_QUOTA` calls. In general, `UPDATE_QUOTA` request
    should fail where `SET_QUOTA` fails. When the existing
    test expects `SET_QUOTA` call succeeds, we test the
    `UPDATE_QUOTA` call by first remove the set quota and then
    send the `UPDATE_QUOTA` request.
    
    Review: https://reviews.apache.org/r/71022
---
 src/tests/master_quota_tests.cpp | 412 +++++++++++++++++++++++++++++++++------
 1 file changed, 347 insertions(+), 65 deletions(-)

diff --git a/src/tests/master_quota_tests.cpp b/src/tests/master_quota_tests.cpp
index 4b805e9..0e23bc1 100644
--- a/src/tests/master_quota_tests.cpp
+++ b/src/tests/master_quota_tests.cpp
@@ -58,6 +58,7 @@ using mesos::internal::slave::Slave;
 
 using mesos::master::detector::MasterDetector;
 
+using mesos::quota::QuotaConfig;
 using mesos::quota::QuotaInfo;
 using mesos::quota::QuotaRequest;
 using mesos::quota::QuotaStatus;
@@ -87,6 +88,44 @@ namespace mesos {
 namespace internal {
 namespace tests {
 
+static QuotaConfig createQuotaConfig(
+    const string& role,
+    const string& guaranteesString,
+    const string& limitsString)
+{
+  QuotaConfig config;
+  config.set_role(role);
+
+  ResourceQuantities guarantees =
+    CHECK_NOTERROR(ResourceQuantities::fromString(guaranteesString));
+  ResourceLimits limits =
+    CHECK_NOTERROR(ResourceLimits::fromString(limitsString));
+
+  foreachpair (const string& name, const Value::Scalar& scalar, guarantees) {
+    (*config.mutable_guarantees())[name] = scalar;
+  }
+
+  foreachpair (const string& name, const Value::Scalar& scalar, limits) {
+    (*config.mutable_limits())[name] = scalar;
+  }
+
+  return std::move(config);
+}
+
+
+// A shortcut for generating request body for configuring quota for one role.
+static string createUpdateQuotaRequestBody(
+    const QuotaConfig& config, bool force = false)
+{
+  mesos::master::Call call;
+  call.set_type(mesos::master::Call::UPDATE_QUOTA);
+  call.mutable_update_quota()->set_force(force);
+  *call.mutable_update_quota()->mutable_quota_configs()->Add() = config;
+
+  return stringify(JSON::protobuf(call));
+}
+
+
 // Quota tests that are allocator-agnostic (i.e. we expect every
 // allocator to implement basic quota guarantees) are in this
 // file. All tests are split into logical groups:
@@ -717,6 +756,28 @@ TEST_F(MasterQuotaTest, InsufficientResourcesSingleAgent)
               response->body);
   }
 
+  // Retry to test `UPDATE_QUOTA` call.
+  {
+    process::http::Headers headers = createBasicAuthHeaders(DEFAULT_CREDENTIAL);
+    headers["Content-Type"] = "application/json";
+
+    Future<Response> response = process::http::post(
+        master.get()->pid,
+        "/api/v1",
+        headers,
+        createUpdateQuotaRequestBody(createQuotaConfig(
+            ROLE1, stringify(quotaResources), stringify(quotaResources))));
+
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(BadRequest().status, response);
+
+    EXPECT_EQ(
+        "Invalid QuotaConfig: total quota guarantees"
+        " 'cpus:3; mem:2048' exceed cluster capacity"
+        " 'cpus:2; disk:1024; mem:1024'"
+        " (use 'force' flag to bypass this check)",
+        response->body);
+  }
+
   // Force flag should override the `capacityHeuristic` check and make the
   // request succeed.
   {
@@ -727,6 +788,30 @@ TEST_F(MasterQuotaTest, InsufficientResourcesSingleAgent)
         createRequestBody(ROLE1, quotaResources, true));
 
     AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
+
+    // Remove the quota and retry to test `UPDATE_QUOTA` call.
+    response = process::http::requestDelete(
+        master.get()->pid,
+        "quota/" + ROLE1,
+        createBasicAuthHeaders(DEFAULT_CREDENTIAL));
+
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
+  }
+
+  {
+    process::http::Headers headers = createBasicAuthHeaders(DEFAULT_CREDENTIAL);
+    headers["Content-Type"] = "application/json";
+
+    Future<Response> response = process::http::post(
+        master.get()->pid,
+        "/api/v1",
+        headers,
+        createUpdateQuotaRequestBody(
+            createQuotaConfig(
+                ROLE1, stringify(quotaResources), stringify(quotaResources)),
+            true));
+
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
   }
 }
 
@@ -797,6 +882,27 @@ TEST_F(MasterQuotaTest, InsufficientResourcesMultipleAgents)
               response->body);
   }
 
+  // Retry and test `UPDATE_QUOTA`.
+  {
+    process::http::Headers headers = createBasicAuthHeaders(DEFAULT_CREDENTIAL);
+    headers["Content-Type"] = "application/json";
+
+    Future<Response> response = process::http::post(
+        master.get()->pid,
+        "/api/v1",
+        headers,
+        createUpdateQuotaRequestBody(createQuotaConfig(
+            ROLE1, stringify(quotaResources), stringify(quotaResources))));
+
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(BadRequest().status, response);
+    EXPECT_EQ(
+        "Invalid QuotaConfig: total quota guarantees"
+        " 'cpus:5; mem:3072' exceed cluster capacity"
+        " 'cpus:4; disk:2048; mem:2048'"
+        " (use 'force' flag to bypass this check)",
+        response->body);
+  }
+
   // Force flag should override the `capacityHeuristic` check and make the
   // request succeed.
   {
@@ -807,6 +913,30 @@ TEST_F(MasterQuotaTest, InsufficientResourcesMultipleAgents)
         createRequestBody(ROLE1, quotaResources, true));
 
     AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
+
+    // Remove the quota and retry to test `UPDATE_QUOTA` call.
+    response = process::http::requestDelete(
+        master.get()->pid,
+        "quota/" + ROLE1,
+        createBasicAuthHeaders(DEFAULT_CREDENTIAL));
+
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
+  }
+
+  {
+    process::http::Headers headers = createBasicAuthHeaders(DEFAULT_CREDENTIAL);
+    headers["Content-Type"] = "application/json";
+
+    Future<Response> response = process::http::post(
+        master.get()->pid,
+        "/api/v1",
+        headers,
+        createUpdateQuotaRequestBody(
+            createQuotaConfig(
+                ROLE1, stringify(quotaResources), stringify(quotaResources)),
+            true));
+
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
   }
 }
 
@@ -839,26 +969,64 @@ TEST_F(MasterQuotaTest, AvailableResourcesSingleAgent)
   EXPECT_TRUE(agentTotalResources->contains(quotaResources));
 
   // Send a quota request for the specified role.
-  Future<Quota> receivedQuotaRequest;
-  EXPECT_CALL(allocator, updateQuota(Eq(ROLE1), _))
-    .WillOnce(DoAll(InvokeUpdateQuota(&allocator),
-                    FutureArg<1>(&receivedQuotaRequest)));
+  {
+    Future<Quota> receivedQuotaRequest;
+    EXPECT_CALL(allocator, updateQuota(Eq(ROLE1), _))
+      .WillOnce(DoAll(InvokeUpdateQuota(&allocator),
+                      FutureArg<1>(&receivedQuotaRequest)))
+      .RetiresOnSaturation(); // Don't impose any subsequent expectations.
 
-  Future<Response> response = process::http::post(
-      master.get()->pid,
-      "quota",
-      createBasicAuthHeaders(DEFAULT_CREDENTIAL),
-      createRequestBody(ROLE1, quotaResources));
+    Future<Response> response = process::http::post(
+        master.get()->pid,
+        "quota",
+        createBasicAuthHeaders(DEFAULT_CREDENTIAL),
+        createRequestBody(ROLE1, quotaResources));
 
-  AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
 
-  // Quota request is granted and reached the allocator. Make sure nothing
-  // got lost in-between.
-  AWAIT_READY(receivedQuotaRequest);
+    // Quota request is granted and reached the allocator. Make sure nothing
+    // got lost in-between.
+    AWAIT_READY(receivedQuotaRequest);
 
-  EXPECT_EQ(
-      ResourceQuantities::fromScalarResources(quotaResources),
-      receivedQuotaRequest->guarantees);
+    EXPECT_EQ(
+        ResourceQuantities::fromScalarResources(quotaResources),
+        receivedQuotaRequest->guarantees);
+
+    // Remove the quota and retry to test `UPDATE_QUOTA` call.
+    response = process::http::requestDelete(
+        master.get()->pid,
+        "quota/" + ROLE1,
+        createBasicAuthHeaders(DEFAULT_CREDENTIAL));
+
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
+  }
+
+  {
+    process::http::Headers headers = createBasicAuthHeaders(DEFAULT_CREDENTIAL);
+    headers["Content-Type"] = "application/json";
+
+    Future<Quota> receivedQuotaRequest;
+    EXPECT_CALL(allocator, updateQuota(Eq(ROLE1), _))
+      .WillOnce(DoAll(
+          InvokeUpdateQuota(&allocator), FutureArg<1>(&receivedQuotaRequest)));
+
+    Future<Response> response = process::http::post(
+        master.get()->pid,
+        "/api/v1",
+        headers,
+        createUpdateQuotaRequestBody(createQuotaConfig(
+            ROLE1, stringify(quotaResources), stringify(quotaResources))));
+
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
+
+    // Quota request is granted and reached the allocator. Make sure nothing
+    // got lost in-between.
+    AWAIT_READY(receivedQuotaRequest);
+
+    EXPECT_EQ(
+        ResourceQuantities::fromScalarResources(quotaResources),
+        receivedQuotaRequest->guarantees);
+  }
 }
 
 
@@ -894,26 +1062,64 @@ TEST_F(MasterQuotaTest, AvailableResourcesSingleReservedAgent)
   Resources quotaResources = Resources::parse("cpus:1;mem:512").get();
 
   // Send a quota request for the specified role.
-  Future<Quota> receivedQuotaRequest;
-  EXPECT_CALL(allocator, updateQuota(Eq(ROLE1), _))
-    .WillOnce(DoAll(InvokeUpdateQuota(&allocator),
-                    FutureArg<1>(&receivedQuotaRequest)));
+  {
+    Future<Quota> receivedQuotaRequest;
+    EXPECT_CALL(allocator, updateQuota(Eq(ROLE1), _))
+      .WillOnce(DoAll(InvokeUpdateQuota(&allocator),
+                      FutureArg<1>(&receivedQuotaRequest)))
+      .RetiresOnSaturation(); // Don't impose any subsequent expectations.
 
-  Future<Response> response = process::http::post(
-      master.get()->pid,
-      "quota",
-      createBasicAuthHeaders(DEFAULT_CREDENTIAL),
-      createRequestBody(ROLE1, quotaResources));
+    Future<Response> response = process::http::post(
+        master.get()->pid,
+        "quota",
+        createBasicAuthHeaders(DEFAULT_CREDENTIAL),
+        createRequestBody(ROLE1, quotaResources));
 
-  AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
 
-  // Quota request is granted and reached the allocator. Make sure nothing
-  // got lost in-between.
-  AWAIT_READY(receivedQuotaRequest);
+    // Quota request is granted and reached the allocator. Make sure nothing
+    // got lost in-between.
+    AWAIT_READY(receivedQuotaRequest);
 
-  EXPECT_EQ(
-      ResourceQuantities::fromScalarResources(quotaResources),
-      receivedQuotaRequest->guarantees);
+    EXPECT_EQ(
+        ResourceQuantities::fromScalarResources(quotaResources),
+        receivedQuotaRequest->guarantees);
+
+    // Remove the quota and retry to test `UPDATE_QUOTA` call.
+    response = process::http::requestDelete(
+        master.get()->pid,
+        "quota/" + ROLE1,
+        createBasicAuthHeaders(DEFAULT_CREDENTIAL));
+
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
+  }
+
+  {
+    process::http::Headers headers = createBasicAuthHeaders(DEFAULT_CREDENTIAL);
+    headers["Content-Type"] = "application/json";
+
+    Future<Quota> receivedQuotaRequest;
+    EXPECT_CALL(allocator, updateQuota(Eq(ROLE1), _))
+      .WillOnce(DoAll(
+          InvokeUpdateQuota(&allocator), FutureArg<1>(&receivedQuotaRequest)));
+
+    Future<Response> response = process::http::post(
+        master.get()->pid,
+        "/api/v1",
+        headers,
+        createUpdateQuotaRequestBody(createQuotaConfig(
+            ROLE1, stringify(quotaResources), stringify(quotaResources))));
+
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
+
+    // Quota request is granted and reached the allocator. Make sure nothing
+    // got lost in-between.
+    AWAIT_READY(receivedQuotaRequest);
+
+    EXPECT_EQ(
+        ResourceQuantities::fromScalarResources(quotaResources),
+        receivedQuotaRequest->guarantees);
+  }
 }
 
 
@@ -961,26 +1167,64 @@ TEST_F(MasterQuotaTest, AvailableResourcesSingleDisconnectedAgent)
   EXPECT_TRUE(agentTotalResources->contains(quotaResources));
 
   // Send a quota request for the specified role.
-  Future<Quota> receivedQuotaRequest;
-  EXPECT_CALL(allocator, updateQuota(Eq(ROLE1), _))
-    .WillOnce(DoAll(InvokeUpdateQuota(&allocator),
-                    FutureArg<1>(&receivedQuotaRequest)));
+  {
+    Future<Quota> receivedQuotaRequest;
+    EXPECT_CALL(allocator, updateQuota(Eq(ROLE1), _))
+      .WillOnce(DoAll(InvokeUpdateQuota(&allocator),
+                      FutureArg<1>(&receivedQuotaRequest)))
+      .RetiresOnSaturation(); // Don't impose any subsequent expectations.
 
-  Future<Response> response = process::http::post(
-      master.get()->pid,
-      "quota",
-      createBasicAuthHeaders(DEFAULT_CREDENTIAL),
-      createRequestBody(ROLE1, quotaResources));
+    Future<Response> response = process::http::post(
+        master.get()->pid,
+        "quota",
+        createBasicAuthHeaders(DEFAULT_CREDENTIAL),
+        createRequestBody(ROLE1, quotaResources));
 
-  AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
 
-  // Quota request is granted and reached the allocator. Make sure nothing
-  // got lost in-between.
-  AWAIT_READY(receivedQuotaRequest);
+    // Quota request is granted and reached the allocator. Make sure nothing
+    // got lost in-between.
+    AWAIT_READY(receivedQuotaRequest);
 
-  EXPECT_EQ(
-      ResourceQuantities::fromScalarResources(quotaResources),
-      receivedQuotaRequest->guarantees);
+    EXPECT_EQ(
+        ResourceQuantities::fromScalarResources(quotaResources),
+        receivedQuotaRequest->guarantees);
+
+    // Remove the quota and retry to test `UPDATE_QUOTA` call.
+    response = process::http::requestDelete(
+        master.get()->pid,
+        "quota/" + ROLE1,
+        createBasicAuthHeaders(DEFAULT_CREDENTIAL));
+
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
+  }
+
+  {
+    process::http::Headers headers = createBasicAuthHeaders(DEFAULT_CREDENTIAL);
+    headers["Content-Type"] = "application/json";
+
+    Future<Quota> receivedQuotaRequest;
+    EXPECT_CALL(allocator, updateQuota(Eq(ROLE1), _))
+      .WillOnce(DoAll(
+          InvokeUpdateQuota(&allocator), FutureArg<1>(&receivedQuotaRequest)));
+
+    Future<Response> response = process::http::post(
+        master.get()->pid,
+        "/api/v1",
+        headers,
+        createUpdateQuotaRequestBody(createQuotaConfig(
+            ROLE1, stringify(quotaResources), stringify(quotaResources))));
+
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
+
+    // Quota request is granted and reached the allocator. Make sure nothing
+    // got lost in-between.
+    AWAIT_READY(receivedQuotaRequest);
+
+    EXPECT_EQ(
+        ResourceQuantities::fromScalarResources(quotaResources),
+        receivedQuotaRequest->guarantees);
+  }
 }
 
 
@@ -1029,26 +1273,64 @@ TEST_F(MasterQuotaTest, AvailableResourcesMultipleAgents)
       return (resource.name() == "cpus" || resource.name() == "mem");
     });
 
-  // Send a quota request for the specified role.
-  Future<Quota> receivedQuotaRequest;
-  EXPECT_CALL(allocator, updateQuota(Eq(ROLE1), _))
-    .WillOnce(DoAll(InvokeUpdateQuota(&allocator),
-                    FutureArg<1>(&receivedQuotaRequest)));
+  {
+    // Send a quota request for the specified role.
+    Future<Quota> receivedQuotaRequest;
+    EXPECT_CALL(allocator, updateQuota(Eq(ROLE1), _))
+      .WillOnce(DoAll(
+          InvokeUpdateQuota(&allocator), FutureArg<1>(&receivedQuotaRequest)))
+      .RetiresOnSaturation(); // Don't impose any subsequent expectations.
 
-  Future<Response> response = process::http::post(
-      master.get()->pid,
-      "quota",
-      createBasicAuthHeaders(DEFAULT_CREDENTIAL),
-      createRequestBody(ROLE1, quotaResources));
+    Future<Response> response = process::http::post(
+        master.get()->pid,
+        "quota",
+        createBasicAuthHeaders(DEFAULT_CREDENTIAL),
+        createRequestBody(ROLE1, quotaResources));
 
-  AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
 
-  // Quota request is granted and reached the allocator. Make sure nothing
-  // got lost in-between.
-  AWAIT_READY(receivedQuotaRequest);
-  EXPECT_EQ(
-      ResourceQuantities::fromScalarResources(quotaResources),
-      receivedQuotaRequest->guarantees);
+    // Quota request is granted and reached the allocator. Make sure nothing
+    // got lost in-between.
+    AWAIT_READY(receivedQuotaRequest);
+    EXPECT_EQ(
+        ResourceQuantities::fromScalarResources(quotaResources),
+        receivedQuotaRequest->guarantees);
+
+    // Remove the quota and retry to test `UPDATE_QUOTA` call.
+    response = process::http::requestDelete(
+        master.get()->pid,
+        "quota/" + ROLE1,
+        createBasicAuthHeaders(DEFAULT_CREDENTIAL));
+
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
+  }
+
+  {
+    process::http::Headers headers = createBasicAuthHeaders(DEFAULT_CREDENTIAL);
+    headers["Content-Type"] = "application/json";
+
+    Future<Quota> receivedQuotaRequest;
+    EXPECT_CALL(allocator, updateQuota(Eq(ROLE1), _))
+      .WillOnce(DoAll(
+          InvokeUpdateQuota(&allocator), FutureArg<1>(&receivedQuotaRequest)));
+
+    Future<Response> response = process::http::post(
+        master.get()->pid,
+        "/api/v1",
+        headers,
+        createUpdateQuotaRequestBody(createQuotaConfig(
+            ROLE1, stringify(quotaResources), stringify(quotaResources))));
+
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
+
+    // Quota request is granted and reached the allocator. Make sure nothing
+    // got lost in-between.
+    AWAIT_READY(receivedQuotaRequest);
+
+    EXPECT_EQ(
+        ResourceQuantities::fromScalarResources(quotaResources),
+        receivedQuotaRequest->guarantees);
+  }
 }