This is an automated email from the ASF dual-hosted git repository. mzhu pushed a commit to branch 1.6.x in repository https://gitbox.apache.org/repos/asf/mesos.git
commit 610f9c6c06509db2131891083f7dd803caa6737f Author: Meng Zhu <m...@mesosphere.io> AuthorDate: Wed Dec 19 14:49:11 2018 -0800 Added a `Resources` method `contains(ResourceQuantities)`. This method checks if the quantities of this `Resources` is a superset of the given `ResourceQuantities`. If a `Resource` object is `SCALAR` type, its quantity is its scalar value. For `RANGES` and `SET` type, their quantities are the number of different instances in the range or set. For example, "range:[1-5]" has a quantity of 5 and "set:{a,b}" has a quantity of 2. Also added a dedicated test. Review: https://reviews.apache.org/r/69601 --- include/mesos/resources.hpp | 11 +++++ include/mesos/v1/resources.hpp | 11 +++++ src/common/resources.cpp | 44 +++++++++++++++++++ src/tests/resources_tests.cpp | 96 ++++++++++++++++++++++++++++++++++++++++++ src/v1/resources.cpp | 44 +++++++++++++++++++ 5 files changed, 206 insertions(+) diff --git a/include/mesos/resources.hpp b/include/mesos/resources.hpp index 175833c..d8481a7 100644 --- a/include/mesos/resources.hpp +++ b/include/mesos/resources.hpp @@ -57,6 +57,8 @@ namespace mesos { // Forward declaration. class ResourceConversion; +namespace internal { class ResourceQuantities; } + // Helper functions. bool operator==( @@ -400,6 +402,15 @@ public: // Checks if this Resources contains the given Resource. bool contains(const Resource& that) const; + // Checks if the quantities of this `Resources` is a superset of the + // given `ResourceQuantities`. If a `Resource` object is `SCALAR` type, + // its quantity is its scalar value. For `RANGES` and `SET` type, their + // quantities are the number of different instances in the range or set. + // For example, "range:[1-5]" has a quantity of 5 and "set:{a,b}" has a + // quantity of 2. + bool contains( + const mesos::internal::ResourceQuantities& quantities) const; + // Count the Resource objects that match the specified value. // // NOTE: diff --git a/include/mesos/v1/resources.hpp b/include/mesos/v1/resources.hpp index b607b68..7da6e41 100644 --- a/include/mesos/v1/resources.hpp +++ b/include/mesos/v1/resources.hpp @@ -52,6 +52,9 @@ // but instead just written for correct semantics. namespace mesos { + +namespace internal { class ResourceQuantities; } + namespace v1 { // Forward declaration. @@ -400,6 +403,14 @@ public: // Checks if this Resources contains the given Resource. bool contains(const Resource& that) const; + // Checks if the quantities of this `Resources` is a superset of the + // given `ResourceQuantities`. If a `Resource` object is `SCALAR` type, + // its quantity is its scalar value. For `RANGES` and `SET` type, their + // quantities are the number of different instances in the range or set. + // For example, "range:[1-5]" has a quantity of 5 and "set:{a,b}" has a + // quantity of 2. + bool contains( + const mesos::internal::ResourceQuantities& quantities) const; // Count the Resource objects that match the specified value. // // NOTE: diff --git a/src/common/resources.cpp b/src/common/resources.cpp index 253b8bc..06327d4 100644 --- a/src/common/resources.cpp +++ b/src/common/resources.cpp @@ -38,16 +38,20 @@ #include <stout/strings.hpp> #include <stout/unreachable.hpp> +#include "common/resource_quantities.hpp" #include "common/resources_utils.hpp" using std::map; using std::ostream; +using std::pair; using std::set; using std::string; using std::vector; using google::protobuf::RepeatedPtrField; +using mesos::internal::ResourceQuantities; + namespace mesos { ///////////////////////////////////////////////// @@ -1467,6 +1471,46 @@ bool Resources::contains(const Resource& that) const } +// This function assumes all quantities with the same name are merged +// in the input `quantities` which is a guaranteed property of +// `ResourceQuantities`. +bool Resources::contains(const ResourceQuantities& quantities) const +{ + foreach (auto& quantity, quantities){ + double remaining = quantity.second.value(); + + foreach (const Resource& r, get(quantity.first)) { + switch (r.type()) { + case Value::SCALAR: remaining -= r.scalar().value(); break; + case Value::SET: remaining -= r.set().item_size(); break; + case Value::RANGES: + foreach (const Value::Range& range, r.ranges().range()) { + remaining -= range.end() - range.begin() + 1; + if (remaining <= 0) { + break; + } + } + break; + case Value::TEXT: + LOG(FATAL) << "Unexpected TEXT type resource " << r << " in " + << *this; + break; + } + + if (remaining <= 0) { + break; + } + } + + if (remaining > 0) { + return false; + } + } + + return true; +} + + size_t Resources::count(const Resource& that) const { foreach (const Resource_& resource_, resources) { diff --git a/src/tests/resources_tests.cpp b/src/tests/resources_tests.cpp index da3d313..489e867 100644 --- a/src/tests/resources_tests.cpp +++ b/src/tests/resources_tests.cpp @@ -31,6 +31,7 @@ #include <mesos/v1/resources.hpp> +#include "common/resource_quantities.hpp" #include "common/resources_utils.hpp" #include "internal/evolve.hpp" @@ -57,6 +58,8 @@ using mesos::internal::evolve; using mesos::internal::protobuf::createLabel; +using mesos::internal::ResourceQuantities; + namespace mesos { namespace internal { namespace tests { @@ -1974,6 +1977,99 @@ TEST(ResourcesTest, isScalarQuantity) } +TEST(ResourcesTest, ContainsResourceQuantities) +{ + auto resources = [](const string& s) { + return CHECK_NOTERROR(Resources::parse(s)); + }; + + auto quantities = [](const string& s) { + return CHECK_NOTERROR(ResourceQuantities::fromString(s)); + }; + + // Empty case tests. + + Resources emptyResources; + ResourceQuantities emptyQuantities; + + EXPECT_TRUE(emptyResources.contains(emptyQuantities)); + EXPECT_FALSE(emptyResources.contains(quantities("cpus:1"))); + EXPECT_TRUE(resources("cpus:1").contains(emptyQuantities)); + + // Single scalar resource tests. + + EXPECT_TRUE(resources("cpus:2").contains(quantities("cpus:1"))); + + EXPECT_TRUE(resources("cpus:1").contains(quantities("cpus:1"))); + + EXPECT_FALSE(resources("cpus:0.5").contains(quantities("cpus:1"))); + + // Single range resource tests. + + EXPECT_TRUE(resources("ports:[1-3]").contains(quantities("ports:2"))); + + EXPECT_TRUE(resources("ports:[1-2]").contains(quantities("ports:2"))); + + EXPECT_FALSE(resources("ports:[1-1]").contains(quantities("ports:2"))); + + // Single set resources tests. + + EXPECT_TRUE(resources("features:{a,b,c}").contains(quantities("features:2"))); + + EXPECT_TRUE(resources("features:{a,b}").contains(quantities("features:2"))); + + EXPECT_FALSE(resources("features:{a}").contains(quantities("features:2"))); + + // Multiple resources tests. + + EXPECT_TRUE(resources("cpus:3;ports:[1-3];features:{a,b,c};mem:10") + .contains(quantities("cpus:3;ports:3;features:3"))); + + EXPECT_TRUE(resources("cpus:3;ports:[1-3];features:{a,b,c}") + .contains(quantities("cpus:3;ports:3;features:3"))); + + EXPECT_FALSE(resources("cpus:1;ports:[1-3];features:{a,b,c}") + .contains(quantities("cpus:3;ports:3;features:3"))); + + EXPECT_FALSE(resources("cpus:3;ports:[1-3]") + .contains(quantities("cpus:3;ports:3;features:3"))); + + // Duplicate names. + + EXPECT_FALSE(resources("cpus(role1):2").contains(quantities("cpus:3"))); + + EXPECT_TRUE(resources("cpus(role1):2;cpus:1").contains(quantities("cpus:3"))); + + Resource::ReservationInfo reservation = + createDynamicReservationInfo("role", "principal"); + Resources resources_ = createReservedResource("ports", "[1-10]", reservation); + + EXPECT_FALSE(resources_.contains(quantities("ports:12"))); + + resources_ += + CHECK_NOTERROR(Resources::parse("ports:[20-25]")); // 15 ports in total. + + EXPECT_TRUE(resources_.contains(quantities("ports:12"))); + + resources_ = createPersistentVolume( + Megabytes(64), + "role1", + "id1", + "path1", + None(), + None(), + "principal1", + true); // Shared. + + EXPECT_FALSE(resources_.contains(quantities("disk:128"))); + + resources_ += + CHECK_NOTERROR(Resources::parse("disk:64")); // 128M disk in total. + + EXPECT_TRUE(resources_.contains(quantities("disk:128"))); +} + + TEST(ReservedResourcesTest, Validation) { // Unreserved. diff --git a/src/v1/resources.cpp b/src/v1/resources.cpp index ab8fc3e..95e6259 100644 --- a/src/v1/resources.cpp +++ b/src/v1/resources.cpp @@ -39,16 +39,20 @@ #include <stout/strings.hpp> #include <stout/unreachable.hpp> +#include "common/resource_quantities.hpp" #include "common/resources_utils.hpp" using std::map; using std::ostream; +using std::pair; using std::set; using std::string; using std::vector; using google::protobuf::RepeatedPtrField; +using mesos::internal::ResourceQuantities; + namespace mesos { namespace v1 { @@ -1485,6 +1489,46 @@ bool Resources::contains(const Resource& that) const } +// This function assumes all quantities with the same name are merged +// in the input `quantities` which is a guaranteed property of +// `ResourceQuantities`. +bool Resources::contains(const ResourceQuantities& quantities) const +{ + foreach (auto& quantity, quantities){ + double remaining = quantity.second.value(); + + foreach (const Resource& r, get(quantity.first)) { + switch (r.type()) { + case Value::SCALAR: remaining -= r.scalar().value(); break; + case Value::SET: remaining -= r.set().item_size(); break; + case Value::RANGES: + foreach (const Value::Range& range, r.ranges().range()) { + remaining -= range.end() - range.begin() + 1; + if (remaining <= 0) { + break; + } + } + break; + case Value::TEXT: + LOG(FATAL) << "Unexpected TEXT type resource " << r << " in " + << *this; + break; + } + + if (remaining <= 0) { + break; + } + } + + if (remaining > 0) { + return false; + } + } + + return true; +} + + size_t Resources::count(const Resource& that) const { foreach (const Resource_& resource_, resources) {