You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by gr...@apache.org on 2017/07/19 00:19:51 UTC
[1/3] mesos git commit: Added class definition for the 'IDAcceptor'.
Repository: mesos
Updated Branches:
refs/heads/master 91b3abcfa -> 916a5c9fd
Added class definition for the 'IDAcceptor'.
This commit contains the class definition for 'IDAcceptor', which is
used to filter IDs in the '/master/frameworks', '/master/slaves',
'/master/tasks', and '/slave/containers' endpoints.
Review: https://reviews.apache.org/r/60820/
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/aa244baa
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/aa244baa
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/aa244baa
Branch: refs/heads/master
Commit: aa244baa45d8db84e98e6dca9944a3f679da70d1
Parents: 91b3abc
Author: Quinn Leng <qu...@gmail.com>
Authored: Tue Jul 18 17:06:56 2017 -0700
Committer: Greg Mann <gr...@gmail.com>
Committed: Tue Jul 18 17:07:40 2017 -0700
----------------------------------------------------------------------
src/common/http.cpp | 41 -----------------------------------------
src/common/http.hpp | 42 +++++++++++++++++++++---------------------
src/master/http.cpp | 27 +++++++++++++--------------
3 files changed, 34 insertions(+), 76 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/aa244baa/src/common/http.cpp
----------------------------------------------------------------------
diff --git a/src/common/http.cpp b/src/common/http.cpp
index 3825a13..dfd5f33 100644
--- a/src/common/http.cpp
+++ b/src/common/http.cpp
@@ -1165,45 +1165,4 @@ Future<Owned<AuthorizationAcceptor>> AuthorizationAcceptor::create(
}
-FrameworkIDAcceptor::FrameworkIDAcceptor(
- const Option<std::string>& _frameworkId)
-{
- if (_frameworkId.isSome()) {
- FrameworkID frameworkId_;
- frameworkId_.set_value(_frameworkId.get());
- frameworkId = frameworkId_;
- }
-}
-
-
-TaskIDAcceptor::TaskIDAcceptor(const Option<std::string>& _taskId)
-{
- if (_taskId.isSome()) {
- TaskID taskId_;
- taskId_.set_value(_taskId.get());
- taskId = taskId_;
- }
-}
-
-
-bool FrameworkIDAcceptor::accept(const FrameworkID& _frameworkId)
-{
- if (frameworkId.isSome()) {
- return frameworkId.get() == _frameworkId;
- }
-
- return true;
-}
-
-
-bool TaskIDAcceptor::accept(const TaskID& _taskId)
-{
- if (taskId.isSome()) {
- return taskId.get() == _taskId;
- }
-
- return true;
-}
-
-
} // namespace mesos {
http://git-wip-us.apache.org/repos/asf/mesos/blob/aa244baa/src/common/http.hpp
----------------------------------------------------------------------
diff --git a/src/common/http.hpp b/src/common/http.hpp
index 4822a23..ba8dda1 100644
--- a/src/common/http.hpp
+++ b/src/common/http.hpp
@@ -195,34 +195,34 @@ protected:
/**
- * Filtering results based on framework ID. When no framework ID is specified
- * it will accept all inputs.
+ * Used to filter results for API handlers. Provides the 'accept()' method to
+ * test whether the supplied ID is equal to a stored target ID. If no target
+ * ID is provided when the acceptor is constructed, it will accept all inputs.
*/
-class FrameworkIDAcceptor
+template <typename T>
+class IDAcceptor
{
public:
- FrameworkIDAcceptor(const Option<std::string>& _frameworkId);
-
- bool accept(const FrameworkID& frameworkId);
-
-protected:
- Option<FrameworkID> frameworkId;
-};
-
+ IDAcceptor(const Option<std::string>& id = None())
+ {
+ if (id.isSome()) {
+ T targetId_;
+ targetId_.set_value(id.get());
+ targetId = targetId_;
+ }
+ }
-/**
- * Filtering results based on task ID. When no task ID is specified
- * it will accept all inputs.
- */
-class TaskIDAcceptor
-{
-public:
- TaskIDAcceptor(const Option<std::string>& _taskId);
+ bool accept(const T& candidateId) const
+ {
+ if (targetId.isNone()) {
+ return true;
+ }
- bool accept(const TaskID& taskId);
+ return candidateId.value() == targetId->value();
+ }
protected:
- Option<TaskID> taskId;
+ Option<T> targetId;
};
http://git-wip-us.apache.org/repos/asf/mesos/blob/aa244baa/src/master/http.cpp
----------------------------------------------------------------------
diff --git a/src/master/http.cpp b/src/master/http.cpp
index 3ddb54b..cbe6d96 100644
--- a/src/master/http.cpp
+++ b/src/master/http.cpp
@@ -3900,11 +3900,10 @@ Future<Response> Master::Http::tasks(
principal,
master->authorizer,
authorization::VIEW_TASK);
- Future<Owned<FrameworkIDAcceptor>> selectFrameworkId =
- Owned<FrameworkIDAcceptor>(
- new FrameworkIDAcceptor(request.url.query.get("framework_id")));
- Future<Owned<TaskIDAcceptor>> selectTaskId =
- Owned<TaskIDAcceptor>(new TaskIDAcceptor(request.url.query.get("task_id")));
+ Future<IDAcceptor<FrameworkID>> selectFrameworkId =
+ IDAcceptor<FrameworkID>(request.url.query.get("framework_id"));
+ Future<IDAcceptor<TaskID>> selectTaskId =
+ IDAcceptor<TaskID>(request.url.query.get("task_id"));
return collect(
authorizeFrameworkInfo,
@@ -3915,12 +3914,12 @@ Future<Response> Master::Http::tasks(
master->self(),
[=](const tuple<Owned<AuthorizationAcceptor>,
Owned<AuthorizationAcceptor>,
- Owned<FrameworkIDAcceptor>,
- Owned<TaskIDAcceptor>>& acceptors)-> Future<Response> {
+ IDAcceptor<FrameworkID>,
+ IDAcceptor<TaskID>>& acceptors)-> Future<Response> {
Owned<AuthorizationAcceptor> authorizeFrameworkInfo;
Owned<AuthorizationAcceptor> authorizeTask;
- Owned<FrameworkIDAcceptor> selectFrameworkId;
- Owned<TaskIDAcceptor> selectTaskId;
+ IDAcceptor<FrameworkID> selectFrameworkId;
+ IDAcceptor<TaskID> selectTaskId;
tie(authorizeFrameworkInfo,
authorizeTask,
selectFrameworkId,
@@ -3931,7 +3930,7 @@ Future<Response> Master::Http::tasks(
foreachvalue (Framework* framework, master->frameworks.registered) {
// Skip unauthorized frameworks or frameworks without matching
// framework ID.
- if (!selectFrameworkId->accept(framework->id()) ||
+ if (!selectFrameworkId.accept(framework->id()) ||
!authorizeFrameworkInfo->accept(framework->info)) {
continue;
}
@@ -3943,7 +3942,7 @@ Future<Response> Master::Http::tasks(
master->frameworks.completed) {
// Skip unauthorized frameworks or frameworks without matching
// framework ID.
- if (!selectFrameworkId->accept(framework->id()) ||
+ if (!selectFrameworkId.accept(framework->id()) ||
!authorizeFrameworkInfo->accept(framework->info)) {
continue;
}
@@ -3958,7 +3957,7 @@ Future<Response> Master::Http::tasks(
foreachvalue (Task* task, framework->tasks) {
CHECK_NOTNULL(task);
// Skip unauthorized tasks or tasks without matching task ID.
- if (!selectTaskId->accept(task->task_id()) ||
+ if (!selectTaskId.accept(task->task_id()) ||
!authorizeTask->accept(*task, framework->info)) {
continue;
}
@@ -3970,7 +3969,7 @@ Future<Response> Master::Http::tasks(
const Owned<Task>& task,
framework->unreachableTasks) {
// Skip unauthorized tasks or tasks without matching task ID.
- if (!selectTaskId->accept(task.get()->task_id()) ||
+ if (!selectTaskId.accept(task.get()->task_id()) ||
!authorizeTask->accept(*task.get(), framework->info)) {
continue;
}
@@ -3980,7 +3979,7 @@ Future<Response> Master::Http::tasks(
foreach (const Owned<Task>& task, framework->completedTasks) {
// Skip unauthorized tasks or tasks without matching task ID.
- if (!selectTaskId->accept(task.get()->task_id()) ||
+ if (!selectTaskId.accept(task.get()->task_id()) ||
!authorizeTask->accept(*task.get(), framework->info)) {
continue;
}
[2/3] mesos git commit: Added filtering to /slaves,
/containers and /frameworks endpoints.
Posted by gr...@apache.org.
Added filtering to /slaves, /containers and /frameworks endpoints.
Added query parameter support for the '/slaves', '/frameworks' and
'/containers' endpoints.
This allows slaves, frameworks and containers to be queried
by ID.
If no ID is specified, all records are returned, consistent
with current behavior.
Review: https://reviews.apache.org/r/60822/
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/8363449c
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/8363449c
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/8363449c
Branch: refs/heads/master
Commit: 8363449c130298b9c77560c5df583dc1226dd17c
Parents: aa244ba
Author: Quinn Leng <qu...@gmail.com>
Authored: Tue Jul 18 17:06:59 2017 -0700
Committer: Greg Mann <gr...@gmail.com>
Committed: Tue Jul 18 17:08:43 2017 -0700
----------------------------------------------------------------------
src/master/http.cpp | 445 +++++++++++++++++++++++------------------------
src/slave/http.cpp | 96 +++++-----
src/slave/http.hpp | 3 +-
3 files changed, 265 insertions(+), 279 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/8363449c/src/master/http.cpp
----------------------------------------------------------------------
diff --git a/src/master/http.cpp b/src/master/http.cpp
index cbe6d96..9df086c 100644
--- a/src/master/http.cpp
+++ b/src/master/http.cpp
@@ -219,11 +219,11 @@ static void json(JSON::ObjectWriter* writer, const Summary<Framework>& summary);
// user is authorized to view them.
struct FullFrameworkWriter {
FullFrameworkWriter(
- const Owned<ObjectApprover>& taskApprover,
- const Owned<ObjectApprover>& executorApprover,
+ const Owned<AuthorizationAcceptor>& authorizeTask,
+ const Owned<AuthorizationAcceptor>& authorizeExecutorInfo,
const Framework* framework)
- : taskApprover_(taskApprover),
- executorApprover_(executorApprover),
+ : authorizeTask_(authorizeTask),
+ authorizeExecutorInfo_(authorizeExecutorInfo),
framework_(framework) {}
void operator()(JSON::ObjectWriter* writer) const
@@ -268,7 +268,7 @@ struct FullFrameworkWriter {
writer->field("tasks", [this](JSON::ArrayWriter* writer) {
foreachvalue (const TaskInfo& taskInfo, framework_->pendingTasks) {
// Skip unauthorized tasks.
- if (!approveViewTaskInfo(taskApprover_, taskInfo, framework_->info)) {
+ if (!authorizeTask_->accept(taskInfo, framework_->info)) {
continue;
}
@@ -309,7 +309,7 @@ struct FullFrameworkWriter {
foreachvalue (Task* task, framework_->tasks) {
// Skip unauthorized tasks.
- if (!approveViewTask(taskApprover_, *task, framework_->info)) {
+ if (!authorizeTask_->accept(*task, framework_->info)) {
continue;
}
@@ -320,7 +320,7 @@ struct FullFrameworkWriter {
writer->field("unreachable_tasks", [this](JSON::ArrayWriter* writer) {
foreachvalue (const Owned<Task>& task, framework_->unreachableTasks) {
// Skip unauthorized tasks.
- if (!approveViewTask(taskApprover_, *task.get(), framework_->info)) {
+ if (!authorizeTask_->accept(*task.get(), framework_->info)) {
continue;
}
@@ -331,7 +331,7 @@ struct FullFrameworkWriter {
writer->field("completed_tasks", [this](JSON::ArrayWriter* writer) {
foreach (const Owned<Task>& task, framework_->completedTasks) {
// Skip unauthorized tasks.
- if (!approveViewTask(taskApprover_, *task.get(), framework_->info)) {
+ if (!authorizeTask_->accept(*task.get(), framework_->info)) {
continue;
}
@@ -357,10 +357,7 @@ struct FullFrameworkWriter {
&executor,
&slaveId](JSON::ObjectWriter* writer) {
// Skip unauthorized executors.
- if (!approveViewExecutorInfo(
- executorApprover_,
- executor,
- framework_->info)) {
+ if (!authorizeExecutorInfo_->accept(executor, framework_->info)) {
return;
}
@@ -377,16 +374,18 @@ struct FullFrameworkWriter {
}
}
- const Owned<ObjectApprover>& taskApprover_;
- const Owned<ObjectApprover>& executorApprover_;
+ const Owned<AuthorizationAcceptor>& authorizeTask_;
+ const Owned<AuthorizationAcceptor>& authorizeExecutorInfo_;
const Framework* framework_;
};
struct SlaveWriter
{
- SlaveWriter(const Slave& slave, const Owned<ObjectApprover>& roleApprover)
- : slave_(slave), roleApprover_(roleApprover) {}
+ SlaveWriter(
+ const Slave& slave,
+ const Owned<AuthorizationAcceptor>& authorizeRole)
+ : slave_(slave), authorizeRole_(authorizeRole) {}
void operator()(JSON::ObjectWriter* writer) const
{
@@ -411,7 +410,7 @@ struct SlaveWriter
// TODO(arojas): Consider showing unapproved resources in an
// aggregated special field, so that all resource values add up
// MESOS-7779.
- if (approveViewRole(roleApprover_, role)) {
+ if (authorizeRole_->accept(role)) {
writer->field(role, reservation);
}
}
@@ -424,7 +423,7 @@ struct SlaveWriter
}
const Slave& slave_;
- const Owned<ObjectApprover>& roleApprover_;
+ const Owned<AuthorizationAcceptor>& authorizeRole_;
};
@@ -432,14 +431,20 @@ struct SlavesWriter
{
SlavesWriter(
const Master::Slaves& slaves,
- const Owned<ObjectApprover>& roleApprover)
+ const Owned<AuthorizationAcceptor>& authorizeRole,
+ const IDAcceptor<SlaveID>& selectSlaveId)
: slaves_(slaves),
- roleApprover_(roleApprover) {}
+ authorizeRole_(authorizeRole),
+ selectSlaveId_(selectSlaveId) {}
void operator()(JSON::ObjectWriter* writer) const
{
writer->field("slaves", [this](JSON::ArrayWriter* writer) {
foreachvalue (const Slave* slave, slaves_.registered) {
+ if (!selectSlaveId_.accept(slave->id)) {
+ continue;
+ }
+
writer->element([this, &slave](JSON::ObjectWriter* writer) {
writeSlave(slave, writer);
});
@@ -448,6 +453,10 @@ struct SlavesWriter
writer->field("recovered_slaves", [this](JSON::ArrayWriter* writer) {
foreachvalue (const SlaveInfo& slaveInfo, slaves_.recovered) {
+ if (!selectSlaveId_.accept(slaveInfo.id())) {
+ continue;
+ }
+
writer->element([&slaveInfo](JSON::ObjectWriter* writer) {
json(writer, slaveInfo);
});
@@ -457,7 +466,7 @@ struct SlavesWriter
void writeSlave(const Slave* slave, JSON::ObjectWriter* writer) const
{
- SlaveWriter(*slave, roleApprover_)(writer);
+ SlaveWriter(*slave, authorizeRole_)(writer);
// Add the complete protobuf->JSON for all used, reserved,
// and offered resources. The other endpoints summarize
@@ -474,7 +483,7 @@ struct SlavesWriter
foreachpair (const string& role,
const Resources& resources,
reserved) {
- if (approveViewRole(roleApprover_, role)) {
+ if (authorizeRole_->accept(role)) {
writer->field(role, [&resources](JSON::ArrayWriter* writer) {
foreach (Resource resource, resources) {
convertResourceFormat(&resource, ENDPOINT);
@@ -502,9 +511,8 @@ struct SlavesWriter
"used_resources_full",
[&usedResources, this](JSON::ArrayWriter* writer) {
foreach (Resource resource, usedResources) {
- if (approveViewRole(roleApprover_, resource.role()) &&
- approveViewRole(roleApprover_,
- resource.allocation_info().role())) {
+ if (authorizeRole_->accept(resource.role()) &&
+ authorizeRole_->accept(resource.allocation_info().role())) {
convertResourceFormat(&resource, ENDPOINT);
writer->element(JSON::Protobuf(resource));
}
@@ -517,7 +525,7 @@ struct SlavesWriter
"offered_resources_full",
[&offeredResources, this](JSON::ArrayWriter* writer) {
foreach (Resource resource, offeredResources) {
- if (approveViewRole(roleApprover_, resource.role())) {
+ if (authorizeRole_->accept(resource.role())) {
convertResourceFormat(&resource, ENDPOINT);
writer->element(JSON::Protobuf(resource));
}
@@ -525,8 +533,9 @@ struct SlavesWriter
});
}
- const Master::Slaves &slaves_;
- const Owned<ObjectApprover> &roleApprover_;
+ const Master::Slaves& slaves_;
+ const Owned<AuthorizationAcceptor>& authorizeRole_;
+ const IDAcceptor<SlaveID>& selectSlaveId_;
};
@@ -1467,7 +1476,11 @@ string Master::Http::FRAMEWORKS_HELP()
"current master is not the leader.",
"",
"Returns 503 SERVICE_UNAVAILABLE if the leading master cannot be",
- "found."),
+ "found.",
+ "",
+ "Query parameters:",
+ "> framework_id=VALUE The ID of the framework returned "
+ "(when no framework ID specified, all frameworks will be returned)."),
AUTHENTICATION(true),
AUTHORIZATION(
"This endpoint might be filtered based on the user accessing it.",
@@ -1493,87 +1506,89 @@ Future<Response> Master::Http::frameworks(
return redirect(request);
}
- // Retrieve `ObjectApprover`s for authorizing frameworks and tasks and
- // executors.
- Future<Owned<ObjectApprover>> frameworksApprover;
- Future<Owned<ObjectApprover>> tasksApprover;
- Future<Owned<ObjectApprover>> executorsApprover;
-
- if (master->authorizer.isSome()) {
- Option<authorization::Subject> subject = createSubject(principal);
-
- frameworksApprover = master->authorizer.get()->getObjectApprover(
- subject, authorization::VIEW_FRAMEWORK);
-
- tasksApprover = master->authorizer.get()->getObjectApprover(
- subject, authorization::VIEW_TASK);
-
- executorsApprover = master->authorizer.get()->getObjectApprover(
- subject, authorization::VIEW_EXECUTOR);
- } else {
- frameworksApprover = Owned<ObjectApprover>(new AcceptingObjectApprover());
- tasksApprover = Owned<ObjectApprover>(new AcceptingObjectApprover());
- executorsApprover = Owned<ObjectApprover>(new AcceptingObjectApprover());
- }
+ Future<Owned<AuthorizationAcceptor>> authorizeFrameworkInfo =
+ AuthorizationAcceptor::create(
+ principal, master->authorizer, authorization::VIEW_FRAMEWORK);
+ Future<Owned<AuthorizationAcceptor>> authorizeTask =
+ AuthorizationAcceptor::create(
+ principal, master->authorizer, authorization::VIEW_TASK);
+ Future<Owned<AuthorizationAcceptor>> authorizeExecutorInfo =
+ AuthorizationAcceptor::create(
+ principal, master->authorizer, authorization::VIEW_EXECUTOR);
+ Future<IDAcceptor<FrameworkID>> selectFrameworkId =
+ IDAcceptor<FrameworkID>(request.url.query.get("framework_id"));
- return collect(frameworksApprover, tasksApprover, executorsApprover)
+ return collect(
+ authorizeFrameworkInfo,
+ authorizeTask,
+ authorizeExecutorInfo,
+ selectFrameworkId)
.then(defer(master->self(),
- [this, request](const tuple<Owned<ObjectApprover>,
- Owned<ObjectApprover>,
- Owned<ObjectApprover>>& approvers)
+ [this, request](const tuple<Owned<AuthorizationAcceptor>,
+ Owned<AuthorizationAcceptor>,
+ Owned<AuthorizationAcceptor>,
+ IDAcceptor<FrameworkID>>& acceptors)
-> Response {
// This lambda is consumed before the outer lambda
// returns, hence capture by reference is fine here.
- auto frameworks = [this, &approvers](JSON::ObjectWriter* writer) {
- // Get approver from tuple.
- Owned<ObjectApprover> frameworksApprover;
- Owned<ObjectApprover> tasksApprover;
- Owned<ObjectApprover> executorsApprover;
- tie(frameworksApprover, tasksApprover, executorsApprover) = approvers;
+ auto frameworks = [this, &acceptors](JSON::ObjectWriter* writer) {
+ Owned<AuthorizationAcceptor> authorizeFrameworkInfo;
+ Owned<AuthorizationAcceptor> authorizeTask;
+ Owned<AuthorizationAcceptor> authorizeExecutorInfo;
+ IDAcceptor<FrameworkID> selectFrameworkId;
+ tie(authorizeFrameworkInfo,
+ authorizeTask,
+ authorizeExecutorInfo,
+ selectFrameworkId) = acceptors;
// Model all of the frameworks.
writer->field(
"frameworks",
- [this, &frameworksApprover, &executorsApprover, &tasksApprover](
- JSON::ArrayWriter* writer) {
- foreachvalue (Framework* framework,
- master->frameworks.registered) {
- // Skip unauthorized frameworks.
- if (!approveViewFrameworkInfo(
- frameworksApprover, framework->info)) {
- continue;
- }
+ [this,
+ &authorizeFrameworkInfo,
+ &authorizeTask,
+ &authorizeExecutorInfo,
+ &selectFrameworkId](JSON::ArrayWriter* writer) {
+ foreachvalue (Framework* framework, master->frameworks.registered) {
+ // Skip unauthorized frameworks or frameworks without a matching ID.
+ if (!selectFrameworkId.accept(framework->id()) ||
+ !authorizeFrameworkInfo->accept(framework->info)) {
+ continue;
+ }
- FullFrameworkWriter frameworkWriter(
- tasksApprover,
- executorsApprover,
- framework);
+ FullFrameworkWriter frameworkWriter(
+ authorizeTask,
+ authorizeExecutorInfo,
+ framework);
- writer->element(frameworkWriter);
- }
- });
+ writer->element(frameworkWriter);
+ }
+ });
// Model all of the completed frameworks.
writer->field(
"completed_frameworks",
- [this, &frameworksApprover, &executorsApprover, &tasksApprover](
- JSON::ArrayWriter* writer) {
- foreachvalue (const Owned<Framework>& framework,
- master->frameworks.completed) {
- // Skip unauthorized frameworks.
- if (!approveViewFrameworkInfo(
- frameworksApprover, framework->info)) {
- continue;
- }
+ [this,
+ &authorizeFrameworkInfo,
+ &authorizeTask,
+ &authorizeExecutorInfo,
+ &selectFrameworkId](JSON::ArrayWriter* writer) {
+ foreachvalue (const Owned<Framework>& framework,
+ master->frameworks.completed) {
+ // Skip unauthorized frameworks or frameworks without a matching ID.
+ if (!selectFrameworkId.accept(framework->id()) ||
+ !authorizeFrameworkInfo->accept(framework->info)) {
+ continue;
+ }
- FullFrameworkWriter frameworkWriter(
- tasksApprover,
- executorsApprover,
- framework.get());
+ FullFrameworkWriter frameworkWriter(
+ authorizeTask,
+ authorizeExecutorInfo,
+ framework.get());
- writer->element(frameworkWriter);
- }
- });
+ writer->element(frameworkWriter);
+ }
+ });
// Unregistered frameworks are no longer possible. We emit an
// empty array for the sake of backward compatibility.
@@ -2433,7 +2448,11 @@ string Master::Http::SLAVES_HELP()
"",
"This endpoint shows information about the agents which are registered",
"in this master or recovered from registry, formatted as a JSON",
- "object."),
+ "object.",
+ "",
+ "Query parameters:",
+ "> slave_id=VALUE The ID of the slave returned "
+ "(when no slave_id is specified, all slaves will be returned)."),
AUTHENTICATION(true));
}
@@ -2447,26 +2466,28 @@ Future<Response> Master::Http::slaves(
return redirect(request);
}
- // Retrieve `ObjectApprover`s for authorizing roles.
- Future<Owned<ObjectApprover>> rolesApprover;
-
- if (master->authorizer.isSome()) {
- Option<authorization::Subject> subject = createSubject(principal);
-
- rolesApprover = master->authorizer.get()->getObjectApprover(
- subject, authorization::VIEW_ROLE);
- } else {
- rolesApprover = Owned<ObjectApprover>(new AcceptingObjectApprover());
- }
+ Future<Owned<AuthorizationAcceptor>> authorizeRole =
+ AuthorizationAcceptor::create(
+ principal, master->authorizer, authorization::VIEW_ROLE);
+ Future<IDAcceptor<SlaveID>> selectSlaveId =
+ IDAcceptor<SlaveID>(request.url.query.get("slave_id"));
Master* master = this->master;
Option<string> jsonp = request.url.query.get("jsonp");
- return rolesApprover.then(defer(master->self(),
- [master, jsonp](const Owned<ObjectApprover>& rolesApprover)
+ return collect(authorizeRole, selectSlaveId)
+ .then(defer(master->self(),
+ [master, jsonp](const tuple<Owned<AuthorizationAcceptor>,
+ IDAcceptor<SlaveID>>& acceptors)
-> Future<Response> {
- return OK(jsonify(SlavesWriter(master->slaves, rolesApprover)), jsonp);
- }));
+ Owned<AuthorizationAcceptor> authorizeRole;
+ IDAcceptor<SlaveID> selectSlaveId;
+ tie(authorizeRole, selectSlaveId) = acceptors;
+
+ return OK(
+ jsonify(SlavesWriter(master->slaves, authorizeRole, selectSlaveId)),
+ jsonp);
+ }));
}
@@ -2745,66 +2766,49 @@ Future<Response> Master::Http::state(
return redirect(request);
}
- // Retrieve `ObjectApprover`s for authorizing frameworks and tasks.
- Future<Owned<ObjectApprover>> rolesApprover;
- Future<Owned<ObjectApprover>> frameworksApprover;
- Future<Owned<ObjectApprover>> tasksApprover;
- Future<Owned<ObjectApprover>> executorsApprover;
- Future<Owned<ObjectApprover>> flagsApprover;
-
- if (master->authorizer.isSome()) {
- Option<authorization::Subject> subject = createSubject(principal);
-
- rolesApprover = master->authorizer.get()->getObjectApprover(
- subject, authorization::VIEW_ROLE);
-
- frameworksApprover = master->authorizer.get()->getObjectApprover(
- subject, authorization::VIEW_FRAMEWORK);
-
- tasksApprover = master->authorizer.get()->getObjectApprover(
- subject, authorization::VIEW_TASK);
-
- executorsApprover = master->authorizer.get()->getObjectApprover(
- subject, authorization::VIEW_EXECUTOR);
-
- flagsApprover = master->authorizer.get()->getObjectApprover(
- subject, authorization::VIEW_FLAGS);
- } else {
- rolesApprover = Owned<ObjectApprover>(new AcceptingObjectApprover());
- frameworksApprover = Owned<ObjectApprover>(new AcceptingObjectApprover());
- tasksApprover = Owned<ObjectApprover>(new AcceptingObjectApprover());
- executorsApprover = Owned<ObjectApprover>(new AcceptingObjectApprover());
- flagsApprover = Owned<ObjectApprover>(new AcceptingObjectApprover());
- }
+ Future<Owned<AuthorizationAcceptor>> authorizeRole =
+ AuthorizationAcceptor::create(
+ principal, master->authorizer, authorization::VIEW_ROLE);
+ Future<Owned<AuthorizationAcceptor>> authorizeFrameworkInfo =
+ AuthorizationAcceptor::create(
+ principal, master->authorizer, authorization::VIEW_FRAMEWORK);
+ Future<Owned<AuthorizationAcceptor>> authorizeTask =
+ AuthorizationAcceptor::create(
+ principal, master->authorizer, authorization::VIEW_TASK);
+ Future<Owned<AuthorizationAcceptor>> authorizeExecutorInfo =
+ AuthorizationAcceptor::create(
+ principal, master->authorizer, authorization::VIEW_EXECUTOR);
+ Future<Owned<AuthorizationAcceptor>> authorizeFlags =
+ AuthorizationAcceptor::create(
+ principal, master->authorizer, authorization::VIEW_FLAGS);
return collect(
- rolesApprover,
- frameworksApprover,
- tasksApprover,
- executorsApprover,
- flagsApprover)
+ authorizeRole,
+ authorizeFrameworkInfo,
+ authorizeTask,
+ authorizeExecutorInfo,
+ authorizeFlags)
.then(defer(
master->self(),
- [this, request](const tuple<Owned<ObjectApprover>,
- Owned<ObjectApprover>,
- Owned<ObjectApprover>,
- Owned<ObjectApprover>,
- Owned<ObjectApprover>>& approvers)
+ [this, request](const tuple<Owned<AuthorizationAcceptor>,
+ Owned<AuthorizationAcceptor>,
+ Owned<AuthorizationAcceptor>,
+ Owned<AuthorizationAcceptor>,
+ Owned<AuthorizationAcceptor>>& acceptors)
-> Response {
// This lambda is consumed before the outer lambda
// returns, hence capture by reference is fine here.
- auto state = [this, &approvers](JSON::ObjectWriter* writer) {
- // Get approver from tuple.
- Owned<ObjectApprover> rolesApprover;
- Owned<ObjectApprover> frameworksApprover;
- Owned<ObjectApprover> tasksApprover;
- Owned<ObjectApprover> executorsApprover;
- Owned<ObjectApprover> flagsApprover;
- tie(rolesApprover,
- frameworksApprover,
- tasksApprover,
- executorsApprover,
- flagsApprover) = approvers;
+ auto state = [this, &acceptors](JSON::ObjectWriter* writer) {
+ Owned<AuthorizationAcceptor> authorizeRole;
+ Owned<AuthorizationAcceptor> authorizeFrameworkInfo;
+ Owned<AuthorizationAcceptor> authorizeTask;
+ Owned<AuthorizationAcceptor> authorizeExecutorInfo;
+ Owned<AuthorizationAcceptor> authorizeFlags;
+ tie(authorizeRole,
+ authorizeFrameworkInfo,
+ authorizeTask,
+ authorizeExecutorInfo,
+ authorizeFlags) = acceptors;
writer->field("version", MESOS_VERSION);
@@ -2851,7 +2855,7 @@ Future<Response> Master::Http::state(
});
}
- if (approveViewFlags(flagsApprover)) {
+ if (authorizeFlags->accept()) {
if (master->flags.cluster.isSome()) {
writer->field("cluster", master->flags.cluster.get());
}
@@ -2877,9 +2881,9 @@ Future<Response> Master::Http::state(
// Model all of the registered slaves.
writer->field("slaves",
- [this, rolesApprover](JSON::ArrayWriter* writer) {
+ [this, &authorizeRole](JSON::ArrayWriter* writer) {
foreachvalue (Slave* slave, master->slaves.registered) {
- writer->element(SlaveWriter(*slave, rolesApprover));
+ writer->element(SlaveWriter(*slave, authorizeRole));
}
});
@@ -2895,43 +2899,49 @@ Future<Response> Master::Http::state(
// Model all of the frameworks.
writer->field(
"frameworks",
- [this, &frameworksApprover, &executorsApprover, &tasksApprover](
- JSON::ArrayWriter* writer) {
- foreachvalue (
- Framework* framework,
- master->frameworks.registered) {
- // Skip unauthorized frameworks.
- if (!approveViewFrameworkInfo(
- frameworksApprover, framework->info)) {
- continue;
- }
+ [this,
+ &authorizeFrameworkInfo,
+ &authorizeTask,
+ &authorizeExecutorInfo](JSON::ArrayWriter* writer) {
+ foreachvalue (
+ Framework* framework,
+ master->frameworks.registered) {
+ // Skip unauthorized frameworks.
+ if (!authorizeFrameworkInfo->accept(framework->info)) {
+ continue;
+ }
- auto frameworkWriter = FullFrameworkWriter(
- tasksApprover, executorsApprover, framework);
+ auto frameworkWriter = FullFrameworkWriter(
+ authorizeTask,
+ authorizeExecutorInfo,
+ framework);
- writer->element(frameworkWriter);
- }
- });
+ writer->element(frameworkWriter);
+ }
+ });
// Model all of the completed frameworks.
writer->field(
"completed_frameworks",
- [this, &frameworksApprover, &executorsApprover, &tasksApprover](
- JSON::ArrayWriter* writer) {
- foreachvalue (const Owned<Framework>& framework,
- master->frameworks.completed) {
- // Skip unauthorized frameworks.
- if (!approveViewFrameworkInfo(
- frameworksApprover, framework->info)) {
- continue;
- }
+ [this,
+ &authorizeFrameworkInfo,
+ &authorizeTask,
+ &authorizeExecutorInfo](JSON::ArrayWriter* writer) {
+ foreachvalue (const Owned<Framework>& framework,
+ master->frameworks.completed) {
+ // Skip unauthorized frameworks.
+ if (!authorizeFrameworkInfo->accept(framework->info)) {
+ continue;
+ }
- auto frameworkWriter = FullFrameworkWriter(
- tasksApprover, executorsApprover, framework.get());
+ auto frameworkWriter = FullFrameworkWriter(
+ authorizeTask,
+ authorizeExecutorInfo,
+ framework.get());
- writer->element(frameworkWriter);
- }
- });
+ writer->element(frameworkWriter);
+ }
+ });
// Orphan tasks are no longer possible. We emit an empty array
// for the sake of backward compatibility.
@@ -3218,31 +3228,22 @@ Future<Response> Master::Http::stateSummary(
return redirect(request);
}
- Future<Owned<ObjectApprover>> rolesApprover;
- Future<Owned<ObjectApprover>> frameworksApprover;
-
- if (master->authorizer.isSome()) {
- Option<authorization::Subject> subject = createSubject(principal);
-
- rolesApprover = master->authorizer.get()->getObjectApprover(
- subject, authorization::VIEW_ROLE);
- frameworksApprover = master->authorizer.get()->getObjectApprover(
- subject, authorization::VIEW_FRAMEWORK);
- } else {
- rolesApprover = Owned<ObjectApprover>(new AcceptingObjectApprover());
- frameworksApprover = Owned<ObjectApprover>(new AcceptingObjectApprover());
- }
+ Future<Owned<AuthorizationAcceptor>> authorizeRole =
+ AuthorizationAcceptor::create(
+ principal, master->authorizer, authorization::VIEW_ROLE);
+ Future<Owned<AuthorizationAcceptor>> authorizeFrameworkInfo =
+ AuthorizationAcceptor::create(
+ principal, master->authorizer, authorization::VIEW_FRAMEWORK);
- return collect(rolesApprover, frameworksApprover).then(defer(
+ return collect(authorizeRole, authorizeFrameworkInfo).then(defer(
master->self(),
- [this, request](const tuple<Owned<ObjectApprover>,
- Owned<ObjectApprover>>& approvers)
+ [this, request](const tuple<Owned<AuthorizationAcceptor>,
+ Owned<AuthorizationAcceptor>>& acceptors)
-> Response {
- auto stateSummary = [this, &approvers](JSON::ObjectWriter* writer) {
- Owned<ObjectApprover> rolesApprover;
- Owned<ObjectApprover> frameworksApprover;
- tie(rolesApprover,
- frameworksApprover) = approvers;
+ auto stateSummary = [this, &acceptors](JSON::ObjectWriter* writer) {
+ Owned<AuthorizationAcceptor> authorizeRole;
+ Owned<AuthorizationAcceptor> authorizeFrameworkInfo;
+ tie(authorizeRole, authorizeFrameworkInfo) = acceptors;
writer->field("hostname", master->info().hostname());
@@ -3271,14 +3272,14 @@ Future<Response> Master::Http::stateSummary(
[this,
&slaveFrameworkMapping,
&taskStateSummaries,
- &rolesApprover](JSON::ArrayWriter* writer) {
+ &authorizeRole](JSON::ArrayWriter* writer) {
foreachvalue (Slave* slave, master->slaves.registered) {
writer->element(
[&slave,
&slaveFrameworkMapping,
&taskStateSummaries,
- &rolesApprover](JSON::ObjectWriter* writer) {
- SlaveWriter slaveWriter(*slave, rolesApprover);
+ &authorizeRole](JSON::ObjectWriter* writer) {
+ SlaveWriter slaveWriter(*slave, authorizeRole);
slaveWriter(writer);
// Add the 'TaskState' summary for this slave.
@@ -3326,14 +3327,12 @@ Future<Response> Master::Http::stateSummary(
[this,
&slaveFrameworkMapping,
&taskStateSummaries,
- &frameworksApprover](JSON::ArrayWriter* writer) {
+ &authorizeFrameworkInfo](JSON::ArrayWriter* writer) {
foreachpair (const FrameworkID& frameworkId,
Framework* framework,
master->frameworks.registered) {
// Skip unauthorized frameworks.
- if (!approveViewFrameworkInfo(
- frameworksApprover,
- framework->info)) {
+ if (!authorizeFrameworkInfo->accept(framework->info)) {
continue;
}
http://git-wip-us.apache.org/repos/asf/mesos/blob/8363449c/src/slave/http.cpp
----------------------------------------------------------------------
diff --git a/src/slave/http.cpp b/src/slave/http.cpp
index 5824661..2d33f0b 100644
--- a/src/slave/http.cpp
+++ b/src/slave/http.cpp
@@ -2050,20 +2050,14 @@ Future<Response> Http::getContainers(
{
CHECK_EQ(mesos::agent::Call::GET_CONTAINERS, call.type());
- Future<Owned<ObjectApprover>> approver;
-
- if (slave->authorizer.isSome()) {
- Option<authorization::Subject> subject = createSubject(principal);
-
- approver = slave->authorizer.get()->getObjectApprover(
- subject, authorization::VIEW_CONTAINER);
- } else {
- approver = Owned<ObjectApprover>(new AcceptingObjectApprover());
- }
-
- return approver.then(defer(slave->self(), [this](
- const Owned<ObjectApprover>& approver) {
- return __containers(approver);
+ Future<Owned<AuthorizationAcceptor>> authorizeContainer =
+ AuthorizationAcceptor::create(
+ principal, slave->authorizer, authorization::VIEW_CONTAINER);
+
+ return authorizeContainer.then(defer(slave->self(),
+ [this](const Owned<AuthorizationAcceptor>& authorizeContainer) {
+ // Use an empty container ID filter.
+ return __containers(authorizeContainer, None());
})).then([acceptType](const Future<JSON::Array>& result)
-> Future<Response> {
if (!result.isReady()) {
@@ -2089,22 +2083,23 @@ Future<Response> Http::_containers(
const Request& request,
const Option<Principal>& principal) const
{
- Future<Owned<ObjectApprover>> approver;
-
- if (slave->authorizer.isSome()) {
- Option<authorization::Subject> subject = createSubject(principal);
-
- approver = slave->authorizer.get()->getObjectApprover(
- subject, authorization::VIEW_CONTAINER);
- } else {
- approver = Owned<ObjectApprover>(new AcceptingObjectApprover());
- }
+ Future<Owned<AuthorizationAcceptor>> authorizeContainer =
+ AuthorizationAcceptor::create(
+ principal, slave->authorizer, authorization::VIEW_CONTAINER);
+ Future<IDAcceptor<ContainerID>> selectContainerId =
+ IDAcceptor<ContainerID>(request.url.query.get("container_id"));
- return approver.then(defer(slave->self(), [this](
- const Owned<ObjectApprover>& approver) {
- return __containers(approver);
- }))
- .then([request](const Future<JSON::Array>& result) -> Future<Response> {
+ return collect(authorizeContainer, selectContainerId)
+ .then(defer(
+ slave->self(),
+ [this](const tuple<Owned<AuthorizationAcceptor>,
+ IDAcceptor<ContainerID>>& acceptors) {
+ Owned<AuthorizationAcceptor> authorizeContainer;
+ Option<IDAcceptor<ContainerID>> selectContainerId;
+ tie(authorizeContainer, selectContainerId) = acceptors;
+
+ return __containers(authorizeContainer, selectContainerId);
+ })).then([request](const Future<JSON::Array>& result) -> Future<Response> {
if (!result.isReady()) {
LOG(WARNING) << "Could not collect container status and statistics: "
<< (result.isFailed()
@@ -2118,12 +2113,13 @@ Future<Response> Http::_containers(
return process::http::OK(
result.get(), request.url.query.get("jsonp"));
- });
+ });
}
Future<JSON::Array> Http::__containers(
- Option<Owned<ObjectApprover>> approver) const
+ Owned<AuthorizationAcceptor> authorizeContainer,
+ Option<IDAcceptor<ContainerID>> selectContainerId) const
{
Owned<list<JSON::Object>> metadata(new list<JSON::Object>());
list<Future<ContainerStatus>> statusFutures;
@@ -2140,32 +2136,22 @@ Future<JSON::Array> Http::__containers(
const ExecutorInfo& info = executor->info;
const ContainerID& containerId = executor->containerId;
- Try<bool> authorized = true;
-
- if (approver.isSome()) {
- ObjectApprover::Object object(info, framework->info);
-
- authorized = approver.get()->approved(object);
-
- if (authorized.isError()) {
- LOG(WARNING) << "Error during ViewContainer authorization: "
- << authorized.error();
- authorized = false;
- }
+ if ((selectContainerId.isSome() &&
+ !selectContainerId->accept(containerId)) ||
+ !authorizeContainer->accept(info, framework->info)) {
+ continue;
}
- if (authorized.get()) {
- JSON::Object entry;
- entry.values["framework_id"] = info.framework_id().value();
- entry.values["executor_id"] = info.executor_id().value();
- entry.values["executor_name"] = info.name();
- entry.values["source"] = info.source();
- entry.values["container_id"] = containerId.value();
-
- metadata->push_back(entry);
- statusFutures.push_back(slave->containerizer->status(containerId));
- statsFutures.push_back(slave->containerizer->usage(containerId));
- }
+ JSON::Object entry;
+ entry.values["framework_id"] = info.framework_id().value();
+ entry.values["executor_id"] = info.executor_id().value();
+ entry.values["executor_name"] = info.name();
+ entry.values["source"] = info.source();
+ entry.values["container_id"] = containerId.value();
+
+ metadata->push_back(entry);
+ statusFutures.push_back(slave->containerizer->status(containerId));
+ statsFutures.push_back(slave->containerizer->usage(containerId));
}
}
http://git-wip-us.apache.org/repos/asf/mesos/blob/8363449c/src/slave/http.hpp
----------------------------------------------------------------------
diff --git a/src/slave/http.hpp b/src/slave/http.hpp
index ad412d4..44a95de 100644
--- a/src/slave/http.hpp
+++ b/src/slave/http.hpp
@@ -114,7 +114,8 @@ private:
// Helper function to collect containers status and resource statistics.
process::Future<JSON::Array> __containers(
- Option<process::Owned<ObjectApprover>> approver) const;
+ process::Owned<AuthorizationAcceptor> authorizeContainer,
+ Option<IDAcceptor<ContainerID>> selectContainerId) const;
// Helper routines for endpoint authorization.
Try<std::string> extractEndpoint(const process::http::URL& url) const;
[3/3] mesos git commit: Added test cases for /slaves, /containers,
/frameworks endpoints.
Posted by gr...@apache.org.
Added test cases for /slaves, /containers, /frameworks endpoints.
Added query parameter test cases for '/slaves' and '/frameworks' on
the master, and '/containers' on the agent.
Review: https://reviews.apache.org/r/60847/
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/916a5c9f
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/916a5c9f
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/916a5c9f
Branch: refs/heads/master
Commit: 916a5c9fdbc7619b7c9356c21afb83e043feef88
Parents: 8363449
Author: Quinn Leng <qu...@gmail.com>
Authored: Tue Jul 18 17:07:02 2017 -0700
Committer: Greg Mann <gr...@gmail.com>
Committed: Tue Jul 18 17:11:34 2017 -0700
----------------------------------------------------------------------
src/tests/master_tests.cpp | 317 +++++++++++++++++++++++++++++++++++++---
src/tests/slave_tests.cpp | 306 ++++++++++++++++++++++++++++----------
2 files changed, 526 insertions(+), 97 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/916a5c9f/src/tests/master_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/master_tests.cpp b/src/tests/master_tests.cpp
index 6e6461c..5742860 100644
--- a/src/tests/master_tests.cpp
+++ b/src/tests/master_tests.cpp
@@ -2505,6 +2505,125 @@ TEST_F(MasterTest, SlavesEndpointTwoSlaves)
}
+// Ensures that the '/slaves' endpoint returns the correct slave and it's in
+// the correct field of the response when provided with a slave ID query
+// parameter.
+TEST_F(MasterTest, SlavesEndpointQuerySlave)
+{
+ master::Flags masterFlags = CreateMasterFlags();
+
+ // Ensure that master can recover from the same work_dir.
+ masterFlags.registry = "replicated_log";
+ Try<Owned<cluster::Master>> master = StartMaster(masterFlags);
+ ASSERT_SOME(master);
+
+ Owned<MasterDetector> detector = master.get()->createDetector();
+
+ // Start two agents.
+
+ Future<SlaveRegisteredMessage> slave1RegisteredMessage =
+ FUTURE_PROTOBUF(SlaveRegisteredMessage(), master.get()->pid, _);
+
+ Try<Owned<cluster::Slave>> slave1 = StartSlave(detector.get());
+ ASSERT_SOME(slave1);
+
+ AWAIT_READY(slave1RegisteredMessage);
+
+ Future<SlaveRegisteredMessage> slave2RegisteredMessage =
+ FUTURE_PROTOBUF(
+ SlaveRegisteredMessage(),
+ master.get()->pid,
+ Not(slave1.get()->pid));
+
+ Try<Owned<cluster::Slave>> slave2 = StartSlave(detector.get());
+ ASSERT_SOME(slave2);
+
+ AWAIT_READY(slave2RegisteredMessage);
+
+ // Query the information about the first agent.
+ {
+ string slaveId = slave1RegisteredMessage->slave_id().value();
+
+ Future<Response> response = process::http::get(
+ master.get()->pid,
+ "slaves?slave_id=" + slaveId,
+ None(),
+ createBasicAuthHeaders(DEFAULT_CREDENTIAL));
+
+ AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
+ AWAIT_EXPECT_RESPONSE_HEADER_EQ(APPLICATION_JSON, "Content-Type", response);
+
+ const Try<JSON::Value> value = JSON::parse<JSON::Value>(response->body);
+
+ ASSERT_SOME(value);
+
+ Try<JSON::Object> object = value->as<JSON::Object>();
+
+ Result<JSON::Array> array = object->find<JSON::Array>("slaves");
+ ASSERT_SOME(array);
+ EXPECT_EQ(1u, array->values.size());
+
+ Try<JSON::Value> expected = JSON::parse(
+ "{"
+ "\"slaves\":"
+ "[{"
+ "\"id\":\"" + slaveId + "\""
+ "}]"
+ "}");
+
+ ASSERT_SOME(expected);
+
+ EXPECT_TRUE(value->contains(expected.get()));
+ }
+
+ // Stop agents while the master is down.
+ master->reset();
+ slave1.get()->terminate();
+ slave1->reset();
+ slave2.get()->terminate();
+ slave2->reset();
+
+ // Restart the master, now two agents should be in the 'recovered' state.
+ master = StartMaster(masterFlags);
+ ASSERT_SOME(master);
+
+ // Check if the second agent is in the 'recovered_slaves' field.
+ {
+ string slaveId = slave2RegisteredMessage->slave_id().value();
+
+ Future<Response> response = process::http::get(
+ master.get()->pid,
+ "slaves?slave_id=" + slaveId,
+ None(),
+ createBasicAuthHeaders(DEFAULT_CREDENTIAL));
+
+ AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
+ AWAIT_EXPECT_RESPONSE_HEADER_EQ(APPLICATION_JSON, "Content-Type", response);
+
+ const Try<JSON::Value> value = JSON::parse<JSON::Value>(response->body);
+
+ ASSERT_SOME(value);
+ Try<JSON::Object> object = value->as<JSON::Object>();
+
+ Result<JSON::Array> array = object->find<JSON::Array>("recovered_slaves");
+ ASSERT_SOME(array);
+ EXPECT_EQ(1u, array->values.size());
+
+ Try<JSON::Value> expected = JSON::parse(
+ "{"
+ "\"recovered_slaves\":"
+ "[{"
+ "\"id\":\"" + slaveId + "\""
+ "}]"
+ "}");
+
+ ASSERT_SOME(expected);
+
+ EXPECT_TRUE(value->contains(expected.get()));
+ }
+}
+
+
// This test ensures that when a slave is recovered from the registry
// but does not re-register with the master, it is marked unreachable
// in the registry, the framework is informed that the slave is lost,
@@ -5848,43 +5967,195 @@ TEST_F(MasterTest, FrameworksEndpointWithoutFrameworks)
}
-TEST_F(MasterTest, FrameworksEndpointOneFramework)
+// Ensures that the '/master/frameworks' endpoint returns the correct framework
+// when provided with a framework ID query parameter.
+TEST_F(MasterTest, FrameworksEndpointMultipleFrameworks)
{
Try<Owned<cluster::Master>> master = StartMaster();
ASSERT_SOME(master);
- FrameworkInfo framework = DEFAULT_FRAMEWORK_INFO;
+ // Start a slave to receive shutdown message when framework is terminated.
+ Owned<MasterDetector> detector = master.get()->createDetector();
+ Try<Owned<cluster::Slave>> slave = StartSlave(detector.get());
+ ASSERT_SOME(slave);
- MockScheduler sched;
- MesosSchedulerDriver driver(
- &sched, framework, master.get()->pid, DEFAULT_CREDENTIAL);
+ Future<RegisterSlaveMessage> registerSlaveMessage =
+ FUTURE_PROTOBUF(RegisterSlaveMessage(), _, _);
- Future<Nothing> registered;
- EXPECT_CALL(sched, registered(&driver, _, _))
- .WillOnce(FutureSatisfy(®istered));
+ AWAIT_READY(registerSlaveMessage);
- driver.start();
+ // Start two frameworks.
- AWAIT_READY(registered);
+ Future<FrameworkID> frameworkId1;
+ Future<FrameworkID> frameworkId2;
- Future<Response> response = process::http::get(
+ MockScheduler sched1;
+ MesosSchedulerDriver driver1(
+ &sched1,
+ DEFAULT_FRAMEWORK_INFO,
master.get()->pid,
- "frameworks",
- None(),
- createBasicAuthHeaders(DEFAULT_CREDENTIAL));
+ DEFAULT_CREDENTIAL);
- AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
- AWAIT_EXPECT_RESPONSE_HEADER_EQ(APPLICATION_JSON, "Content-Type", response);
+ EXPECT_CALL(sched1, registered(_, _, _))
+ .WillOnce(FutureArg<1>(&frameworkId1));
- Try<JSON::Object> parse = JSON::parse<JSON::Object>(response->body);
- ASSERT_SOME(parse);
+ // Ignore any incoming resource offers to the scheduler.
+ EXPECT_CALL(sched1, resourceOffers(_, _))
+ .WillRepeatedly(Return());
- Result<JSON::Array> array = parse->find<JSON::Array>("frameworks");
- ASSERT_SOME(array);
- EXPECT_EQ(1u, array->values.size());
+ driver1.start();
- driver.stop();
- driver.join();
+ MockScheduler sched2;
+ MesosSchedulerDriver driver2(
+ &sched2,
+ DEFAULT_FRAMEWORK_INFO,
+ master.get()->pid,
+ DEFAULT_CREDENTIAL);
+
+ EXPECT_CALL(sched2, registered(_, _, _))
+ .WillOnce(FutureArg<1>(&frameworkId2));
+
+ // Ignore any incoming resource offers to the scheduler.
+ EXPECT_CALL(sched2, resourceOffers(_, _))
+ .WillRepeatedly(Return());
+
+ driver2.start();
+
+ AWAIT_READY(frameworkId1);
+ AWAIT_READY(frameworkId2);
+
+ // Request with no query parameter.
+ {
+ Future<Response> response = process::http::get(
+ master.get()->pid,
+ "frameworks",
+ None(),
+ createBasicAuthHeaders(DEFAULT_CREDENTIAL));
+
+ AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
+ AWAIT_EXPECT_RESPONSE_HEADER_EQ(APPLICATION_JSON, "Content-Type", response);
+
+ Try<JSON::Value> value = JSON::parse<JSON::Value>(response->body);
+ ASSERT_SOME(value);
+
+ JSON::Object object = value->as<JSON::Object>();
+
+ Result<JSON::Array> array = object.find<JSON::Array>("frameworks");
+ ASSERT_SOME(array);
+ EXPECT_EQ(2u, array->values.size());
+
+ Try<JSON::Value> frameworkJson1 = JSON::parse(
+ "{"
+ "\"id\":\"" + frameworkId1->value() + "\","
+ "\"name\":\"default\""
+ "}");
+
+ Try<JSON::Value> frameworkJson2 = JSON::parse(
+ "{"
+ "\"id\":\"" + frameworkId2->value() + "\","
+ "\"name\":\"default\""
+ "}");
+
+ ASSERT_SOME(frameworkJson1);
+ ASSERT_SOME(frameworkJson2);
+
+ // Since frameworks are stored in a hashmap, there is no strict guarantee of
+ // their ordering when listed. For this reason, we test both possibilities.
+ if (array->values[0].contains(frameworkJson1.get())) {
+ ASSERT_TRUE(array->values[1].contains(frameworkJson2.get()));
+ } else {
+ ASSERT_TRUE(array->values[0].contains(frameworkJson2.get()));
+ ASSERT_TRUE(array->values[1].contains(frameworkJson1.get()));
+ }
+ }
+
+ // Query the first framework.
+ {
+ Future<Response> response = process::http::get(
+ master.get()->pid,
+ "frameworks?framework_id=" + frameworkId1->value(),
+ None(),
+ createBasicAuthHeaders(DEFAULT_CREDENTIAL));
+
+ AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
+ AWAIT_EXPECT_RESPONSE_HEADER_EQ(APPLICATION_JSON, "Content-Type", response);
+
+ Try<JSON::Value> value = JSON::parse<JSON::Value>(response->body);
+ ASSERT_SOME(value);
+
+ JSON::Object object = value->as<JSON::Object>();
+
+ Result<JSON::Array> array = object.find<JSON::Array>("frameworks");
+ ASSERT_SOME(array);
+ EXPECT_EQ(1u, array->values.size());
+
+ Try<JSON::Value> expected = JSON::parse(
+ "{"
+ "\"frameworks\":"
+ "[{"
+ "\"id\":\"" + frameworkId1->value() + "\","
+ "\"name\":\"default\""
+ "}]"
+ "}");
+
+ ASSERT_SOME(expected);
+
+ EXPECT_TRUE(value->contains(expected.get()));
+ }
+
+ // Expect a teardown call and a shutdown message to ensure that the
+ // master has marked the framework as completed.
+ Future<mesos::scheduler::Call> teardownCall = FUTURE_CALL(
+ mesos::scheduler::Call(), mesos::scheduler::Call::TEARDOWN, _, _);
+ Future<ShutdownFrameworkMessage> shutdownFrameworkMessage =
+ FUTURE_PROTOBUF(ShutdownFrameworkMessage(), _, _);
+
+ // Complete the first framework. As a result, it will appear in the response's
+ // 'completed_frameworks' field.
+ driver1.stop();
+ driver1.join();
+
+ AWAIT_READY(teardownCall);
+
+ AWAIT_READY(shutdownFrameworkMessage);
+
+ // Query the first framework.
+ {
+ Future<Response> response = process::http::get(
+ master.get()->pid,
+ "frameworks?framework_id=" + frameworkId1->value(),
+ None(),
+ createBasicAuthHeaders(DEFAULT_CREDENTIAL));
+
+ AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
+ AWAIT_EXPECT_RESPONSE_HEADER_EQ(APPLICATION_JSON, "Content-Type", response);
+
+ Try<JSON::Value> value = JSON::parse<JSON::Value>(response->body);
+ ASSERT_SOME(value);
+
+ JSON::Object object = value->as<JSON::Object>();
+
+ Result<JSON::Array> array =
+ object.find<JSON::Array>("completed_frameworks");
+ ASSERT_SOME(array);
+ EXPECT_EQ(1u, array->values.size());
+
+ Try<JSON::Value> expected = JSON::parse(
+ "{"
+ "\"completed_frameworks\":"
+ "[{"
+ "\"id\":\"" + frameworkId1->value() + "\","
+ "\"name\":\"default\""
+ "}]"
+ "}");
+
+ ASSERT_SOME(expected);
+
+ EXPECT_TRUE(value->contains(expected.get()));
+ }
+
+ driver2.stop();
+ driver2.join();
}
http://git-wip-us.apache.org/repos/asf/mesos/blob/916a5c9f/src/tests/slave_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/slave_tests.cpp b/src/tests/slave_tests.cpp
index 053a14d..e1cc96d 100644
--- a/src/tests/slave_tests.cpp
+++ b/src/tests/slave_tests.cpp
@@ -2358,30 +2358,39 @@ TEST_F(SlaveTest, ContainersEndpointNoExecutor)
// This is an end-to-end test that verifies that the slave returns the
-// correct container status and resource statistics based on the
-// currently running executors, and the values returned by the
-// '/containers' endpoint are as expected.
+// correct container status and resource statistics based on the currently
+// running executors, and ensures that '/containers' endpoint returns the
+// correct container when it is provided a container ID query parameter.
TEST_F(SlaveTest, ContainersEndpoint)
{
Try<Owned<cluster::Master>> master = StartMaster();
ASSERT_SOME(master);
- MockExecutor exec(DEFAULT_EXECUTOR_ID);
- TestContainerizer containerizer(&exec);
- StandaloneMasterDetector detector(master.get()->pid);
+ // Create two executors so that we can launch tasks in two separate
+ // containers.
+ ExecutorInfo executor1 = createExecutorInfo("executor-1", "exit 1");
+ ExecutorInfo executor2 = createExecutorInfo("executor-2", "exit 1");
- MockSlave slave(CreateSlaveFlags(), &detector, &containerizer);
- spawn(slave);
+ MockExecutor exec1(executor1.executor_id());
+ MockExecutor exec2(executor2.executor_id());
+
+ hashmap<ExecutorID, Executor*> execs;
+ execs[executor1.executor_id()] = &exec1;
+ execs[executor2.executor_id()] = &exec2;
+
+ TestContainerizer containerizer(execs);
+
+ Owned<MasterDetector> detector = master.get()->createDetector();
+ Try<Owned<cluster::Slave>> slave = StartSlave(detector.get(), &containerizer);
+ ASSERT_SOME(slave);
MockScheduler sched;
MesosSchedulerDriver driver(
&sched, DEFAULT_FRAMEWORK_INFO, master.get()->pid, DEFAULT_CREDENTIAL);
- EXPECT_CALL(sched, registered(_, _, _));
- EXPECT_CALL(exec, registered(_, _, _, _));
+ EXPECT_CALL(sched, registered(&driver, _, _));
Future<vector<Offer>> offers;
-
EXPECT_CALL(sched, resourceOffers(&driver, _))
.WillOnce(FutureArg<1>(&offers))
.WillRepeatedly(Return()); // Ignore subsequent offers.
@@ -2389,98 +2398,247 @@ TEST_F(SlaveTest, ContainersEndpoint)
driver.start();
AWAIT_READY(offers);
- EXPECT_NE(0u, offers->size());
+ ASSERT_NE(0u, offers->size());
- const Offer& offer = offers.get()[0];
+ // Launch two tasks, each under a different executor.
+ vector<TaskInfo> tasks;
- TaskInfo task = createTask(
- offer.slave_id(),
- Resources::parse("cpus:0.1;mem:32").get(),
- SLEEP_COMMAND(1000),
- exec.id);
+ TaskInfo task1;
+ {
+ task1.set_name("");
+ task1.mutable_task_id()->set_value("1");
+ task1.mutable_slave_id()->MergeFrom(offers->front().slave_id());
+ task1.mutable_resources()->MergeFrom(
+ Resources::parse("cpus:1;mem:512").get());
+ task1.mutable_executor()->MergeFrom(executor1);
+ tasks.push_back(task1);
+ }
- EXPECT_CALL(exec, launchTask(_, _))
- .WillOnce(SendStatusUpdateFromTask(TASK_RUNNING));
+ TaskInfo task2;
+ {
+ task2.set_name("");
+ task2.mutable_task_id()->set_value("2");
+ task2.mutable_slave_id()->MergeFrom(offers->front().slave_id());
+ task2.mutable_resources()->MergeFrom(
+ Resources::parse("cpus:1;mem:512").get());
+ task2.mutable_executor()->MergeFrom(executor2);
+ tasks.push_back(task2);
+ }
- Future<TaskStatus> status;
+ EXPECT_CALL(exec1, registered(_, _, _, _));
+
+ Future<TaskInfo> launchedTask1;
+ EXPECT_CALL(exec1, launchTask(_, _))
+ .WillOnce(DoAll(SendStatusUpdateFromTask(TASK_RUNNING),
+ FutureArg<1>(&launchedTask1)));
+
+ EXPECT_CALL(exec2, registered(_, _, _, _));
+
+ Future<TaskInfo> launchedTask2;
+ EXPECT_CALL(exec2, launchTask(_, _))
+ .WillOnce(DoAll(SendStatusUpdateFromTask(TASK_RUNNING),
+ FutureArg<1>(&launchedTask2)));
+
+ Future<TaskStatus> status1, status2;
EXPECT_CALL(sched, statusUpdate(&driver, _))
- .WillOnce(FutureArg<1>(&status));
+ .WillOnce(FutureArg<1>(&status1))
+ .WillOnce(FutureArg<1>(&status2));
- driver.launchTasks(offer.id(), {task});
+ driver.launchTasks(offers->front().id(), tasks);
- AWAIT_READY(status);
- EXPECT_EQ(TASK_RUNNING, status->state());
+ AWAIT_READY(launchedTask1);
+ EXPECT_EQ(task1.task_id(), launchedTask1->task_id());
+
+ AWAIT_READY(launchedTask2);
+ EXPECT_EQ(task2.task_id(), launchedTask2->task_id());
+
+ AWAIT_READY(status1);
+ EXPECT_EQ(TASK_RUNNING, status1->state());
+
+ AWAIT_READY(status2);
+ EXPECT_EQ(TASK_RUNNING, status2->state());
+
+ // Prepare container statistics.
+ ResourceStatistics statistics1;
+ statistics1.set_mem_limit_bytes(2048);
- ResourceStatistics statistics;
- statistics.set_mem_limit_bytes(2048);
+ ResourceStatistics statistics2;
+ statistics2.set_mem_limit_bytes(2048);
+ // Get the container ID and return simulated statistics.
+ Future<ContainerID> containerId1;
+ Future<ContainerID> containerId2;
+
+ // Will be called twice during the first request. We extract the assigned
+ // container IDs for use when requesting information on a single container.
EXPECT_CALL(containerizer, usage(_))
- .WillOnce(Return(statistics));
+ .WillOnce(DoAll(FutureArg<0>(&containerId1), Return(statistics1)))
+ .WillOnce(DoAll(FutureArg<0>(&containerId2), Return(statistics2)));
- ContainerStatus containerStatus;
+ // Construct the container statuses to be returned. Note that
+ // these container IDs will be different than the actual container
+ // IDs assigned by the agent, but creating them here allows us to
+ // easily confirm the output of '/containers'.
+ ContainerStatus containerStatus1;
+ ContainerStatus containerStatus2;
ContainerID parent;
- ContainerID child;
parent.set_value("parent");
- child.set_value("child");
- child.mutable_parent()->CopyFrom(parent);
- containerStatus.mutable_container_id()->CopyFrom(child);
- CgroupInfo* cgroupInfo = containerStatus.mutable_cgroup_info();
- CgroupInfo::NetCls* netCls = cgroupInfo->mutable_net_cls();
- netCls->set_classid(42);
+ {
+ ContainerID child;
+ child.set_value("child1");
+ child.mutable_parent()->CopyFrom(parent);
+ containerStatus1.mutable_container_id()->CopyFrom(child);
+
+ CgroupInfo* cgroupInfo = containerStatus1.mutable_cgroup_info();
+ CgroupInfo::NetCls* netCls = cgroupInfo->mutable_net_cls();
+ netCls->set_classid(42);
+
+ NetworkInfo* networkInfo = containerStatus1.add_network_infos();
+ NetworkInfo::IPAddress* ipAddr = networkInfo->add_ip_addresses();
+ ipAddr->set_ip_address("192.168.1.20");
+ }
- NetworkInfo* networkInfo = containerStatus.add_network_infos();
- NetworkInfo::IPAddress* ipAddr = networkInfo->add_ip_addresses();
- ipAddr->set_ip_address("192.168.1.20");
+ {
+ ContainerID child;
+ child.set_value("child2");
+ child.mutable_parent()->CopyFrom(parent);
+ containerStatus2.mutable_container_id()->CopyFrom(child);
+
+ CgroupInfo* cgroupInfo = containerStatus2.mutable_cgroup_info();
+ CgroupInfo::NetCls* netCls = cgroupInfo->mutable_net_cls();
+ netCls->set_classid(42);
+
+ NetworkInfo* networkInfo = containerStatus2.add_network_infos();
+ NetworkInfo::IPAddress* ipAddr = networkInfo->add_ip_addresses();
+ ipAddr->set_ip_address("192.168.1.21");
+ }
+ // Will be called twice during the first request.
EXPECT_CALL(containerizer, status(_))
- .WillOnce(Return(containerStatus));
+ .WillOnce(Return(containerStatus1))
+ .WillOnce(Return(containerStatus2));
- Future<Response> response = process::http::get(
- slave.self(),
- "containers",
- None(),
- createBasicAuthHeaders(DEFAULT_CREDENTIAL));
+ // Request information about all containers.
+ {
+ Future<Response> response = process::http::get(
+ slave.get()->pid,
+ "containers",
+ None(),
+ createBasicAuthHeaders(DEFAULT_CREDENTIAL));
- AWAIT_READY(response);
- AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
- AWAIT_EXPECT_RESPONSE_HEADER_EQ(APPLICATION_JSON, "Content-Type", response);
+ Try<JSON::Value> value = JSON::parse<JSON::Value>(response->body);
+ ASSERT_SOME(value);
+
+ JSON::Array array = value->as<JSON::Array>();
+
+ EXPECT_TRUE(array.values.size() == 2);
+
+ Try<JSON::Value> containerJson1 = JSON::parse(
+ "{"
+ "\"executor_name\":\"\","
+ "\"source\":\"\","
+ "\"statistics\":{"
+ "\"mem_limit_bytes\":2048"
+ "},"
+ "\"status\":{"
+ "\"container_id\":{"
+ "\"parent\":{\"value\":\"parent\"},"
+ "\"value\":\"child1\""
+ "},"
+ "\"cgroup_info\":{\"net_cls\":{\"classid\":42}},"
+ "\"network_infos\":[{"
+ "\"ip_addresses\":[{\"ip_address\":\"192.168.1.20\"}]"
+ "}]"
+ "}"
+ "}");
+
+ Try<JSON::Value> containerJson2 = JSON::parse(
+ "{"
+ "\"executor_name\":\"\","
+ "\"source\":\"\","
+ "\"statistics\":{"
+ "\"mem_limit_bytes\":2048"
+ "},"
+ "\"status\":{"
+ "\"container_id\":{"
+ "\"parent\":{\"value\":\"parent\"},"
+ "\"value\":\"child2\""
+ "},"
+ "\"cgroup_info\":{\"net_cls\":{\"classid\":42}},"
+ "\"network_infos\":[{"
+ "\"ip_addresses\":[{\"ip_address\":\"192.168.1.21\"}]"
+ "}]"
+ "}"
+ "}");
+
+ // Since containers are stored in a hashmap, there is no strict guarantee of
+ // their ordering when listed. For this reason, we test both possibilities.
+ if (array.values[0].contains(containerJson1.get())) {
+ ASSERT_TRUE(array.values[1].contains(containerJson2.get()));
+ } else {
+ ASSERT_TRUE(array.values[0].contains(containerJson2.get()));
+ ASSERT_TRUE(array.values[1].contains(containerJson1.get()));
+ }
+ }
- Try<JSON::Value> value = JSON::parse(response->body);
- ASSERT_SOME(value);
+ AWAIT_READY(containerId1);
+ AWAIT_READY(containerId2);
- Try<JSON::Value> expected = JSON::parse(
- "[{"
- "\"executor_id\":\"default\","
- "\"executor_name\":\"\","
- "\"source\":\"\","
- "\"statistics\":{"
- "\"mem_limit_bytes\":2048"
- "},"
- "\"status\":{"
+ // Will be called once during the second request.
+ EXPECT_CALL(containerizer, usage(_))
+ .WillOnce(Return(statistics1));
+
+ // Will be called once during the second request.
+ EXPECT_CALL(containerizer, status(_))
+ .WillOnce(Return(containerStatus1));
+
+ {
+ Future<Response> response = process::http::get(
+ slave.get()->pid,
+ "containers?container_id=" + containerId1->value(),
+ None(),
+ createBasicAuthHeaders(DEFAULT_CREDENTIAL));
+
+ Try<JSON::Value> value = JSON::parse<JSON::Value>(response->body);
+ ASSERT_SOME(value);
+
+ JSON::Array array = value->as<JSON::Array>();
+
+ EXPECT_TRUE(array.values.size() == 1);
+
+ Try<JSON::Value> expected = JSON::parse(
+ "[{"
+ "\"container_id\":\"" + containerId1->value() + "\","
+ "\"executor_name\":\"\","
+ "\"source\":\"\","
+ "\"statistics\":{"
+ "\"mem_limit_bytes\":2048"
+ "},"
+ "\"status\":{"
"\"container_id\":{"
"\"parent\":{\"value\":\"parent\"},"
- "\"value\":\"child\""
- "},"
- "\"cgroup_info\":{\"net_cls\":{\"classid\":42}},"
- "\"network_infos\":[{"
- "\"ip_addresses\":[{\"ip_address\":\"192.168.1.20\"}]"
- "}]"
- "}"
- "}]");
-
- ASSERT_SOME(expected);
- EXPECT_TRUE(value->contains(expected.get()));
+ "\"value\":\"child1\""
+ "},"
+ "\"cgroup_info\":{\"net_cls\":{\"classid\":42}},"
+ "\"network_infos\":[{"
+ "\"ip_addresses\":[{\"ip_address\":\"192.168.1.20\"}]"
+ "}]"
+ "}"
+ "}]");
+
+ ASSERT_SOME(expected);
+ EXPECT_TRUE(value->contains(expected.get()));
+ }
- EXPECT_CALL(exec, shutdown(_))
+ EXPECT_CALL(exec1, shutdown(_))
+ .Times(AtMost(1));
+ EXPECT_CALL(exec2, shutdown(_))
.Times(AtMost(1));
driver.stop();
driver.join();
-
- terminate(slave);
- wait(slave);
}