/* * * Copyright 2018 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_XDS_XDS_API_H #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_XDS_XDS_API_H #include #include #include #include "absl/container/inlined_vector.h" #include "absl/types/optional.h" #include #include "re2/re2.h" #include "src/core/ext/filters/client_channel/server_address.h" #include "src/core/ext/filters/client_channel/xds/xds_bootstrap.h" #include "src/core/ext/filters/client_channel/xds/xds_client_stats.h" namespace grpc_core { class XdsClient; class XdsApi { public: static const char* kLdsTypeUrl; static const char* kRdsTypeUrl; static const char* kCdsTypeUrl; static const char* kEdsTypeUrl; struct RdsUpdate { // TODO(donnadionne): When we can use absl::variant<>, consider using that // for: PathMatcher, HeaderMatcher, cluster_name and weighted_clusters struct RdsRoute { // Matchers for this route. struct Matchers { struct PathMatcher { enum class PathMatcherType { PATH, // path stored in string_matcher field PREFIX, // prefix stored in string_matcher field REGEX, // regex stored in regex_matcher field }; PathMatcherType type; std::string string_matcher; std::unique_ptr regex_matcher; bool operator==(const PathMatcher& other) const { if (type != other.type) return false; if (type == PathMatcherType::REGEX) { // Should never be null. if (regex_matcher == nullptr || other.regex_matcher == nullptr) { return false; } return regex_matcher->pattern() == other.regex_matcher->pattern(); } return string_matcher == other.string_matcher; } std::string ToString() const; }; struct HeaderMatcher { enum class HeaderMatcherType { EXACT, // value stored in string_matcher field REGEX, // uses regex_match field RANGE, // uses range_start and range_end fields PRESENT, // uses present_match field PREFIX, // prefix stored in string_matcher field SUFFIX, // suffix stored in string_matcher field }; std::string name; HeaderMatcherType type; int64_t range_start; int64_t range_end; std::string string_matcher; std::unique_ptr regex_match; bool present_match; // invert_match field may or may not exisit, so initialize it to // false. bool invert_match = false; bool operator==(const HeaderMatcher& other) const { return (name == other.name && type == other.type && range_start == other.range_start && range_end == other.range_end && string_matcher == other.string_matcher && present_match == other.present_match && invert_match == other.invert_match); } std::string ToString() const; }; PathMatcher path_matcher; std::vector header_matchers; absl::optional fraction_per_million; bool operator==(const Matchers& other) const { return (path_matcher == other.path_matcher && header_matchers == other.header_matchers && fraction_per_million == other.fraction_per_million); } std::string ToString() const; }; Matchers matchers; // Action for this route. std::string cluster_name; struct ClusterWeight { std::string name; uint32_t weight; bool operator==(const ClusterWeight& other) const { return (name == other.name && weight == other.weight); } std::string ToString() const; }; std::vector weighted_clusters; bool operator==(const RdsRoute& other) const { return (matchers == other.matchers && cluster_name == other.cluster_name && weighted_clusters == other.weighted_clusters); } std::string ToString() const; }; std::vector routes; bool operator==(const RdsUpdate& other) const { return routes == other.routes; } std::string ToString() const; }; // TODO(roth): When we can use absl::variant<>, consider using that // here, to enforce the fact that only one of the two fields can be set. struct LdsUpdate { // The name to use in the RDS request. std::string route_config_name; // The name to use in the CDS request. Present if the LDS response has it // inlined. absl::optional rds_update; bool operator==(const LdsUpdate& other) const { return route_config_name == other.route_config_name && rds_update == other.rds_update; } }; using LdsUpdateMap = std::map; using RdsUpdateMap = std::map; struct CdsUpdate { // The name to use in the EDS request. // If empty, the cluster name will be used. std::string eds_service_name; // The LRS server to use for load reporting. // If not set, load reporting will be disabled. // If set to the empty string, will use the same server we obtained the CDS // data from. absl::optional lrs_load_reporting_server_name; }; using CdsUpdateMap = std::map; class PriorityListUpdate { public: struct LocalityMap { struct Locality { bool operator==(const Locality& other) const { return *name == *other.name && serverlist == other.serverlist && lb_weight == other.lb_weight && priority == other.priority; } // This comparator only compares the locality names. struct Less { bool operator()(const Locality& lhs, const Locality& rhs) const { return XdsLocalityName::Less()(lhs.name, rhs.name); } }; RefCountedPtr name; ServerAddressList serverlist; uint32_t lb_weight; uint32_t priority; }; bool Contains(const RefCountedPtr& name) const { return localities.find(name) != localities.end(); } size_t size() const { return localities.size(); } std::map, Locality, XdsLocalityName::Less> localities; }; bool operator==(const PriorityListUpdate& other) const; bool operator!=(const PriorityListUpdate& other) const { return !(*this == other); } void Add(LocalityMap::Locality locality); const LocalityMap* Find(uint32_t priority) const; bool Contains(uint32_t priority) const { return priority < priorities_.size(); } bool Contains(const RefCountedPtr& name); bool empty() const { return priorities_.empty(); } size_t size() const { return priorities_.size(); } // Callers should make sure the priority list is non-empty. uint32_t LowestPriority() const { return static_cast(priorities_.size()) - 1; } private: absl::InlinedVector priorities_; }; // There are two phases of accessing this class's content: // 1. to initialize in the control plane combiner; // 2. to use in the data plane combiner. // So no additional synchronization is needed. class DropConfig : public RefCounted { public: struct DropCategory { bool operator==(const DropCategory& other) const { return name == other.name && parts_per_million == other.parts_per_million; } std::string name; const uint32_t parts_per_million; }; using DropCategoryList = absl::InlinedVector; void AddCategory(std::string name, uint32_t parts_per_million) { drop_category_list_.emplace_back( DropCategory{std::move(name), parts_per_million}); if (parts_per_million == 1000000) drop_all_ = true; } // The only method invoked from the data plane combiner. bool ShouldDrop(const std::string** category_name) const; const DropCategoryList& drop_category_list() const { return drop_category_list_; } bool drop_all() const { return drop_all_; } bool operator==(const DropConfig& other) const { return drop_category_list_ == other.drop_category_list_; } bool operator!=(const DropConfig& other) const { return !(*this == other); } private: DropCategoryList drop_category_list_; bool drop_all_ = false; }; struct EdsUpdate { PriorityListUpdate priority_list_update; RefCountedPtr drop_config; }; using EdsUpdateMap = std::map; struct ClusterLoadReport { XdsClusterDropStats::DroppedRequestsMap dropped_requests; std::map, XdsClusterLocalityStats::Snapshot, XdsLocalityName::Less> locality_stats; grpc_millis load_report_interval; }; using ClusterLoadReportMap = std::map< std::pair, ClusterLoadReport>; XdsApi(XdsClient* client, TraceFlag* tracer, const XdsBootstrap::Node* node); // Creates an ADS request. // Takes ownership of \a error. grpc_slice CreateAdsRequest(const std::string& type_url, const std::set& resource_names, const std::string& version, const std::string& nonce, grpc_error* error, bool populate_node); // Parses the ADS response and outputs the validated update for either CDS or // EDS. If the response can't be parsed at the top level, \a type_url will // point to an empty string; otherwise, it will point to the received data. grpc_error* ParseAdsResponse( const grpc_slice& encoded_response, const std::string& expected_server_name, const std::set& expected_route_configuration_names, const std::set& expected_cluster_names, const std::set& expected_eds_service_names, absl::optional* lds_update, absl::optional* rds_update, CdsUpdateMap* cds_update_map, EdsUpdateMap* eds_update_map, std::string* version, std::string* nonce, std::string* type_url); // Creates an LRS request querying \a server_name. grpc_slice CreateLrsInitialRequest(const std::string& server_name); // Creates an LRS request sending a client-side load report. grpc_slice CreateLrsRequest(ClusterLoadReportMap cluster_load_report_map); // Parses the LRS response and returns \a // load_reporting_interval for client-side load reporting. If there is any // error, the output config is invalid. grpc_error* ParseLrsResponse(const grpc_slice& encoded_response, bool* send_all_clusters, std::set* cluster_names, grpc_millis* load_reporting_interval); private: XdsClient* client_; TraceFlag* tracer_; const XdsBootstrap::Node* node_; const std::string build_version_; const std::string user_agent_name_; }; } // namespace grpc_core #endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_XDS_XDS_API_H */