Implemented operator API to grow and shrink persistent volume. These operator APIs is implemented as speculative for now, but we plan to convert them to non-speculative in the future.
Review: https://reviews.apache.org/r/66051/ Project: http://git-wip-us.apache.org/repos/asf/mesos/repo Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/e2d3112f Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/e2d3112f Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/e2d3112f Branch: refs/heads/master Commit: e2d3112f3aaa19f33319db2dad16b2076a2df046 Parents: d418f15 Author: Zhitao Li <zhitaoli...@gmail.com> Authored: Thu May 3 17:05:02 2018 -0700 Committer: Chun-Hung Hsiao <chhs...@mesosphere.io> Committed: Thu May 3 17:05:02 2018 -0700 ---------------------------------------------------------------------- src/master/http.cpp | 140 +++++++++++++++++++++++++++++++++++++++++ src/master/master.hpp | 10 +++ src/master/validation.cpp | 26 ++++++++ 3 files changed, 176 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mesos/blob/e2d3112f/src/master/http.cpp ---------------------------------------------------------------------- diff --git a/src/master/http.cpp b/src/master/http.cpp index 135ae43..77cf47a 100644 --- a/src/master/http.cpp +++ b/src/master/http.cpp @@ -773,6 +773,12 @@ Future<Response> Master::Http::api( case mesos::master::Call::DESTROY_VOLUMES: return destroyVolumes(call, principal, acceptType); + case mesos::master::Call::GROW_VOLUME: + return growVolume(call, principal, acceptType); + + case mesos::master::Call::SHRINK_VOLUME: + return shrinkVolume(call, principal, acceptType); + case mesos::master::Call::GET_MAINTENANCE_STATUS: return getMaintenanceStatus(call, principal, acceptType); @@ -1490,6 +1496,140 @@ Future<Response> Master::Http::destroyVolumes( } +Future<Response> Master::Http::growVolume( + const mesos::master::Call& call, + const Option<Principal>& principal, + ContentType /*contentType*/) const +{ + // TODO(greggomann): Remove this check once the `Principal` type is used in + // `ReservationInfo`, `DiskInfo`, and within the master's `principals` map. + // See MESOS-7202. + if (principal.isSome() && principal->value.isNone()) { + return Forbidden( + "The request's authenticated principal contains claims, but no value " + "string. The master currently requires that principals have a value"); + } + + CHECK_EQ(mesos::master::Call::GROW_VOLUME, call.type()); + CHECK(call.has_grow_volume()); + + // Only agent default resources are supported right now. + CHECK(call.grow_volume().has_slave_id()); + + const SlaveID& slaveId = call.grow_volume().slave_id(); + + Slave* slave = master->slaves.registered.get(slaveId); + if (slave == nullptr) { + return BadRequest("No agent found with specified ID"); + } + + // Create an operation. + Offer::Operation operation; + operation.set_type(Offer::Operation::GROW_VOLUME); + + operation.mutable_grow_volume()->mutable_volume()->CopyFrom( + call.grow_volume().volume()); + + operation.mutable_grow_volume()->mutable_addition()->CopyFrom( + call.grow_volume().addition()); + + Option<Error> error = validateAndUpgradeResources(&operation); + if (error.isSome()) { + return BadRequest(error->message); + } + + error = validation::operation::validate( + operation.grow_volume(), slave->capabilities); + + if (error.isSome()) { + return BadRequest( + "Invalid GROW_VOLUME operation on agent " + + stringify(*slave) + ": " + error->message); + } + + return master->authorizeResizeVolume( + operation.grow_volume().volume(), principal) + .then(defer(master->self(), [=](bool authorized) -> Future<Response> { + if (!authorized) { + return Forbidden(); + } + + // The `volume` and `addition` fields contain the resources required for + // this operation. + return _operation( + slaveId, + Resources(operation.grow_volume().volume()) + + Resources(operation.grow_volume().addition()), + operation); + })); +} + + +Future<Response> Master::Http::shrinkVolume( + const mesos::master::Call& call, + const Option<Principal>& principal, + ContentType /*contentType*/) const +{ + // TODO(greggomann): Remove this check once the `Principal` type is used in + // `ReservationInfo`, `DiskInfo`, and within the master's `principals` map. + // See MESOS-7202. + if (principal.isSome() && principal->value.isNone()) { + return Forbidden( + "The request's authenticated principal contains claims, but no value " + "string. The master currently requires that principals have a value"); + } + + CHECK_EQ(mesos::master::Call::SHRINK_VOLUME, call.type()); + CHECK(call.has_shrink_volume()); + + // Only persistent volumes are supported right now. + CHECK(call.shrink_volume().has_slave_id()); + + const SlaveID& slaveId = call.shrink_volume().slave_id(); + + Slave* slave = master->slaves.registered.get(slaveId); + if (slave == nullptr) { + return BadRequest("No agent found with specified ID"); + } + + // Create an operation. + Offer::Operation operation; + operation.set_type(Offer::Operation::SHRINK_VOLUME); + + operation.mutable_shrink_volume()->mutable_volume()->CopyFrom( + call.shrink_volume().volume()); + + operation.mutable_shrink_volume()->mutable_subtract()->CopyFrom( + call.shrink_volume().subtract()); + + Option<Error> error = validateAndUpgradeResources(&operation); + if (error.isSome()) { + return BadRequest(error->message); + } + + error = validation::operation::validate( + operation.shrink_volume(), slave->capabilities); + + if (error.isSome()) { + return BadRequest( + "Invalid SHRINK_VOLUME operation on agent " + + stringify(*slave) + ": " + error->message); + } + + return master->authorizeResizeVolume( + operation.shrink_volume().volume(), principal) + .then(defer(master->self(), [=](bool authorized) -> Future<Response> { + if (!authorized) { + return Forbidden(); + } + + // The `volume` field contains the resources required for this operation. + return _operation( + slaveId, operation.shrink_volume().volume(), operation); + })); +} + + string Master::Http::FRAMEWORKS_HELP() { return HELP( http://git-wip-us.apache.org/repos/asf/mesos/blob/e2d3112f/src/master/master.hpp ---------------------------------------------------------------------- diff --git a/src/master/master.hpp b/src/master/master.hpp index 270f60a..e4f7b45 100644 --- a/src/master/master.hpp +++ b/src/master/master.hpp @@ -1700,6 +1700,16 @@ private: const Option<process::http::authentication::Principal>& principal, ContentType contentType) const; + process::Future<process::http::Response> growVolume( + const mesos::master::Call& call, + const Option<process::http::authentication::Principal>& principal, + ContentType contentType) const; + + process::Future<process::http::Response> shrinkVolume( + const mesos::master::Call& call, + const Option<process::http::authentication::Principal>& principal, + ContentType contentType) const; + process::Future<process::http::Response> reserveResources( const mesos::master::Call& call, const Option<process::http::authentication::Principal>& principal, http://git-wip-us.apache.org/repos/asf/mesos/blob/e2d3112f/src/master/validation.cpp ---------------------------------------------------------------------- diff --git a/src/master/validation.cpp b/src/master/validation.cpp index 74ed171..0c1c924 100644 --- a/src/master/validation.cpp +++ b/src/master/validation.cpp @@ -190,6 +190,32 @@ Option<Error> validate( } return None(); + case mesos::master::Call::GROW_VOLUME: + if (!call.has_grow_volume()) { + return Error("Expecting 'grow_volume' to be present"); + } + + if (!call.grow_volume().has_slave_id()) { + return Error( + "Expecting 'agent_id' to be present; only agent default resources " + "are supported right now"); + } + + return None(); + + case mesos::master::Call::SHRINK_VOLUME: + if (!call.has_shrink_volume()) { + return Error("Expecting 'shrink_volume' to be present"); + } + + if (!call.shrink_volume().has_slave_id()) { + return Error( + "Expecting 'agent_id' to be present; only agent default resources " + "are supported right now"); + } + + return None(); + case mesos::master::Call::GET_MAINTENANCE_STATUS: return None();