You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by mp...@apache.org on 2017/06/17 09:18:52 UTC

[1/2] mesos git commit: Resources: Updated the validation logic.

Repository: mesos
Updated Branches:
  refs/heads/master dc679b2c3 -> 4f1653570


Resources: Updated the validation logic.

This patch updates `Resources::validate`, and `resource::validate`.

The `Resources::validate` function ensures that the resources are
either in the 'pre-' or 'post-' reservation-refinement formats,
but not both.

The `resource::validate` function takes an optional framework
capabilities and ensures that the resources format matches
the framework's RESERVATION_REFINEMENT capbility.

Review: https://reviews.apache.org/r/60013/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/fe6a3eb4
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/fe6a3eb4
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/fe6a3eb4

Branch: refs/heads/master
Commit: fe6a3eb48af793b5cf2e1308d9164cdec1c4875a
Parents: dc679b2
Author: Michael Park <mp...@apache.org>
Authored: Sat Jun 17 01:41:55 2017 -0700
Committer: Michael Park <mp...@apache.org>
Committed: Sat Jun 17 02:00:33 2017 -0700

----------------------------------------------------------------------
 src/common/resources.cpp  |  67 +++++++++++++++++++++++++--
 src/master/validation.cpp | 103 ++++++++++++++++++++++++++++++++++-------
 src/master/validation.hpp |  22 ++++++---
 src/v1/resources.cpp      |  67 +++++++++++++++++++++++++--
 4 files changed, 227 insertions(+), 32 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/fe6a3eb4/src/common/resources.cpp
----------------------------------------------------------------------
diff --git a/src/common/resources.cpp b/src/common/resources.cpp
index 05f20e9..7672938 100644
--- a/src/common/resources.cpp
+++ b/src/common/resources.cpp
@@ -512,8 +512,8 @@ static Option<Error> validateCommandLineResources(const Resources& resources)
           " not include a 'revocable' key in the resources JSON");
     } else if (Resources::isDynamicallyReserved(resource)) {
       return Error(
-          "Dynamic reservations cannot be specified at the command line; "
-          "do not include a 'reservation' key in the resources JSON");
+          "Dynamic reservations cannot be specified at the command line; do"
+          " not include a reservation with DYNAMIC type in the resources JSON");
     }
 
     if (nameTypes.contains(resource.name()) &&
@@ -835,10 +835,10 @@ Option<Error> Resources::validate(const Resource& resource)
     }
   }
 
-  // Checks for the invalid state of (role, reservation) pair.
-  if (resource.role() == "*" && resource.has_reservation()) {
+  if (resource.has_reservation() && resource.reservations_size() > 0) {
     return Error(
-        "Invalid reservation: role \"*\" cannot be dynamically reserved");
+        "Invalid reservation: only one of 'reservation' or 'reservations'"
+        " may be present");
   }
 
   // Check role name.
@@ -847,6 +847,63 @@ Option<Error> Resources::validate(const Resource& resource)
     return error;
   }
 
+  if (resource.has_reservation()) {
+    if (resource.reservation().has_type()) {
+      return Error(
+          "'Resource.ReservationInfo.type' must not be set for the"
+          " 'Resource.reservation' field");
+    }
+
+    if (resource.reservation().has_role()) {
+      return Error(
+          "'Resource.ReservationInfo.role' must not be set for the"
+          " 'Resource.reservation' field");
+    }
+
+    // Checks for the invalid state of (role, reservation) pair.
+    if (resource.role() == "*") {
+      return Error(
+          "Invalid reservation: role \"*\" cannot be dynamically reserved");
+    }
+  }
+
+  // Validate all of the roles in `reservations`.
+  foreach (
+      const Resource::ReservationInfo& reservation, resource.reservations()) {
+    Option<Error> error = roles::validate(reservation.role());
+    if (error.isSome()) {
+      return error;
+    }
+
+    if (reservation.role() == "*") {
+      return Error("Invalid reservation: role \"*\" cannot be reserved");
+    }
+  }
+
+  if (resource.reservations_size() > 0) {
+    string ancestor = resource.reservations(0).role();
+
+    for (int i = 1; i < resource.reservations_size(); ++i) {
+      const Resource::ReservationInfo& reservation = resource.reservations(i);
+
+      if (reservation.type() == Resource::ReservationInfo::STATIC) {
+        return Error(
+            "Invalid refined reservation: A refined reservation"
+            " cannot be STATIC");
+      }
+
+      const string& descendant = reservation.role();
+
+      if (!roles::isStrictSubroleOf(descendant, ancestor)) {
+        return Error(
+            "Invalid refined reservation: role '" + descendant + "'" +
+            " is not a refinement of '" + ancestor + "'");
+      }
+
+      ancestor = descendant;
+    }
+  }
+
   // Check that shareability is enabled for supported resource types.
   // For now, it is for persistent volumes only.
   // NOTE: We need to modify this once we extend shareability to other

