You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by jo...@apache.org on 2015/11/25 21:50:20 UTC

mesos git commit: Quota: Added Request Validation Tests.

Repository: mesos
Updated Branches:
  refs/heads/master 9235d4cb4 -> 6fbcd7663


Quota: Added Request Validation Tests.

Review: https://reviews.apache.org/r/39223


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/6fbcd766
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/6fbcd766
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/6fbcd766

Branch: refs/heads/master
Commit: 6fbcd76636ac67e3ac8721d5bc8c7aa4a11951bf
Parents: 9235d4c
Author: Joerg Schad <jo...@mesosphere.io>
Authored: Wed Nov 25 14:01:58 2015 -0500
Committer: Joris Van Remoortere <jo...@gmail.com>
Committed: Wed Nov 25 15:49:54 2015 -0500

----------------------------------------------------------------------
 src/tests/master_quota_tests.cpp | 241 +++++++++++++++++++++++++++++++++-
 1 file changed, 236 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/6fbcd766/src/tests/master_quota_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/master_quota_tests.cpp b/src/tests/master_quota_tests.cpp
index 330e591..16b291d 100644
--- a/src/tests/master_quota_tests.cpp
+++ b/src/tests/master_quota_tests.cpp
@@ -140,15 +140,10 @@ protected:
 // while irrelevant fields are not present.
 
 // TODO(alexr): Tests to implement:
-//   * Malformed JSON (parsing fails).
-//   * JSON is OK but could not be converted to QuotaInfo proto
-//     (merge with the previous one)?
-//   * Non-existent role.
 //   * Role is absent.
 //   * Role is an empty string.
 //   * Role is '*'?
 //   * Resources with the same name are present.
-//   * Irrelevant fields (reservation info, disk info, etc).
 
 // Verifies that a request for a non-existent role is rejected.
 // TODO(alexr): This may be revisited once we allow dynamic roles and
@@ -179,6 +174,242 @@ TEST_F(MasterQuotaTest, NonExistentRole)
 }
 
 
