1 /* 2 * 3 * Copyright 2018 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_XDS_XDS_API_H 20 #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_XDS_XDS_API_H 21 22 #include <grpc/support/port_platform.h> 23 24 #include <stdint.h> 25 26 #include <set> 27 28 #include "absl/container/inlined_vector.h" 29 #include "absl/types/optional.h" 30 31 #include <grpc/slice_buffer.h> 32 33 #include "re2/re2.h" 34 #include "src/core/ext/filters/client_channel/server_address.h" 35 #include "src/core/ext/filters/client_channel/xds/xds_bootstrap.h" 36 #include "src/core/ext/filters/client_channel/xds/xds_client_stats.h" 37 38 namespace grpc_core { 39 40 class XdsClient; 41 42 class XdsApi { 43 public: 44 static const char* kLdsTypeUrl; 45 static const char* kRdsTypeUrl; 46 static const char* kCdsTypeUrl; 47 static const char* kEdsTypeUrl; 48 49 struct RdsUpdate { 50 // TODO(donnadionne): When we can use absl::variant<>, consider using that 51 // for: PathMatcher, HeaderMatcher, cluster_name and weighted_clusters 52 struct RdsRoute { 53 // Matchers for this route. 54 struct Matchers { 55 struct PathMatcher { 56 enum class PathMatcherType { 57 PATH, // path stored in string_matcher field 58 PREFIX, // prefix stored in string_matcher field 59 REGEX, // regex stored in regex_matcher field 60 }; 61 PathMatcherType type; 62 std::string string_matcher; 63 std::unique_ptr<RE2> regex_matcher; 64 bool operator==(const PathMatcher& other) const { 65 if (type != other.type) return false; 66 if (type == PathMatcherType::REGEX) { 67 // Should never be null. 68 if (regex_matcher == nullptr || other.regex_matcher == nullptr) { 69 return false; 70 } 71 return regex_matcher->pattern() == other.regex_matcher->pattern(); 72 } 73 return string_matcher == other.string_matcher; 74 } 75 std::string ToString() const; 76 }; 77 struct HeaderMatcher { 78 enum class HeaderMatcherType { 79 EXACT, // value stored in string_matcher field 80 REGEX, // uses regex_match field 81 RANGE, // uses range_start and range_end fields 82 PRESENT, // uses present_match field 83 PREFIX, // prefix stored in string_matcher field 84 SUFFIX, // suffix stored in string_matcher field 85 }; 86 std::string name; 87 HeaderMatcherType type; 88 int64_t range_start; 89 int64_t range_end; 90 std::string string_matcher; 91 std::unique_ptr<RE2> regex_match; 92 bool present_match; 93 // invert_match field may or may not exisit, so initialize it to 94 // false. 95 bool invert_match = false; 96 bool operator==(const HeaderMatcher& other) const { 97 return (name == other.name && type == other.type && 98 range_start == other.range_start && 99 range_end == other.range_end && 100 string_matcher == other.string_matcher && 101 present_match == other.present_match && 102 invert_match == other.invert_match); 103 } 104 std::string ToString() const; 105 }; 106 PathMatcher path_matcher; 107 std::vector<HeaderMatcher> header_matchers; 108 absl::optional<uint32_t> fraction_per_million; 109 bool operator==(const Matchers& other) const { 110 return (path_matcher == other.path_matcher && 111 header_matchers == other.header_matchers && 112 fraction_per_million == other.fraction_per_million); 113 } 114 std::string ToString() const; 115 }; 116 Matchers matchers; 117 // Action for this route. 118 std::string cluster_name; 119 struct ClusterWeight { 120 std::string name; 121 uint32_t weight; 122 123 bool operator==(const ClusterWeight& other) const { 124 return (name == other.name && weight == other.weight); 125 } 126 std::string ToString() const; 127 }; 128 std::vector<ClusterWeight> weighted_clusters; 129 130 bool operator==(const RdsRoute& other) const { 131 return (matchers == other.matchers && 132 cluster_name == other.cluster_name && 133 weighted_clusters == other.weighted_clusters); 134 } 135 std::string ToString() const; 136 }; 137 138 std::vector<RdsRoute> routes; 139 140 bool operator==(const RdsUpdate& other) const { 141 return routes == other.routes; 142 } 143 std::string ToString() const; 144 }; 145 146 // TODO(roth): When we can use absl::variant<>, consider using that 147 // here, to enforce the fact that only one of the two fields can be set. 148 struct LdsUpdate { 149 // The name to use in the RDS request. 150 std::string route_config_name; 151 // The name to use in the CDS request. Present if the LDS response has it 152 // inlined. 153 absl::optional<RdsUpdate> rds_update; 154 155 bool operator==(const LdsUpdate& other) const { 156 return route_config_name == other.route_config_name && 157 rds_update == other.rds_update; 158 } 159 }; 160 161 using LdsUpdateMap = std::map<std::string /*server_name*/, LdsUpdate>; 162 163 using RdsUpdateMap = std::map<std::string /*route_config_name*/, RdsUpdate>; 164 165 struct CdsUpdate { 166 // The name to use in the EDS request. 167 // If empty, the cluster name will be used. 168 std::string eds_service_name; 169 // The LRS server to use for load reporting. 170 // If not set, load reporting will be disabled. 171 // If set to the empty string, will use the same server we obtained the CDS 172 // data from. 173 absl::optional<std::string> lrs_load_reporting_server_name; 174 }; 175 176 using CdsUpdateMap = std::map<std::string /*cluster_name*/, CdsUpdate>; 177 178 class PriorityListUpdate { 179 public: 180 struct LocalityMap { 181 struct Locality { 182 bool operator==(const Locality& other) const { 183 return *name == *other.name && serverlist == other.serverlist && 184 lb_weight == other.lb_weight && priority == other.priority; 185 } 186 187 // This comparator only compares the locality names. 188 struct Less { operatorLocalityMap::Locality::Less189 bool operator()(const Locality& lhs, const Locality& rhs) const { 190 return XdsLocalityName::Less()(lhs.name, rhs.name); 191 } 192 }; 193 194 RefCountedPtr<XdsLocalityName> name; 195 ServerAddressList serverlist; 196 uint32_t lb_weight; 197 uint32_t priority; 198 }; 199 ContainsLocalityMap200 bool Contains(const RefCountedPtr<XdsLocalityName>& name) const { 201 return localities.find(name) != localities.end(); 202 } 203 sizeLocalityMap204 size_t size() const { return localities.size(); } 205 206 std::map<RefCountedPtr<XdsLocalityName>, Locality, XdsLocalityName::Less> 207 localities; 208 }; 209 210 bool operator==(const PriorityListUpdate& other) const; 211 bool operator!=(const PriorityListUpdate& other) const { 212 return !(*this == other); 213 } 214 215 void Add(LocalityMap::Locality locality); 216 217 const LocalityMap* Find(uint32_t priority) const; 218 Contains(uint32_t priority)219 bool Contains(uint32_t priority) const { 220 return priority < priorities_.size(); 221 } 222 bool Contains(const RefCountedPtr<XdsLocalityName>& name); 223 empty()224 bool empty() const { return priorities_.empty(); } size()225 size_t size() const { return priorities_.size(); } 226 227 // Callers should make sure the priority list is non-empty. LowestPriority()228 uint32_t LowestPriority() const { 229 return static_cast<uint32_t>(priorities_.size()) - 1; 230 } 231 232 private: 233 absl::InlinedVector<LocalityMap, 2> priorities_; 234 }; 235 236 // There are two phases of accessing this class's content: 237 // 1. to initialize in the control plane combiner; 238 // 2. to use in the data plane combiner. 239 // So no additional synchronization is needed. 240 class DropConfig : public RefCounted<DropConfig> { 241 public: 242 struct DropCategory { 243 bool operator==(const DropCategory& other) const { 244 return name == other.name && 245 parts_per_million == other.parts_per_million; 246 } 247 248 std::string name; 249 const uint32_t parts_per_million; 250 }; 251 252 using DropCategoryList = absl::InlinedVector<DropCategory, 2>; 253 AddCategory(std::string name,uint32_t parts_per_million)254 void AddCategory(std::string name, uint32_t parts_per_million) { 255 drop_category_list_.emplace_back( 256 DropCategory{std::move(name), parts_per_million}); 257 if (parts_per_million == 1000000) drop_all_ = true; 258 } 259 260 // The only method invoked from the data plane combiner. 261 bool ShouldDrop(const std::string** category_name) const; 262 drop_category_list()263 const DropCategoryList& drop_category_list() const { 264 return drop_category_list_; 265 } 266 drop_all()267 bool drop_all() const { return drop_all_; } 268 269 bool operator==(const DropConfig& other) const { 270 return drop_category_list_ == other.drop_category_list_; 271 } 272 bool operator!=(const DropConfig& other) const { return !(*this == other); } 273 274 private: 275 DropCategoryList drop_category_list_; 276 bool drop_all_ = false; 277 }; 278 279 struct EdsUpdate { 280 PriorityListUpdate priority_list_update; 281 RefCountedPtr<DropConfig> drop_config; 282 }; 283 284 using EdsUpdateMap = std::map<std::string /*eds_service_name*/, EdsUpdate>; 285 286 struct ClusterLoadReport { 287 XdsClusterDropStats::DroppedRequestsMap dropped_requests; 288 std::map<RefCountedPtr<XdsLocalityName>, XdsClusterLocalityStats::Snapshot, 289 XdsLocalityName::Less> 290 locality_stats; 291 grpc_millis load_report_interval; 292 }; 293 using ClusterLoadReportMap = std::map< 294 std::pair<std::string /*cluster_name*/, std::string /*eds_service_name*/>, 295 ClusterLoadReport>; 296 297 XdsApi(XdsClient* client, TraceFlag* tracer, const XdsBootstrap::Node* node); 298 299 // Creates an ADS request. 300 // Takes ownership of \a error. 301 grpc_slice CreateAdsRequest(const std::string& type_url, 302 const std::set<absl::string_view>& resource_names, 303 const std::string& version, 304 const std::string& nonce, grpc_error* error, 305 bool populate_node); 306 307 // Parses the ADS response and outputs the validated update for either CDS or 308 // EDS. If the response can't be parsed at the top level, \a type_url will 309 // point to an empty string; otherwise, it will point to the received data. 310 grpc_error* ParseAdsResponse( 311 const grpc_slice& encoded_response, 312 const std::string& expected_server_name, 313 const std::set<absl::string_view>& expected_route_configuration_names, 314 const std::set<absl::string_view>& expected_cluster_names, 315 const std::set<absl::string_view>& expected_eds_service_names, 316 absl::optional<LdsUpdate>* lds_update, 317 absl::optional<RdsUpdate>* rds_update, CdsUpdateMap* cds_update_map, 318 EdsUpdateMap* eds_update_map, std::string* version, std::string* nonce, 319 std::string* type_url); 320 321 // Creates an LRS request querying \a server_name. 322 grpc_slice CreateLrsInitialRequest(const std::string& server_name); 323 324 // Creates an LRS request sending a client-side load report. 325 grpc_slice CreateLrsRequest(ClusterLoadReportMap cluster_load_report_map); 326 327 // Parses the LRS response and returns \a 328 // load_reporting_interval for client-side load reporting. If there is any 329 // error, the output config is invalid. 330 grpc_error* ParseLrsResponse(const grpc_slice& encoded_response, 331 bool* send_all_clusters, 332 std::set<std::string>* cluster_names, 333 grpc_millis* load_reporting_interval); 334 335 private: 336 XdsClient* client_; 337 TraceFlag* tracer_; 338 const XdsBootstrap::Node* node_; 339 const std::string build_version_; 340 const std::string user_agent_name_; 341 }; 342 343 } // namespace grpc_core 344 345 #endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_XDS_XDS_API_H */ 346