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();
 

Reply via email to