You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by me...@apache.org on 2015/12/19 10:21:04 UTC

[2/5] mesos git commit: Added test cases for implicit roles.

Added test cases for implicit roles.

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


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

Branch: refs/heads/master
Commit: ff4f8ca9b3e8a0938bb4b167329d3612b13067b3
Parents: f81adfa
Author: Neil Conway <ne...@gmail.com>
Authored: Fri Dec 18 15:35:00 2015 -0800
Committer: Adam B <ad...@mesosphere.io>
Committed: Fri Dec 18 23:35:56 2015 -0800

----------------------------------------------------------------------
 src/tests/role_tests.cpp | 428 ++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 414 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/ff4f8ca9/src/tests/role_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/role_tests.cpp b/src/tests/role_tests.cpp
index 240017c..2c5f68c 100644
--- a/src/tests/role_tests.cpp
+++ b/src/tests/role_tests.cpp
@@ -14,6 +14,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <string>
+#include <vector>
+
+#include <mesos/http.hpp>
+
 #include <process/pid.hpp>
 
 #include "tests/mesos.hpp"
@@ -21,10 +26,16 @@
 using mesos::internal::master::Master;
 using mesos::internal::slave::Slave;
 
+using std::string;
+using std::vector;
+
+using google::protobuf::RepeatedPtrField;
+
 using process::Future;
 using process::PID;
 
