You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by ar...@apache.org on 2017/09/04 15:53:08 UTC

[2/2] mesos git commit: Added test to verify filtering of resource reservations & allocations.

Added test to verify filtering of resource reservations & allocations.

This patch extends `SlaveAuthorizerTest.FilterStateEndpoint` test to
check that agent's `/state` endpoint shows resource reservations and
allocations based on the `VIEW_ROLE` permissions of the principal
doing the request.

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


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

Branch: refs/heads/master
Commit: b9aebbbca755eafae0480193c172dc67e90cc547
Parents: a931bfb
Author: Andrei Budnik <ab...@mesosphere.com>
Authored: Mon Sep 4 17:09:51 2017 +0200
Committer: Alexander Rojas <al...@mesosphere.io>
Committed: Mon Sep 4 17:52:52 2017 +0200

----------------------------------------------------------------------
 src/tests/slave_authorization_tests.cpp | 282 +++++++++++++++++++++------
 1 file changed, 227 insertions(+), 55 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/b9aebbbc/src/tests/slave_authorization_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/slave_authorization_tests.cpp b/src/tests/slave_authorization_tests.cpp
index 536a8ef..30eceae 100644
--- a/src/tests/slave_authorization_tests.cpp
+++ b/src/tests/slave_authorization_tests.cpp
@@ -101,12 +101,19 @@ TYPED_TEST_CASE(SlaveAuthorizerTest, AuthorizerTypes);
 
 // This test verifies that authorization based endpoint filtering
 // works correctly on the /state endpoint.
-// Both default users are allowed to to view high level frameworks, but only
+// Both default users are allowed to view high level frameworks, but only
 // one is allowed to view the tasks.
