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/01 00:33:34 UTC
mesos git commit: Added filtering to the '/tasks' endpoint.
Repository: mesos
Updated Branches:
refs/heads/master cb601b225 -> 0d277bb64
Added filtering to the '/tasks' endpoint.
Added filtering to the '/tasks' endpoint.
Review: https://reviews.apache.org/r/60107/
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/0d277bb6
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/0d277bb6
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/0d277bb6
Branch: refs/heads/master
Commit: 0d277bb64fa5a4d0b4f741daedf64095beab4773
Parents: cb601b2
Author: Quinn Leng <qu...@gmail.com>
Authored: Fri Jun 30 16:58:34 2017 -0700
Committer: Greg Mann <gr...@gmail.com>
Committed: Fri Jun 30 17:32:52 2017 -0700
----------------------------------------------------------------------
src/common/http.cpp | 126 ++++++++++++++++++++++++++
src/common/http.hpp | 85 ++++++++++++++++++
src/master/http.cpp | 191 ++++++++++++++++++++++------------------
src/tests/master_tests.cpp | 174 +++++++++++++++++++++++++++---------
4 files changed, 449 insertions(+), 127 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/0d277bb6/src/common/http.cpp
----------------------------------------------------------------------
diff --git a/src/common/http.cpp b/src/common/http.cpp
index 2f7718c..fdb591e 100644
--- a/src/common/http.cpp
+++ b/src/common/http.cpp
@@ -57,6 +57,8 @@ using std::set;
using std::string;
using std::vector;
+using process::Future;
+using process::Owned;
using process::Failure;
using process::Owned;
@@ -1123,4 +1125,128 @@ void logRequest(const process::http::Request& request)
: "");
}
+
+Future<Owned<AuthorizeFrameworkInfoAcceptor>>
+ AuthorizeFrameworkInfoAcceptor::create(
+ const Option<Principal>& principal,
+ const Option<Authorizer*>& authorizer)
+{
+ if (authorizer.isNone()) {
+ return Owned<AuthorizeFrameworkInfoAcceptor>(
+ new AuthorizeFrameworkInfoAcceptor(Owned<ObjectApprover>(
+ new AcceptingObjectApprover())));
+ }
+
+ const Option<authorization::Subject> subject =
+ authorization::createSubject(principal);
+
+ return authorizer.get()->getObjectApprover(
+ subject,
+ authorization::VIEW_FRAMEWORK)
+ .then([=](const Owned<ObjectApprover>& approver) {
+ return Owned<AuthorizeFrameworkInfoAcceptor>(
+ new AuthorizeFrameworkInfoAcceptor(approver));
+ });
+}
+
+
+Future<Owned<AuthorizeTaskAcceptor>> AuthorizeTaskAcceptor::create(
+ const Option<Principal>& principal,
+ const Option<Authorizer*>& authorizer)
+{
+ if (authorizer.isNone()) {
+ return Owned<AuthorizeTaskAcceptor>(
+ new AuthorizeTaskAcceptor(Owned<ObjectApprover>(
+ new AcceptingObjectApprover())));
+ }
+
+ const Option<authorization::Subject> subject =
+ authorization::createSubject(principal);
+
+ return authorizer.get()->getObjectApprover(
+ subject,
+ authorization::VIEW_TASK)
+ .then([=](const Owned<ObjectApprover>& approver) {
+ return Owned<AuthorizeTaskAcceptor>(
+ new AuthorizeTaskAcceptor(approver));
+ });
+}
+
+
+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 AuthorizeFrameworkInfoAcceptor::accept(const FrameworkInfo& frameworkInfo)
+{
+ ObjectApprover::Object object;
+ object.framework_info = &frameworkInfo;
+
+ Try<bool> approved = objectApprover->approved(object);
+ if (approved.isError()) {
+ LOG(WARNING) << "Error during FrameworkInfo authorization: "
+ << approved.error();
+ return false;
+ }
+
+ return approved.get();
+}
+
+
+bool AuthorizeTaskAcceptor::accept(
+ const Task& task,
+ const FrameworkInfo& frameworkInfo)
+{
+ ObjectApprover::Object object;
+ object.task = &task;
+ object.framework_info = &frameworkInfo;
+
+ Try<bool> approved = objectApprover->approved(object);
+
+ if (approved.isError()) {
+ LOG(WARNING) << "Error during Task authorization: " << approved.error();
+ return false;
+ }
+
+ return approved.get();
+}
+
+
+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/0d277bb6/src/common/http.hpp
----------------------------------------------------------------------
diff --git a/src/common/http.hpp b/src/common/http.hpp
index 93d6088..b7e4a8a 100644
--- a/src/common/http.hpp
+++ b/src/common/http.hpp
@@ -160,6 +160,91 @@ public:
};
+/**
+ * Determines which objects will be accepted when filtering results based on
+ * authorization or other criteria.
+ */
+class ObjectAcceptor
+{
+public:
+ virtual ~ObjectAcceptor() = default;
+};
+
+
+// Parent class for authorization-based acceptors.
+class AuthorizationAcceptor : public ObjectAcceptor
+{
+protected:
+ AuthorizationAcceptor(const process::Owned<ObjectApprover>& approver)
+ : objectApprover(approver) {}
+
+ const process::Owned<ObjectApprover> objectApprover;
+};
+
+
+class AuthorizeFrameworkInfoAcceptor : public AuthorizationAcceptor
+{
+public:
+ static process::Future<process::Owned<AuthorizeFrameworkInfoAcceptor>> create(
+ const Option<process::http::authentication::Principal>& principal,
+ const Option<Authorizer*>& authorizer);
+
+ bool accept(const FrameworkInfo& frameworkInfo);
+
+protected:
+ AuthorizeFrameworkInfoAcceptor(const process::Owned<ObjectApprover>& approver)
+ : AuthorizationAcceptor(approver) {}
+};
+
+
+class AuthorizeTaskAcceptor : public AuthorizationAcceptor
+{
+public:
+ static process::Future<process::Owned<AuthorizeTaskAcceptor>> create(
+ const Option<process::http::authentication::Principal>& principal,
+ const Option<Authorizer*>& authorizer);
+
+ bool accept(
+ const Task& task,
+ const FrameworkInfo& frameworkInfo);
+
+protected:
+ AuthorizeTaskAcceptor(const process::Owned<ObjectApprover>& approver)
+ : AuthorizationAcceptor(approver) {}
+};
+
+
+/**
+ * Filtering results based on framework ID. When no framework ID is specified
+ * it will accept all inputs.
+ */
+class FrameworkIDAcceptor : public ObjectAcceptor
+{
+public:
+ FrameworkIDAcceptor(const Option<std::string>& _frameworkId);
+ bool accept(const FrameworkID& frameworkId);
+
+protected:
+ Option<FrameworkID> frameworkId;
+};
+
+
+/**
+ * Filtering results based on task ID. When no task ID is specified
+ * it will accept all inputs.
+ */
+class TaskIDAcceptor : public ObjectAcceptor
+{
+public:
+ TaskIDAcceptor(const Option<std::string>& _taskId);
+
+ bool accept(const TaskID& taskId);
+
+protected:
+ Option<TaskID> taskId;
+};
+
+
bool approveViewFrameworkInfo(
const process::Owned<ObjectApprover>& frameworksApprover,
const FrameworkInfo& frameworkInfo);
http://git-wip-us.apache.org/repos/asf/mesos/blob/0d277bb6/src/master/http.cpp
----------------------------------------------------------------------
diff --git a/src/master/http.cpp b/src/master/http.cpp
index 4dd43fd..64b7cdd 100644
--- a/src/master/http.cpp
+++ b/src/master/http.cpp
@@ -3755,11 +3755,15 @@ string Master::Http::TASKS_HELP()
"",
"Query parameters:",
"",
+ "> framework_id=VALUE Only return tasks belonging to the "
+ "framework with this ID.",
"> limit=VALUE Maximum number of tasks returned "
"(default is " + stringify(TASK_LIMIT) + ").",
"> offset=VALUE Starts task list at offset.",
"> order=(asc|desc) Ascending or descending sort order "
- "(default is descending)."
+ "(default is descending).",
+ "> task_id=VALUE Only return tasks with this ID "
+ "(should be used together with parameter 'framework_id')."
""),
AUTHENTICATION(true),
AUTHORIZATION(
@@ -3798,106 +3802,121 @@ Future<Response> Master::Http::tasks(
Option<string> order = request.url.query.get("order");
string _order = order.isSome() && (order.get() == "asc") ? "asc" : "des";
- // Retrieve Approvers for authorizing frameworks and tasks.
- Future<Owned<ObjectApprover>> frameworksApprover;
- Future<Owned<ObjectApprover>> tasksApprover;
- if (master->authorizer.isSome()) {
- Option<authorization::Subject> subject = createSubject(principal);
+ Future<Owned<AuthorizeFrameworkInfoAcceptor>> authorizeFrameworkInfo =
+ AuthorizeFrameworkInfoAcceptor::create(principal, master->authorizer);
+ Future<Owned<AuthorizeTaskAcceptor>> authorizeTask =
+ AuthorizeTaskAcceptor::create(principal, master->authorizer);
+ 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")));
- frameworksApprover = master->authorizer.get()->getObjectApprover(
- subject, authorization::VIEW_FRAMEWORK);
+ return collect(
+ authorizeFrameworkInfo,
+ authorizeTask,
+ selectFrameworkId,
+ selectTaskId)
+ .then(defer(
+ master->self(),
+ [=](const tuple<Owned<AuthorizeFrameworkInfoAcceptor>,
+ Owned<AuthorizeTaskAcceptor>,
+ Owned<FrameworkIDAcceptor>,
+ Owned<TaskIDAcceptor>>& acceptors)-> Future<Response> {
+ Owned<AuthorizeFrameworkInfoAcceptor> authorizeFrameworkInfo;
+ Owned<AuthorizeTaskAcceptor> authorizeTask;
+ Owned<FrameworkIDAcceptor> selectFrameworkId;
+ Owned<TaskIDAcceptor> selectTaskId;
+ tie(authorizeFrameworkInfo,
+ authorizeTask,
+ selectFrameworkId,
+ selectTaskId) = acceptors;
+
+ // Construct framework list with both active and completed frameworks.
+ vector<const Framework*> frameworks;
+ foreachvalue (Framework* framework, master->frameworks.registered) {
+ // Skip unauthorized frameworks or frameworks without matching
+ // framework ID.
+ if (!selectFrameworkId->accept(framework->id()) ||
+ !authorizeFrameworkInfo->accept(framework->info)) {
+ continue;
+ }
- tasksApprover = master->authorizer.get()->getObjectApprover(
- subject, authorization::VIEW_TASK);
- } else {
- frameworksApprover = Owned<ObjectApprover>(new AcceptingObjectApprover());
- tasksApprover = Owned<ObjectApprover>(new AcceptingObjectApprover());
- }
+ frameworks.push_back(framework);
+ }
- return collect(frameworksApprover, tasksApprover)
- .then(defer(master->self(),
- [=](const tuple<Owned<ObjectApprover>,
- Owned<ObjectApprover>>& approvers)
- -> Future<Response> {
- // Get approver from tuple.
- Owned<ObjectApprover> frameworksApprover;
- Owned<ObjectApprover> tasksApprover;
- tie(frameworksApprover, tasksApprover) = approvers;
+ foreachvalue (const Owned<Framework>& framework,
+ master->frameworks.completed) {
+ // Skip unauthorized frameworks or frameworks without matching
+ // framework ID.
+ if (!selectFrameworkId->accept(framework->id()) ||
+ !authorizeFrameworkInfo->accept(framework->info)) {
+ continue;
+ }
- // Construct framework list with both active and completed frameworks.
- vector<const Framework*> frameworks;
- foreachvalue (Framework* framework, master->frameworks.registered) {
- // Skip unauthorized frameworks.
- if (!approveViewFrameworkInfo(frameworksApprover, framework->info)) {
- continue;
- }
+ frameworks.push_back(framework.get());
+ }
- frameworks.push_back(framework);
- }
+ // Construct task list with both running,
+ // completed and unreachable tasks.
+ vector<const Task*> tasks;
+ foreach (const Framework* framework, frameworks) {
+ foreachvalue (Task* task, framework->tasks) {
+ CHECK_NOTNULL(task);
+ // Skip unauthorized tasks or tasks without matching task ID.
+ if (!selectTaskId->accept(task->task_id()) ||
+ !authorizeTask->accept(*task, framework->info)) {
+ continue;
+ }
- foreachvalue (const Owned<Framework>& framework,
- master->frameworks.completed) {
- // Skip unauthorized frameworks.
- if (!approveViewFrameworkInfo(frameworksApprover, framework->info)) {
- continue;
- }
+ tasks.push_back(task);
+ }
- frameworks.push_back(framework.get());
- }
+ foreachvalue (
+ const Owned<Task>& task,
+ framework->unreachableTasks) {
+ // Skip unauthorized tasks or tasks without matching task ID.
+ if (!selectTaskId->accept(task.get()->task_id()) ||
+ !authorizeTask->accept(*task.get(), framework->info)) {
+ continue;
+ }
- // Construct task list with both running and finished tasks.
- vector<const Task*> tasks;
- foreach (const Framework* framework, frameworks) {
- foreachvalue (Task* task, framework->tasks) {
- CHECK_NOTNULL(task);
- // Skip unauthorized tasks.
- if (!approveViewTask(tasksApprover, *task, framework->info)) {
- continue;
- }
+ tasks.push_back(task.get());
+ }
- tasks.push_back(task);
- }
+ foreach (const Owned<Task>& task, framework->completedTasks) {
+ // Skip unauthorized tasks or tasks without matching task ID.
+ if (!selectTaskId->accept(task.get()->task_id()) ||
+ !authorizeTask->accept(*task.get(), framework->info)) {
+ continue;
+ }
- foreachvalue (const Owned<Task>& task, framework->unreachableTasks) {
- // Skip unauthorized tasks.
- if (!approveViewTask(tasksApprover, *task.get(), framework->info)) {
- continue;
+ tasks.push_back(task.get());
+ }
}
- tasks.push_back(task.get());
- }
-
- foreach (const Owned<Task>& task, framework->completedTasks) {
- // Skip unauthorized tasks.
- if (!approveViewTask(tasksApprover, *task.get(), framework->info)) {
- continue;
+ // Sort tasks by task status timestamp. Default order is descending.
+ // The earliest timestamp is chosen for comparison when
+ // multiple are present.
+ if (_order == "asc") {
+ sort(tasks.begin(), tasks.end(), TaskComparator::ascending);
+ } else {
+ sort(tasks.begin(), tasks.end(), TaskComparator::descending);
}
- tasks.push_back(task.get());
- }
- }
-
- // Sort tasks by task status timestamp. Default order is descending.
- // The earliest timestamp is chosen for comparison when
- // multiple are present.
- if (_order == "asc") {
- sort(tasks.begin(), tasks.end(), TaskComparator::ascending);
- } else {
- sort(tasks.begin(), tasks.end(), TaskComparator::descending);
- }
-
- auto tasksWriter = [&tasks, limit, offset](JSON::ObjectWriter* writer) {
- writer->field("tasks",
- [&tasks, limit, offset](JSON::ArrayWriter* writer) {
- // Collect 'limit' number of tasks starting from 'offset'.
- size_t end = std::min(offset + limit, tasks.size());
- for (size_t i = offset; i < end; i++) {
- writer->element(*tasks[i]);
- }
- });
- };
+ auto tasksWriter =
+ [&tasks, limit, offset](JSON::ObjectWriter* writer) {
+ writer->field("tasks",
+ [&tasks, limit, offset](JSON::ArrayWriter* writer) {
+ // Collect 'limit' number of tasks starting from 'offset'.
+ size_t end = std::min(offset + limit, tasks.size());
+ for (size_t i = offset; i < end; i++) {
+ writer->element(*tasks[i]);
+ }
+ });
+ };
- return OK(jsonify(tasksWriter), request.url.query.get("jsonp"));
+ return OK(jsonify(tasksWriter), request.url.query.get("jsonp"));
}));
}
http://git-wip-us.apache.org/repos/asf/mesos/blob/0d277bb6/src/tests/master_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/master_tests.cpp b/src/tests/master_tests.cpp
index cfb799f..d21194f 100644
--- a/src/tests/master_tests.cpp
+++ b/src/tests/master_tests.cpp
@@ -3420,7 +3420,8 @@ TEST_F(MasterTest, UnregisteredFrameworksAfterTearDown)
// This tests /tasks endpoint to return correct task information.
TEST_F(MasterTest, TasksEndpoint)
{
- Try<Owned<cluster::Master>> master = StartMaster();
+ master::Flags masterFlags = CreateMasterFlags();
+ Try<Owned<cluster::Master>> master = StartMaster(masterFlags);
ASSERT_SOME(master);
MockExecutor exec(DEFAULT_EXECUTOR_ID);
@@ -3434,67 +3435,158 @@ TEST_F(MasterTest, TasksEndpoint)
MesosSchedulerDriver driver(
&sched, DEFAULT_FRAMEWORK_INFO, master.get()->pid, DEFAULT_CREDENTIAL);
- EXPECT_CALL(sched, registered(&driver, _, _));
+ Future<FrameworkID> frameworkId;
+ EXPECT_CALL(sched, registered(&driver, _, _))
+ .WillOnce(FutureArg<1>(&frameworkId));
- Future<vector<Offer>> offers;
+ process::Queue<Offer> offers;
EXPECT_CALL(sched, resourceOffers(&driver, _))
- .WillOnce(FutureArg<1>(&offers))
- .WillRepeatedly(Return()); // Ignore subsequent offers.
+ .WillRepeatedly(EnqueueOffers(&offers));
driver.start();
- AWAIT_READY(offers);
- ASSERT_NE(0u, offers->size());
+ Future<Offer> offer = offers.get();
+ AWAIT_READY(offer);
- TaskInfo task;
- task.set_name("test");
- task.mutable_task_id()->set_value("1");
- task.mutable_slave_id()->MergeFrom(offers.get()[0].slave_id());
- task.mutable_resources()->MergeFrom(offers.get()[0].resources());
- task.mutable_executor()->MergeFrom(DEFAULT_EXECUTOR_INFO);
+ // Launch two tasks.
+ TaskInfo task1;
+ task1.set_name("test1");
+ task1.mutable_task_id()->set_value("1");
+ task1.mutable_slave_id()->MergeFrom(offer->slave_id());
+ task1.mutable_resources()->MergeFrom(
+ Resources::parse("cpus:0.1;mem:12").get());
+ task1.mutable_executor()->MergeFrom(DEFAULT_EXECUTOR_INFO);
+
+ TaskInfo task2;
+ task2.set_name("test2");
+ task2.mutable_task_id()->set_value("2");
+ task2.mutable_slave_id()->MergeFrom(offer->slave_id());
+ task2.mutable_resources()->MergeFrom(
+ Resources::parse("cpus:0.1;mem:12").get());
+ task2.mutable_executor()->MergeFrom(DEFAULT_EXECUTOR_INFO);
+
+ vector<TaskInfo> tasks;
+ tasks.push_back(task1);
+ tasks.push_back(task2);
EXPECT_CALL(exec, registered(_, _, _, _));
EXPECT_CALL(exec, launchTask(_, _))
+ .WillOnce(SendStatusUpdateFromTask(TASK_RUNNING))
.WillOnce(SendStatusUpdateFromTask(TASK_RUNNING));
- Future<TaskStatus> status;
+ Future<TaskStatus> status1, status2;
EXPECT_CALL(sched, statusUpdate(&driver, _))
- .WillOnce(FutureArg<1>(&status));
+ .WillOnce(FutureArg<1>(&status1))
+ .WillOnce(FutureArg<1>(&status2));
- driver.launchTasks(offers.get()[0].id(), {task});
+ driver.launchTasks(offer->id(), tasks);
- AWAIT_READY(status);
- EXPECT_EQ(TASK_RUNNING, status->state());
- EXPECT_TRUE(status->has_executor_id());
- EXPECT_EQ(exec.id, status->executor_id());
+ AWAIT_READY(status1);
+ EXPECT_EQ(TASK_RUNNING, status1->state());
+ EXPECT_TRUE(status1->has_executor_id());
+ EXPECT_EQ(exec.id, status1->executor_id());
- Future<Response> response = process::http::get(
- master.get()->pid,
- "tasks",
- None(),
- createBasicAuthHeaders(DEFAULT_CREDENTIAL));
+ AWAIT_READY(status2);
+ EXPECT_EQ(TASK_RUNNING, status2->state());
+ EXPECT_TRUE(status2->has_executor_id());
+ EXPECT_EQ(exec.id, status2->executor_id());
- AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
- AWAIT_EXPECT_RESPONSE_HEADER_EQ(APPLICATION_JSON, "Content-Type", response);
+ // Testing the '/master/tasks' endpoint without parameters,
+ // which returns information about all tasks.
+ {
+ Future<Response> response = process::http::get(
+ master.get()->pid,
+ "tasks",
+ None(),
+ createBasicAuthHeaders(DEFAULT_CREDENTIAL));
- Try<JSON::Value> value = JSON::parse<JSON::Value>(response->body);
- ASSERT_SOME(value);
+ AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
+ AWAIT_EXPECT_RESPONSE_HEADER_EQ(APPLICATION_JSON, "Content-Type", response);
- Try<JSON::Value> expected = JSON::parse(
- "{"
- "\"tasks\":"
- "[{"
- "\"executor_id\":\"default\","
- "\"id\":\"1\","
- "\"name\":\"test\","
- "\"state\":\"TASK_RUNNING\""
- "}]"
- "}");
+ Try<JSON::Value> value = JSON::parse<JSON::Value>(response->body);
+ ASSERT_SOME(value);
+
+ // Two possible orderings of the result.
+ Try<JSON::Value> expected1 = JSON::parse(
+ "{"
+ "\"tasks\":"
+ "[{"
+ "\"executor_id\":\"default\","
+ "\"framework_id\":\"" + frameworkId->value() + "\","
+ "\"id\":\"1\","
+ "\"name\":\"test1\","
+ "\"state\":\"TASK_RUNNING\""
+ "},{"
+ "\"executor_id\":\"default\","
+ "\"framework_id\":\"" + frameworkId->value() + "\","
+ "\"id\":\"2\","
+ "\"name\":\"test2\","
+ "\"state\":\"TASK_RUNNING\""
+ "}]"
+ "}");
+
+ Try<JSON::Value> expected2 = JSON::parse(
+ "{"
+ "\"tasks\":"
+ "[{"
+ "\"executor_id\":\"default\","
+ "\"framework_id\":\"" + frameworkId->value() + "\","
+ "\"id\":\"2\","
+ "\"name\":\"test2\","
+ "\"state\":\"TASK_RUNNING\""
+ "},{"
+ "\"executor_id\":\"default\","
+ "\"framework_id\":\"" + frameworkId->value() + "\","
+ "\"id\":\"1\","
+ "\"name\":\"test1\","
+ "\"state\":\"TASK_RUNNING\""
+ "}]"
+ "}");
+
+ ASSERT_SOME(expected1);
+ ASSERT_SOME(expected2);
+
+ EXPECT_TRUE(
+ value->contains(expected1.get()) ||
+ value->contains(expected2.get()));
+ }
- ASSERT_SOME(expected);
+ // Testing the query for a specific task.
+ {
+ Future<Response> response = process::http::get(
+ master.get()->pid,
+ "tasks?task_id=1;framework_id=" + frameworkId->value(),
+ None(),
+ createBasicAuthHeaders(DEFAULT_CREDENTIAL));
- EXPECT_TRUE(value->contains(expected.get()));
+ 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> taskArray = object.find<JSON::Array>("tasks");
+ ASSERT_SOME(taskArray);
+
+ EXPECT_TRUE(taskArray->values.size() == 1);
+
+ Try<JSON::Value> expected = JSON::parse(
+ "{"
+ "\"tasks\":"
+ "[{"
+ "\"executor_id\":\"default\","
+ "\"framework_id\":\"" + frameworkId->value() + "\","
+ "\"id\":\"1\","
+ "\"name\":\"test1\","
+ "\"state\":\"TASK_RUNNING\""
+ "}]"
+ "}");
+
+ ASSERT_SOME(expected);
+ EXPECT_TRUE(value->contains(expected.get()));
+ }
EXPECT_CALL(exec, shutdown(_))
.Times(AtMost(1));