-using std::vector;
+using process::http::OK;
+using process::http::Response;
 
 namespace mesos {
 namespace internal {
@@ -66,6 +77,178 @@ TEST_F(RoleTest, BadRegister)
 }
 
 
+// This test checks that when using implicit roles, a framework can
+// register with a new role, make a dynamic reservation, and create a
+// persistent volume.
+TEST_F(RoleTest, ImplicitRoleRegister)
+{
+  master::Flags masterFlags = CreateMasterFlags();
+  masterFlags.allocation_interval = Milliseconds(50);
+
+  Try<PID<Master>> master = StartMaster(masterFlags);
+  ASSERT_SOME(master);
+
+  slave::Flags slaveFlags = CreateSlaveFlags();
+  slaveFlags.resources = "cpus:1;mem:512;disk:1024";
+
+  Try<PID<Slave>> slave = StartSlave(slaveFlags);
+  ASSERT_SOME(slave);
+
+  FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO;
+  frameworkInfo.set_role("new-role-name");
+
+  MockScheduler sched;
+  MesosSchedulerDriver driver(
+      &sched, frameworkInfo, master.get(), DEFAULT_CREDENTIAL);
+
+  // We use the filter explicitly here so that the resources will not
+  // be filtered for 5 seconds (by default).
+  Filters filters;
+  filters.set_refuse_seconds(0);
+
+  Resources unreserved = Resources::parse("disk:1024").get();
+  Resources dynamicallyReserved = unreserved.flatten(
+      frameworkInfo.role(), createReservationInfo(frameworkInfo.principal()));
+
+  // We use this to capture offers from `resourceOffers`.
+  Future<vector<Offer>> offers;
+
+  EXPECT_CALL(sched, registered(&driver, _, _));
+
+  // The expectation for the first offer.
+  EXPECT_CALL(sched, resourceOffers(&driver, _))
+    .WillOnce(FutureArg<1>(&offers));
+
+  driver.start();
+
+  // In the first offer, expect an offer with unreserved resources.
+  AWAIT_READY(offers);
+
+  ASSERT_EQ(1u, offers.get().size());
+  Offer offer = offers.get()[0];
+
+  EXPECT_TRUE(Resources(offer.resources()).contains(unreserved));
+  EXPECT_FALSE(Resources(offer.resources()).contains(dynamicallyReserved));
+
+  // The expectation for the next offer.
+  EXPECT_CALL(sched, resourceOffers(&driver, _))
+    .WillOnce(FutureArg<1>(&offers));
+
+  // Reserve the resources.
+  driver.acceptOffers({offer.id()}, {RESERVE(dynamicallyReserved)}, filters);
+
+  // In the next offer, expect an offer with reserved resources.
+  AWAIT_READY(offers);
+
+  ASSERT_EQ(1u, offers.get().size());
+  offer = offers.get()[0];
+
+  EXPECT_TRUE(Resources(offer.resources()).contains(dynamicallyReserved));
+  EXPECT_FALSE(Resources(offer.resources()).contains(unreserved));
+
+  Resources volume = createPersistentVolume(
+      Megabytes(64),
+      frameworkInfo.role(),
+      "id1",
+      "path1",
+      frameworkInfo.principal());
+
+  // The expectation for the next offer.
+  EXPECT_CALL(sched, resourceOffers(&driver, _))
+    .WillOnce(FutureArg<1>(&offers));
+
+  driver.acceptOffers({offer.id()}, {CREATE(volume)}, filters);
+
+  // In the next offer, expect an offer with a persistent volume.
+  AWAIT_READY(offers);
+
+  ASSERT_EQ(1u, offers.get().size());
+  offer = offers.get()[0];
+
+  EXPECT_TRUE(Resources(offer.resources()).contains(volume));
+  EXPECT_FALSE(Resources(offer.resources()).contains(dynamicallyReserved));
+  EXPECT_FALSE(Resources(offer.resources()).contains(unreserved));
+
+  driver.stop();
+  driver.join();
+
+  Shutdown();
+}
+
+
+// This test checks that when using implicit roles, a static
+// reservation for a new role can be made and used to launch a task.
+TEST_F(RoleTest, ImplicitRoleStaticReservation)
+{
+  master::Flags masterFlags = CreateMasterFlags();
+  masterFlags.allocation_interval = Milliseconds(50);
+
+  Try<PID<Master>> master = StartMaster(masterFlags);
+  ASSERT_SOME(master);
+
+  MockExecutor exec(DEFAULT_EXECUTOR_ID);
+
+  slave::Flags slaveFlags = CreateSlaveFlags();
+  slaveFlags.resources = "cpus(role):1;mem(role):512";
+
+  Try<PID<Slave>> slave = StartSlave(&exec, slaveFlags);
+  ASSERT_SOME(slave);
+
+  FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO;
+  frameworkInfo.set_role("role");
+
+  MockScheduler sched;
+  MesosSchedulerDriver driver(
+      &sched, frameworkInfo, master.get(), DEFAULT_CREDENTIAL);
+
+  // We use the filter explicitly here so that the resources will not
+  // be filtered for 5 seconds (by default).
+  Filters filters;
+  filters.set_refuse_seconds(0);
+
+  Resources staticallyReserved =
+    Resources::parse(slaveFlags.resources.get()).get();
+
+  // We use this to capture offers from `resourceOffers`.
+  Future<vector<Offer>> offers;
+
+  EXPECT_CALL(sched, registered(&driver, _, _));
+
+  // The expectation for the first offer.
+  EXPECT_CALL(sched, resourceOffers(&driver, _))
+    .WillOnce(FutureArg<1>(&offers));
+
+  driver.start();
+
+  AWAIT_READY(offers);
+
+  ASSERT_EQ(1u, offers.get().size());
+  Offer offer = offers.get()[0];
+
+  EXPECT_TRUE(Resources(offer.resources()).contains(staticallyReserved));
+
+  // Create a task to launch with the resources of `staticallyReserved`.
+  TaskInfo taskInfo =
+    createTask(offer.slave_id(), staticallyReserved, "exit 1", exec.id);
+
+  EXPECT_CALL(exec, registered(_, _, _, _));
+
+  Future<TaskInfo> launchTask;
+
+  EXPECT_CALL(exec, launchTask(_, _))
+    .WillOnce(FutureArg<1>(&launchTask));
+
+  driver.acceptOffers({offer.id()}, {LAUNCH({taskInfo})}, filters);
+
+  AWAIT_READY(launchTask);
+
+  driver.stop();
+  driver.join();
+
+  Shutdown();
+}
+
+
 // This test checks that the "/roles" endpoint returns the expected
 // information when there are no active roles.
 TEST_F(RoleTest, EndpointEmpty)
@@ -73,14 +256,12 @@ TEST_F(RoleTest, EndpointEmpty)
   Try<PID<Master>> master = StartMaster();
   ASSERT_SOME(master);
 
-  Future<process::http::Response> response =
-    process::http::get(master.get(), "roles");
+  Future<Response> response = process::http::get(master.get(), "roles");
 
-  AWAIT_READY(response);
+  AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response)
+    << response.get().body;
 
-  EXPECT_SOME_EQ(
-      "application/json",
-      response.get().headers.get("Content-Type"));
+  AWAIT_EXPECT_RESPONSE_HEADER_EQ(APPLICATION_JSON, "Content-Type", response);
 
   Try<JSON::Value> parse = JSON::parse(response.get().body);
   ASSERT_SOME(parse);
@@ -110,7 +291,7 @@ TEST_F(RoleTest, EndpointEmpty)
 
 // This test checks that the "/roles" endpoint returns the expected
 // information when there are configured weights and explicit roles,
