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 2018/07/02 19:45:52 UTC

[1/5] mesos git commit: Introduced a CHECK_NOTERROR macro.

Repository: mesos
Updated Branches:
  refs/heads/1.5.x 64341865d -> c25ff6494


Introduced a CHECK_NOTERROR macro.

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


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

Branch: refs/heads/1.5.x
Commit: c9efa4048be540f6cee47c012a7637ffed5e203e
Parents: 6434186
Author: Benjamin Mahler <bm...@apache.org>
Authored: Mon Feb 5 13:32:37 2018 -0800
Committer: Greg Mann <gr...@gmail.com>
Committed: Mon Jul 2 12:21:39 2018 -0700

----------------------------------------------------------------------
 3rdparty/stout/include/stout/check.hpp | 79 ++++++++++++++++++++++++++---
 1 file changed, 73 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/c9efa404/3rdparty/stout/include/stout/check.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/stout/include/stout/check.hpp b/3rdparty/stout/include/stout/check.hpp
index 6a33579..7651150 100644
--- a/3rdparty/stout/include/stout/check.hpp
+++ b/3rdparty/stout/include/stout/check.hpp
@@ -23,9 +23,15 @@
 #include <stout/error.hpp>
 #include <stout/none.hpp>
 #include <stout/option.hpp>
-#include <stout/result.hpp>
 #include <stout/some.hpp>
-#include <stout/try.hpp>
+
+
+template <typename T>
+class Result;
+
+template <typename T, typename E>
+class Try;
+
 
 // A generic macro to facilitate definitions of CHECK_*, akin to CHECK.
 // This appends the error if possible to the end of the log message,
@@ -102,6 +108,67 @@ const T& _check_not_none(
       (expression))
 
 
+// A private helper for CHECK_NOTERROR which is similar to the
+// CHECK_NOTNULL provided by glog.
+template <typename T, typename E>
+T&& _check_not_error(
+    const char* file,
+    int line,
+    const char* message,
+    Try<T, E>&& t) {
+  if (t.isError()) {
+    google::LogMessageFatal(
+        file,
+        line,
+        new std::string(
+            std::string(message) + ": " + Error(t.error()).message));
+  }
+  return std::move(t).get();
+}
+
+
+template <typename T, typename E>
+T& _check_not_error(
+    const char* file,
+    int line,
+    const char* message,
+    Try<T, E>& t) {
+  if (t.isError()) {
+    google::LogMessageFatal(
+        file,
+        line,
+        new std::string(
+            std::string(message) + ": " + Error(t.error()).message));
+  }
+  return t.get();
+}
+
+
+template <typename T, typename E>
+const T& _check_not_error(
+    const char* file,
+    int line,
+    const char* message,
+    const Try<T, E>& t) {
+  if (t.isError()) {
+    google::LogMessageFatal(
+        file,
+        line,
+        new std::string(
+            std::string(message) + ": " + Error(t.error()).message));
+  }
+  return t.get();
+}
+
+
+#define CHECK_NOTERROR(expression) \
+  _check_not_error( \
+      __FILE__, \
+      __LINE__, \
+      "'" #expression "' Must be SOME", \
+      (expression))
+
+
 // Private structs/functions used for CHECK_*.
 
 template <typename T>
@@ -116,8 +183,8 @@ Option<Error> _check_some(const Option<T>& o)
 }
 
 
