Maintenance Primitives: Added maintenance status endpoint. Endpoint: /maintenance/status Retrieves the maintenance status of machines.
Also adds a protobuf for the purpose of serialization. Review: https://reviews.apache.org/r/37364 Project: http://git-wip-us.apache.org/repos/asf/mesos/repo Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/09e4ba2f Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/09e4ba2f Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/09e4ba2f Branch: refs/heads/master Commit: 09e4ba2faf8eb8f4b82b640b641ec4199a11001d Parents: bf4ca54 Author: Joseph Wu <[email protected]> Authored: Sun Aug 30 13:56:45 2015 -0400 Committer: Joris Van Remoortere <[email protected]> Committed: Mon Aug 31 13:15:18 2015 -0400 ---------------------------------------------------------------------- include/mesos/maintenance/maintenance.proto | 10 ++ src/master/http.cpp | 41 +++++++ src/master/master.cpp | 6 + src/master/master.hpp | 5 + src/tests/master_maintenance_tests.cpp | 139 +++++++++++++++++++++++ 5 files changed, 201 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mesos/blob/09e4ba2f/include/mesos/maintenance/maintenance.proto ---------------------------------------------------------------------- diff --git a/include/mesos/maintenance/maintenance.proto b/include/mesos/maintenance/maintenance.proto index ba28d0f..ee01c5d 100644 --- a/include/mesos/maintenance/maintenance.proto +++ b/include/mesos/maintenance/maintenance.proto @@ -65,3 +65,13 @@ message Window { message Schedule { repeated Window windows = 1; } + + +/** + * Represents the maintenance status of each machine in the cluster. + * Corresponds to the `MachineInfo.Mode` enumeration. + */ +message ClusterStatus { + repeated MachineID draining_machines = 1; + repeated MachineID down_machines = 2; +} http://git-wip-us.apache.org/repos/asf/mesos/blob/09e4ba2f/src/master/http.cpp ---------------------------------------------------------------------- diff --git a/src/master/http.cpp b/src/master/http.cpp index 6fad959..94e97a2 100644 --- a/src/master/http.cpp +++ b/src/master/http.cpp @@ -1651,6 +1651,47 @@ Future<Response> Master::Http::machineUp(const Request& request) const } +// /master/maintenance/status endpoint help. +const string Master::Http::MAINTENANCE_STATUS_HELP = HELP( + TLDR( + "Retrieves the maintenance status of the cluster."), + USAGE( + "/master/maintenance/status"), + DESCRIPTION( + "Returns an object with one list of machines per machine mode.")); + + +// /master/maintenance/status endpoint handler. +Future<Response> Master::Http::maintenanceStatus(const Request& request) const +{ + if (request.method != "GET") { + return BadRequest("Expecting GET, got '" + request.method + "'"); + } + + // Unwrap the master's machine information into two arrays of machines. + mesos::maintenance::ClusterStatus status; + foreachkey (const MachineID& id, master->machineInfos) { + switch (master->machineInfos[id].mode()) { + case MachineInfo::DRAINING: { + status.add_draining_machines()->CopyFrom(id); + break; + } + case MachineInfo::DOWN: { + status.add_down_machines()->CopyFrom(id); + break; + } + // Currently, `UP` machines are not specifically tracked in the master. + case MachineInfo::UP: {} + default: { + break; + } + } + } + + return OK(JSON::Protobuf(status), request.query.get("jsonp")); +} + + Result<Credential> Master::Http::authenticate(const Request& request) const { // By default, assume everyone is authenticated if no credentials http://git-wip-us.apache.org/repos/asf/mesos/blob/09e4ba2f/src/master/master.cpp ---------------------------------------------------------------------- diff --git a/src/master/master.cpp b/src/master/master.cpp index cd1b386..56bcbcc 100644 --- a/src/master/master.cpp +++ b/src/master/master.cpp @@ -821,6 +821,12 @@ void Master::initialize() Http::log(request); return http.maintenanceSchedule(request); }); + route("/maintenance/status", + Http::MAINTENANCE_STATUS_HELP, + [http](const process::http::Request& request) { + Http::log(request); + return http.maintenanceStatus(request); + }); route("/machine/down", Http::MACHINE_DOWN_HELP, [http](const process::http::Request& request) { http://git-wip-us.apache.org/repos/asf/mesos/blob/09e4ba2f/src/master/master.hpp ---------------------------------------------------------------------- diff --git a/src/master/master.hpp b/src/master/master.hpp index 68be718..594dd25 100644 --- a/src/master/master.hpp +++ b/src/master/master.hpp @@ -836,6 +836,10 @@ private: process::Future<process::http::Response> maintenanceSchedule( const process::http::Request& request) const; + // /master/maintenance/status + process::Future<process::http::Response> maintenanceStatus( + const process::http::Request& request) const; + // /master/machine/down process::Future<process::http::Response> machineDown( const process::http::Request& request) const; @@ -855,6 +859,7 @@ private: const static std::string STATESUMMARY_HELP; const static std::string TASKS_HELP; const static std::string MAINTENANCE_SCHEDULE_HELP; + const static std::string MAINTENANCE_STATUS_HELP; const static std::string MACHINE_DOWN_HELP; const static std::string MACHINE_UP_HELP; http://git-wip-us.apache.org/repos/asf/mesos/blob/09e4ba2f/src/tests/master_maintenance_tests.cpp ---------------------------------------------------------------------- diff --git a/src/tests/master_maintenance_tests.cpp b/src/tests/master_maintenance_tests.cpp index c3acd7a..fb8dca3 100644 --- a/src/tests/master_maintenance_tests.cpp +++ b/src/tests/master_maintenance_tests.cpp @@ -239,6 +239,58 @@ TEST_F(MasterMaintenanceTest, UpdateSchedule) } +// Tries to remove deactivated machines from the schedule. +TEST_F(MasterMaintenanceTest, FailToUnscheduleDeactivatedMachines) +{ + // Set up a master. + Try<PID<Master>> master = StartMaster(); + ASSERT_SOME(master); + + // Schedule two machines. + maintenance::Schedule schedule = createSchedule( + {createWindow({machine1, machine2}, unavailability)}); + + Future<Response> response = process::http::post( + master.get(), + "maintenance/schedule", + headers, + stringify(JSON::Protobuf(schedule))); + + AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response); + + // Deactivate machine1. + MachineIDs machines = createMachineList({machine1}); + response = process::http::post( + master.get(), + "machine/down", + headers, + stringify(JSON::Protobuf(machines))); + + AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response); + + // Try to unschedule machine1, by posting a schedule without it. + schedule = createSchedule({createWindow({machine2}, unavailability)}); + + response = process::http::post( + master.get(), + "maintenance/schedule", + headers, + stringify(JSON::Protobuf(schedule))); + + AWAIT_EXPECT_RESPONSE_STATUS_EQ(BadRequest().status, response); + + // Reactivate machine1. + machines = createMachineList({machine1}); + response = process::http::post( + master.get(), + "machine/up", + headers, + stringify(JSON::Protobuf(machines))); + + AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response); +} + + // Posts valid and invalid machines to the maintenance start endpoint. TEST_F(MasterMaintenanceTest, BringDownMachines) { @@ -448,6 +500,93 @@ TEST_F(MasterMaintenanceTest, BringUpMachines) ASSERT_EQ(0, masterSchedule.get().windows().size()); } + +// Queries for machine statuses in between maintenance mode transitions. +TEST_F(MasterMaintenanceTest, MachineStatus) +{ + // Set up a master. + Try<PID<Master>> master = StartMaster(); + ASSERT_SOME(master); + + // Try to stop maintenance on an unscheduled machine. + maintenance::Schedule schedule = createSchedule( + {createWindow({machine1, machine2}, unavailability)}); + + Future<Response> response = process::http::post( + master.get(), + "maintenance/schedule", + headers, + stringify(JSON::Protobuf(schedule))); + + AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response); + + // Get the maintenance statuses. + response = process::http::get(master.get(), "maintenance/status"); + AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response); + + // Check that both machines are draining. + Try<JSON::Object> statuses_ = + JSON::parse<JSON::Object>(response.get().body); + + ASSERT_SOME(statuses_); + Try<maintenance::ClusterStatus> statuses = + ::protobuf::parse<maintenance::ClusterStatus>(statuses_.get()); + + ASSERT_SOME(statuses); + ASSERT_EQ(2, statuses.get().draining_machines().size()); + ASSERT_EQ(0, statuses.get().down_machines().size()); + + // Deactivate machine1. + MachineIDs machines = createMachineList({machine1}); + response = process::http::post( + master.get(), + "machine/down", + headers, + stringify(JSON::Protobuf(machines))); + + AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response); + + // Get the maintenance statuses. + response = process::http::get(master.get(), "maintenance/status"); + AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response); + + // Check one machine is deactivated. + statuses_ = JSON::parse<JSON::Object>(response.get().body); + + ASSERT_SOME(statuses_); + statuses = ::protobuf::parse<maintenance::ClusterStatus>(statuses_.get()); + + ASSERT_SOME(statuses); + ASSERT_EQ(1, statuses.get().draining_machines().size()); + ASSERT_EQ(1, statuses.get().down_machines().size()); + ASSERT_EQ("Machine1", statuses.get().down_machines(0).hostname()); + + // Reactivate machine1. + machines = createMachineList({machine1}); + response = process::http::post( + master.get(), + "machine/up", + headers, + stringify(JSON::Protobuf(machines))); + + AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response); + + // Get the maintenance statuses. + response = process::http::get(master.get(), "maintenance/status"); + AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response); + + // Check that only one machine remains. + statuses_ = JSON::parse<JSON::Object>(response.get().body); + + ASSERT_SOME(statuses_); + statuses = ::protobuf::parse<maintenance::ClusterStatus>(statuses_.get()); + + ASSERT_SOME(statuses); + ASSERT_EQ(1, statuses.get().draining_machines().size()); + ASSERT_EQ(0, statuses.get().down_machines().size()); + ASSERT_EQ("0.0.0.2", statuses.get().draining_machines(0).ip()); +} + } // namespace tests { } // namespace internal { } // namespace mesos {
