This is an automated email from the ASF dual-hosted git repository.

grag pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mesos.git

commit 497af42d422a7733b178c3f12b5c34c9328114c0
Author: Joseph Wu <[email protected]>
AuthorDate: Fri Jun 28 12:56:11 2019 -0700

    Added ACLs for agent draining APIs.
    
    This adds three ACLs for the three endpoints added as part of the
    agent draining feature:
      * DRAIN_AGENT
      * DEACTIVATE_AGENT
      * REACTIVATE_AGENT
    
    All three ACLs are coarse-grained; a principal is either allowed to
    perform the action, or unauthorized. However, there is scope to
    restrict the action to particular subsets of agents in future.
    
    Review: https://reviews.apache.org/r/70910/
---
 include/mesos/authorizer/acls.proto       |  30 ++++++
 include/mesos/authorizer/authorizer.proto |  12 +++
 src/authorizer/local/authorizer.cpp       |  64 ++++++++++++
 src/tests/authorization_tests.cpp         | 165 ++++++++++++++++++++++++++++++
 src/tests/master_authorization_tests.cpp  |   3 +
 5 files changed, 274 insertions(+)

diff --git a/include/mesos/authorizer/acls.proto 
b/include/mesos/authorizer/acls.proto
index 78acfe6..f40c13e 100644
--- a/include/mesos/authorizer/acls.proto
+++ b/include/mesos/authorizer/acls.proto
@@ -419,6 +419,33 @@ message ACL {
     required Entity machines = 2;
   }
 
