You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by ti...@apache.org on 2015/12/18 21:01:59 UTC
[1/7] mesos git commit: Quota: Added "SetQuota" message to ACL
protobuf.
Repository: mesos
Updated Branches:
refs/heads/master aa497e81c -> d13b65e1b
Quota: Added "SetQuota" message to ACL protobuf.
Review: https://reviews.apache.org/r/40345/
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/0f2bd3bb
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/0f2bd3bb
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/0f2bd3bb
Branch: refs/heads/master
Commit: 0f2bd3bb72457298f73e3cea09fc2ad3676b715a
Parents: aa497e8
Author: Jan Schlicht <ja...@mesosphere.io>
Authored: Fri Dec 18 17:43:15 2015 +0100
Committer: Till Toenshoff <to...@me.com>
Committed: Fri Dec 18 17:48:56 2015 +0100
----------------------------------------------------------------------
include/mesos/authorizer/authorizer.proto | 10 ++++++++++
1 file changed, 10 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/0f2bd3bb/include/mesos/authorizer/authorizer.proto
----------------------------------------------------------------------
diff --git a/include/mesos/authorizer/authorizer.proto b/include/mesos/authorizer/authorizer.proto
index 7f981e6..7b729e1 100644
--- a/include/mesos/authorizer/authorizer.proto
+++ b/include/mesos/authorizer/authorizer.proto
@@ -107,6 +107,15 @@ message ACL {
// Objects: Principal of the entity that created the volume.
required Entity creator_principals = 2;
}
+
+ // Which principals are authorized to set quotas for given roles.
+ message SetQuota {
+ // Subjects: Operator username.
+ required Entity principals = 1;
+
+ // Objects: The list of roles for which a quota can be set.
+ required Entity roles = 2;
+ }
}
@@ -138,4 +147,5 @@ message ACLs {
repeated ACL.UnreserveResources unreserve_resources = 6;
repeated ACL.CreateVolume create_volumes = 7;
repeated ACL.DestroyVolume destroy_volumes = 8;
+ repeated ACL.SetQuota set_quotas = 9;
}
[5/7] mesos git commit: Quota: Added authentication,
authorization tests.
Posted by ti...@apache.org.
Quota: Added authentication, authorization tests.
Review: https://reviews.apache.org/r/39520/
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/38a18c4d
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/38a18c4d
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/38a18c4d
Branch: refs/heads/master
Commit: 38a18c4df2602121447a3f4ec89373465c094857
Parents: 50bf193
Author: Jan Schlicht <ja...@mesosphere.io>
Authored: Fri Dec 18 17:45:32 2015 +0100
Committer: Till Toenshoff <to...@me.com>
Committed: Fri Dec 18 20:52:30 2015 +0100
----------------------------------------------------------------------
src/tests/authorization_tests.cpp | 78 ++++++++++++++
src/tests/master_quota_tests.cpp | 192 ++++++++++++++++++++++++++++++++-
2 files changed, 269 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/38a18c4d/src/tests/authorization_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/authorization_tests.cpp b/src/tests/authorization_tests.cpp
index dcf348a..1d11a02 100644
--- a/src/tests/authorization_tests.cpp
+++ b/src/tests/authorization_tests.cpp
@@ -683,6 +683,84 @@ TYPED_TEST(AuthorizationTest, DestroyVolume)
AWAIT_EXPECT_FALSE(authorizer.get()->authorize(request6));
}
+
+// This tests the authorization of requests to set quotas.
+TYPED_TEST(AuthorizationTest, SetQuota)
+{
+ ACLs acls;
+
+ // "foo" principal can set quotas for all roles.
+ mesos::ACL::SetQuota* acl1 = acls.add_set_quotas();
+ acl1->mutable_principals()->add_values("foo");
+ acl1->mutable_roles()->set_type(mesos::ACL::Entity::ANY);
+
+ // "bar" principal can set quotas for "dev" role.
+ mesos::ACL::SetQuota* acl2 = acls.add_set_quotas();
+ acl2->mutable_principals()->add_values("bar");
+ acl2->mutable_roles()->add_values("dev");
+
+ // Anyone can set quotas for "test" role.
+ mesos::ACL::SetQuota* acl3 = acls.add_set_quotas();
+ acl3->mutable_principals()->set_type(mesos::ACL::Entity::ANY);
+ acl3->mutable_roles()->add_values("test");
+
+ // No other principal can set quotas.
+ mesos::ACL::SetQuota* acl4 = acls.add_set_quotas();
+ acl4->mutable_principals()->set_type(mesos::ACL::Entity::ANY);
+ acl4->mutable_roles()->set_type(mesos::ACL::Entity::NONE);
+
+ // Create an `Authorizer` with the ACLs.
+ Try<Authorizer*> create = TypeParam::create();
+ ASSERT_SOME(create);
+ Owned<Authorizer> authorizer(create.get());
+
+ Try<Nothing> initialized = authorizer.get()->initialize(acls);
+ ASSERT_SOME(initialized);
+
+ // Principal "foo" can set quota for all roles, so requests 1 and 2 will pass.
+ mesos::ACL::SetQuota request1;
+ request1.mutable_principals()->add_values("foo");
+ request1.mutable_roles()->set_type(mesos::ACL::Entity::ANY);
+ AWAIT_EXPECT_TRUE(authorizer.get()->authorize(request1));
+
+ mesos::ACL::SetQuota request2;
+ request2.mutable_principals()->add_values("foo");
+ request2.mutable_roles()->add_values("prod");
+ AWAIT_EXPECT_TRUE(authorizer.get()->authorize(request2));
+
+ // Principal "bar" can set quotas for role "dev", so this will pass.
+ mesos::ACL::SetQuota request3;
+ request3.mutable_principals()->add_values("bar");
+ request3.mutable_roles()->add_values("dev");
+ AWAIT_EXPECT_TRUE(authorizer.get()->authorize(request3));
+
+ // Principal "bar" can only set quotas for role "dev",
+ // so request 4 and 5 will fail.
+ mesos::ACL::SetQuota request4;
+ request4.mutable_principals()->add_values("bar");
+ request4.mutable_roles()->add_values("prod");
+ AWAIT_EXPECT_FALSE(authorizer.get()->authorize(request4));
+
+ mesos::ACL::SetQuota request5;
+ request5.mutable_principals()->add_values("bar");
+ request5.mutable_roles()->set_type(mesos::ACL::Entity::ANY);
+ AWAIT_EXPECT_FALSE(authorizer.get()->authorize(request5));
+
+ // Anyone can set quotas for role "test", so request 6 will pass.
+ mesos::ACL::SetQuota request6;
+ request6.mutable_principals()->set_type(mesos::ACL::Entity::ANY);
+ request6.mutable_roles()->add_values("test");
+ AWAIT_EXPECT_TRUE(authorizer.get()->authorize(request6));
+
+ // Principal "jeff" is not mentioned in the ACLs of the `Authorizer`, so it
+ // will be caught by the final ACL, which provides a default case that denies
+ // access for all other principals. This case will fail.
+ mesos::ACL::SetQuota request7;
+ request7.mutable_principals()->add_values("jeff");
+ request7.mutable_roles()->set_type(mesos::ACL::Entity::ANY);
+ AWAIT_EXPECT_FALSE(authorizer.get()->authorize(request7));
+}
+
} // namespace tests {
} // namespace internal {
} // namespace mesos {
http://git-wip-us.apache.org/repos/asf/mesos/blob/38a18c4d/src/tests/master_quota_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/master_quota_tests.cpp b/src/tests/master_quota_tests.cpp
index 0473869..ce2ecee 100644
--- a/src/tests/master_quota_tests.cpp
+++ b/src/tests/master_quota_tests.cpp
@@ -59,6 +59,7 @@ using process::http::BadRequest;
using process::http::Conflict;
using process::http::OK;
using process::http::Response;
+using process::http::Unauthorized;
using testing::_;
using testing::DoAll;
@@ -74,7 +75,8 @@ namespace tests {
// * Request validation tests.
// * Sanity check tests.
// * Quota functionality tests.
-// * Failover, and recovery tests.
+// * Failover and recovery tests.
+// * Authentication and authorization tests.
// TODO(alexr): Once we have other allocators, convert this test into a
// typed test over multiple allocators.
@@ -975,6 +977,194 @@ TEST_F(MasterQuotaTest, AvailableResourcesAfterRescinding)
// * Master fails simultaneously with multiple agents, rendering the cluster
// under quota (total quota sanity check).
+
+// These tests verify the authentication and authorization of quota requests.
+
+// Checks that a set quota request is rejected for unauthenticated principals.
+TEST_F(MasterQuotaTest, UnauthenticatedQuotaRequest)
+{
+ 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.
+
+ // A request can contain any amount of resources because it will be rejected
+ // before we start looking at available resources.
+ Resources quotaResources = Resources::parse("cpus:1;mem:512", ROLE1).get();
+
+ // The master is configured so that only requests from `DEFAULT_CREDENTIAL`
+ // are authenticated.
+ Credential credential;
+ credential.set_principal("unknown-principal");
+ credential.set_secret("test-secret");
+
+ Future<Response> response1 = process::http::post(
+ master.get(),
+ "quota",
+ createBasicAuthHeaders(credential),
+ createRequestBody(quotaResources));
+
+ AWAIT_EXPECT_RESPONSE_STATUS_EQ(
+ Unauthorized("Mesos master").status, response1) << response1.get().body;
+
+ // The absense of credentials leads to authentication failure as well.
+ Future<Response> response2 = process::http::post(
+ master.get(),
+ "quota",
+ None(),
+ createRequestBody(quotaResources));
+
+ AWAIT_EXPECT_RESPONSE_STATUS_EQ(
+ Unauthorized("Mesos master").status, response2) << response2.get().body;
+
+ Shutdown();
+}
+
+
+// Checks that an authorized principal can set quota.
+TEST_F(MasterQuotaTest, AuthorizedQuotaSetRequest)
+{
+ TestAllocator<> allocator;
+ EXPECT_CALL(allocator, initialize(_, _, _, _));
+
+ // Setup ACLs so that the default principal can set quotas for `ROLE1`.
+ ACLs acls;
+ mesos::ACL::SetQuota* acl = acls.add_set_quotas();
+ acl->mutable_principals()->add_values(DEFAULT_CREDENTIAL.principal());
+ acl->mutable_roles()->add_values(ROLE1);
+
+ master::Flags masterFlags = CreateMasterFlags();
+ masterFlags.acls = acls;
+
+ Try<PID<Master>> master = StartMaster(&allocator, masterFlags);
+ ASSERT_SOME(master);
+
+ // Start an agent and wait until it 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());
+
+ // Request quota for a portion of the resources available on the agent.
+ Resources quotaResources = Resources::parse("cpus:1;mem:512;", ROLE1).get();
+ EXPECT_TRUE(agentTotalResources.get().contains(quotaResources.flatten()));
+
+ Future<QuotaInfo> quotaInfo;
+ EXPECT_CALL(allocator, setQuota(Eq(ROLE1), _))
+ .WillOnce(DoAll(InvokeSetQuota(&allocator),
+ FutureArg<1>("aInfo)));
+
+ 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;
+
+ AWAIT_READY(quotaInfo);
+
+ // TODO(nfnt): Quota removal authorization will add a principal field to
+ // `QuotaInfo`. Check it for the correct principal value.
+ EXPECT_EQ(ROLE1, quotaInfo.get().role());
+ EXPECT_EQ(quotaResources.flatten(), quotaInfo.get().guarantee());
+
+ Shutdown();
+}
+
+
+// Checks that set quota requests can be authorized without authentication
+// if an authorization rule exists that applies to anyone. The authorizer
+// will map the absence of a principal to "ANY".
+TEST_F(MasterQuotaTest, AuthorizedQuotaSetRequestWithoutPrincipal)
+{
+ TestAllocator<> allocator;
+ EXPECT_CALL(allocator, initialize(_, _, _, _));
+
+ // Setup ACLs so that the default principal can set quotas for `ROLE1`.
+ ACLs acls;
+ mesos::ACL::SetQuota* acl = acls.add_set_quotas();
+ acl->mutable_principals()->set_type(mesos::ACL::Entity::ANY);
+ acl->mutable_roles()->add_values(ROLE1);
+
+ // Disable authentication by not providing credentials.
+ master::Flags masterFlags = CreateMasterFlags();
+ masterFlags.acls = acls;
+ masterFlags.credentials = None();
+
+ Try<PID<Master>> master = StartMaster(&allocator, masterFlags);
+ ASSERT_SOME(master);
+
+ // Start an agent and wait until it 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());
+
+ // Request quota for a portion of the resources available on the agent.
+ Resources quotaResources = Resources::parse("cpus:1;mem:512;", ROLE1).get();
+ EXPECT_TRUE(agentTotalResources.get().contains(quotaResources.flatten()));
+
+ // Create a HTTP request without authorization headers.
+ Future<Response> response = process::http::post(
+ master.get(),
+ "quota",
+ None(),
+ createRequestBody(quotaResources));
+
+ AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response) << response.get().body;
+
+ Shutdown();
+}
+
+
+// Checks that an unauthorized principal cannot set quota.
+TEST_F(MasterQuotaTest, UnauthorizedQuotaSetRequest)
+{
+ // Setup ACLs so that no principal can set quotas for `ROLE1`.
+ ACLs acls;
+ mesos::ACL::SetQuota* acl = acls.add_set_quotas();
+ acl->mutable_principals()->set_type(mesos::ACL::Entity::NONE);
+ acl->mutable_roles()->add_values(ROLE1);
+
+ master::Flags masterFlags = CreateMasterFlags();
+ masterFlags.acls = acls;
+
+ Try<PID<Master>> master = StartMaster(masterFlags);
+ ASSERT_SOME(master);
+
+ // We do not need an agent since a request should be rejected before
+ // we start looking at available resources.
+
+ // A request can contain any amount of resources because it will be rejected
+ // before we start looking at available resources.
+ Resources quotaResources = Resources::parse("cpus:1;mem:512", ROLE1).get();
+
+ Future<Response> response = process::http::post(
+ master.get(),
+ "quota",
+ createBasicAuthHeaders(DEFAULT_CREDENTIAL),
+ createRequestBody(quotaResources));
+
+ AWAIT_EXPECT_RESPONSE_STATUS_EQ(
+ Unauthorized("Mesos master").status, response) << response.get().body;
+
+ Shutdown();
+}
+
} // namespace tests {
} // namespace internal {
} // namespace mesos {
[4/7] mesos git commit: Quota: Documented quota authorization.
Posted by ti...@apache.org.
Quota: Documented quota authorization.
Review: https://reviews.apache.org/r/40348/
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/50bf193f
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/50bf193f
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/50bf193f
Branch: refs/heads/master
Commit: 50bf193f4853a96d1341ada5f167b5a98fe34336
Parents: 2940586
Author: Jan Schlicht <ja...@mesosphere.io>
Authored: Fri Dec 18 17:45:08 2015 +0100
Committer: Till Toenshoff <to...@me.com>
Committed: Fri Dec 18 17:49:43 2015 +0100
----------------------------------------------------------------------
docs/authorization.md | 12 ++++++++----
src/master/flags.cpp | 6 ++++++
2 files changed, 14 insertions(+), 4 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/50bf193f/docs/authorization.md
----------------------------------------------------------------------
diff --git a/docs/authorization.md b/docs/authorization.md
index 1fc0e3f..0b108bf 100644
--- a/docs/authorization.md
+++ b/docs/authorization.md
@@ -4,16 +4,17 @@ layout: documentation
# Authorization
-Mesos 0.20.0 adds support for framework authorization. Authorization allows
+Authorization currently allows
1. Frameworks to (re-)register with authorized _roles_.
2. Frameworks to launch tasks/executors as authorized _users_.
3. Authorized _principals_ to shutdown frameworks through the "/teardown" HTTP endpoint.
+ 4. Authorized _principals_ to set quotas through the "/quota" HTTP endpoint.
## ACLs
-Authorization is implemented via Access Control Lists (ACLs). For each of the 3 cases described above, ACLs can be used to restrict access. Operators can setup ACLs in JSON format. See [mesos.proto](https://github.com/apache/mesos/blob/master/include/mesos/mesos.proto) for details.
+Authorization is implemented via Access Control Lists (ACLs). For each of the above cases, ACLs can be used to restrict access. Operators can setup ACLs in JSON format. See [mesos.proto](https://github.com/apache/mesos/blob/master/include/mesos/mesos.proto) for details.
Each ACL specifies a set of `Subjects` that can perform an `Action` on a set of `Objects`.
@@ -22,16 +23,17 @@ The currently supported `Actions` are:
1. "register_frameworks": Register frameworks
2. "run_tasks": Run tasks/executors
3. "shutdown_frameworks": Shutdown frameworks
+4. "set_quotas": Set quotas
The currently supported `Subjects` are:
1. "principals"
- Framework principals (used by "register_frameworks" and "run_tasks" actions)
- - Usernames (used by "shutdown_frameworks" action)
+ - Usernames (used by "shutdown_frameworks" and "set_quotas" actions)
The currently supported `Objects` are:
-1. "roles": Resource [roles](roles.md) that framework can register with (used by "register_frameworks" actions)
+1. "roles": Resource [roles](roles.md) that framework can register with (used by "register_frameworks" and "set_quotas" actions)
2. "users": Unix user to launch the task/executor as (used by "run_tasks" actions)
3. "framework_principals": Framework principals that can be shutdown by HTTP POST (used by "shutdown_frameworks" actions).
@@ -48,6 +50,8 @@ Similarly, when a framework launches a task, "run_tasks" ACLs are checked to see
In the same vein, when a user/principal attempts to shutdown a framework using the "/teardown" HTTP endpoint on the master, "shutdown_frameworks" ACLs are checked to see if the principal is authorized to shutdown the given framework. If not authorized, the shutdown is rejected and the user receives an `Unauthorized` HTTP response.
+If no user/principal is provided in a request to an HTTP endpoint and authentication is disabled, the `ANY` subject is used in the authorization.
+
There are couple of important things to note:
1. ACLs are matched in the order that they are specified. In other words, the first matching ACL determines whether a request is authorized or not.
http://git-wip-us.apache.org/repos/asf/mesos/blob/50bf193f/src/master/flags.cpp
----------------------------------------------------------------------
diff --git a/src/master/flags.cpp b/src/master/flags.cpp
index f9223ed..a8861f3 100644
--- a/src/master/flags.cpp
+++ b/src/master/flags.cpp
@@ -259,6 +259,12 @@ mesos::internal::master::Flags::Flags()
" \"principals\": { \"values\": [\"a\", \"b\"] },\n"
" \"framework_principals\": { \"values\": [\"c\"] }\n"
" }\n"
+ " ],\n"
+ " \"set_quotas\": [\n"
+ " {\n"
+ " \"principals\": { \"values\": [\"a\"] },\n"
+ " \"roles\": { \"values\": [\"a\", \"b\"] }\n"
+ " }\n"
" ]\n"
"}");
[7/7] mesos git commit: Quota: Updated comments in quota tests.
Posted by ti...@apache.org.
Quota: Updated comments in quota tests.
Review: https://reviews.apache.org/r/41556/
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/d13b65e1
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/d13b65e1
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/d13b65e1
Branch: refs/heads/master
Commit: d13b65e1bcd6ccae459cacfec5ee7bd427a64979
Parents: 8e9469d
Author: Alexander Rukletsov <ru...@gmail.com>
Authored: Fri Dec 18 17:46:18 2015 +0100
Committer: Till Toenshoff <to...@me.com>
Committed: Fri Dec 18 20:52:30 2015 +0100
----------------------------------------------------------------------
src/tests/master_quota_tests.cpp | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/d13b65e1/src/tests/master_quota_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/master_quota_tests.cpp b/src/tests/master_quota_tests.cpp
index d855a0e..f134a8b 100644
--- a/src/tests/master_quota_tests.cpp
+++ b/src/tests/master_quota_tests.cpp
@@ -517,7 +517,7 @@ TEST_F(MasterQuotaTest, InsufficientResourcesSingleAgent)
Try<PID<Master>> master = StartMaster(&allocator);
ASSERT_SOME(master);
- // Start an agent and wait until it registers.
+ // Start an agent and wait until its resources are available.
Future<Resources> agentTotalResources;
EXPECT_CALL(allocator, addSlave(_, _, _, _, _))
.WillOnce(DoAll(InvokeAddSlave(&allocator),
@@ -612,7 +612,7 @@ TEST_F(MasterQuotaTest, InsufficientResourcesMultipleAgents)
Try<PID<Master>> master = StartMaster(&allocator);
ASSERT_SOME(master);
- // Start one agent and wait until it registers.
+ // Start one agent and wait until its resources are available.
Future<Resources> agent1TotalResources;
EXPECT_CALL(allocator, addSlave(_, _, _, _, _))
.WillOnce(DoAll(InvokeAddSlave(&allocator),
@@ -624,7 +624,7 @@ TEST_F(MasterQuotaTest, InsufficientResourcesMultipleAgents)
AWAIT_READY(agent1TotalResources);
EXPECT_EQ(defaultAgentResources, agent1TotalResources.get());
- // Start another agent and wait until it registers.
+ // Start another agent and wait until its resources are available.
Future<Resources> agent2TotalResources;
EXPECT_CALL(allocator, addSlave(_, _, _, _, _))
.WillOnce(DoAll(InvokeAddSlave(&allocator),
@@ -674,7 +674,7 @@ TEST_F(MasterQuotaTest, AvailableResourcesSingleAgent)
Try<PID<Master>> master = StartMaster(&allocator);
ASSERT_SOME(master);
- // Start an agent and wait until it registers.
+ // Start an agent and wait until its resources are available.
Future<Resources> agentTotalResources;
EXPECT_CALL(allocator, addSlave(_, _, _, _, _))
.WillOnce(DoAll(InvokeAddSlave(&allocator),
@@ -726,7 +726,7 @@ TEST_F(MasterQuotaTest, AvailableResourcesMultipleAgents)
Try<PID<Master>> master = StartMaster(&allocator);
ASSERT_SOME(master);
- // Start one agent and wait until it registers.
+ // Start one agent and wait until its resources are available.
Future<Resources> agent1TotalResources;
EXPECT_CALL(allocator, addSlave(_, _, _, _, _))
.WillOnce(DoAll(InvokeAddSlave(&allocator),
@@ -738,7 +738,7 @@ TEST_F(MasterQuotaTest, AvailableResourcesMultipleAgents)
AWAIT_READY(agent1TotalResources);
EXPECT_EQ(defaultAgentResources, agent1TotalResources.get());
- // Start another agent and wait until it registers.
+ // Start another agent and wait until its resources are available.
Future<Resources> agent2TotalResources;
EXPECT_CALL(allocator, addSlave(_, _, _, _, _))
.WillOnce(DoAll(InvokeAddSlave(&allocator),
@@ -799,7 +799,7 @@ TEST_F(MasterQuotaTest, AvailableResourcesAfterRescinding)
Try<PID<Master>> master = StartMaster(&allocator);
ASSERT_SOME(master);
- // Start one agent and wait until it registers.
+ // Start one agent and wait until its resources are available.
Future<Resources> agent1TotalResources;
EXPECT_CALL(allocator, addSlave(_, _, _, _, _))
.WillOnce(DoAll(InvokeAddSlave(&allocator),
@@ -811,7 +811,7 @@ TEST_F(MasterQuotaTest, AvailableResourcesAfterRescinding)
AWAIT_READY(agent1TotalResources);
EXPECT_EQ(defaultAgentResources, agent1TotalResources.get());
- // Start another agent and wait until it registers.
+ // Start another agent and wait until its resources are available.
Future<Resources> agent2TotalResources;
EXPECT_CALL(allocator, addSlave(_, _, _, _, _))
.WillOnce(DoAll(InvokeAddSlave(&allocator),
@@ -823,7 +823,7 @@ TEST_F(MasterQuotaTest, AvailableResourcesAfterRescinding)
AWAIT_READY(agent2TotalResources);
EXPECT_EQ(defaultAgentResources, agent2TotalResources.get());
- // Start one more agent and wait until it registers.
+ // Start one more agent and wait until its resources are available.
Future<Resources> agent3TotalResources;
EXPECT_CALL(allocator, addSlave(_, _, _, _, _))
.WillOnce(DoAll(InvokeAddSlave(&allocator),
@@ -999,7 +999,7 @@ TEST_F(MasterQuotaTest, NoAuthenticationNoAuthorization)
Try<PID<Master>> master = StartMaster(&allocator, masterFlags);
ASSERT_SOME(master);
- // Start an agent and wait until it registers.
+ // Start an agent and wait until its resources are available.
Future<Resources> agentTotalResources;
EXPECT_CALL(allocator, addSlave(_, _, _, _, _))
.WillOnce(DoAll(InvokeAddSlave(&allocator),
@@ -1118,7 +1118,7 @@ TEST_F(MasterQuotaTest, AuthorizedQuotaSetRequest)
Try<PID<Master>> master = StartMaster(&allocator, masterFlags);
ASSERT_SOME(master);
- // Start an agent and wait until it registers.
+ // Start an agent and wait until its resources are available.
Future<Resources> agentTotalResources;
EXPECT_CALL(allocator, addSlave(_, _, _, _, _))
.WillOnce(DoAll(InvokeAddSlave(&allocator),
@@ -1180,7 +1180,7 @@ TEST_F(MasterQuotaTest, AuthorizedQuotaSetRequestWithoutPrincipal)
Try<PID<Master>> master = StartMaster(&allocator, masterFlags);
ASSERT_SOME(master);
- // Start an agent and wait until it registers.
+ // Start an agent and wait until its resources are available.
Future<Resources> agentTotalResources;
EXPECT_CALL(allocator, addSlave(_, _, _, _, _))
.WillOnce(DoAll(InvokeAddSlave(&allocator),
[6/7] mesos git commit: Quota: Verified quota requests succeed when
authn and authz are disabled.
Posted by ti...@apache.org.
Quota: Verified quota requests succeed when authn and authz are disabled.
Review: https://reviews.apache.org/r/41544/
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/8e9469df
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/8e9469df
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/8e9469df
Branch: refs/heads/master
Commit: 8e9469df884ad148bdc7070f7347f4fcef4dcd10
Parents: 38a18c4
Author: Alexander Rukletsov <ru...@gmail.com>
Authored: Fri Dec 18 17:46:07 2015 +0100
Committer: Till Toenshoff <to...@me.com>
Committed: Fri Dec 18 20:52:30 2015 +0100
----------------------------------------------------------------------
src/tests/master_quota_tests.cpp | 78 +++++++++++++++++++++++++++++++++++
1 file changed, 78 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/8e9469df/src/tests/master_quota_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/master_quota_tests.cpp b/src/tests/master_quota_tests.cpp
index ce2ecee..d855a0e 100644
--- a/src/tests/master_quota_tests.cpp
+++ b/src/tests/master_quota_tests.cpp
@@ -980,6 +980,84 @@ TEST_F(MasterQuotaTest, AvailableResourcesAfterRescinding)
// These tests verify the authentication and authorization of quota requests.
+// Checks that quota set and remove requests succeed if both authentication
+// and authorization are disabled.
+TEST_F(MasterQuotaTest, NoAuthenticationNoAuthorization)
+{
+ TestAllocator<> allocator;
+ EXPECT_CALL(allocator, initialize(_, _, _, _));
+
+ // Disable authentication and authorization by providing neither
+ // credentials nor ACLs.
+ // TODO(alexr): Setting master `--acls` flag to `ACLs()` or `None()` seems
+ // to be semantically equal, however, the test harness currently does not
+ // allow `None()`. Once MESOS-4196 is resolved, use `None()` for clarity.
+ master::Flags masterFlags = CreateMasterFlags();
+ masterFlags.credentials = None();
+ masterFlags.acls = ACLs();
+
+ Try<PID<Master>> master = StartMaster(&allocator, masterFlags);
+ ASSERT_SOME(master);
+
+ // Start an agent and wait until it 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());
+
+ // Check whether quota can be set.
+ {
+ // Request quota for a portion of the resources available on the agent.
+ Resources quotaResources = Resources::parse("cpus:1;mem:512", ROLE1).get();
+ EXPECT_TRUE(agentTotalResources.get().contains(quotaResources.flatten()));
+
+ Future<QuotaInfo> receivedSetRequest;
+ EXPECT_CALL(allocator, setQuota(Eq(ROLE1), _))
+ .WillOnce(DoAll(InvokeSetQuota(&allocator),
+ FutureArg<1>(&receivedSetRequest)));
+
+ // Send a set quota request with absent credentials.
+ Future<Response> response = process::http::post(
+ master.get(),
+ "quota",
+ None(),
+ createRequestBody(quotaResources));
+
+ // Quota request succeeds and reaches the allocator.
+ AWAIT_READY(receivedSetRequest);
+ AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response)
+ << response.get().body;
+ }
+
+ // Check whether quota can be removed.
+ {
+ Future<Nothing> receivedRemoveRequest;
+ EXPECT_CALL(allocator, removeQuota(Eq(ROLE1)))
+ .WillOnce(DoAll(InvokeRemoveQuota(&allocator),
+ FutureSatisfy(&receivedRemoveRequest)));
+
+ // Send a remove quota request with absent credentials.
+ Future<Response> response = process::http::requestDelete(
+ master.get(),
+ "quota/" + ROLE1,
+ None());
+
+ // Quota request succeeds and reaches the allocator.
+ AWAIT_READY(receivedRemoveRequest);
+ AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response)
+ << response.get().body;
+ }
+
+ Shutdown();
+}
+
+
// Checks that a set quota request is rejected for unauthenticated principals.
TEST_F(MasterQuotaTest, UnauthenticatedQuotaRequest)
{
[2/7] mesos git commit: Quota: Implemented authorization of set quota
requests in the authorizer.
Posted by ti...@apache.org.
Quota: Implemented authorization of set quota requests in the authorizer.
Review: https://reviews.apache.org/r/40346/
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/df99dbb8
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/df99dbb8
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/df99dbb8
Branch: refs/heads/master
Commit: df99dbb87cd93e392efb59d8d49c1376652e3bbb
Parents: 0f2bd3b
Author: Jan Schlicht <ja...@mesosphere.io>
Authored: Fri Dec 18 17:43:43 2015 +0100
Committer: Till Toenshoff <to...@me.com>
Committed: Fri Dec 18 17:49:19 2015 +0100
----------------------------------------------------------------------
include/mesos/authorizer/authorizer.hpp | 19 ++++++++++++++++++
src/authorizer/local/authorizer.cpp | 30 ++++++++++++++++++++++++++++
src/authorizer/local/authorizer.hpp | 2 ++
src/tests/mesos.cpp | 3 +++
src/tests/mesos.hpp | 2 ++
5 files changed, 56 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/df99dbb8/include/mesos/authorizer/authorizer.hpp
----------------------------------------------------------------------
diff --git a/include/mesos/authorizer/authorizer.hpp b/include/mesos/authorizer/authorizer.hpp
index aa9bb8b..19f6e1a 100644
--- a/include/mesos/authorizer/authorizer.hpp
+++ b/include/mesos/authorizer/authorizer.hpp
@@ -211,6 +211,25 @@ public:
virtual process::Future<bool> authorize(
const ACL::DestroyVolume& request) = 0;
+ /**
+ * Used to verify if a principal is allowed to set a quota for a specific
+ * role. The principal and role parameters are packed in `request`. If the
+ * principal is allowed to perform the action, this method returns true,
+ * otherwise it returns false. A third possible outcome is that the future
+ * fails. This indicates that the request could not be checked at the
+ * moment. This may be a temporary condition.
+ *
+ * @param request An instance of an `ACL::SetQuota` protobuf message. It
+ * packs all the parameters needed to verify if the given principal is
+ * allowed to request a quota with the specified role.
+ *
+ * @return true if the principal is allowed to set a quota for the specified
+ * role or false otherwise. A failed future however indicates a problem
+ * processing the request and the request can be retried.
+ */
+ virtual process::Future<bool> authorize(
+ const ACL::SetQuota& request) = 0;
+
protected:
Authorizer() {}
};
http://git-wip-us.apache.org/repos/asf/mesos/blob/df99dbb8/src/authorizer/local/authorizer.cpp
----------------------------------------------------------------------
diff --git a/src/authorizer/local/authorizer.cpp b/src/authorizer/local/authorizer.cpp
index 46e181a..1d135fb 100644
--- a/src/authorizer/local/authorizer.cpp
+++ b/src/authorizer/local/authorizer.cpp
@@ -149,6 +149,21 @@ public:
return acls.permissive(); // None of the ACLs match.
}
+ Future<bool> authorize(const ACL::SetQuota& request)
+ {
+ foreach (const ACL::SetQuota& acl, acls.set_quotas()) {
+ // ACL matches if both subjects and objects match.
+ if (matches(request.principals(), acl.principals()) &&
+ matches(request.roles(), acl.roles())) {
+ // ACL is allowed if both subjects and objects are allowed.
+ return allows(request.principals(), acl.principals()) &&
+ allows(request.roles(), acl.roles());
+ }
+ }
+
+ return acls.permissive(); // None of the ACLs match.
+ }
+
private:
// Match matrix:
//
@@ -283,6 +298,7 @@ LocalAuthorizer::~LocalAuthorizer()
}
}
+
Try<Nothing> LocalAuthorizer::initialize(const Option<ACLs>& acls)
{
if (!acls.isSome()) {
@@ -408,5 +424,19 @@ Future<bool> LocalAuthorizer::authorize(const ACL::DestroyVolume& request)
process, static_cast<F>(&LocalAuthorizerProcess::authorize), request);
}
+
+Future<bool> LocalAuthorizer::authorize(const ACL::SetQuota& request)
+{
+ if (process == NULL) {
+ return Failure("Authorizer not initialized");
+ }
+
+ // Necessary to disambiguate.
+ typedef Future<bool>(LocalAuthorizerProcess::*F)(const ACL::SetQuota&);
+
+ return dispatch(
+ process, static_cast<F>(&LocalAuthorizerProcess::authorize), request);
+}
+
} // namespace internal {
} // namespace mesos {
http://git-wip-us.apache.org/repos/asf/mesos/blob/df99dbb8/src/authorizer/local/authorizer.hpp
----------------------------------------------------------------------
diff --git a/src/authorizer/local/authorizer.hpp b/src/authorizer/local/authorizer.hpp
index eb95c9f..1563c11 100644
--- a/src/authorizer/local/authorizer.hpp
+++ b/src/authorizer/local/authorizer.hpp
@@ -65,6 +65,8 @@ public:
const ACL::CreateVolume& request);
virtual process::Future<bool> authorize(
const ACL::DestroyVolume& request);
+ virtual process::Future<bool> authorize(
+ const ACL::SetQuota& request);
private:
LocalAuthorizer();
http://git-wip-us.apache.org/repos/asf/mesos/blob/df99dbb8/src/tests/mesos.cpp
----------------------------------------------------------------------
diff --git a/src/tests/mesos.cpp b/src/tests/mesos.cpp
index 50ba8c4..11ca051 100644
--- a/src/tests/mesos.cpp
+++ b/src/tests/mesos.cpp
@@ -696,6 +696,9 @@ MockAuthorizer::MockAuthorizer()
EXPECT_CALL(*this, authorize(An<const mesos::ACL::DestroyVolume&>()))
.WillRepeatedly(Return(true));
+ EXPECT_CALL(*this, authorize(An<const mesos::ACL::SetQuota&>()))
+ .WillRepeatedly(Return(true));
+
EXPECT_CALL(*this, initialize(An<const Option<ACLs>&>()))
.WillRepeatedly(Return(Nothing()));
}
http://git-wip-us.apache.org/repos/asf/mesos/blob/df99dbb8/src/tests/mesos.hpp
----------------------------------------------------------------------
diff --git a/src/tests/mesos.hpp b/src/tests/mesos.hpp
index 1c6acab..582b018 100644
--- a/src/tests/mesos.hpp
+++ b/src/tests/mesos.hpp
@@ -1209,6 +1209,8 @@ public:
authorize, process::Future<bool>(const ACL::CreateVolume& request));
MOCK_METHOD1(
authorize, process::Future<bool>(const ACL::DestroyVolume& request));
+ MOCK_METHOD1(
+ authorize, process::Future<bool>(const ACL::SetQuota& request));
};
[3/7] mesos git commit: Quota: Implemented set quota request
authorization.
Posted by ti...@apache.org.
Quota: Implemented set quota request authorization.
Review: https://reviews.apache.org/r/40347/
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/2940586f
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/2940586f
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/2940586f
Branch: refs/heads/master
Commit: 2940586f64383668ecaf722c279f325d04fb3781
Parents: df99dbb
Author: Jan Schlicht <ja...@mesosphere.io>
Authored: Fri Dec 18 17:44:44 2015 +0100
Committer: Till Toenshoff <to...@me.com>
Committed: Fri Dec 18 17:49:34 2015 +0100
----------------------------------------------------------------------
src/master/master.hpp | 8 ++++++
src/master/quota_handler.cpp | 51 ++++++++++++++++++++++++++++++++++++---
2 files changed, 56 insertions(+), 3 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/2940586f/src/master/master.hpp
----------------------------------------------------------------------
diff --git a/src/master/master.hpp b/src/master/master.hpp
index 7cb0e16..d493109 100644
--- a/src/master/master.hpp
+++ b/src/master/master.hpp
@@ -992,6 +992,14 @@ private:
// (including rescinding) is moved to allocator.
void rescindOffers(const mesos::quota::QuotaInfo& request) const;
+ process::Future<bool> authorize(
+ const Option<std::string>& principal,
+ const std::string& role) const;
+
+ process::Future<process::http::Response> _set(
+ const mesos::quota::QuotaInfo& quota,
+ bool forced) const;
+
// To perform actions related to quota management, we require access to the
// master data structures. No synchronization primitives are needed here
// since `QuotaHandler`'s functions are invoked in the Master's actor.
http://git-wip-us.apache.org/repos/asf/mesos/blob/2940586f/src/master/quota_handler.cpp
----------------------------------------------------------------------
diff --git a/src/master/quota_handler.cpp b/src/master/quota_handler.cpp
index 1116787..27e12ab 100644
--- a/src/master/quota_handler.cpp
+++ b/src/master/quota_handler.cpp
@@ -263,8 +263,6 @@ Future<http::Response> Master::QuotaHandler::set(
return Unauthorized("Mesos master", credential.error());
}
- // TODO(nfnt): Authorize the request.
-
// Check that the request type is POST which is guaranteed by the master.
CHECK_EQ("POST", request.method);
@@ -327,7 +325,28 @@ Future<http::Response> Master::QuotaHandler::set(
const QuotaInfo& quotaInfo = create.get();
// The force flag can be used to overwrite the capacityHeuristic check.
- if (values.contains("force") && strings::lower(values["force"]) == "true") {
+ bool forced = values.contains("force") &&
+ strings::lower(values["force"]) == "true";
+
+ Option<string> principal =
+ credential.isSome() ? credential.get().principal() : Option<string>::none();
+
+ return authorize(principal, quotaInfo.role())
+ .then(defer(master->self(), [=](bool authorized) -> Future<http::Response> {
+ if (!authorized) {
+ return Unauthorized("Mesos master");
+ }
+
+ return _set(quotaInfo, forced);
+ }));
+}
+
+
+Future<http::Response> Master::QuotaHandler::_set(
+ const QuotaInfo& quotaInfo,
+ bool forced) const
+{
+ if (forced) {
VLOG(1) << "Using force flag to override quota capacityHeuristic check";
} else {
// Validate whether a quota request can be satisfied.
@@ -438,6 +457,32 @@ Future<http::Response> Master::QuotaHandler::remove(
}));
}
+
+Future<bool> Master::QuotaHandler::authorize(
+ const Option<string>& principal,
+ const string& role) const
+{
+ if (master->authorizer.isNone()) {
+ return true;
+ }
+
+ LOG(INFO) << "Authorizing principal '"
+ << (principal.isSome() ? principal.get() : "ANY")
+ << "' to request quota for role '" << role << "'";
+
+ mesos::ACL::SetQuota request;
+
+ if (principal.isSome()) {
+ request.mutable_principals()->add_values(principal.get());
+ } else {
+ request.mutable_principals()->set_type(mesos::ACL::Entity::ANY);
+ }
+
+ request.mutable_roles()->add_values(role);
+
+ return master->authorizer.get()->authorize(request);
+}
+
} // namespace master {
} // namespace internal {
} // namespace mesos {