-// but no active frameworks.
+// but no registered frameworks.
 TEST_F(RoleTest, EndpointNoFrameworks)
 {
   master::Flags masterFlags = CreateMasterFlags();
@@ -120,14 +301,12 @@ TEST_F(RoleTest, EndpointNoFrameworks)
   Try<PID<Master>> master = StartMaster(masterFlags);
   ASSERT_SOME(master);
 
-  Future<process::http::Response> response =
-    process::http::get(master.get(), "roles");
+  Future<Response> response = process::http::get(master.get(), "roles");
 
-  AWAIT_READY(response);
+  AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response)
+    << response.get().body;
 
-  EXPECT_SOME_EQ(
-      "application/json",
-      response.get().headers.get("Content-Type"));
+  AWAIT_EXPECT_RESPONSE_HEADER_EQ(APPLICATION_JSON, "Content-Type", response);
 
   Try<JSON::Value> parse = JSON::parse(response.get().body);
   ASSERT_SOME(parse);
@@ -175,6 +354,227 @@ TEST_F(RoleTest, EndpointNoFrameworks)
 }
 
 
+// This test checks that when using implicit roles, the "/roles"
+// endpoint shows roles that have a configured weight even if they
+// have no registered frameworks.
+TEST_F(RoleTest, EndpointImplicitRolesWeights)
+{
+  master::Flags masterFlags = CreateMasterFlags();
+  masterFlags.weights = "roleX=5,roleY=4";
+
+  Try<PID<Master>> master = StartMaster(masterFlags);
+  ASSERT_SOME(master);
+
+  FrameworkInfo frameworkInfo1 = DEFAULT_FRAMEWORK_INFO;
+  frameworkInfo1.set_role("roleX");
+
+  MockScheduler sched1;
+  MesosSchedulerDriver driver1(
+      &sched1, frameworkInfo1, master.get(), DEFAULT_CREDENTIAL);
+
+  Future<FrameworkID> frameworkId1;
+  EXPECT_CALL(sched1, registered(&driver1, _, _))
+    .WillOnce(FutureArg<1>(&frameworkId1));;
+
+  driver1.start();
+
+  FrameworkInfo frameworkInfo2 = DEFAULT_FRAMEWORK_INFO;
+  frameworkInfo2.set_role("roleZ");
+
+  MockScheduler sched2;
+  MesosSchedulerDriver driver2(
+      &sched2, frameworkInfo2, master.get(), DEFAULT_CREDENTIAL);
+
+  Future<FrameworkID> frameworkId2;
+  EXPECT_CALL(sched2, registered(&driver2, _, _))
+    .WillOnce(FutureArg<1>(&frameworkId2));;
+
+  driver2.start();
+
+  AWAIT_READY(frameworkId1);
+  AWAIT_READY(frameworkId2);
+
+  Future<Response> response = process::http::get(master.get(), "roles");
+
+  AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response)
+    << response.get().body;
+
+  AWAIT_EXPECT_RESPONSE_HEADER_EQ(APPLICATION_JSON, "Content-Type", response);
+
+  Try<JSON::Value> parse = JSON::parse(response.get().body);
+  ASSERT_SOME(parse);
+
+  Try<JSON::Value> expected = JSON::parse(
+      "{"
+      "  \"roles\": ["
+      "    {"
+      "      \"frameworks\": [],"
+      "      \"name\": \"*\","
+      "      \"resources\": {"
+      "        \"cpus\": 0,"
+      "        \"disk\": 0,"
+      "        \"mem\":  0"
+      "      },"
+      "      \"weight\": 1.0"
+      "    },"
+      "    {"
+      "      \"frameworks\": [\"" + frameworkId1.get().value() + "\"],"
+      "      \"name\": \"roleX\","
+      "      \"resources\": {"
+      "        \"cpus\": 0,"
+      "        \"disk\": 0,"
+      "        \"mem\":  0"
+      "      },"
+      "      \"weight\": 5.0"
+      "    },"
+      "    {"
+      "      \"frameworks\": [],"
+      "      \"name\": \"roleY\","
+      "      \"resources\": {"
+      "        \"cpus\": 0,"
+      "        \"disk\": 0,"
+      "        \"mem\":  0"
+      "      },"
+      "      \"weight\": 4.0"
+      "    },"
+      "    {"
+      "      \"frameworks\": [\"" + frameworkId2.get().value() + "\"],"
+      "      \"name\": \"roleZ\","
+      "      \"resources\": {"
+      "        \"cpus\": 0,"
+      "        \"disk\": 0,"
+      "        \"mem\":  0"
+      "      },"
+      "      \"weight\": 1.0"
+      "    }"
+      "  ]"
+      "}");
+
+  ASSERT_SOME(expected);
+  EXPECT_EQ(expected.get(), parse.get());
+
+  driver1.stop();
+  driver1.join();
+
+  driver2.stop();
+  driver2.join();
+
+  Shutdown();
+}
+
+
+// This test checks that when using implicit roles, the "/roles"
+// endpoint shows roles that have a configured quota even if they have
+// no registered frameworks.
+TEST_F(RoleTest, EndpointImplicitRolesQuotas)
+{
+  Try<PID<Master>> master = StartMaster();
+  ASSERT_SOME(master);
+
+  Resources quotaResources =
+    Resources::parse("cpus:1;mem:512", "non-existent-role").get();
+  const RepeatedPtrField<Resource>& jsonQuotaResources =
+    static_cast<const RepeatedPtrField<Resource>&>(quotaResources);
+
+  // Send a quota request for a new role name. Note that we set
+  // "force" to true because we're setting a quota that can't
+  // currently be satisfied by the resources in the cluster (because
+  // there are no slaves registered).
+  string quotaRequestBody = strings::format(
+      "resources=%s&force=true", JSON::protobuf(jsonQuotaResources)).get();
+
+  Future<Response> quotaResponse = process::http::post(
+      master.get(),
+      "quota",
+      createBasicAuthHeaders(DEFAULT_CREDENTIAL),
+      quotaRequestBody);
+
+  AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, quotaResponse)
+    << quotaResponse.get().body;
+
+  Future<Response> rolesResponse = process::http::get(master.get(), "roles");
+
+  AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, rolesResponse)
+    << rolesResponse.get().body;
+
+  AWAIT_EXPECT_RESPONSE_HEADER_EQ(
+      APPLICATION_JSON, "Content-Type", rolesResponse);
+
+  Try<JSON::Value> parse = JSON::parse(rolesResponse.get().body);
+  ASSERT_SOME(parse);
+
+  Try<JSON::Value> expected = JSON::parse(
+      "{"
+      "  \"roles\": ["
+      "    {"
+      "      \"frameworks\": [],"
+      "      \"name\": \"*\","
+      "      \"resources\": {"
+      "        \"cpus\": 0,"
+      "        \"disk\": 0,"
+      "        \"mem\":  0"
+      "      },"
+      "      \"weight\": 1.0"
+      "    },"
+      "    {"
+      "      \"frameworks\": [],"
+      "      \"name\": \"non-existent-role\","
+      "      \"resources\": {"
+      "        \"cpus\": 0,"
+      "        \"disk\": 0,"
+      "        \"mem\":  0"
+      "      },"
+      "      \"weight\": 1.0"
+      "    }"
+      "  ]"
+      "}");
+
+  ASSERT_SOME(expected);
+  EXPECT_EQ(expected.get(), parse.get());
+
+  // Remove the quota, and check that the role no longer appears in
+  // the "/roles" endpoint.
+  Future<Response> deleteResponse = process::http::requestDelete(
+      master.get(),
+      "quota/non-existent-role",
+      createBasicAuthHeaders(DEFAULT_CREDENTIAL));
+
+  AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, deleteResponse)
+    << deleteResponse.get().body;
+
+  rolesResponse = process::http::get(master.get(), "roles");
+
+  AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, rolesResponse)
+    << rolesResponse.get().body;
+
+  AWAIT_EXPECT_RESPONSE_HEADER_EQ(
+      APPLICATION_JSON, "Content-Type", rolesResponse);
+
+  parse = JSON::parse(rolesResponse.get().body);
+  ASSERT_SOME(parse);
+
+  expected = JSON::parse(
+      "{"
+      "  \"roles\": ["
+      "    {"
+      "      \"frameworks\": [],"
+      "      \"name\": \"*\","
+      "      \"resources\": {"
+      "        \"cpus\": 0,"
+      "        \"disk\": 0,"
+      "        \"mem\":  0"
+      "      },"
+      "      \"weight\": 1.0"
+      "    }"
+      "  ]"
+      "}");
+
+  ASSERT_SOME(expected);
+  EXPECT_EQ(expected.get(), parse.get());
+
+  Shutdown();
+}
+
 }  // namespace tests {
 }  // namespace internal {
 }  // namespace mesos {