This is an automated email from the ASF dual-hosted git repository. asekretenko pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/mesos.git
The following commit(s) were added to refs/heads/master by this push: new 7aee5a8 Added an endpoint for debugging offer constraints. 7aee5a8 is described below commit 7aee5a87d117601b0f551a68dac4ea25903e5f81 Author: Andrei Sekretenko <asekrete...@apache.org> AuthorDate: Wed Sep 9 14:48:00 2020 +0200 Added an endpoint for debugging offer constraints. Review: https://reviews.apache.org/r/72851 --- include/mesos/allocator/allocator.hpp | 9 +- src/master/allocator/mesos/hierarchical.cpp | 125 ++++++++++++++++++++++++++++ src/master/allocator/mesos/hierarchical.hpp | 9 ++ src/master/master.cpp | 2 + 4 files changed, 143 insertions(+), 2 deletions(-) diff --git a/include/mesos/allocator/allocator.hpp b/include/mesos/allocator/allocator.hpp index c6fca65..6d67d5d 100644 --- a/include/mesos/allocator/allocator.hpp +++ b/include/mesos/allocator/allocator.hpp @@ -24,10 +24,9 @@ #include <mesos/allocator/allocator.pb.h> #include <mesos/scheduler/scheduler.pb.h> +#include <mesos/authorizer/authorizer.hpp> #include <mesos/maintenance/maintenance.hpp> - #include <mesos/quota/quota.hpp> - #include <mesos/resource_quantities.hpp> #include <mesos/resources.hpp> @@ -66,6 +65,12 @@ struct Options size_t maxCompletedFrameworks = 0; bool publishPerFrameworkMetrics = true; + + // Authentication realm for HTTP debug endpoints exposed by the allocator. + Option<std::string> readonlyHttpAuthenticationRealm; + + // Mesos master's authorizer. + Option<::mesos::Authorizer*> authorizer; }; diff --git a/src/master/allocator/mesos/hierarchical.cpp b/src/master/allocator/mesos/hierarchical.cpp index 0b9588d..d4374c3 100644 --- a/src/master/allocator/mesos/hierarchical.cpp +++ b/src/master/allocator/mesos/hierarchical.cpp @@ -32,6 +32,7 @@ #include <process/delay.hpp> #include <process/dispatch.hpp> #include <process/event.hpp> +#include <process/help.hpp> #include <process/id.hpp> #include <process/loop.hpp> #include <process/timeout.hpp> @@ -42,6 +43,7 @@ #include <stout/stopwatch.hpp> #include <stout/stringify.hpp> +#include "common/authorization.hpp" #include "common/http.hpp" #include "common/protobuf_utils.hpp" #include "common/resources_utils.hpp" @@ -58,6 +60,7 @@ using mesos::allocator::InverseOfferStatus; using mesos::allocator::Options; using process::after; +using process::http::authentication::Principal; using process::Continue; using process::ControlFlow; using process::Failure; @@ -612,6 +615,9 @@ Framework::Framework( {} +static string OFFER_CONSTRAINTS_DEBUG_HELP(); + + void HierarchicalAllocatorProcess::initialize( const Options& _options, const lambda::function< @@ -633,6 +639,18 @@ void HierarchicalAllocatorProcess::initialize( BoundedHashMap<FrameworkID, process::Owned<FrameworkMetrics>>( options.maxCompletedFrameworks); + route("/offer_constraints_debug", + options.readonlyHttpAuthenticationRealm.getOrElse(""), + OFFER_CONSTRAINTS_DEBUG_HELP(), + [this](const process::http::Request& request, + const Option<Principal>& principal) { + logRequest(request); + return offerConstraintsDebug(request, principal) + .onReady([request](const process::http::Response& response) { + logResponse(request, response); + }); + }); + roleSorter->initialize(options.fairnessExcludeResourceNames); VLOG(1) << "Initialized hierarchical allocator process"; @@ -3212,6 +3230,113 @@ void HierarchicalAllocatorProcess::untrackAllocatedResources( } +static string OFFER_CONSTRAINTS_DEBUG_HELP() +{ + return process::HELP( + process::TLDR( + "Evaluates current framework offer constraints and returns results."), + process::DESCRIPTION( + "This endpoint evaluates for each role of each framework a list", + "of agents excluded from allocation by offer constraints.", + "", + "Example:", + "```", + "{\"frameworks\": {", + " \"0f4c63a9-be1e-4a90-9e11-d7bf0aa6c8ad-0017\": {", + " \"excluded_by_attribute_constraints\": {", + " \"role1\": [", + " \"0b1e7d60-dfbc-44c9-8222-48b57eca8637-S123\",", + " \"654af69c-80f7-45ad-bcb3-c7c917f1811b-S045\"],", + " \"role2\": [] }},", + " \"b0377da6-090d-4338-9e2e-bf6cf0f309b7-0011\": {}", + "}}", + "```", + "In this example, two agents are excluded from allocation", + "to the first framework (-0017) under the role \"role1\", no agents", + "are excluded from allocation to this framework under \"role2\",", + "and the second framework (-0011) has no offer constraints set." + ), + process::AUTHENTICATION(true), + process::AUTHORIZATION( + "This endpoint skips frameworks for which the user is not authorized" + "to perform a VIEW_FRAMEWORK action.")); +} + + +Future<process::http::Response> +HierarchicalAllocatorProcess::offerConstraintsDebug( + const process::http::Request&, + const Option<process::http::authentication::Principal>& principal) +{ + if (options.authorizer.isNone()) { + return offerConstraintsDebug_(nullptr); + } + + return (*options.authorizer) + ->getApprover( + authorization::createSubject(principal), + authorization::Action::VIEW_FRAMEWORK) + .then(defer(self(), &Self::offerConstraintsDebug_, lambda::_1)); +} + + +process::http::Response HierarchicalAllocatorProcess::offerConstraintsDebug_( + shared_ptr<const ObjectApprover> frameworksApprover) +{ + vector<const Framework*> approvedFrameworks; + foreachvalue (const Framework& framework, frameworks) { + Try<bool> approved = frameworksApprover + ? frameworksApprover->approved(framework.info) + : true; + + if (approved.isError()) { + LOG(WARNING) << "Error authorizing VIEW_FRAMEWORK for framework " + << framework.info.id() << ": " << approved.error(); + + return process::http::InternalServerError( + "Failed to authorize VIEW_FRAMEWORK: " + approved.error()); + } + + if (*approved) { + approvedFrameworks.push_back(&framework); + } + } + + auto writeFrameworks = [&](JSON::ObjectWriter* writer) { + for (const Framework* framework : approvedFrameworks) { + auto writeFramework = [&](JSON::ObjectWriter* writer) { + if (framework->offerConstraintsFilter.isNone()) { + // For an authorized frameworks without offer constraints, + // an empty object is written. + return; + } + + writer->field( + "excluded_by_attribute_constraints", + [&](JSON::ObjectWriter* writer) { + for (const string& role : framework->roles) { + writer->field(role, [&](JSON::ArrayWriter* writer) { + foreachvalue (const Slave& slave, slaves) { + if (framework->offerConstraintsFilter->isAgentExcluded( + role, slave.info)) { + writer->element(stringify(slave.id)); + } + } + }); + } + }); + }; + + writer->field(stringify(framework->info.id()), writeFramework); + } + }; + + return process::http::OK(jsonify([&](JSON::ObjectWriter* writer) { + writer->field("frameworks", writeFrameworks); + })); +} + + } // namespace internal { } // namespace allocator { } // namespace master { diff --git a/src/master/allocator/mesos/hierarchical.hpp b/src/master/allocator/mesos/hierarchical.hpp index 225de16..63444de 100644 --- a/src/master/allocator/mesos/hierarchical.hpp +++ b/src/master/allocator/mesos/hierarchical.hpp @@ -21,10 +21,12 @@ #include <set> #include <string> +#include <mesos/authorizer/authorizer.hpp> #include <mesos/mesos.hpp> #include <process/future.hpp> #include <process/id.hpp> +#include <process/http.hpp> #include <process/owned.hpp> #include <stout/boundedhashmap.hpp> @@ -865,6 +867,13 @@ protected: const std::function<Sorter*()> frameworkSorterFactory; private: + process::Future<process::http::Response> offerConstraintsDebug( + const process::http::Request&, + const Option<process::http::authentication::Principal>& principal); + + process::http::Response offerConstraintsDebug_( + std::shared_ptr<const ObjectApprover> frameworksApprover); + bool isFrameworkTrackedUnderRole( const FrameworkID& frameworkId, const std::string& role) const; diff --git a/src/master/master.cpp b/src/master/master.cpp index 599d2c9..fefa72d 100644 --- a/src/master/master.cpp +++ b/src/master/master.cpp @@ -794,6 +794,8 @@ void Master::initialize() options.minAllocatableResources = minAllocatableResources; options.maxCompletedFrameworks = flags.max_completed_frameworks; options.publishPerFrameworkMetrics = flags.publish_per_framework_metrics; + options.readonlyHttpAuthenticationRealm = READONLY_HTTP_AUTHENTICATION_REALM; + options.authorizer = authorizer; // Initialize the allocator. allocator->initialize(