-template <typename T>
-Option<Error> _check_some(const Try<T>& t)
+template <typename T, typename E>
+Option<Error> _check_some(const Try<T, E>& t)
 {
   if (t.isError()) {
     return Error(t.error());
@@ -168,8 +235,8 @@ Option<Error> _check_none(const Result<T>& r)
 }
 
 
-template <typename T>
-Option<Error> _check_error(const Try<T>& t)
+template <typename T, typename E>
+Option<Error> _check_error(const Try<T, E>& t)
 {
   if (t.isSome()) {
     return Error("is SOME");


[4/5] mesos git commit: Added a master flag to configure minimum allocatable resources.

Posted by gr...@apache.org.
Added a master flag to configure minimum allocatable resources.

This patch adds a new master flag `min_allocatable_resources`.
It specifies one or more resource quantities that define the
minimum allocatable resources for the allocator. The allocator
will only offer resources that contain at least one of the
specified resource quantities.

For example, the setting `disk:1000|cpus:1;mem:32` means that
the allocator will only allocate resources when they contain
1000MB of disk, or when they contain both 1 cpu and 32MB of
memory.

The default value for this new flag is such that it maintains
previous default behavior.

Also fixed all related tests and updated documentation.

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


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

Branch: refs/heads/1.5.x
Commit: 2e16cdb16ee9cc4162fad8b3957d69b9af7dbd8b
Parents: be07709
Author: Meng Zhu <mz...@mesosphere.io>
Authored: Wed Jun 20 17:00:03 2018 -0700
Committer: Greg Mann <gr...@gmail.com>
Committed: Mon Jul 2 12:21:40 2018 -0700

----------------------------------------------------------------------
 docs/configuration/master.md                | 18 +++++++++++
 include/mesos/allocator/allocator.hpp       |  4 ++-
 src/master/allocator/mesos/allocator.hpp    | 13 +++++---
 src/master/allocator/mesos/hierarchical.cpp | 25 ++++++++++-----
 src/master/allocator/mesos/hierarchical.hpp |  9 ++++--
 src/master/flags.cpp                        | 13 ++++++++
 src/master/flags.hpp                        |  1 +
 src/master/master.cpp                       | 39 +++++++++++++++++++++++-
 src/tests/allocator.hpp                     | 11 ++++---
 src/tests/api_tests.cpp                     |  4 +--
 src/tests/hierarchical_allocator_tests.cpp  | 11 ++++++-
 src/tests/master_allocator_tests.cpp        | 36 +++++++++++-----------
 src/tests/master_quota_tests.cpp            | 20 ++++++------
 src/tests/reservation_tests.cpp             |  6 ++--
 src/tests/resource_offers_tests.cpp         |  2 +-
 src/tests/slave_recovery_tests.cpp          |  2 +-
 16 files changed, 158 insertions(+), 56 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/2e16cdb1/docs/configuration/master.md
----------------------------------------------------------------------
diff --git a/docs/configuration/master.md b/docs/configuration/master.md
index 3ff97ea..c1ed05b 100644
--- a/docs/configuration/master.md
+++ b/docs/configuration/master.md
@@ -191,6 +191,24 @@ load an alternate allocator module using <code>--modules</code>.
 (default: HierarchicalDRF)
   </td>
 </tr>
+
+<tr>
+  <td>
+    --min_allocatable_resources=VALUE
+  </td>
+  <td>
+One or more sets of resources that define the minimum allocatable
+resources for the allocator. The allocator will only offer resources
+that contain at least one of the specified sets. The resources in each
+set should be delimited by semicolons, and the sets should be delimited
+by the pipe character.
+(Example: <code>disk:1|cpu:1;mem:32</code>
+configures the allocator to only offer resources if they contain a disk
+resource of at least 1 megabyte, or if they contain both 1 cpu and
+32 megabytes of memory.) (default: cpus:0.01|mem:32).
+  </td>
+</tr>
+
 <tr>
   <td>
     --[no-]authenticate_agents,

http://git-wip-us.apache.org/repos/asf/mesos/blob/2e16cdb1/include/mesos/allocator/allocator.hpp
----------------------------------------------------------------------
diff --git a/include/mesos/allocator/allocator.hpp b/include/mesos/allocator/allocator.hpp
index 6478692..c19ab64 100644
--- a/include/mesos/allocator/allocator.hpp
+++ b/include/mesos/allocator/allocator.hpp
@@ -101,7 +101,9 @@ public:
       const Option<std::set<std::string>>&
         fairnessExcludeResourceNames = None(),
       bool filterGpuResources = true,
-      const Option<DomainInfo>& domain = None()) = 0;
+      const Option<DomainInfo>& domain = None(),
+      const Option<std::vector<Resources>>&
+        minAllocatableResources = None()) = 0;
 
   /**
    * Informs the allocator of the recovered state from the master.

http://git-wip-us.apache.org/repos/asf/mesos/blob/2e16cdb1/src/master/allocator/mesos/allocator.hpp
----------------------------------------------------------------------
diff --git a/src/master/allocator/mesos/allocator.hpp b/src/master/allocator/mesos/allocator.hpp
index c453c01..900c8ee 100644
--- a/src/master/allocator/mesos/allocator.hpp
+++ b/src/master/allocator/mesos/allocator.hpp
@@ -59,7 +59,8 @@ public:
       const Option<std::set<std::string>>&
         fairnessExcludeResourceNames = None(),
       bool filterGpuResources = true,
-      const Option<DomainInfo>& domain = None());
+      const Option<DomainInfo>& domain = None(),
+      const Option<std::vector<Resources>>& minAllocatableResources = None());
 
   void recover(
       const int expectedAgentCount,
@@ -205,7 +206,9 @@ public:
       const Option<std::set<std::string>>&
         fairnessExcludeResourceNames = None(),
       bool filterGpuResources = true,
-      const Option<DomainInfo>& domain = None()) = 0;
+      const Option<DomainInfo>& domain = None(),
+      const Option<std::vector<Resources>>&
+        minAllocatableResources = None()) = 0;
 
   virtual void recover(
       const int expectedAgentCount,
@@ -360,7 +363,8 @@ inline void MesosAllocator<AllocatorProcess>::initialize(
       inverseOfferCallback,
     const Option<std::set<std::string>>& fairnessExcludeResourceNames,
     bool filterGpuResources,
-    const Option<DomainInfo>& domain)
+    const Option<DomainInfo>& domain,
+    const Option<std::vector<Resources>>& minAllocatableResources)
 {
   process::dispatch(
       process,
@@ -370,7 +374,8 @@ inline void MesosAllocator<AllocatorProcess>::initialize(
       inverseOfferCallback,
       fairnessExcludeResourceNames,
       filterGpuResources,
-      domain);
+      domain,
+      minAllocatableResources);
 }
 
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/2e16cdb1/src/master/allocator/mesos/hierarchical.cpp
----------------------------------------------------------------------
diff --git a/src/master/allocator/mesos/hierarchical.cpp b/src/master/allocator/mesos/hierarchical.cpp
index 4d9b80f..a483dfb 100644
--- a/src/master/allocator/mesos/hierarchical.cpp
+++ b/src/master/allocator/mesos/hierarchical.cpp
@@ -155,7 +155,8 @@ void HierarchicalAllocatorProcess::initialize(
       _inverseOfferCallback,
     const Option<set<string>>& _fairnessExcludeResourceNames,
     bool _filterGpuResources,
-    const Option<DomainInfo>& _domain)
+    const Option<DomainInfo>& _domain,
+    const Option<std::vector<Resources>>& _minAllocatableResources)
 {
   allocationInterval = _allocationInterval;
   offerCallback = _offerCallback;
@@ -163,6 +164,7 @@ void HierarchicalAllocatorProcess::initialize(
   fairnessExcludeResourceNames = _fairnessExcludeResourceNames;
   filterGpuResources = _filterGpuResources;
   domain = _domain;
+  minAllocatableResources = _minAllocatableResources;
   initialized = true;
   paused = false;
 
@@ -2468,14 +2470,23 @@ bool HierarchicalAllocatorProcess::isFiltered(
 }
 
 
-bool HierarchicalAllocatorProcess::allocatable(
-    const Resources& resources)
+bool HierarchicalAllocatorProcess::allocatable(const Resources& resources)
 {
-  Option<double> cpus = resources.cpus();
-  Option<Bytes> mem = resources.mem();
+  if (minAllocatableResources.isNone() ||
+      CHECK_NOTNONE(minAllocatableResources).empty()) {
+    return true;
+  }
 
-  return (cpus.isSome() && cpus.get() >= MIN_CPUS) ||
-         (mem.isSome() && mem.get() >= MIN_MEM);
+  // We remove the static reservation metadata here via `toUnreserved()`.
+  Resources quantity = resources.createStrippedScalarQuantity().toUnreserved();
+  foreach (
+      const Resources& minResources, CHECK_NOTNONE(minAllocatableResources)) {
+    if (quantity.contains(minResources)) {
+      return true;
+    }
+  }
+
+  return false;
 }
 
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/2e16cdb1/src/master/allocator/mesos/hierarchical.hpp
----------------------------------------------------------------------
diff --git a/src/master/allocator/mesos/hierarchical.hpp b/src/master/allocator/mesos/hierarchical.hpp
index 2678390..37d7b75 100644
--- a/src/master/allocator/mesos/hierarchical.hpp
+++ b/src/master/allocator/mesos/hierarchical.hpp
@@ -111,7 +111,9 @@ public:
       const Option<std::set<std::string>>&
         fairnessExcludeResourceNames = None(),
       bool filterGpuResources = true,
-      const Option<DomainInfo>& domain = None());
+      const Option<DomainInfo>& domain = None(),
+      const Option<std::vector<Resources>>&
+        minAllocatableResources = None());
 
   void recover(
       const int _expectedAgentCount,
@@ -287,7 +289,7 @@ protected:
       const FrameworkID& frameworkID,
       const SlaveID& slaveID) const;
 
-  static bool allocatable(const Resources& resources);
+  bool allocatable(const Resources& resources);
 
   bool initialized;
   bool paused;
@@ -480,6 +482,9 @@ protected:
   // The master's domain, if any.
   Option<DomainInfo> domain;
 
+  // The minimum allocatable resources, if any.
+  Option<std::vector<Resources>> minAllocatableResources;
+
   // There are two stages of allocation. During the first stage resources
   // are allocated only to frameworks under roles with quota set. During
   // the second stage remaining resources that would not be required to

http://git-wip-us.apache.org/repos/asf/mesos/blob/2e16cdb1/src/master/flags.cpp
----------------------------------------------------------------------
diff --git a/src/master/flags.cpp b/src/master/flags.cpp
index 79edc69..97e9479 100644
--- a/src/master/flags.cpp
+++ b/src/master/flags.cpp
@@ -476,6 +476,19 @@ mesos::internal::master::Flags::Flags()
       "  https://issues.apache.org/jira/browse/MESOS-7576",
       true);
 
+  add(&Flags::min_allocatable_resources,
+      "min_allocatable_resources",
+      "One or more sets of resources that define the minimum allocatable\n"
+      "resources for the allocator. The allocator will only offer resources\n"
+      "that contain at least one of the specified sets. The resources in\n"
+      "each set should be delimited by semicolons, and the sets should be\n"
+      "delimited by the pipe character.\n"
+      "(Example: `disk:1|cpu:1;mem:32` configures the allocator to only offer\n"
+      "resources if they contain a disk resource of at least 1 megabyte, or\n"
+      "if they contain both 1 cpu and 32 megabytes of memory.)\n",
+      "cpus:" + stringify(MIN_CPUS) +
+        "|mem:" + stringify((double)MIN_MEM.bytes() / Bytes::MEGABYTES));
+
   add(&Flags::hooks,
       "hooks",
       "A comma-separated list of hook modules to be\n"

http://git-wip-us.apache.org/repos/asf/mesos/blob/2e16cdb1/src/master/flags.hpp
----------------------------------------------------------------------
diff --git a/src/master/flags.hpp b/src/master/flags.hpp
index cd6ed05..9d48eb2 100644
--- a/src/master/flags.hpp
+++ b/src/master/flags.hpp
@@ -82,6 +82,7 @@ public:
   std::string allocator;
   Option<std::set<std::string>> fair_sharing_excluded_resource_names;
   bool filter_gpu_resources;
+  std::string min_allocatable_resources;
   Option<std::string> hooks;
   Duration agent_ping_timeout;
   size_t max_agent_ping_timeouts;

http://git-wip-us.apache.org/repos/asf/mesos/blob/2e16cdb1/src/master/master.cpp
----------------------------------------------------------------------
diff --git a/src/master/master.cpp b/src/master/master.cpp
index 6b1764f..a2e8b96 100644
--- a/src/master/master.cpp
+++ b/src/master/master.cpp
@@ -759,6 +759,42 @@ void Master::initialize()
       << " for --offer_timeout: Must be greater than zero";
   }
 
+  // Parse min_allocatable_resources.
+  Try<vector<Resources>> minAllocatableResources =
+    [](const string& resourceString) -> Try<vector<Resources>> {
+      vector<Resources> result;
+
+      foreach (const string& token, strings::tokenize(resourceString, "|")) {
+        Try<vector<Resource>> resourceVector =
+          Resources::fromSimpleString(token);
+
+        if (resourceVector.isError()) {
+          return Error(resourceVector.error());
+        }
+
+        result.push_back(Resources(CHECK_NOTERROR(resourceVector)));
+      }
+
+      return result;
+  }(flags.min_allocatable_resources);
+
+  if (minAllocatableResources.isError()) {
+    EXIT(EXIT_FAILURE) << "Error parsing min_allocatable_resources: '"
+                       << flags.min_allocatable_resources
+                       << "': " << minAllocatableResources.error();
+  }
+
+  // Validate that configured minimum resources are "pure" scalar quantities.
+  foreach (
+      const Resources& resources, CHECK_NOTERROR(minAllocatableResources)) {
+    if (!Resources::isScalarQuantity(resources)) {
+      EXIT(EXIT_FAILURE) << "Invalid min_allocatable_resources: '"
+                         << flags.min_allocatable_resources << "': "
+                         << "minimum allocatable resources should only"
+                         << "have name, type (scalar) and value set";
+    }
+  }
+
   // Initialize the allocator.
   allocator->initialize(
       flags.allocation_interval,
@@ -766,7 +802,8 @@ void Master::initialize()
       defer(self(), &Master::inverseOffer, lambda::_1, lambda::_2),
       flags.fair_sharing_excluded_resource_names,
       flags.filter_gpu_resources,
-      flags.domain);
+      flags.domain,
+      CHECK_NOTERROR(minAllocatableResources));
 
   // Parse the whitelist. Passing Allocator::updateWhitelist()
   // callback is safe because we shut down the whitelistWatcher in

http://git-wip-us.apache.org/repos/asf/mesos/blob/2e16cdb1/src/tests/allocator.hpp
----------------------------------------------------------------------
diff --git a/src/tests/allocator.hpp b/src/tests/allocator.hpp
index 341efa6..73fc060 100644
--- a/src/tests/allocator.hpp
+++ b/src/tests/allocator.hpp
@@ -45,7 +45,7 @@ namespace tests {
 
 ACTION_P(InvokeInitialize, allocator)
 {
-  allocator->real->initialize(arg0, arg1, arg2, arg3, arg4);
+  allocator->real->initialize(arg0, arg1, arg2, arg3, arg4, arg5, arg6);
 }
 
 
@@ -235,9 +235,9 @@ public:
     // to get the best of both worlds: the ability to use 'DoDefault'
     // and no warnings when expectations are not explicit.
 
-    ON_CALL(*this, initialize(_, _, _, _, _, _))
+    ON_CALL(*this, initialize(_, _, _, _, _, _, _))
       .WillByDefault(InvokeInitialize(this));
-    EXPECT_CALL(*this, initialize(_, _, _, _, _, _))
+    EXPECT_CALL(*this, initialize(_, _, _, _, _, _, _))
       .WillRepeatedly(DoDefault());
 
     ON_CALL(*this, recover(_, _))
@@ -368,7 +368,7 @@ public:
 
   virtual ~TestAllocator() {}
 
-  MOCK_METHOD6(initialize, void(
+  MOCK_METHOD7(initialize, void(
       const Duration&,
       const lambda::function<
           void(const FrameworkID&,
@@ -378,7 +378,8 @@ public:
                const hashmap<SlaveID, UnavailableResources>&)>&,
       const Option<std::set<std::string>>&,
       bool,
-      const Option<DomainInfo>&));
+      const Option<DomainInfo>&,
+      const Option<std::vector<Resources>>&));
 
   MOCK_METHOD2(recover, void(
       const int expectedAgentCount,

http://git-wip-us.apache.org/repos/asf/mesos/blob/2e16cdb1/src/tests/api_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/api_tests.cpp b/src/tests/api_tests.cpp
index fb45879..1b01427 100644
--- a/src/tests/api_tests.cpp
+++ b/src/tests/api_tests.cpp
@@ -1060,7 +1060,7 @@ TEST_P(MasterAPITest, ReserveResources)
 {
   TestAllocator<> allocator;
 
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   Try<Owned<cluster::Master>> master = StartMaster(&allocator);
   ASSERT_SOME(master);
@@ -1151,7 +1151,7 @@ TEST_P(MasterAPITest, UnreserveResources)
 {
   TestAllocator<> allocator;
 
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   Try<Owned<cluster::Master>> master = StartMaster(&allocator);
   ASSERT_SOME(master);

http://git-wip-us.apache.org/repos/asf/mesos/blob/2e16cdb1/src/tests/hierarchical_allocator_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/hierarchical_allocator_tests.cpp b/src/tests/hierarchical_allocator_tests.cpp
index 9bc939d..5b83061 100644
--- a/src/tests/hierarchical_allocator_tests.cpp
+++ b/src/tests/hierarchical_allocator_tests.cpp
@@ -176,11 +176,20 @@ protected:
         };
     }
 
+    vector<Resources> minAllocatableResources;
+    minAllocatableResources.push_back(
+        Resources::parse("cpus:" + stringify(MIN_CPUS)).get());
+    minAllocatableResources.push_back(Resources::parse(
+        "mem:" + stringify((double)MIN_MEM.bytes() / Bytes::MEGABYTES)).get());
+
     allocator->initialize(
         flags.allocation_interval,
         offerCallback.get(),
         inverseOfferCallback.get(),
-        flags.fair_sharing_excluded_resource_names);
+        flags.fair_sharing_excluded_resource_names,
+        true,
+        None(),
+        minAllocatableResources);
   }
 
   SlaveInfo createSlaveInfo(const Resources& resources)

http://git-wip-us.apache.org/repos/asf/mesos/blob/2e16cdb1/src/tests/master_allocator_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/master_allocator_tests.cpp b/src/tests/master_allocator_tests.cpp
index 9bca27c..9b73277 100644
--- a/src/tests/master_allocator_tests.cpp
+++ b/src/tests/master_allocator_tests.cpp
@@ -164,7 +164,7 @@ TYPED_TEST(MasterAllocatorTest, SingleFramework)
 {
   TestAllocator<TypeParam> allocator;
 
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   Try<Owned<cluster::Master>> master = this->StartMaster(&allocator);
   ASSERT_SOME(master);
@@ -212,7 +212,7 @@ TYPED_TEST(MasterAllocatorTest, ResourcesUnused)
 
   TestAllocator<TypeParam> allocator;
 
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   Try<Owned<cluster::Master>> master = this->StartMaster(&allocator);
   ASSERT_SOME(master);
@@ -321,7 +321,7 @@ TYPED_TEST(MasterAllocatorTest, OutOfOrderDispatch)
 {
   TestAllocator<TypeParam> allocator;
 
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   Try<Owned<cluster::Master>> master = this->StartMaster(&allocator);
   ASSERT_SOME(master);
@@ -450,7 +450,7 @@ TYPED_TEST(MasterAllocatorTest, SchedulerFailover)
 {
   TestAllocator<TypeParam> allocator;
 
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   Try<Owned<cluster::Master>> master = this->StartMaster(&allocator);
   ASSERT_SOME(master);
@@ -576,7 +576,7 @@ TYPED_TEST(MasterAllocatorTest, FrameworkExited)
 
   TestAllocator<TypeParam> allocator;
 
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   master::Flags masterFlags = this->CreateMasterFlags();
   masterFlags.allocation_interval = Milliseconds(50);
@@ -736,7 +736,7 @@ TYPED_TEST(MasterAllocatorTest, SlaveLost)
 {
   TestAllocator<TypeParam> allocator;
 
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   Try<Owned<cluster::Master>> master = this->StartMaster(&allocator);
   ASSERT_SOME(master);
@@ -860,7 +860,7 @@ TYPED_TEST(MasterAllocatorTest, SlaveAdded)
 {
   TestAllocator<TypeParam> allocator;
 
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   master::Flags masterFlags = this->CreateMasterFlags();
   masterFlags.allocation_interval = Milliseconds(50);
@@ -956,7 +956,7 @@ TYPED_TEST(MasterAllocatorTest, TaskFinished)
 {
   TestAllocator<TypeParam> allocator;
 
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   master::Flags masterFlags = this->CreateMasterFlags();
   masterFlags.allocation_interval = Milliseconds(50);
@@ -1060,7 +1060,7 @@ TYPED_TEST(MasterAllocatorTest, CpusOnlyOfferedAndTaskLaunched)
 {
   TestAllocator<TypeParam> allocator;
 
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   master::Flags masterFlags = this->CreateMasterFlags();
   masterFlags.allocation_interval = Milliseconds(50);
@@ -1141,7 +1141,7 @@ TYPED_TEST(MasterAllocatorTest, MemoryOnlyOfferedAndTaskLaunched)
 {
   TestAllocator<TypeParam> allocator;
 
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   master::Flags masterFlags = this->CreateMasterFlags();
   masterFlags.allocation_interval = Milliseconds(50);
@@ -1235,7 +1235,7 @@ TYPED_TEST(MasterAllocatorTest, Whitelist)
 
   TestAllocator<TypeParam> allocator;
 
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   Future<Nothing> updateWhitelist1;
   EXPECT_CALL(allocator, updateWhitelist(Option<hashset<string>>(hosts)))
@@ -1274,7 +1274,7 @@ TYPED_TEST(MasterAllocatorTest, RoleTest)
 {
   TestAllocator<TypeParam> allocator;
 
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   master::Flags masterFlags = this->CreateMasterFlags();
   masterFlags.roles = Some("role2");
@@ -1369,7 +1369,7 @@ TYPED_TEST(MasterAllocatorTest, FrameworkReregistersFirst)
   {
     TestAllocator<TypeParam> allocator;
 
-    EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+    EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
     Try<Owned<cluster::Master>> master = this->StartMaster(
         &allocator, masterFlags);
@@ -1427,7 +1427,7 @@ TYPED_TEST(MasterAllocatorTest, FrameworkReregistersFirst)
   {
     TestAllocator<TypeParam> allocator2;
 
-    EXPECT_CALL(allocator2, initialize(_, _, _, _, _, _));
+    EXPECT_CALL(allocator2, initialize(_, _, _, _, _, _, _));
 
     Future<Nothing> addFramework;
     EXPECT_CALL(allocator2, addFramework(_, _, _, _, _))
@@ -1495,7 +1495,7 @@ TYPED_TEST(MasterAllocatorTest, SlaveReregistersFirst)
   {
     TestAllocator<TypeParam> allocator;
 
-    EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+    EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
     Try<Owned<cluster::Master>> master = this->StartMaster(&allocator);
     ASSERT_SOME(master);
@@ -1551,7 +1551,7 @@ TYPED_TEST(MasterAllocatorTest, SlaveReregistersFirst)
   {
     TestAllocator<TypeParam> allocator2;
 
-    EXPECT_CALL(allocator2, initialize(_, _, _, _, _, _));
+    EXPECT_CALL(allocator2, initialize(_, _, _, _, _, _, _));
 
     Future<Nothing> addSlave;
     EXPECT_CALL(allocator2, addSlave(_, _, _, _, _, _))
@@ -1618,7 +1618,7 @@ TYPED_TEST(MasterAllocatorTest, RebalancedForUpdatedWeights)
 
   TestAllocator<TypeParam> allocator;
 
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   // Start Mesos master.
   master::Flags masterFlags = this->CreateMasterFlags();
@@ -1811,7 +1811,7 @@ TYPED_TEST(MasterAllocatorTest, NestedRoles)
 
   TestAllocator<TypeParam> allocator;
 
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   master::Flags masterFlags = this->CreateMasterFlags();
   Try<Owned<cluster::Master>> master =

http://git-wip-us.apache.org/repos/asf/mesos/blob/2e16cdb1/src/tests/master_quota_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/master_quota_tests.cpp b/src/tests/master_quota_tests.cpp
index ddb2903..e49e5cc 100644
--- a/src/tests/master_quota_tests.cpp
+++ b/src/tests/master_quota_tests.cpp
@@ -424,7 +424,7 @@ TEST_F(MasterQuotaTest, SetExistingQuota)
 TEST_F(MasterQuotaTest, RemoveSingleQuota)
 {
   TestAllocator<> allocator;
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   Try<Owned<cluster::Master>> master = StartMaster(&allocator);
   ASSERT_SOME(master);
@@ -588,7 +588,7 @@ TEST_F(MasterQuotaTest, Status)
 TEST_F(MasterQuotaTest, InsufficientResourcesSingleAgent)
 {
   TestAllocator<> allocator;
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   Try<Owned<cluster::Master>> master = StartMaster(&allocator);
   ASSERT_SOME(master);
@@ -648,7 +648,7 @@ TEST_F(MasterQuotaTest, InsufficientResourcesSingleAgent)
 TEST_F(MasterQuotaTest, InsufficientResourcesMultipleAgents)
 {
   TestAllocator<> allocator;
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   Try<Owned<cluster::Master>> master = StartMaster(&allocator);
   ASSERT_SOME(master);
@@ -723,7 +723,7 @@ TEST_F(MasterQuotaTest, InsufficientResourcesMultipleAgents)
 TEST_F(MasterQuotaTest, AvailableResourcesSingleAgent)
 {
   TestAllocator<> allocator;
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   Try<Owned<cluster::Master>> master = StartMaster(&allocator);
   ASSERT_SOME(master);
@@ -773,7 +773,7 @@ TEST_F(MasterQuotaTest, AvailableResourcesSingleAgent)
 TEST_F(MasterQuotaTest, AvailableResourcesMultipleAgents)
 {
   TestAllocator<> allocator;
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   Try<Owned<cluster::Master>> master = StartMaster(&allocator);
   ASSERT_SOME(master);
@@ -842,7 +842,7 @@ TEST_F(MasterQuotaTest, AvailableResourcesMultipleAgents)
 TEST_F(MasterQuotaTest, AvailableResourcesAfterRescinding)
 {
   TestAllocator<> allocator;
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   Try<Owned<cluster::Master>> master = StartMaster(&allocator);
   ASSERT_SOME(master);
@@ -1077,7 +1077,7 @@ TEST_F(MasterQuotaTest, RecoverQuotaEmptyCluster)
   }
 
   TestAllocator<> allocator;
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   // Restart the master; configured quota should be recovered from the registry.
   master->reset();
@@ -1110,7 +1110,7 @@ TEST_F(MasterQuotaTest, RecoverQuotaEmptyCluster)
 TEST_F(MasterQuotaTest, NoAuthenticationNoAuthorization)
 {
   TestAllocator<> allocator;
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   // Disable http_readwrite authentication and authorization.
   // TODO(alexr): Setting master `--acls` flag to `ACLs()` or `None()` seems
@@ -1216,7 +1216,7 @@ TEST_F(MasterQuotaTest, UnauthenticatedQuotaRequest)
 TEST_F(MasterQuotaTest, AuthorizeGetUpdateQuotaRequests)
 {
   TestAllocator<> allocator;
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   // Setup ACLs so that only the default principal can modify quotas
   // for `ROLE1` and read status.
@@ -1764,7 +1764,7 @@ TEST_F(MasterQuotaTest, DISABLED_ChildRoleDeleteParentQuota)
 TEST_F(MasterQuotaTest, DISABLED_ClusterCapacityWithNestedRoles)
 {
   TestAllocator<> allocator;
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   Try<Owned<cluster::Master>> master = StartMaster(&allocator);
   ASSERT_SOME(master);

http://git-wip-us.apache.org/repos/asf/mesos/blob/2e16cdb1/src/tests/reservation_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/reservation_tests.cpp b/src/tests/reservation_tests.cpp
index 8d8e9c8..eabcaeb 100644
--- a/src/tests/reservation_tests.cpp
+++ b/src/tests/reservation_tests.cpp
@@ -655,7 +655,7 @@ TEST_P(ReservationTest, DropReserveTooLarge)
   masterFlags.allocation_interval = Milliseconds(5);
   masterFlags.roles = frameworkInfo.roles(0);
 
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   Try<Owned<cluster::Master>> master = StartMaster(&allocator, masterFlags);
   ASSERT_SOME(master);
@@ -2160,7 +2160,7 @@ TEST_P(ReservationTest, DropReserveWithDifferentRole)
   masterFlags.allocation_interval = Milliseconds(5);
 
   TestAllocator<> allocator;
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   Try<Owned<cluster::Master>> master = StartMaster(&allocator, masterFlags);
   ASSERT_SOME(master);
@@ -2261,7 +2261,7 @@ TEST_P(ReservationTest, PreventUnreservingAlienResources)
   masterFlags.allocation_interval = Milliseconds(5);
 
   TestAllocator<> allocator;
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   Try<Owned<cluster::Master>> master = StartMaster(&allocator, masterFlags);
   ASSERT_SOME(master);

http://git-wip-us.apache.org/repos/asf/mesos/blob/2e16cdb1/src/tests/resource_offers_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/resource_offers_tests.cpp b/src/tests/resource_offers_tests.cpp
index 5564636..6b1e494 100644
--- a/src/tests/resource_offers_tests.cpp
+++ b/src/tests/resource_offers_tests.cpp
@@ -284,7 +284,7 @@ TEST_F(ResourceOffersTest, Request)
 {
   TestAllocator<master::allocator::HierarchicalDRFAllocator> allocator;
 
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   Try<Owned<cluster::Master>> master = StartMaster(&allocator);
   ASSERT_SOME(master);

http://git-wip-us.apache.org/repos/asf/mesos/blob/2e16cdb1/src/tests/slave_recovery_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/slave_recovery_tests.cpp b/src/tests/slave_recovery_tests.cpp
index d84748c..60efba5 100644
--- a/src/tests/slave_recovery_tests.cpp
+++ b/src/tests/slave_recovery_tests.cpp
@@ -3707,7 +3707,7 @@ TYPED_TEST(SlaveRecoveryTest, ReconcileTasksMissingFromSlave)
 {
   TestAllocator<master::allocator::HierarchicalDRFAllocator> allocator;
 
-  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _));
+  EXPECT_CALL(allocator, initialize(_, _, _, _, _, _, _));
 
   Try<Owned<cluster::Master>> master = this->StartMaster(&allocator);
   ASSERT_SOME(master);


[5/5] mesos git commit: Added MESOS-8935 to the 1.5.2 CHANGELOG.

Posted by gr...@apache.org.
Added MESOS-8935 to the 1.5.2 CHANGELOG.


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

Branch: refs/heads/1.5.x
Commit: c25ff64941e1e4e6c52ea7b34652758c5d2519b6
Parents: 2e16cdb
Author: Greg Mann <gr...@gmail.com>
Authored: Mon Jul 2 12:24:51 2018 -0700
Committer: Greg Mann <gr...@gmail.com>
Committed: Mon Jul 2 12:24:51 2018 -0700

----------------------------------------------------------------------
 CHANGELOG | 1 +
 1 file changed, 1 insertion(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/c25ff649/CHANGELOG
----------------------------------------------------------------------
diff --git a/CHANGELOG b/CHANGELOG
index 8e6bb1e..f8c45e5 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -6,6 +6,7 @@ Release Notes - Mesos - Version 1.5.2 (WIP)
   * [MESOS-3790] - ZooKeeper connection should retry on `EAI_NONAME`.
   * [MESOS-8904] - Master crash when removing quota.
   * [MESOS-8906] - `UriDiskProfileAdaptor` fails to update profile selectors.
+  * [MESOS-8935] - Quota limit "chopping" can lead to cpu-only and memory-only offers.
   * [MESOS-8936] - Implement a Random Sorter for offer allocations.
   * [MESOS-8942] - Master streaming API does not send (health) check updates for tasks.
   * [MESOS-8945] - Master check failure due to CHECK_SOME(providerId).


[3/5] mesos git commit: Fixed a bug in `createStrippedScalarQuantity()`.

Posted by gr...@apache.org.
Fixed a bug in `createStrippedScalarQuantity()`.

This patch fixes `createStrippedScalarQuantity()` by
stripping the revocable field in resources.

Also added a test.

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


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

Branch: refs/heads/1.5.x
Commit: 7a19d085c8aead7693c5d6212dbba7db771e60f6
Parents: c9efa40
Author: Meng Zhu <mz...@mesosphere.io>
Authored: Wed Jun 20 16:59:54 2018 -0700
Committer: Greg Mann <gr...@gmail.com>
Committed: Mon Jul 2 12:21:40 2018 -0700

----------------------------------------------------------------------
 src/common/resources.cpp      |  1 +
 src/tests/resources_tests.cpp | 13 +++++++++++++
 src/v1/resources.cpp          |  1 +
 3 files changed, 15 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/7a19d085/src/common/resources.cpp
----------------------------------------------------------------------
diff --git a/src/common/resources.cpp b/src/common/resources.cpp
index 6b2084b..76e14e8 100644
--- a/src/common/resources.cpp
+++ b/src/common/resources.cpp
@@ -1633,6 +1633,7 @@ Resources Resources::createStrippedScalarQuantity() const
 
       scalar.clear_disk();
       scalar.clear_shared();
+      scalar.clear_revocable();
       stripped.add(scalar);
     }
   }

http://git-wip-us.apache.org/repos/asf/mesos/blob/7a19d085/src/tests/resources_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/resources_tests.cpp b/src/tests/resources_tests.cpp
index 0b25e42..30144a9 100644
--- a/src/tests/resources_tests.cpp
+++ b/src/tests/resources_tests.cpp
@@ -2683,6 +2683,19 @@ TEST(ResourcesOperationTest, StrippedResourcesNonScalar)
 }
 
 
+TEST(ResourceOperationTest, StrippedResourcesRevocable)
+{
+  Resource plain = Resources::parse("cpus", "1", "*").get();
+
+  Resource revocable = plain;
+  revocable.mutable_revocable();
+
+  Resources stripped = Resources(revocable).createStrippedScalarQuantity();
+
+  EXPECT_EQ(Resources(plain), stripped);
+}
+
+
 TEST(ResourcesOperationTest, CreatePersistentVolumeFromMount)
 {
   Resource::DiskInfo::Source source = createDiskSourceMount("mnt");

http://git-wip-us.apache.org/repos/asf/mesos/blob/7a19d085/src/v1/resources.cpp
----------------------------------------------------------------------
diff --git a/src/v1/resources.cpp b/src/v1/resources.cpp
index 06e310f..5ebdc9a 100644
--- a/src/v1/resources.cpp
+++ b/src/v1/resources.cpp
@@ -1652,6 +1652,7 @@ Resources Resources::createStrippedScalarQuantity() const
 
       scalar.clear_disk();
       scalar.clear_shared();
+      scalar.clear_revocable();
       stripped.add(scalar);
     }
   }


[2/5] mesos git commit: Added a resource utility `isScalarQuantity`.

Posted by gr...@apache.org.
Added a resource utility `isScalarQuantity`.

`isScalarQuantity()` checks if a `Resources` object
is a "pure" scalar quantity; i.e., its resources only have
name, type (set to scalar) and scalar fields set.

Also added tests.

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


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

Branch: refs/heads/1.5.x
Commit: be077099b4dfcc1f82fe7f5ed222567eeb0c082c
Parents: 7a19d08
Author: Meng Zhu <mz...@mesosphere.io>
Authored: Wed Jun 20 16:59:58 2018 -0700
Committer: Greg Mann <gr...@gmail.com>
Committed: Mon Jul 2 12:21:40 2018 -0700

----------------------------------------------------------------------
 include/mesos/resources.hpp    |  5 +++++
 include/mesos/v1/resources.hpp |  5 +++++
 src/common/resources.cpp       | 11 +++++++++++
 src/tests/resources_tests.cpp  | 31 +++++++++++++++++++++++++++++++
 src/v1/resources.cpp           | 11 +++++++++++
 5 files changed, 63 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/be077099/include/mesos/resources.hpp
----------------------------------------------------------------------
diff --git a/include/mesos/resources.hpp b/include/mesos/resources.hpp
index 7afe0d8..bd6d6d6 100644
--- a/include/mesos/resources.hpp
+++ b/include/mesos/resources.hpp
@@ -324,6 +324,11 @@ public:
   // Tests if the given Resource object is shared.
   static bool isShared(const Resource& resource);
 
+  // Tests if the given Resources object is a "pure" scalar quantity which
+  // consists of resource objects with ONLY name, type (set to "Scalar")
+  // and scalar fields set.
+  static bool isScalarQuantity(const Resources& resources);
+
   // Tests if the given Resource object has refined reservations.
   static bool hasRefinedReservations(const Resource& resource);
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/be077099/include/mesos/v1/resources.hpp
----------------------------------------------------------------------
diff --git a/include/mesos/v1/resources.hpp b/include/mesos/v1/resources.hpp
index c712057..c065dd1 100644
--- a/include/mesos/v1/resources.hpp
+++ b/include/mesos/v1/resources.hpp
@@ -324,6 +324,11 @@ public:
   // Tests if the given Resource object is shared.
   static bool isShared(const Resource& resource);
 
+  // Tests if the given Resources object is a "pure" scalar quantity which
+  // only consists of resource object with ONLY name, type (set to "Scalar")
+  // and scalar fields set.
+  static bool isScalarQuantity(const Resources& resources);
+
   // Tests if the given Resource object has refined reservations.
   static bool hasRefinedReservations(const Resource& resource);
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/be077099/src/common/resources.cpp
----------------------------------------------------------------------
diff --git a/src/common/resources.cpp b/src/common/resources.cpp
index 76e14e8..f0abbca 100644
--- a/src/common/resources.cpp
+++ b/src/common/resources.cpp
@@ -1235,6 +1235,17 @@ bool Resources::isShared(const Resource& resource)
 }
 
 
+bool Resources::isScalarQuantity(const Resources& resources)
+{
+  // Instead of checking the absence of non-scalar-quantity fields,
+  // we do an equality check between the original resources object and
+  // its stripped counterpart.
+  //
+  // We remove the static reservation metadata here via `toUnreserved()`.
+  return resources == resources.createStrippedScalarQuantity().toUnreserved();
+}
+
+
 bool Resources::hasRefinedReservations(const Resource& resource)
 {
   CHECK(!resource.has_role()) << resource;

http://git-wip-us.apache.org/repos/asf/mesos/blob/be077099/src/tests/resources_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/resources_tests.cpp b/src/tests/resources_tests.cpp
index 30144a9..299847a 100644
--- a/src/tests/resources_tests.cpp
+++ b/src/tests/resources_tests.cpp
@@ -1943,6 +1943,37 @@ TEST(ResourcesTest, AbsentResources)
 }
 
 
+TEST(ResourcesTest, isScalarQuantity)
+{
+  Resources scalarQuantity1 = Resources::parse("cpus:1").get();
+  EXPECT_TRUE(Resources::isScalarQuantity(scalarQuantity1));
+
+  Resources scalarQuantity2 = Resources::parse("cpus:1;mem:1").get();
+  EXPECT_TRUE(Resources::isScalarQuantity(scalarQuantity2));
+
+  Resources range = Resources::parse("ports", "[1-16000]", "*").get();
+  EXPECT_FALSE(Resources::isScalarQuantity(range));
+
+  Resources set = Resources::parse("names:{foo,bar}").get();
+  EXPECT_FALSE(Resources::isScalarQuantity(set));
+
+  Resources reserved = createReservedResource(
+      "cpus", "1", createDynamicReservationInfo("role", "principal"));
+  EXPECT_FALSE(Resources::isScalarQuantity(reserved));
+
+  Resources disk = createDiskResource("10", "role1", "1", "path");
+  EXPECT_FALSE(Resources::isScalarQuantity(disk));
+
+  Resources allocated = Resources::parse("cpus:1;mem:512").get();
+  allocated.allocate("role");
+  EXPECT_FALSE(Resources::isScalarQuantity(allocated));
+
+  Resource revocable = Resources::parse("cpus", "1", "*").get();
+  revocable.mutable_revocable();
+  EXPECT_FALSE(Resources::isScalarQuantity(revocable));
+}
+
+
 TEST(ReservedResourcesTest, Validation)
 {
   // Unreserved.

http://git-wip-us.apache.org/repos/asf/mesos/blob/be077099/src/v1/resources.cpp
----------------------------------------------------------------------
diff --git a/src/v1/resources.cpp b/src/v1/resources.cpp
index 5ebdc9a..0593990 100644
--- a/src/v1/resources.cpp
+++ b/src/v1/resources.cpp
@@ -1256,6 +1256,17 @@ bool Resources::isShared(const Resource& resource)
 }
 
 
+bool Resources::isScalarQuantity(const Resources& resources)
+{
+  // Instead of checking the absence of non-scalar-quantity fields,
+  // we do an equality check between the original `Resources` object and
+  // its stripped counterpart.
+  //
+  // We remove the static reservation metadata here via `toUnreserved()`.
+  return resources == resources.createStrippedScalarQuantity().toUnreserved();
+}
+
+
 bool Resources::hasRefinedReservations(const Resource& resource)
 {
   CHECK(!resource.has_role()) << resource;