• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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