+// After launching a single task per each framework, one for role "superhero"
+// and the other for role "muggle", this test verifies that each of two
+// default users can view resource allocations and resource reservations for
+// corresponding allowed roles only.
 TYPED_TEST(SlaveAuthorizerTest, FilterStateEndpoint)
 {
   ACLs acls;
 
+  const string roleSuperhero = "superhero";
+  const string roleMuggle = "muggle";
+
   {
     // Default principal can see all frameworks.
     mesos::ACL::ViewFramework* acl = acls.add_view_frameworks();
@@ -156,6 +163,28 @@ TYPED_TEST(SlaveAuthorizerTest, FilterStateEndpoint)
     acl->mutable_users()->set_type(ACL::Entity::NONE);
   }
 
+  {
+    // Default principal can view "superhero" role only.
+    ACL::ViewRole* acl = acls.add_view_roles();
+    acl->mutable_principals()->add_values(DEFAULT_CREDENTIAL.principal());
+    acl->mutable_roles()->add_values(roleSuperhero);
+
+    acl = acls.add_view_roles();
+    acl->mutable_principals()->add_values(DEFAULT_CREDENTIAL.principal());
+    acl->mutable_roles()->set_type(mesos::ACL::Entity::NONE);
+  }
+
+  {
+    // Second default principal can view "muggle" role only.
+    ACL::ViewRole* acl = acls.add_view_roles();
+    acl->mutable_principals()->add_values(DEFAULT_CREDENTIAL_2.principal());
+    acl->mutable_roles()->add_values(roleMuggle);
+
+    acl = acls.add_view_roles();
+    acl->mutable_principals()->add_values(DEFAULT_CREDENTIAL_2.principal());
+    acl->mutable_roles()->set_type(mesos::ACL::Entity::NONE);
+  }
+
   // Create an `Authorizer` with the ACLs.
   Try<Authorizer*> create = TypeParam::create(parameterize(acls));
   ASSERT_SOME(create);
@@ -164,69 +193,146 @@ TYPED_TEST(SlaveAuthorizerTest, FilterStateEndpoint)
   Try<Owned<cluster::Master>> master = this->StartMaster(authorizer.get());
   ASSERT_SOME(master);
 
-  // Register framework with user "bar".
-  FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo.set_role("role");
-  frameworkInfo.set_user("bar");
+  // Register framework with user "bar" and role "superhero".
+  FrameworkInfo frameworkSuperhero = DEFAULT_FRAMEWORK_INFO;
+  frameworkSuperhero.set_name("framework-" + roleSuperhero);
+  frameworkSuperhero.set_role(roleSuperhero);
+  frameworkSuperhero.set_user("bar");
 
   // Create an executor with user "bar".
-  ExecutorInfo executor = createExecutorInfo("test-executor", "sleep 2");
-  executor.mutable_command()->set_user("bar");
-
-  MockExecutor exec(executor.executor_id());
-  TestContainerizer containerizer(&exec);
+  ExecutorInfo executorSuperhero =
+    createExecutorInfo("test-executor-" + roleSuperhero, "sleep 2");
+  executorSuperhero.mutable_command()->set_user("bar");
+  MockExecutor execSuperhero(executorSuperhero.executor_id());
+
+  // Register framework with user "foo" and role "muggle".
+  FrameworkInfo frameworkMuggle = DEFAULT_FRAMEWORK_INFO;
+  frameworkMuggle.set_name("framework-" + roleMuggle);
+  frameworkMuggle.set_principal(DEFAULT_CREDENTIAL_2.principal());
+  frameworkMuggle.set_role(roleMuggle);
+  frameworkMuggle.set_user("foo");
+
+  // Create an executor with user "foo".
+  ExecutorInfo executorMuggle =
+    createExecutorInfo("test-executor-" + roleMuggle, "sleep 2");
+  executorMuggle.mutable_command()->set_user("foo");
+  MockExecutor execMuggle(executorMuggle.executor_id());
+
+  TestContainerizer containerizer(
+      {{executorSuperhero.executor_id(), &execSuperhero},
+       {executorMuggle.executor_id(), &execMuggle}});
+
+  slave::Flags flags = this->CreateSlaveFlags();
+  // Statically reserve resources for each role.
+  flags.resources = "cpus(" + roleSuperhero + "):2;" + "cpus(" + roleMuggle +
+    "):3;mem(" + roleSuperhero + "):512;" + "mem(" + roleMuggle + "):1024;";
 
   Owned<MasterDetector> detector = master.get()->createDetector();
-  Try<Owned<cluster::Slave>> slave =
-      this->StartSlave(detector.get(), &containerizer, authorizer.get());
+  Try<Owned<cluster::Slave>> slave = this->StartSlave(
+      detector.get(), &containerizer, authorizer.get(), flags);
 
   ASSERT_SOME(slave);
 
-  MockScheduler sched;
-  MesosSchedulerDriver driver(
-      &sched, frameworkInfo, master.get()->pid, DEFAULT_CREDENTIAL);
+  MockScheduler schedSuperhero;
+  MesosSchedulerDriver driverSuperhero(
+      &schedSuperhero,
+      frameworkSuperhero,
+      master.get()->pid,
+      DEFAULT_CREDENTIAL);
 
-  EXPECT_CALL(exec, registered(_, _, _, _))
+  EXPECT_CALL(execSuperhero, registered(_, _, _, _))
     .Times(AtMost(1));
 
-  Future<Nothing> registered;
-  EXPECT_CALL(sched, registered(&driver, _, _))
-    .WillOnce(FutureSatisfy(&registered))
+  Future<FrameworkID> frameworkIdSuperhero;
+  EXPECT_CALL(schedSuperhero, registered(&driverSuperhero, _, _))
+    .WillOnce(FutureArg<1>(&frameworkIdSuperhero));
+
+  Future<vector<Offer>> offersSuperhero;
+  EXPECT_CALL(schedSuperhero, resourceOffers(&driverSuperhero, _))
+    .WillOnce(FutureArg<1>(&offersSuperhero))
+    .WillRepeatedly(Return()); // Ignore subsequent offers.
+
+  driverSuperhero.start();
+
+  AWAIT_READY(frameworkIdSuperhero);
+
+  AWAIT_READY(offersSuperhero);
+  EXPECT_FALSE(offersSuperhero->empty());
+
+  // Define a task which will run on executorSuperhero of frameworkSuperhero.
+  TaskInfo taskSuperhero;
+  taskSuperhero.set_name("test-" + roleSuperhero);
+  taskSuperhero.mutable_task_id()->set_value("1");
+  taskSuperhero.mutable_slave_id()->MergeFrom(
+      offersSuperhero.get()[0].slave_id());
+  taskSuperhero.mutable_resources()->MergeFrom(
+      offersSuperhero.get()[0].resources());
+  taskSuperhero.mutable_executor()->MergeFrom(executorSuperhero);
+
+  EXPECT_CALL(execSuperhero, launchTask(_, _))
+    .WillOnce(SendStatusUpdateFromTask(TASK_RUNNING))
     .WillRepeatedly(Return());
 
-  Future<vector<Offer>> offers;
-  EXPECT_CALL(sched, resourceOffers(&driver, _))
-    .WillOnce(FutureArg<1>(&offers))
+  Future<TaskStatus> statusSuperhero;
+  EXPECT_CALL(schedSuperhero, statusUpdate(&driverSuperhero, _))
+    .WillOnce(FutureArg<1>(&statusSuperhero));
+
+  driverSuperhero.launchTasks(offersSuperhero.get()[0].id(), {taskSuperhero});
+
+  AWAIT_READY(statusSuperhero);
+  EXPECT_EQ(TASK_RUNNING, statusSuperhero->state());
+
+  MockScheduler schedMuggle;
+  MesosSchedulerDriver driverMuggle(
+      &schedMuggle,
+      frameworkMuggle,
+      master.get()->pid,
+      DEFAULT_CREDENTIAL_2);
+
+  EXPECT_CALL(execMuggle, registered(_, _, _, _))
+    .Times(AtMost(1));
+
+  Future<FrameworkID> frameworkIdMuggle;
+  EXPECT_CALL(schedMuggle, registered(&driverMuggle, _, _))
+    .WillOnce(FutureArg<1>(&frameworkIdMuggle));
+
+  Future<vector<Offer>> offersMuggle;
+  EXPECT_CALL(schedMuggle, resourceOffers(&driverMuggle, _))
+    .WillOnce(FutureArg<1>(&offersMuggle))
     .WillRepeatedly(Return()); // Ignore subsequent offers.
 
-  driver.start();
+  driverMuggle.start();
 
-  AWAIT_READY(registered);
+  AWAIT_READY(frameworkIdMuggle);
 
-  AWAIT_READY(offers);
-  EXPECT_FALSE(offers->empty());
+  AWAIT_READY(offersMuggle);
+  EXPECT_FALSE(offersMuggle->empty());
 
-  TaskInfo task;
-  task.set_name("test");
-  task.mutable_task_id()->set_value("1");
-  task.mutable_slave_id()->MergeFrom(offers.get()[0].slave_id());
-  task.mutable_resources()->MergeFrom(offers.get()[0].resources());
-  task.mutable_executor()->MergeFrom(executor);
+  // Define a task which will run on executorMuggle of frameworkMuggle.
+  TaskInfo taskMuggle;
+  taskMuggle.set_name("test-" + roleMuggle);
+  taskMuggle.mutable_task_id()->set_value("2");
+  taskMuggle.mutable_slave_id()->MergeFrom(
+      offersMuggle.get()[0].slave_id());
+  taskMuggle.mutable_resources()->MergeFrom(
+      offersMuggle.get()[0].resources());
+  taskMuggle.mutable_executor()->MergeFrom(executorMuggle);
 
-  EXPECT_CALL(exec, launchTask(_, _))
+  EXPECT_CALL(execMuggle, launchTask(_, _))
     .WillOnce(SendStatusUpdateFromTask(TASK_RUNNING))
     .WillRepeatedly(Return());
 
-  Future<TaskStatus> status;
-  EXPECT_CALL(sched, statusUpdate(&driver, _))
-    .WillOnce(FutureArg<1>(&status));
+  Future<TaskStatus> statusMuggle;
+  EXPECT_CALL(schedMuggle, statusUpdate(&driverMuggle, _))
+    .WillOnce(FutureArg<1>(&statusMuggle));
 
-  driver.launchTasks(offers.get()[0].id(), {task});
+  driverMuggle.launchTasks(offersMuggle.get()[0].id(), {taskMuggle});
 
-  AWAIT_READY(status);
-  EXPECT_EQ(TASK_RUNNING, status->state());
+  AWAIT_READY(statusMuggle);
+  ASSERT_EQ(TASK_RUNNING, statusMuggle->state());
 
-  // Retrieve endpoint with the user allowed to view the framework.
+  // Retrieve endpoint with the user allowed to view the frameworks.
+  // The default user allowed to view role "superhero" only.
   {
     Future<Response> response = http::get(
         slave.get()->pid,
@@ -245,20 +351,50 @@ TYPED_TEST(SlaveAuthorizerTest, FilterStateEndpoint)
     ASSERT_TRUE(state.values["frameworks"].is<JSON::Array>());
 
     JSON::Array frameworks = state.values["frameworks"].as<JSON::Array>();
-    EXPECT_EQ(1u, frameworks.values.size());
+    EXPECT_EQ(2u, frameworks.values.size());
+
+    foreach (const JSON::Value& value, frameworks.values) {
+      JSON::Object framework = value.as<JSON::Object>();
+      EXPECT_FALSE(framework.values.empty());
+      ASSERT_TRUE(framework.values["executors"].is<JSON::Array>());
 
-    JSON::Object framework = frameworks.values.front().as<JSON::Object>();
-    ASSERT_TRUE(framework.values["executors"].is<JSON::Array>());
+      JSON::Array executors = framework.values["executors"].as<JSON::Array>();
+      EXPECT_EQ(1u, executors.values.size());
+
+      JSON::Object executor = executors.values.front().as<JSON::Object>();
+      EXPECT_EQ(1u, executor.values["tasks"].as<JSON::Array>().values.size());
+    }
 
-    JSON::Array executors = framework.values["executors"].as<JSON::Array>();
-    EXPECT_EQ(1u, executors.values.size());
+    ASSERT_TRUE(state.values["reserved_resources"].is<JSON::Object>());
 
-    JSON::Object executor = executors.values.front().as<JSON::Object>();
-    EXPECT_EQ(1u, executor.values["tasks"].as<JSON::Array>().values.size());
+    JSON::Object reserved_resources =
+      state.values["reserved_resources"].as<JSON::Object>();
+    EXPECT_TRUE(reserved_resources.values[roleSuperhero].is<JSON::Object>());
+    EXPECT_FALSE(reserved_resources.values[roleMuggle].is<JSON::Object>());
+
+    ASSERT_TRUE(
+        state.values["reserved_resources_allocated"].is<JSON::Object>());
+
+    JSON::Object reserved_resources_allocated =
+      state.values["reserved_resources_allocated"].as<JSON::Object>();
+    EXPECT_TRUE(
+        reserved_resources_allocated.values[roleSuperhero].is<JSON::Object>());
+    EXPECT_FALSE(
+        reserved_resources_allocated.values[roleMuggle].is<JSON::Object>());
+
+    ASSERT_TRUE(state.values["reserved_resources_full"].is<JSON::Object>());
+
+    JSON::Object reserved_resources_full =
+      state.values["reserved_resources_full"].as<JSON::Object>();
+    EXPECT_TRUE(
+        reserved_resources_full.values[roleSuperhero].is<JSON::Array>());
+    EXPECT_FALSE(
+        reserved_resources_full.values[roleMuggle].is<JSON::Array>());
   }
 
-  // Retrieve endpoint with the user allowed to view the framework,
-  // but not the executor.
+  // Retrieve endpoint with the user allowed to view the frameworks,
+  // but not the executors.
+  // The second default user allowed to view role "muggle" only.
   {
     Future<Response> response = http::get(
         slave.get()->pid,
@@ -276,17 +412,53 @@ TYPED_TEST(SlaveAuthorizerTest, FilterStateEndpoint)
     ASSERT_TRUE(state.values["frameworks"].is<JSON::Array>());
 
     JSON::Array frameworks = state.values["frameworks"].as<JSON::Array>();
-    EXPECT_EQ(1u, frameworks.values.size());
+    EXPECT_EQ(2u, frameworks.values.size());
 
-    JSON::Object framework = frameworks.values.front().as<JSON::Object>();
-    EXPECT_TRUE(framework.values["executors"].as<JSON::Array>().values.empty());
+    foreach (const JSON::Value& value, frameworks.values) {
+      JSON::Object framework = value.as<JSON::Object>();
+      EXPECT_FALSE(framework.values.empty());
+      EXPECT_TRUE(
+          framework.values["executors"].as<JSON::Array>().values.empty());
     }
 
-  EXPECT_CALL(exec, shutdown(_))
+    ASSERT_TRUE(state.values["reserved_resources"].is<JSON::Object>());
+
+    JSON::Object reserved_resources =
+      state.values["reserved_resources"].as<JSON::Object>();
+    EXPECT_TRUE(reserved_resources.values[roleMuggle].is<JSON::Object>());
+    EXPECT_FALSE(reserved_resources.values[roleSuperhero].is<JSON::Object>());
+
+    ASSERT_TRUE(
+        state.values["reserved_resources_allocated"].is<JSON::Object>());
+
+    JSON::Object reserved_resources_allocated =
+      state.values["reserved_resources_allocated"].as<JSON::Object>();
+    EXPECT_TRUE(
+        reserved_resources_allocated.values[roleMuggle].is<JSON::Object>());
+    EXPECT_FALSE(
+        reserved_resources_allocated.values[roleSuperhero].is<JSON::Object>());
+
+    ASSERT_TRUE(state.values["reserved_resources_full"].is<JSON::Object>());
+
+    JSON::Object reserved_resources_full =
+      state.values["reserved_resources_full"].as<JSON::Object>();
+    EXPECT_TRUE(
+        reserved_resources_full.values[roleMuggle].is<JSON::Array>());
+    EXPECT_FALSE(
+        reserved_resources_full.values[roleSuperhero].is<JSON::Array>());
+  }
+
+  EXPECT_CALL(execSuperhero, shutdown(_))
     .Times(AtMost(1));
 
-  driver.stop();
-  driver.join();
+  EXPECT_CALL(execMuggle, shutdown(_))
+    .Times(AtMost(1));
+
+  driverSuperhero.stop();
+  driverSuperhero.join();
+
+  driverMuggle.stop();
+  driverMuggle.join();
 }