http://git-wip-us.apache.org/repos/asf/mesos/blob/fe6a3eb4/src/master/validation.cpp
----------------------------------------------------------------------
diff --git a/src/master/validation.cpp b/src/master/validation.cpp
index 8bd8cd2..0164e67 100644
--- a/src/master/validation.cpp
+++ b/src/master/validation.cpp
@@ -598,6 +598,30 @@ Option<Error> validate(
 
 namespace resource {
 
+Option<Error> validateFormat(
+    const RepeatedPtrField<Resource>& resources,
+    const protobuf::framework::Capabilities& capabilities)
+{
+  foreach (const Resource& resource, resources) {
+    if (capabilities.reservationRefinement) {
+      if (resource.has_role() || resource.has_reservation()) {
+        return Error(
+            "A framework with the RESERVATION_REFINEMENT capability must"
+            " not have 'Resource::role' set, nor 'Resource::reservation'");
+      }
+    } else {
+      if (resource.reservations_size() > 0) {
+        return Error(
+            "A framework without the RESERVATION_REFINEMENT capability must"
+            " not have 'Resource::reservations' set");
+      }
+    }
+  }
+
+  return None();
+}
+
+
 // Validates that the `gpus` resource is not fractional.
 // We rely on scalar resources only having 3 digits of precision.
 Option<Error> validateGpus(const RepeatedPtrField<Resource>& resources)
@@ -772,13 +796,22 @@ Option<Error> validateAllocatedToSingleRole(const Resources& resources)
 }
 
 
-Option<Error> validate(const RepeatedPtrField<Resource>& resources)
+Option<Error> validate(
+    const RepeatedPtrField<Resource>& resources,
+    const Option<protobuf::framework::Capabilities>& capabilities)
 {
   Option<Error> error = Resources::validate(resources);
   if (error.isSome()) {
     return Error("Invalid resources: " + error.get().message);
   }
 
+  if (capabilities.isSome()) {
+    error = validateFormat(resources, capabilities.get());
+    if (error.isSome()) {
+      return Error("Invalid resource format: " + error.get().message);
+    }
+  }
+
   error = validateGpus(resources);
   if (error.isSome()) {
     return Error("Invalid 'gpus' resource: " + error.get().message);
@@ -923,9 +956,11 @@ Option<Error> validateShutdownGracePeriod(const ExecutorInfo& executor)
 }
 
 
-Option<Error> validateResources(const ExecutorInfo& executor)
+Option<Error> validateResources(
+    const ExecutorInfo& executor,
+    const Option<protobuf::framework::Capabilities>& capabilities)
 {
-  Option<Error> error = resource::validate(executor.resources());
+  Option<Error> error = resource::validate(executor.resources(), capabilities);
   if (error.isSome()) {
     return Error("Executor uses invalid resources: " + error->message);
   }
@@ -983,7 +1018,8 @@ Option<Error> validate(
 
   const vector<lambda::function<Option<Error>()>> executorValidators = {
     lambda::bind(internal::validateFrameworkID, executor, framework),
-    lambda::bind(internal::validateResources, executor),
+    lambda::bind(
+        internal::validateResources, executor, framework->capabilities),
     lambda::bind(
         internal::validateCompatibleExecutorInfo, executor, framework, slave),
   };
@@ -1109,13 +1145,15 @@ Option<Error> validateHealthCheck(const TaskInfo& task)
 }
 
 
-Option<Error> validateResources(const TaskInfo& task)
+Option<Error> validateResources(
+    const TaskInfo& task,
+    const Option<protobuf::framework::Capabilities>& capabilities)
 {
   if (task.resources().empty()) {
     return Error("Task uses no resources");
   }
 
-  Option<Error> error = resource::validate(task.resources());
+  Option<Error> error = resource::validate(task.resources(), capabilities);
   if (error.isSome()) {
     return Error("Task uses invalid resources: " + error->message);
   }
@@ -1142,14 +1180,16 @@ Option<Error> validateResources(const TaskInfo& task)
 }
 
 
-Option<Error> validateTaskAndExecutorResources(const TaskInfo& task)
+Option<Error> validateTaskAndExecutorResources(
+    const TaskInfo& task,
+    const Option<protobuf::framework::Capabilities>& capabilities)
 {
   Resources total = task.resources();
   if (task.has_executor()) {
     total += task.executor().resources();
   }
 
-  Option<Error> error = resource::validate(total);
+  Option<Error> error = resource::validate(total, capabilities);
   if (error.isSome()) {
     return Error(
         "Task and its executor use invalid resources: " + error->message);
@@ -1204,7 +1244,7 @@ Option<Error> validateTask(
     lambda::bind(internal::validateKillPolicy, task),
     lambda::bind(internal::validateCheck, task),
     lambda::bind(internal::validateHealthCheck, task),
-    lambda::bind(internal::validateResources, task),
+    lambda::bind(internal::validateResources, task, framework->capabilities),
     lambda::bind(internal::validateCommandInfo, task)
   };
 
@@ -1312,7 +1352,9 @@ Option<Error> validateExecutor(
 
   // NOTE: This is refactored into a separate function
   // so that it can be easily unit tested.
-  error = task::internal::validateTaskAndExecutorResources(task);
+  error = task::internal::validateTaskAndExecutorResources(
+      task, framework->capabilities);
+
   if (error.isSome()) {
     return error;
   }
@@ -1830,8 +1872,15 @@ Option<Error> validate(
     const protobuf::slave::Capabilities& agentCapabilities,
     const Option<FrameworkInfo>& frameworkInfo)
 {
+  Option<protobuf::framework::Capabilities> frameworkCapabilities;
+  if (frameworkInfo.isSome()) {
+    frameworkCapabilities = frameworkInfo->capabilities();
+  }
+
   // NOTE: this ensures the reservation is not being made to the "*" role.
-  Option<Error> error = resource::validate(reserve.resources());
+  Option<Error> error =
+    resource::validate(reserve.resources(), frameworkCapabilities);
+
   if (error.isSome()) {
     return Error("Invalid resources: " + error.get().message);
   }
@@ -1950,9 +1999,18 @@ Option<Error> validate(
 }
 
 
-Option<Error> validate(const Offer::Operation::Unreserve& unreserve)
+Option<Error> validate(
+    const Offer::Operation::Unreserve& unreserve,
+    const Option<FrameworkInfo>& frameworkInfo)
 {
-  Option<Error> error = resource::validate(unreserve.resources());
+  Option<protobuf::framework::Capabilities> frameworkCapabilities;
+  if (frameworkInfo.isSome()) {
+    frameworkCapabilities = frameworkInfo->capabilities();
+  }
+
+  Option<Error> error =
+    resource::validate(unreserve.resources(), frameworkCapabilities);
+
   if (error.isSome()) {
     return Error("Invalid resources: " + error.get().message);
   }
@@ -1989,7 +2047,14 @@ Option<Error> validate(
     const protobuf::slave::Capabilities& agentCapabilities,
     const Option<FrameworkInfo>& frameworkInfo)
 {
-  Option<Error> error = resource::validate(create.volumes());
+  Option<protobuf::framework::Capabilities> frameworkCapabilities;
+  if (frameworkInfo.isSome()) {
+    frameworkCapabilities = frameworkInfo->capabilities();
+  }
+
+  Option<Error> error =
+    resource::validate(create.volumes(), frameworkCapabilities);
+
   if (error.isSome()) {
     return Error("Invalid resources: " + error.get().message);
   }
@@ -2074,7 +2139,8 @@ Option<Error> validate(
     const Offer::Operation::Destroy& destroy,
     const Resources& checkpointedResources,
     const hashmap<FrameworkID, Resources>& usedResources,
-    const hashmap<FrameworkID, hashmap<TaskID, TaskInfo>>& pendingTasks)
+    const hashmap<FrameworkID, hashmap<TaskID, TaskInfo>>& pendingTasks,
+    const Option<FrameworkInfo>& frameworkInfo)
 {
   // The operation can either contain allocated resources
   // (in the case of a framework accepting offers), or
@@ -2094,7 +2160,12 @@ Option<Error> validate(
 
   Resources volumes = unallocated(destroy.volumes());
 
-  Option<Error> error = resource::validate(volumes);
+  Option<protobuf::framework::Capabilities> frameworkCapabilities;
+  if (frameworkInfo.isSome()) {
+    frameworkCapabilities = frameworkInfo->capabilities();
+  }
+
+  Option<Error> error = resource::validate(volumes, frameworkCapabilities);
   if (error.isSome()) {
     return Error("Invalid resources: " + error.get().message);
   }

http://git-wip-us.apache.org/repos/asf/mesos/blob/fe6a3eb4/src/master/validation.hpp
----------------------------------------------------------------------
diff --git a/src/master/validation.hpp b/src/master/validation.hpp
index 1be9f08..1295059 100644
--- a/src/master/validation.hpp
+++ b/src/master/validation.hpp
@@ -137,7 +137,8 @@ namespace resource {
 // NOTE: We cannot take 'Resources' here because invalid resources are
 // silently ignored within its constructor.
 Option<Error> validate(
-    const google::protobuf::RepeatedPtrField<Resource>& resources);
+    const google::protobuf::RepeatedPtrField<Resource>& resources,
+    const Option<protobuf::framework::Capabilities>& capabilities = None());
 
 } // namespace resource {
 
@@ -153,7 +154,9 @@ Option<Error> validateExecutorID(const ExecutorInfo& executor);
 Option<Error> validateType(const ExecutorInfo& executor);
 
 // Validates resources of the executor.
-Option<Error> validateResources(const ExecutorInfo& executor);
+Option<Error> validateResources(
+    const ExecutorInfo& executor,
+    const Option<protobuf::framework::Capabilities>& capabilities = None());
 
 } // namespace internal {
 
@@ -181,10 +184,14 @@ Option<Error> validate(
 namespace internal {
 
 // Validates resources of the task.
-Option<Error> validateResources(const TaskInfo& task);
+Option<Error> validateResources(
+    const TaskInfo& task,
+    const Option<protobuf::framework::Capabilities>& capabilities = None());
 
 // Validates resources of the task and its executor.
-Option<Error> validateTaskAndExecutorResources(const TaskInfo& task);
+Option<Error> validateTaskAndExecutorResources(
+    const TaskInfo& task,
+    const Option<protobuf::framework::Capabilities>& capabilities = None());
 
 // Validates the kill policy of the task.
 Option<Error> validateKillPolicy(const TaskInfo& task);
@@ -268,7 +275,9 @@ Option<Error> validate(
 
 
 // Validates the UNRESERVE operation.
-Option<Error> validate(const Offer::Operation::Unreserve& unreserve);
+Option<Error> validate(
+    const Offer::Operation::Unreserve& unreserve,
+    const Option<FrameworkInfo>& frameworkInfo = None());
 
 
 // Validates the CREATE operation. We need slave's checkpointed resources so
@@ -293,7 +302,8 @@ Option<Error> validate(
     const Offer::Operation::Destroy& destroy,
     const Resources& checkpointedResources,
     const hashmap<FrameworkID, Resources>& usedResources,
-    const hashmap<FrameworkID, hashmap<TaskID, TaskInfo>>& pendingTasks);
+    const hashmap<FrameworkID, hashmap<TaskID, TaskInfo>>& pendingTasks,
+    const Option<FrameworkInfo>& frameworkInfo = None());
 
 } // namespace operation {
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/fe6a3eb4/src/v1/resources.cpp
----------------------------------------------------------------------
diff --git a/src/v1/resources.cpp b/src/v1/resources.cpp
index dfc797a..5558805 100644
--- a/src/v1/resources.cpp
+++ b/src/v1/resources.cpp
@@ -514,8 +514,8 @@ static Option<Error> validateCommandLineResources(const Resources& resources)
           " not include a 'revocable' key in the resources JSON");
     } else if (Resources::isDynamicallyReserved(resource)) {
       return Error(
-          "Dynamic reservations cannot be specified at the command line; "
-          "do not include a 'reservation' key in the resources JSON");
+          "Dynamic reservations cannot be specified at the command line; do"
+          " not include a reservation with DYNAMIC type in the resources JSON");
     }
 
     if (nameTypes.contains(resource.name()) &&
@@ -837,10 +837,10 @@ Option<Error> Resources::validate(const Resource& resource)
     }
   }
 
-  // Checks for the invalid state of (role, reservation) pair.
-  if (resource.role() == "*" && resource.has_reservation()) {
+  if (resource.has_reservation() && resource.reservations_size() > 0) {
     return Error(
-        "Invalid reservation: role \"*\" cannot be dynamically reserved");
+        "Invalid reservation: only one of 'reservation' or 'reservations'"
+        " may be present");
   }
 
   // Check role name.
@@ -849,6 +849,63 @@ Option<Error> Resources::validate(const Resource& resource)
     return error;
   }
 
+  if (resource.has_reservation()) {
+    if (resource.reservation().has_type()) {
+      return Error(
+          "'Resource.ReservationInfo.type' must not be set for the"
+          " 'Resource.reservation' field");
+    }
+
+    if (resource.reservation().has_role()) {
+      return Error(
+          "'Resource.ReservationInfo.role' must not be set for the"
+          " 'Resource.reservation' field");
+    }
+
+    // Checks for the invalid state of (role, reservation) pair.
+    if (resource.role() == "*") {
+      return Error(
+          "Invalid reservation: role \"*\" cannot be dynamically reserved");
+    }
+  }
+
+  // Validate all of the roles in `reservations`.
+  foreach (
+      const Resource::ReservationInfo& reservation, resource.reservations()) {
+    Option<Error> error = roles::validate(reservation.role());
+    if (error.isSome()) {
+      return error;
+    }
+
+    if (reservation.role() == "*") {
+      return Error("Invalid reservation: role \"*\" cannot be reserved");
+    }
+  }
+
+  if (resource.reservations_size() > 0) {
+    string ancestor = resource.reservations(0).role();
+
+    for (int i = 1; i < resource.reservations_size(); ++i) {
+      const Resource::ReservationInfo& reservation = resource.reservations(i);
+
+      if (reservation.type() == Resource::ReservationInfo::STATIC) {
+        return Error(
+            "Invalid refined reservation: A refined reservation"
+            " cannot be STATIC");
+      }
+
+      const string& descendant = reservation.role();
+
+      if (!roles::isStrictSubroleOf(descendant, ancestor)) {
+        return Error(
+            "Invalid refined reservation: role '" + descendant + "'" +
+            " is not a refinement of '" + ancestor + "'");
+      }
+
+      ancestor = descendant;
+    }
+  }
+
   // Check that shareability is enabled for supported resource types.
   // For now, it is for persistent volumes only.
   // NOTE: We need to modify this once we extend shareability to other


[2/2] mesos git commit: Resources: Added `toUnreserved()` and `(push|pop)Reservation`.

Posted by mp...@apache.org.
Resources: Added `toUnreserved()` and `(push|pop)Reservation`.

This patches introduces utilities `toUnreserved`, `pushReservation` and
`popReservation`. They are intended to replace the current `flatten`.
We'll switch over to use them once the 'post-reservation-refinement'
format is used for `Resources`.

Review: https://reviews.apache.org/r/60020


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/4f165357
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/4f165357
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/4f165357

Branch: refs/heads/master
Commit: 4f16535704ac6f8417dfcef6c6347658114db97e
Parents: fe6a3eb
Author: Michael Park <mp...@apache.org>
Authored: Wed Jun 14 21:28:04 2017 -0700
Committer: Michael Park <mp...@apache.org>
Committed: Sat Jun 17 02:00:52 2017 -0700

----------------------------------------------------------------------
 include/mesos/resources.hpp    | 17 +++++++++++++++
 include/mesos/v1/resources.hpp | 17 +++++++++++++++
 src/common/resources.cpp       | 42 +++++++++++++++++++++++++++++++++++++
 src/v1/resources.cpp           | 42 +++++++++++++++++++++++++++++++++++++
 4 files changed, 118 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/4f165357/include/mesos/resources.hpp
----------------------------------------------------------------------
diff --git a/include/mesos/resources.hpp b/include/mesos/resources.hpp
index 2b55847..d380a59 100644
--- a/include/mesos/resources.hpp
+++ b/include/mesos/resources.hpp
@@ -424,14 +424,31 @@ public:
   // 'reservation' field is cleared.
   // Returns an Error when the role is invalid or the reservation
   // is set when the role is '*'.
+  //
+  // TODO(mpark): Switch over to using `(push|pop)Reservation` and remove this
+  //              once we change to the 'post-reservation-refinement' format.
   Try<Resources> flatten(
       const std::string& role,
       const Option<Resource::ReservationInfo>& reservation = None()) const;
 
   // Equivalent to `flatten("*")` except it returns a Resources directly
   // because the result is always a valid in this case.
+  //
+  // TODO(mpark): Switch over to using `toUnreserved` and remove this once
+  //              we change to the 'post-reservation-refinement' format.
   Resources flatten() const;
 
+  // Returns a `Resources` object with the new reservation added to the back.
+  // The new reservation must be a valid refinement of the current reservation.
+  Resources pushReservation(const Resource::ReservationInfo& reservation) const;
+
+  // Returns a `Resources` object with the last reservation removed.
+  // Every resource in `Resources` must have `resource.reservations_size() > 0`.
+  Resources popReservation() const;
+
+  // Returns a `Resources` object with all of the reservations removed.
+  Resources toUnreserved() const;
+
   // Returns a Resources object that contains all the scalar resources
   // in this object, but with their AllocationInfo, ReservationInfo,
   // DiskInfo, and SharedInfo omitted. The `role` and RevocableInfo,

http://git-wip-us.apache.org/repos/asf/mesos/blob/4f165357/include/mesos/v1/resources.hpp
----------------------------------------------------------------------
diff --git a/include/mesos/v1/resources.hpp b/include/mesos/v1/resources.hpp
index 7c6e856..66c2b21 100644
--- a/include/mesos/v1/resources.hpp
+++ b/include/mesos/v1/resources.hpp
@@ -424,14 +424,31 @@ public:
   // 'reservation' field is cleared.
   // Returns an Error when the role is invalid or the reservation
   // is set when the role is '*'.
+  //
+  // TODO(mpark): Switch over to using `(push|pop)Reservation` and remove this
+  //              once we change to the 'post-reservation-refinement' format.
   Try<Resources> flatten(
       const std::string& role,
       const Option<Resource::ReservationInfo>& reservation = None()) const;
 
   // Equivalent to `flatten("*")` except it returns a Resources directly
   // because the result is always a valid in this case.
+  //
+  // TODO(mpark): Switch over to using `toUnreserved` and remove this once
+  //              we change to the 'post-reservation-refinement' format.
   Resources flatten() const;
 
+  // Returns a `Resources` object with the new reservation added to the back.
+  // The new reservation must be a valid refinement of the current reservation.
+  Resources pushReservation(const Resource::ReservationInfo& reservation) const;
+
+  // Returns a `Resources` object with the last reservation removed.
+  // Every resource in `Resources` must have `resource.reservations_size() > 0`.
+  Resources popReservation() const;
+
+  // Returns a `Resources` object with all of the reservations removed.
+  Resources toUnreserved() const;
+
   // Returns a Resources object that contains all the scalar resources
   // in this object, but with their AllocationInfo, ReservationInfo,
   // DiskInfo, and SharedInfo omitted. The `role` and RevocableInfo,

http://git-wip-us.apache.org/repos/asf/mesos/blob/4f165357/src/common/resources.cpp
----------------------------------------------------------------------
diff --git a/src/common/resources.cpp b/src/common/resources.cpp
index 7672938..f12ab97 100644
--- a/src/common/resources.cpp
+++ b/src/common/resources.cpp
@@ -1330,6 +1330,48 @@ Resources Resources::flatten() const
 }
 
 
+Resources Resources::pushReservation(
+    const Resource::ReservationInfo& reservation) const
+{
+  Resources result;
+
+  foreach (Resource_ resource_, *this) {
+    resource_.resource.add_reservations()->CopyFrom(reservation);
+    CHECK_NONE(Resources::validate(resource_.resource));
+    result.add(resource_);
+  }
+
+  return result;
+}
+
+
+Resources Resources::popReservation() const
+{
+  Resources result;
+
+  foreach (Resource_ resource_, resources) {
+    CHECK_GE(resource_.resource.reservations_size(), 0);
+    resource_.resource.mutable_reservations()->RemoveLast();
+    result.add(resource_);
+  }
+
+  return result;
+}
+
+
+Resources Resources::toUnreserved() const
+{
+  Resources result;
+
+  foreach (Resource_ resource_, *this) {
+    resource_.resource.clear_reservations();
+    result.add(resource_);
+  }
+
+  return result;
+}
+
+
 Resources Resources::createStrippedScalarQuantity() const
 {
   Resources stripped;

http://git-wip-us.apache.org/repos/asf/mesos/blob/4f165357/src/v1/resources.cpp
----------------------------------------------------------------------
diff --git a/src/v1/resources.cpp b/src/v1/resources.cpp
index 5558805..30644ee 100644
--- a/src/v1/resources.cpp
+++ b/src/v1/resources.cpp
@@ -1332,6 +1332,48 @@ Resources Resources::flatten() const
 }
 
 
+Resources Resources::pushReservation(
+    const Resource::ReservationInfo& reservation) const
+{
+  Resources result;
+
+  foreach (Resource_ resource_, *this) {
+    resource_.resource.add_reservations()->CopyFrom(reservation);
+    CHECK_NONE(Resources::validate(resource_.resource));
+    result.add(resource_);
+  }
+
+  return result;
+}
+
+
+Resources Resources::popReservation() const
+{
+  Resources result;
+
+  foreach (Resource_ resource_, resources) {
+    CHECK_GE(resource_.resource.reservations_size(), 0);
+    resource_.resource.mutable_reservations()->RemoveLast();
+    result.add(resource_);
+  }
+
+  return result;
+}
+
+
+Resources Resources::toUnreserved() const
+{
+  Resources result;
+
+  foreach (Resource_ resource_, *this) {
+    resource_.resource.clear_reservations();
+    result.add(resource_);
+  }
+
+  return result;
+}
+
+
 Resources Resources::createStrippedScalarQuantity() const
 {
   Resources stripped;