diff options
Diffstat (limited to 'grpc/src/core/ext/xds/xds_api.h')
-rw-r--r-- | grpc/src/core/ext/xds/xds_api.h | 450 |
1 files changed, 326 insertions, 124 deletions
diff --git a/grpc/src/core/ext/xds/xds_api.h b/grpc/src/core/ext/xds/xds_api.h index f2a67072..e7bf1cd1 100644 --- a/grpc/src/core/ext/xds/xds_api.h +++ b/grpc/src/core/ext/xds/xds_api.h @@ -33,9 +33,12 @@ #include <grpc/slice_buffer.h> +#include "envoy/admin/v3/config_dump.upb.h" #include "src/core/ext/filters/client_channel/server_address.h" #include "src/core/ext/xds/xds_bootstrap.h" #include "src/core/ext/xds/xds_client_stats.h" +#include "src/core/ext/xds/xds_http_filters.h" +#include "src/core/lib/matchers/matchers.h" namespace grpc_core { @@ -57,76 +60,58 @@ class XdsApi { int64_t seconds = 0; int32_t nanos = 0; bool operator==(const Duration& other) const { - return (seconds == other.seconds && nanos == other.nanos); + return seconds == other.seconds && nanos == other.nanos; } std::string ToString() const { return absl::StrFormat("Duration seconds: %ld, nanos %d", seconds, nanos); } }; + using TypedPerFilterConfig = + std::map<std::string, XdsHttpFilterImpl::FilterConfig>; + // TODO(donnadionne): When we can use absl::variant<>, consider using that // for: PathMatcher, HeaderMatcher, cluster_name and weighted_clusters struct Route { // 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<RE2> regex_matcher; - bool case_sensitive = true; - - PathMatcher() = default; - PathMatcher(const PathMatcher& other); - PathMatcher& operator=(const PathMatcher& other); - bool operator==(const PathMatcher& other) const; - 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<RE2> regex_match; - bool present_match; - // invert_match field may or may not exisit, so initialize it to - // false. - bool invert_match = false; - - HeaderMatcher() = default; - HeaderMatcher(const HeaderMatcher& other); - HeaderMatcher& operator=(const HeaderMatcher& other); - bool operator==(const HeaderMatcher& other) const; - std::string ToString() const; - }; - - PathMatcher path_matcher; + StringMatcher path_matcher; std::vector<HeaderMatcher> header_matchers; absl::optional<uint32_t> 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); + return path_matcher == other.path_matcher && + header_matchers == other.header_matchers && + fraction_per_million == other.fraction_per_million; } std::string ToString() const; }; + struct HashPolicy { + enum Type { HEADER, CHANNEL_ID }; + Type type; + bool terminal = false; + // Fields used for type HEADER. + std::string header_name; + std::unique_ptr<RE2> regex = nullptr; + std::string regex_substitution; + + HashPolicy() {} + + // Copyable. + HashPolicy(const HashPolicy& other); + HashPolicy& operator=(const HashPolicy& other); + + // Moveable. + HashPolicy(HashPolicy&& other) noexcept; + HashPolicy& operator=(HashPolicy&& other) noexcept; + + bool operator==(const HashPolicy& other) const; + std::string ToString() const; + }; + Matchers matchers; + std::vector<HashPolicy> hash_policies; // Action for this route. // TODO(roth): When we can use absl::variant<>, consider using that @@ -135,8 +120,11 @@ class XdsApi { struct ClusterWeight { std::string name; uint32_t weight; + TypedPerFilterConfig typed_per_filter_config; + bool operator==(const ClusterWeight& other) const { - return (name == other.name && weight == other.weight); + return name == other.name && weight == other.weight && + typed_per_filter_config == other.typed_per_filter_config; } std::string ToString() const; }; @@ -147,11 +135,13 @@ class XdsApi { // not set. absl::optional<Duration> max_stream_duration; + TypedPerFilterConfig typed_per_filter_config; + bool operator==(const Route& other) const { - return (matchers == other.matchers && - cluster_name == other.cluster_name && - weighted_clusters == other.weighted_clusters && - max_stream_duration == other.max_stream_duration); + return matchers == other.matchers && cluster_name == other.cluster_name && + weighted_clusters == other.weighted_clusters && + max_stream_duration == other.max_stream_duration && + typed_per_filter_config == other.typed_per_filter_config; } std::string ToString() const; }; @@ -160,9 +150,11 @@ class XdsApi { struct VirtualHost { std::vector<std::string> domains; std::vector<Route> routes; + TypedPerFilterConfig typed_per_filter_config; bool operator==(const VirtualHost& other) const { - return domains == other.domains && routes == other.routes; + return domains == other.domains && routes == other.routes && + typed_per_filter_config == other.typed_per_filter_config; } }; @@ -175,42 +167,6 @@ class XdsApi { VirtualHost* FindVirtualHostForDomain(const std::string& domain); }; - class StringMatcher { - public: - enum class StringMatcherType { - EXACT, // value stored in string_matcher_ field - PREFIX, // value stored in string_matcher_ field - SUFFIX, // value stored in string_matcher_ field - SAFE_REGEX, // pattern stored in regex_matcher_ field - CONTAINS, // value stored in string_matcher_ field - }; - - StringMatcher() = default; - StringMatcher(const StringMatcher& other); - StringMatcher(StringMatcherType type, const std::string& matcher, - bool ignore_case = false); - StringMatcher& operator=(const StringMatcher& other); - bool operator==(const StringMatcher& other) const; - - bool Match(absl::string_view value) const; - - std::string ToString() const; - - StringMatcherType type() const { return type_; } - - // Valid for EXACT, PREFIX, SUFFIX and CONTAINS - const std::string& string_matcher() const { return string_matcher_; } - - // Valid for SAFE_REGEX - RE2* regex_matcher() const { return regex_matcher_.get(); } - - private: - StringMatcherType type_ = StringMatcherType::EXACT; - std::string string_matcher_; - std::unique_ptr<RE2> regex_matcher_; - bool ignore_case_ = false; - }; - struct CommonTlsContext { struct CertificateValidationContext { std::vector<StringMatcher> match_subject_alt_names; @@ -264,30 +220,182 @@ class XdsApi { bool Empty() const; }; + struct DownstreamTlsContext { + CommonTlsContext common_tls_context; + bool require_client_certificate = false; + + bool operator==(const DownstreamTlsContext& other) const { + return common_tls_context == other.common_tls_context && + require_client_certificate == other.require_client_certificate; + } + + std::string ToString() const; + bool Empty() 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; - // Storing the Http Connection Manager Common Http Protocol Option - // max_stream_duration - Duration http_max_stream_duration; - // The RouteConfiguration to use for this listener. - // Present only if it is inlined in the LDS response. - absl::optional<RdsUpdate> rds_update; + enum class ListenerType { + kTcpListener = 0, + kHttpApiListener, + } type; + + struct HttpConnectionManager { + // The name to use in the RDS request. + std::string route_config_name; + // Storing the Http Connection Manager Common Http Protocol Option + // max_stream_duration + Duration http_max_stream_duration; + // The RouteConfiguration to use for this listener. + // Present only if it is inlined in the LDS response. + absl::optional<RdsUpdate> rds_update; + + struct HttpFilter { + std::string name; + XdsHttpFilterImpl::FilterConfig config; + + bool operator==(const HttpFilter& other) const { + return name == other.name && config == other.config; + } + + std::string ToString() const; + }; + std::vector<HttpFilter> http_filters; + + bool operator==(const HttpConnectionManager& other) const { + return route_config_name == other.route_config_name && + http_max_stream_duration == other.http_max_stream_duration && + rds_update == other.rds_update && + http_filters == other.http_filters; + } + + std::string ToString() const; + }; + + // Populated for type=kHttpApiListener. + HttpConnectionManager http_connection_manager; + + // Populated for type=kTcpListener. + // host:port listening_address set when type is kTcpListener + std::string address; + + struct FilterChainData { + DownstreamTlsContext downstream_tls_context; + // This is in principle the filter list. + // We currently require exactly one filter, which is the HCM. + HttpConnectionManager http_connection_manager; + + bool operator==(const FilterChainData& other) const { + return downstream_tls_context == other.downstream_tls_context && + http_connection_manager == other.http_connection_manager; + } + + std::string ToString() const; + } filter_chain_data; + + // A multi-level map used to determine which filter chain to use for a given + // incoming connection. Determining the right filter chain for a given + // connection checks the following properties, in order: + // - destination port (never matched, so not present in map) + // - destination IP address + // - server name (never matched, so not present in map) + // - transport protocol (allows only "raw_buffer" or unset, prefers the + // former, so only one of those two types is present in map) + // - application protocol (never matched, so not present in map) + // - connection source type (any, local or external) + // - source IP address + // - source port + // https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/listener/v3/listener_components.proto#config-listener-v3-filterchainmatch + // for more details + struct FilterChainMap { + struct FilterChainDataSharedPtr { + std::shared_ptr<FilterChainData> data; + bool operator==(const FilterChainDataSharedPtr& other) const { + return *data == *other.data; + } + }; + struct CidrRange { + grpc_resolved_address address; + uint32_t prefix_len; + + bool operator==(const CidrRange& other) const { + return memcmp(&address, &other.address, sizeof(address)) == 0 && + prefix_len == other.prefix_len; + } + + std::string ToString() const; + }; + using SourcePortsMap = std::map<uint16_t, FilterChainDataSharedPtr>; + struct SourceIp { + absl::optional<CidrRange> prefix_range; + SourcePortsMap ports_map; + + bool operator==(const SourceIp& other) const { + return prefix_range == other.prefix_range && + ports_map == other.ports_map; + } + }; + using SourceIpVector = std::vector<SourceIp>; + enum class ConnectionSourceType { + kAny = 0, + kSameIpOrLoopback, + kExternal + }; + using ConnectionSourceTypesArray = std::array<SourceIpVector, 3>; + struct DestinationIp { + absl::optional<CidrRange> prefix_range; + // We always fail match on server name, so those filter chains are not + // included here. + ConnectionSourceTypesArray source_types_array; + + bool operator==(const DestinationIp& other) const { + return prefix_range == other.prefix_range && + source_types_array == other.source_types_array; + } + }; + // We always fail match on destination ports map + using DestinationIpVector = std::vector<DestinationIp>; + DestinationIpVector destination_ip_vector; + + bool operator==(const FilterChainMap& other) const { + return destination_ip_vector == other.destination_ip_vector; + } + + std::string ToString() const; + } filter_chain_map; + + absl::optional<FilterChainData> default_filter_chain; bool operator==(const LdsUpdate& other) const { - return route_config_name == other.route_config_name && - rds_update == other.rds_update && - http_max_stream_duration == other.http_max_stream_duration; + return http_connection_manager == other.http_connection_manager && + address == other.address && + filter_chain_map == other.filter_chain_map && + default_filter_chain == other.default_filter_chain; } + + std::string ToString() const; }; - using LdsUpdateMap = std::map<std::string /*server_name*/, LdsUpdate>; + struct LdsResourceData { + LdsUpdate resource; + std::string serialized_proto; + }; - using RdsUpdateMap = std::map<std::string /*route_config_name*/, RdsUpdate>; + using LdsUpdateMap = std::map<std::string /*server_name*/, LdsResourceData>; + + struct RdsResourceData { + RdsUpdate resource; + std::string serialized_proto; + }; + + using RdsUpdateMap = + std::map<std::string /*route_config_name*/, RdsResourceData>; struct CdsUpdate { + enum ClusterType { EDS, LOGICAL_DNS, AGGREGATE }; + ClusterType cluster_type; + // For cluster type EDS. // The name to use in the EDS request. // If empty, the cluster name will be used. std::string eds_service_name; @@ -298,22 +406,39 @@ class XdsApi { // If set to the empty string, will use the same server we obtained the CDS // data from. absl::optional<std::string> lrs_load_reporting_server_name; + // The LB policy to use (e.g., "ROUND_ROBIN" or "RING_HASH"). + std::string lb_policy; + // Used for RING_HASH LB policy only. + uint64_t min_ring_size = 1024; + uint64_t max_ring_size = 8388608; + enum HashFunction { XX_HASH, MURMUR_HASH_2 }; + HashFunction hash_function; // Maximum number of outstanding requests can be made to the upstream // cluster. uint32_t max_concurrent_requests = 1024; + // For cluster type AGGREGATE. + // The prioritized list of cluster names. + std::vector<std::string> prioritized_cluster_names; bool operator==(const CdsUpdate& other) const { - return eds_service_name == other.eds_service_name && + return cluster_type == other.cluster_type && + eds_service_name == other.eds_service_name && common_tls_context == other.common_tls_context && lrs_load_reporting_server_name == other.lrs_load_reporting_server_name && + prioritized_cluster_names == other.prioritized_cluster_names && max_concurrent_requests == other.max_concurrent_requests; } std::string ToString() const; }; - using CdsUpdateMap = std::map<std::string /*cluster_name*/, CdsUpdate>; + struct CdsResourceData { + CdsUpdate resource; + std::string serialized_proto; + }; + + using CdsUpdateMap = std::map<std::string /*cluster_name*/, CdsResourceData>; struct EdsUpdate { struct Priority { @@ -397,7 +522,13 @@ class XdsApi { std::string ToString() const; }; - using EdsUpdateMap = std::map<std::string /*eds_service_name*/, EdsUpdate>; + struct EdsResourceData { + EdsUpdate resource; + std::string serialized_proto; + }; + + using EdsUpdateMap = + std::map<std::string /*eds_service_name*/, EdsResourceData>; struct ClusterLoadReport { XdsClusterDropStats::Snapshot dropped_requests; @@ -410,22 +541,75 @@ class XdsApi { std::pair<std::string /*cluster_name*/, std::string /*eds_service_name*/>, ClusterLoadReport>; - XdsApi(XdsClient* client, TraceFlag* tracer, const XdsBootstrap::Node* node); + // The metadata of the xDS resource; used by the xDS config dump. + struct ResourceMetadata { + // Resource status from the view of a xDS client, which tells the + // synchronization status between the xDS client and the xDS server. + enum ClientResourceStatus { + // Client requested this resource but hasn't received any update from + // management server. The client will not fail requests, but will queue + // them + // until update arrives or the client times out waiting for the resource. + REQUESTED = 1, + // This resource has been requested by the client but has either not been + // delivered by the server or was previously delivered by the server and + // then subsequently removed from resources provided by the server. + DOES_NOT_EXIST, + // Client received this resource and replied with ACK. + ACKED, + // Client received this resource and replied with NACK. + NACKED + }; - // Creates an ADS request. - // Takes ownership of \a error. - grpc_slice CreateAdsRequest(const XdsBootstrap::XdsServer& server, - const std::string& type_url, - const std::set<absl::string_view>& resource_names, - const std::string& version, - const std::string& nonce, grpc_error* error, - bool populate_node); + // The client status of this resource. + ClientResourceStatus client_status = REQUESTED; + // The serialized bytes of the last successfully updated raw xDS resource. + std::string serialized_proto; + // The timestamp when the resource was last successfully updated. + grpc_millis update_time = 0; + // The last successfully updated version of the resource. + std::string version; + // The rejected version string of the last failed update attempt. + std::string failed_version; + // Details about the last failed update attempt. + std::string failed_details; + // Timestamp of the last failed update attempt. + grpc_millis failed_update_time = 0; + }; + using ResourceMetadataMap = + std::map<absl::string_view /*resource_name*/, const ResourceMetadata*>; + struct ResourceTypeMetadata { + absl::string_view version; + ResourceMetadataMap resource_metadata_map; + }; + using ResourceTypeMetadataMap = + std::map<absl::string_view /*type_url*/, ResourceTypeMetadata>; + static_assert(static_cast<ResourceMetadata::ClientResourceStatus>( + envoy_admin_v3_REQUESTED) == + ResourceMetadata::ClientResourceStatus::REQUESTED, + ""); + static_assert(static_cast<ResourceMetadata::ClientResourceStatus>( + envoy_admin_v3_DOES_NOT_EXIST) == + ResourceMetadata::ClientResourceStatus::DOES_NOT_EXIST, + ""); + static_assert(static_cast<ResourceMetadata::ClientResourceStatus>( + envoy_admin_v3_ACKED) == + ResourceMetadata::ClientResourceStatus::ACKED, + ""); + static_assert(static_cast<ResourceMetadata::ClientResourceStatus>( + envoy_admin_v3_NACKED) == + ResourceMetadata::ClientResourceStatus::NACKED, + ""); - // Parses an ADS response. // If the response can't be parsed at the top level, the resulting // type_url will be empty. + // If there is any other type of validation error, the parse_error + // field will be set to something other than GRPC_ERROR_NONE and the + // resource_names_failed field will be populated. + // Otherwise, one of the *_update_map fields will be populated, based + // on the type_url field. struct AdsParseResult { - grpc_error* parse_error = GRPC_ERROR_NONE; + grpc_error_handle parse_error = GRPC_ERROR_NONE; std::string version; std::string nonce; std::string type_url; @@ -433,9 +617,23 @@ class XdsApi { RdsUpdateMap rds_update_map; CdsUpdateMap cds_update_map; EdsUpdateMap eds_update_map; + std::set<std::string> resource_names_failed; }; + + XdsApi(XdsClient* client, TraceFlag* tracer, const XdsBootstrap::Node* node); + + // Creates an ADS request. + // Takes ownership of \a error. + grpc_slice CreateAdsRequest(const XdsBootstrap::XdsServer& server, + const std::string& type_url, + const std::set<absl::string_view>& resource_names, + const std::string& version, + const std::string& nonce, grpc_error_handle error, + bool populate_node); + + // Parses an ADS response. AdsParseResult ParseAdsResponse( - const grpc_slice& encoded_response, + const XdsBootstrap::XdsServer& server, const grpc_slice& encoded_response, const std::set<absl::string_view>& expected_listener_names, const std::set<absl::string_view>& expected_route_configuration_names, const std::set<absl::string_view>& expected_cluster_names, @@ -450,10 +648,14 @@ class XdsApi { // 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<std::string>* cluster_names, - grpc_millis* load_reporting_interval); + grpc_error_handle ParseLrsResponse(const grpc_slice& encoded_response, + bool* send_all_clusters, + std::set<std::string>* cluster_names, + grpc_millis* load_reporting_interval); + + // Assemble the client config proto message and return the serialized result. + std::string AssembleClientConfig( + const ResourceTypeMetadataMap& resource_type_metadata_map); private: XdsClient* client_; |