+  message DrainAgent {
+    // Subjects: HTTP Username.
+    required Entity principals = 1;
+
+    // Objects: Given implicitly. Use Entity type ANY or NONE to allow or deny
+    // access.
+    required Entity agents = 2;
+  }
+
+  message DeactivateAgent {
+    // Subjects: HTTP Username.
+    required Entity principals = 1;
+
+    // Objects: Given implicitly. Use Entity type ANY or NONE to allow or deny
+    // access.
+    required Entity agents = 2;
+  }
+
+  message ReactivateAgent {
+    // Subjects: HTTP Username.
+    required Entity principals = 1;
+
+    // Objects: Given implicitly. Use Entity type ANY or NONE to allow or deny
+    // access.
+    required Entity agents = 2;
+  }
+
   // Which principals are authorized to mark an agent as gone.
   message MarkAgentGone {
     // Subjects: HTTP Username.
@@ -657,6 +684,9 @@ message ACLs {
   repeated ACL.StartMaintenance start_maintenances = 37;
   repeated ACL.StopMaintenance stop_maintenances = 38;
   repeated ACL.GetMaintenanceStatus get_maintenance_statuses = 39;
+  repeated ACL.DrainAgent drain_agents = 56;
+  repeated ACL.DeactivateAgent deactivate_agents = 57;
+  repeated ACL.ReactivateAgent reactivate_agents = 58;
   repeated ACL.MarkAgentGone mark_agents_gone = 40;
   repeated ACL.LaunchStandaloneContainer launch_standalone_containers = 41;
   repeated ACL.KillStandaloneContainer kill_standalone_containers = 42;
diff --git a/include/mesos/authorizer/authorizer.proto 
b/include/mesos/authorizer/authorizer.proto
index ad40d3a..d09c234 100644
--- a/include/mesos/authorizer/authorizer.proto
+++ b/include/mesos/authorizer/authorizer.proto
@@ -226,6 +226,18 @@ enum Action {
   GET_MAINTENANCE_STATUS = 33;
 
   // This action will not fill in any object fields, since a principal is
+  // either allowed to drain an agent or is unauthorized.
+  DRAIN_AGENT = 51;
+
+  // This action will not fill in any object fields, since a principal is
+  // either allowed to deactivate an agent or is unauthorized.
+  DEACTIVATE_AGENT = 52;
+
+  // This action will not fill in any object fields, since a principal is
+  // either allowed to reactivate an agent or is unauthorized.
+  REACTIVATE_AGENT = 53;
+
+  // This action will not fill in any object fields, since a principal is
   // either allowed to mark an agent as gone or is unauthorized.
   MARK_AGENT_GONE = 34;
 
diff --git a/src/authorizer/local/authorizer.cpp 
b/src/authorizer/local/authorizer.cpp
index b89010d..85821c6 100644
--- a/src/authorizer/local/authorizer.cpp
+++ b/src/authorizer/local/authorizer.cpp
@@ -412,6 +412,9 @@ public:
         case authorization::START_MAINTENANCE:
         case authorization::STOP_MAINTENANCE:
         case authorization::UPDATE_MAINTENANCE_SCHEDULE:
+        case authorization::DRAIN_AGENT:
+        case authorization::DEACTIVATE_AGENT:
+        case authorization::REACTIVATE_AGENT:
         case authorization::MODIFY_RESOURCE_PROVIDER_CONFIG:
         case authorization::MARK_RESOURCE_PROVIDER_GONE:
         case authorization::VIEW_RESOURCE_PROVIDER:
@@ -734,6 +737,9 @@ public:
         case authorization::SET_LOG_LEVEL:
         case authorization::START_MAINTENANCE:
         case authorization::STOP_MAINTENANCE:
+        case authorization::DRAIN_AGENT:
+        case authorization::DEACTIVATE_AGENT:
+        case authorization::REACTIVATE_AGENT:
         case authorization::TEARDOWN_FRAMEWORK:
         case authorization::UNRESERVE_RESOURCES:
         case authorization::UPDATE_MAINTENANCE_SCHEDULE:
@@ -985,6 +991,9 @@ public:
       case authorization::SET_LOG_LEVEL:
       case authorization::START_MAINTENANCE:
       case authorization::STOP_MAINTENANCE:
+      case authorization::DRAIN_AGENT:
+      case authorization::DEACTIVATE_AGENT:
+      case authorization::REACTIVATE_AGENT:
       case authorization::TEARDOWN_FRAMEWORK:
       case authorization::UNKNOWN:
       case authorization::UNRESERVE_RESOURCES:
@@ -1213,6 +1222,9 @@ public:
       case authorization::TEARDOWN_FRAMEWORK:
       case authorization::UNRESERVE_RESOURCES:
       case authorization::UPDATE_MAINTENANCE_SCHEDULE:
+      case authorization::DRAIN_AGENT:
+      case authorization::DEACTIVATE_AGENT:
+      case authorization::REACTIVATE_AGENT:
       case authorization::VIEW_CONTAINER:
       case authorization::VIEW_EXECUTOR:
       case authorization::VIEW_FLAGS:
@@ -1502,6 +1514,40 @@ private:
         }
 
         return acls_;
+
+      case authorization::DRAIN_AGENT:
+        foreach (const ACL::DrainAgent& acl,
+                 acls.drain_agents()) {
+          GenericACL acl_;
+          acl_.subjects = acl.principals();
+          acl_.objects = acl.agents();
+
+          acls_.push_back(acl_);
+        }
+
+        return acls_;
+      case authorization::DEACTIVATE_AGENT:
+        foreach (const ACL::DeactivateAgent& acl,
+                 acls.deactivate_agents()) {
+          GenericACL acl_;
+          acl_.subjects = acl.principals();
+          acl_.objects = acl.agents();
+
+          acls_.push_back(acl_);
+        }
+
+        return acls_;
+      case authorization::REACTIVATE_AGENT:
+        foreach (const ACL::ReactivateAgent& acl,
+                 acls.reactivate_agents()) {
+          GenericACL acl_;
+          acl_.subjects = acl.principals();
+          acl_.objects = acl.agents();
+
+          acls_.push_back(acl_);
+        }
+
+        return acls_;
       case authorization::MARK_AGENT_GONE:
         foreach (const ACL::MarkAgentGone& acl,
                  acls.mark_agents_gone()) {
@@ -1749,6 +1795,24 @@ Option<Error> LocalAuthorizer::validate(const ACLs& acls)
     }
   }
 
+  foreach (const ACL::DrainAgent& acl, acls.drain_agents()) {
+    if (acl.agents().type() == ACL::Entity::SOME) {
+      return Error("ACL.DrainAgent type must be either NONE or ANY");
+    }
+  }
+
+  foreach (const ACL::DeactivateAgent& acl, acls.deactivate_agents()) {
+    if (acl.agents().type() == ACL::Entity::SOME) {
+      return Error("ACL.DeactivateAgent type must be either NONE or ANY");
+    }
+  }
+
+  foreach (const ACL::ReactivateAgent& acl, acls.reactivate_agents()) {
+    if (acl.agents().type() == ACL::Entity::SOME) {
+      return Error("ACL.ReactivateAgent type must be either NONE or ANY");
+    }
+  }
+
   foreach (const ACL::LaunchStandaloneContainer& acl,
            acls.launch_standalone_containers()) {
     if (acl.users().type() == ACL::Entity::SOME) {
diff --git a/src/tests/authorization_tests.cpp 
b/src/tests/authorization_tests.cpp
index 7bb0bd8..5ad13b9 100644
--- a/src/tests/authorization_tests.cpp
+++ b/src/tests/authorization_tests.cpp
@@ -5919,6 +5919,171 @@ TYPED_TEST(AuthorizationTest, GetMaintenanceStatus)
 }
 
 
+// This tests the authorization of requests to DrainAgent.
+TYPED_TEST(AuthorizationTest, DrainAgent)
+{
+  ACLs acls;
+
+  {
+    // "foo" principal can drain agents.
+    mesos::ACL::DrainAgent* acl = acls.add_drain_agents();
+    acl->mutable_principals()->add_values("foo");
+    acl->mutable_agents()->set_type(mesos::ACL::Entity::ANY);
+  }
+
+  {
+    // Nobody else can drain agents.
+    mesos::ACL::DrainAgent* acl = acls.add_drain_agents();
+    acl->mutable_principals()->set_type(mesos::ACL::Entity::ANY);
+    acl->mutable_agents()->set_type(mesos::ACL::Entity::NONE);
+  }
+
+  Try<Authorizer*> create = TypeParam::create(parameterize(acls));
+  ASSERT_SOME(create);
+  Owned<Authorizer> authorizer(create.get());
+
+  {
+    // "foo" is allowed to drain agents.
+    authorization::Request request;
+    request.set_action(authorization::DRAIN_AGENT);
+    request.mutable_subject()->set_value("foo");
+
+    AWAIT_EXPECT_TRUE(authorizer->authorized(request));
+  }
+
+  {
+    // "bar" is not allowed to drain agents.
+    authorization::Request request;
+    request.set_action(authorization::DRAIN_AGENT);
+    request.mutable_subject()->set_value("bar");
+
+    AWAIT_EXPECT_FALSE(authorizer->authorized(request));
+  }
+
+  {
+    // Test that no authorizer is created with invalid ACLs.
+    ACLs invalid;
+
+    mesos::ACL::DrainAgent* acl = invalid.add_drain_agents();
+    acl->mutable_principals()->add_values("foo");
+    acl->mutable_agents()->add_values("yoda");
+
+    Try<Authorizer*> create = TypeParam::create(parameterize(invalid));
+    EXPECT_ERROR(create);
+  }
+}
+
+
+// This tests the authorization of requests to DeactivateAgent.
+TYPED_TEST(AuthorizationTest, DeactivateAgent)
+{
+  ACLs acls;
+
+  {
+    // "foo" principal can deactivate agents.
+    mesos::ACL::DeactivateAgent* acl = acls.add_deactivate_agents();
+    acl->mutable_principals()->add_values("foo");
+    acl->mutable_agents()->set_type(mesos::ACL::Entity::ANY);
+  }
+
+  {
+    // Nobody else can deactivate agents.
+    mesos::ACL::DeactivateAgent* acl = acls.add_deactivate_agents();
+    acl->mutable_principals()->set_type(mesos::ACL::Entity::ANY);
+    acl->mutable_agents()->set_type(mesos::ACL::Entity::NONE);
+  }
+
+  Try<Authorizer*> create = TypeParam::create(parameterize(acls));
+  ASSERT_SOME(create);
+  Owned<Authorizer> authorizer(create.get());
+
+  {
+    // "foo" is allowed to deactivate agents.
+    authorization::Request request;
+    request.set_action(authorization::DEACTIVATE_AGENT);
+    request.mutable_subject()->set_value("foo");
+
+    AWAIT_EXPECT_TRUE(authorizer->authorized(request));
+  }
+
+  {
+    // "bar" is not allowed to deactivate agents.
+    authorization::Request request;
+    request.set_action(authorization::DEACTIVATE_AGENT);
+    request.mutable_subject()->set_value("bar");
+
+    AWAIT_EXPECT_FALSE(authorizer->authorized(request));
+  }
+
+  {
+    // Test that no authorizer is created with invalid ACLs.
+    ACLs invalid;
+
+    mesos::ACL::DeactivateAgent* acl = invalid.add_deactivate_agents();
+    acl->mutable_principals()->add_values("foo");
+    acl->mutable_agents()->add_values("yoda");
+
+    Try<Authorizer*> create = TypeParam::create(parameterize(invalid));
+    EXPECT_ERROR(create);
+  }
+}
+
+
+// This tests the authorization of requests to ReactivateAgent.
+TYPED_TEST(AuthorizationTest, ReactivateAgent)
+{
+  ACLs acls;
+
+  {
+    // "foo" principal can reactivate agents.
+    mesos::ACL::ReactivateAgent* acl = acls.add_reactivate_agents();
+    acl->mutable_principals()->add_values("foo");
+    acl->mutable_agents()->set_type(mesos::ACL::Entity::ANY);
+  }
+
+  {
+    // Nobody else can reactivate agents.
+    mesos::ACL::ReactivateAgent* acl = acls.add_reactivate_agents();
+    acl->mutable_principals()->set_type(mesos::ACL::Entity::ANY);
+    acl->mutable_agents()->set_type(mesos::ACL::Entity::NONE);
+  }
+
+  Try<Authorizer*> create = TypeParam::create(parameterize(acls));
+  ASSERT_SOME(create);
+  Owned<Authorizer> authorizer(create.get());
+
+  {
+    // "foo" is allowed to reactivate agents.
+    authorization::Request request;
+    request.set_action(authorization::REACTIVATE_AGENT);
+    request.mutable_subject()->set_value("foo");
+
+    AWAIT_EXPECT_TRUE(authorizer->authorized(request));
+  }
+
+  {
+    // "bar" is not allowed to reactivate agents.
+    authorization::Request request;
+    request.set_action(authorization::REACTIVATE_AGENT);
+    request.mutable_subject()->set_value("bar");
+
+    AWAIT_EXPECT_FALSE(authorizer->authorized(request));
+  }
+
+  {
+    // Test that no authorizer is created with invalid ACLs.
+    ACLs invalid;
+
+    mesos::ACL::ReactivateAgent* acl = invalid.add_reactivate_agents();
+    acl->mutable_principals()->add_values("foo");
+    acl->mutable_agents()->add_values("yoda");
+
+    Try<Authorizer*> create = TypeParam::create(parameterize(invalid));
+    EXPECT_ERROR(create);
+  }
+}
+
+
 // This tests the authorization of requests to ViewStandaloneContainer.
 TYPED_TEST(AuthorizationTest, ViewStandaloneContainer)
 {
diff --git a/src/tests/master_authorization_tests.cpp 
b/src/tests/master_authorization_tests.cpp
index 7d40695..2932564 100644
--- a/src/tests/master_authorization_tests.cpp
+++ b/src/tests/master_authorization_tests.cpp
@@ -2991,6 +2991,9 @@ public:
       case authorization::START_MAINTENANCE:
       case authorization::STOP_MAINTENANCE:
       case authorization::GET_MAINTENANCE_STATUS:
+      case authorization::DRAIN_AGENT:
+      case authorization::DEACTIVATE_AGENT:
+      case authorization::REACTIVATE_AGENT:
       case authorization::MARK_AGENT_GONE:
       case authorization::LAUNCH_STANDALONE_CONTAINER:
       case authorization::KILL_STANDALONE_CONTAINER:

Reply via email to