+// Quota requests with invalid structure should return a '400 Bad Request'.
+TEST_F(MasterQuotaTest, SetInvalidRequest)
+{
+  Try<PID<Master>> master = StartMaster();
+  ASSERT_SOME(master);
+
+  // We do not need an agent since a request should be rejected before
+  // we start looking at available resources.
+
+  // We wrap the `http::post` into a lambda for readability of the tests.
+  auto postQuota = [this, &master](const string& request) {
+    return process::http::post(
+        master.get(),
+        "quota",
+        createBasicAuthHeaders(DEFAULT_CREDENTIAL),
+        request);
+  };
+
+  // Tests whether a quota request with missing 'resource=[]' fails.
+  {
+    string badRequest =
+      "{"
+      "  invalidJson"
+      "}";
+
+    Future<Response> response = postQuota(badRequest);
+
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(BadRequest().status, response)
+      << response.get().body;
+  }
+
+  // Tests whether a quota requests with invalid json fails.
+  {
+    string badRequest =
+      "resources=["
+      "  \"invalidJson\" : 1"
+      "]";
+
+    Future<Response> response = postQuota(badRequest);
+
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(BadRequest().status, response)
+      << response.get().body;
+  }
+
+  // Tests whether a quota request with invalid resources fails.
+  {
+    string badRequest =
+      "resources=["
+      "  {\"invalidResource\" : 1}"
+      "]";
+
+    Future<Response> response = postQuota(badRequest);
+
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(BadRequest().status, response)
+      << response.get().body;
+  }
+
+  Shutdown();
+}
+
+
+// A quota request with non-scalar resources should return a '400 Bad Request'.
+TEST_F(MasterQuotaTest, SetNonScalar)
+{
+  Try<PID<Master>> master = StartMaster();
+  ASSERT_SOME(master);
+
+  // We do not need an agent since a request should be rejected before
+  // we start looking at available resources.
+
+  // Quota set request including non-scalar port resources.
+  Resources quotaResources =
+    Resources::parse("cpus:1;mem:512;ports:[31000-31001]", ROLE1).get();
+
+  Future<Response> response = process::http::post(
+      master.get(),
+      "quota",
+      createBasicAuthHeaders(DEFAULT_CREDENTIAL),
+      createRequestBody(quotaResources));
+
+  AWAIT_EXPECT_RESPONSE_STATUS_EQ(BadRequest().status, response)
+    << response.get().body;
+
+  Shutdown();
+}
+
+
+// A quota request with multiple roles should return a '400 Bad Request'.
+TEST_F(MasterQuotaTest, SetMultipleRoles)
+{
+  Try<PID<Master>> master = StartMaster();
+  ASSERT_SOME(master);
+
+  // We do not need an agent since a request should be rejected before
+  // we start looking at available resources.
+
+  // Create a quota request with resources belonging to different roles.
+  Resources quotaResources = Resources::parse("cpus:1;mem:512;", ROLE1).get();
+  quotaResources += Resources::parse("cpus:1;mem:512;", ROLE2).get();
+
+  Future<Response> response = process::http::post(
+      master.get(),
+      "quota",
+      createBasicAuthHeaders(DEFAULT_CREDENTIAL),
+      createRequestBody(quotaResources));
+
+  AWAIT_EXPECT_RESPONSE_STATUS_EQ(BadRequest().status, response)
+    << response.get().body;
+
+  Shutdown();
+}
+
+
+// Updating an exiting quota via POST to the '/master/quota endpoint' should
+// return a '400 BadRequest'.
+TEST_F(MasterQuotaTest, SetExistingQuota)
+{
+  TestAllocator<> allocator;
+  EXPECT_CALL(allocator, initialize(_, _, _, _));
+
+  Try<PID<Master>> master = StartMaster(&allocator);
+  ASSERT_SOME(master);
+
+  // Wait until the agent registers.
+  Future<Resources> agentTotalResources;
+  EXPECT_CALL(allocator, addSlave(_, _, _, _, _))
+    .WillOnce(DoAll(InvokeAddSlave(&allocator),
+                    FutureArg<3>(&agentTotalResources)));
+
+  Try<PID<Slave>> agent = StartSlave();
+  ASSERT_SOME(agent);
+
+  AWAIT_READY(agentTotalResources);
+  EXPECT_EQ(defaultAgentResources, agentTotalResources.get());
+
+  // We request quota for a portion of resources available on the agent.
+  Resources quotaResources = Resources::parse("cpus:1;mem:512;", ROLE1).get();
+  EXPECT_TRUE(agentTotalResources.get().contains(quotaResources.flatten()));
+
+  Future<Response> response = process::http::post(
+      master.get(),
+      "quota",
+      createBasicAuthHeaders(DEFAULT_CREDENTIAL),
+      createRequestBody(quotaResources));
+
+  AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response) << response.get().body;
+
+  // Try to set quota via post a second time.
+  response = process::http::post(
+      master.get(),
+      "quota",
+      createBasicAuthHeaders(DEFAULT_CREDENTIAL),
+      createRequestBody(quotaResources));
+
+  AWAIT_EXPECT_RESPONSE_STATUS_EQ(BadRequest().status, response)
+    << response.get().body;
+
+  Shutdown();
+}
+
+
+// Checks whether a quota request with any invalid field set is rejected:
+//   * `ReservationInfo`.
+//   * `RevocableInfo`.
+//   * `DiskInfo`.
+TEST_F(MasterQuotaTest, SetInvalidResourceInfos)
+{
+  Try<PID<Master>> master = StartMaster();
+  ASSERT_SOME(master);
+
+  // We do not need an agent since a request should be rejected before
+  // we start looking at available resources.
+
+  // Create a quota set request with `DiskInfo` and check that the
+  // request returns a '400 Bad Request' return code.
+  {
+    Resources quotaResources = Resources::parse("cpus:1;mem:512", ROLE1).get();
+
+    Resource volume = Resources::parse("disk", "128", ROLE1).get();
+    volume.mutable_disk()->CopyFrom(createDiskInfo("id1", "path1"));
+    quotaResources += volume;
+
+    Future<Response> response = process::http::post(
+        master.get(),
+        "quota",
+        createBasicAuthHeaders(DEFAULT_CREDENTIAL),
+        createRequestBody(quotaResources));
+
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(BadRequest().status, response)
+      << response.get().body;
+  }
+
+  // Create a quota set request with `RevocableInfo` and check that
+  // the request returns a '400 Bad Request' return code.
+  {
+    Resources quotaResources = Resources::parse("cpus:1;mem:512", ROLE1).get();
+
+    Resource revocable = Resources::parse("cpus", "1", ROLE1).get();
+    revocable.mutable_revocable();
+    quotaResources += revocable;
+
+    Future<Response> response = process::http::post(
+        master.get(),
+        "quota",
+        createBasicAuthHeaders(DEFAULT_CREDENTIAL),
+        createRequestBody(quotaResources));
+
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(BadRequest().status, response)
+      << response.get().body;
+  }
+
+  // Create a quota set request with `ReservationInfo` and check that
+  // the request returns a '400 Bad Request' return code.
+  {
+    Resources quotaResources = Resources::parse("cpus:4;mem:512", ROLE1).get();
+
+    Resource volume = Resources::parse("disk", "128", ROLE1).get();
+    volume.mutable_reservation()->CopyFrom(
+        createReservationInfo(DEFAULT_CREDENTIAL.principal()));
+
+    quotaResources += volume;
+
+    Future<Response> response = process::http::post(
+        master.get(),
+        "quota",
+        createBasicAuthHeaders(DEFAULT_CREDENTIAL),
+        createRequestBody(quotaResources));
+
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(BadRequest().status, response)
+      << response.get().body;
+  }
+
+  Shutdown();
+}
+
+
 // These tests check whether a request makes sense in terms of current cluster
 // status. A quota request may be well-formed, but obviously infeasible, e.g.
 // request for 100 CPUs in a cluster with just 11 CPUs.