1 //
2 // Copyright 2018 gRPC authors.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include <grpc/support/port_platform.h>
18
19 #include "src/core/ext/xds/xds_api.h"
20
21 #include <stdint.h>
22 #include <stdlib.h>
23
24 #include <set>
25 #include <string>
26 #include <vector>
27
28 #include "absl/strings/str_cat.h"
29 #include "absl/strings/strip.h"
30 #include "envoy/config/core/v3/base.upb.h"
31 #include "envoy/config/endpoint/v3/load_report.upb.h"
32 #include "envoy/service/discovery/v3/discovery.upb.h"
33 #include "envoy/service/discovery/v3/discovery.upbdefs.h"
34 #include "envoy/service/load_stats/v3/lrs.upb.h"
35 #include "envoy/service/load_stats/v3/lrs.upbdefs.h"
36 #include "envoy/service/status/v3/csds.upb.h"
37 #include "google/protobuf/any.upb.h"
38 #include "google/protobuf/duration.upb.h"
39 #include "google/protobuf/struct.upb.h"
40 #include "google/protobuf/timestamp.upb.h"
41 #include "google/rpc/status.upb.h"
42 #include "upb/base/string_view.h"
43 #include "upb/mem/arena.hpp"
44 #include "upb/reflection/def.h"
45 #include "upb/text/encode.h"
46
47 #include <grpc/status.h>
48 #include <grpc/support/log.h>
49 #include <grpc/support/time.h>
50
51 #include "src/core/ext/xds/upb_utils.h"
52 #include "src/core/ext/xds/xds_client.h"
53 #include "src/core/lib/json/json.h"
54
55 // IWYU pragma: no_include "upb/msg_internal.h"
56
57 namespace grpc_core {
58
XdsApi(XdsClient * client,TraceFlag * tracer,const XdsBootstrap::Node * node,upb::DefPool * def_pool,std::string user_agent_name,std::string user_agent_version)59 XdsApi::XdsApi(XdsClient* client, TraceFlag* tracer,
60 const XdsBootstrap::Node* node, upb::DefPool* def_pool,
61 std::string user_agent_name, std::string user_agent_version)
62 : client_(client),
63 tracer_(tracer),
64 node_(node),
65 def_pool_(def_pool),
66 user_agent_name_(std::move(user_agent_name)),
67 user_agent_version_(std::move(user_agent_version)) {}
68
69 namespace {
70
71 struct XdsApiContext {
72 XdsClient* client;
73 TraceFlag* tracer;
74 upb_DefPool* def_pool;
75 upb_Arena* arena;
76 };
77
78 void PopulateMetadataValue(google_protobuf_Value* value_pb, const Json& value,
79 upb_Arena* arena);
80
PopulateListValue(google_protobuf_ListValue * list_value,const Json::Array & values,upb_Arena * arena)81 void PopulateListValue(google_protobuf_ListValue* list_value,
82 const Json::Array& values, upb_Arena* arena) {
83 for (const auto& value : values) {
84 auto* value_pb = google_protobuf_ListValue_add_values(list_value, arena);
85 PopulateMetadataValue(value_pb, value, arena);
86 }
87 }
88
PopulateMetadata(google_protobuf_Struct * metadata_pb,const Json::Object & metadata,upb_Arena * arena)89 void PopulateMetadata(google_protobuf_Struct* metadata_pb,
90 const Json::Object& metadata, upb_Arena* arena) {
91 for (const auto& p : metadata) {
92 google_protobuf_Value* value = google_protobuf_Value_new(arena);
93 PopulateMetadataValue(value, p.second, arena);
94 google_protobuf_Struct_fields_set(
95 metadata_pb, StdStringToUpbString(p.first), value, arena);
96 }
97 }
98
PopulateMetadataValue(google_protobuf_Value * value_pb,const Json & value,upb_Arena * arena)99 void PopulateMetadataValue(google_protobuf_Value* value_pb, const Json& value,
100 upb_Arena* arena) {
101 switch (value.type()) {
102 case Json::Type::kNull:
103 google_protobuf_Value_set_null_value(value_pb, 0);
104 break;
105 case Json::Type::kNumber:
106 google_protobuf_Value_set_number_value(
107 value_pb, strtod(value.string().c_str(), nullptr));
108 break;
109 case Json::Type::kString:
110 google_protobuf_Value_set_string_value(
111 value_pb, StdStringToUpbString(value.string()));
112 break;
113 case Json::Type::kBoolean:
114 google_protobuf_Value_set_bool_value(value_pb, value.boolean());
115 break;
116 case Json::Type::kObject: {
117 google_protobuf_Struct* struct_value =
118 google_protobuf_Value_mutable_struct_value(value_pb, arena);
119 PopulateMetadata(struct_value, value.object(), arena);
120 break;
121 }
122 case Json::Type::kArray: {
123 google_protobuf_ListValue* list_value =
124 google_protobuf_Value_mutable_list_value(value_pb, arena);
125 PopulateListValue(list_value, value.array(), arena);
126 break;
127 }
128 }
129 }
130
MaybeLogDiscoveryRequest(const XdsApiContext & context,const envoy_service_discovery_v3_DiscoveryRequest * request)131 void MaybeLogDiscoveryRequest(
132 const XdsApiContext& context,
133 const envoy_service_discovery_v3_DiscoveryRequest* request) {
134 if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) &&
135 gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
136 const upb_MessageDef* msg_type =
137 envoy_service_discovery_v3_DiscoveryRequest_getmsgdef(context.def_pool);
138 char buf[10240];
139 upb_TextEncode(reinterpret_cast<const upb_Message*>(request), msg_type,
140 nullptr, 0, buf, sizeof(buf));
141 gpr_log(GPR_DEBUG, "[xds_client %p] constructed ADS request: %s",
142 context.client, buf);
143 }
144 }
145
SerializeDiscoveryRequest(const XdsApiContext & context,envoy_service_discovery_v3_DiscoveryRequest * request)146 std::string SerializeDiscoveryRequest(
147 const XdsApiContext& context,
148 envoy_service_discovery_v3_DiscoveryRequest* request) {
149 size_t output_length;
150 char* output = envoy_service_discovery_v3_DiscoveryRequest_serialize(
151 request, context.arena, &output_length);
152 return std::string(output, output_length);
153 }
154
155 } // namespace
156
PopulateNode(envoy_config_core_v3_Node * node_msg,upb_Arena * arena)157 void XdsApi::PopulateNode(envoy_config_core_v3_Node* node_msg,
158 upb_Arena* arena) {
159 if (node_ != nullptr) {
160 if (!node_->id().empty()) {
161 envoy_config_core_v3_Node_set_id(node_msg,
162 StdStringToUpbString(node_->id()));
163 }
164 if (!node_->cluster().empty()) {
165 envoy_config_core_v3_Node_set_cluster(
166 node_msg, StdStringToUpbString(node_->cluster()));
167 }
168 if (!node_->metadata().empty()) {
169 google_protobuf_Struct* metadata =
170 envoy_config_core_v3_Node_mutable_metadata(node_msg, arena);
171 PopulateMetadata(metadata, node_->metadata(), arena);
172 }
173 if (!node_->locality_region().empty() || !node_->locality_zone().empty() ||
174 !node_->locality_sub_zone().empty()) {
175 envoy_config_core_v3_Locality* locality =
176 envoy_config_core_v3_Node_mutable_locality(node_msg, arena);
177 if (!node_->locality_region().empty()) {
178 envoy_config_core_v3_Locality_set_region(
179 locality, StdStringToUpbString(node_->locality_region()));
180 }
181 if (!node_->locality_zone().empty()) {
182 envoy_config_core_v3_Locality_set_zone(
183 locality, StdStringToUpbString(node_->locality_zone()));
184 }
185 if (!node_->locality_sub_zone().empty()) {
186 envoy_config_core_v3_Locality_set_sub_zone(
187 locality, StdStringToUpbString(node_->locality_sub_zone()));
188 }
189 }
190 }
191 envoy_config_core_v3_Node_set_user_agent_name(
192 node_msg, StdStringToUpbString(user_agent_name_));
193 envoy_config_core_v3_Node_set_user_agent_version(
194 node_msg, StdStringToUpbString(user_agent_version_));
195 envoy_config_core_v3_Node_add_client_features(
196 node_msg,
197 upb_StringView_FromString("envoy.lb.does_not_support_overprovisioning"),
198 arena);
199 }
200
CreateAdsRequest(absl::string_view type_url,absl::string_view version,absl::string_view nonce,const std::vector<std::string> & resource_names,absl::Status status,bool populate_node)201 std::string XdsApi::CreateAdsRequest(
202 absl::string_view type_url, absl::string_view version,
203 absl::string_view nonce, const std::vector<std::string>& resource_names,
204 absl::Status status, bool populate_node) {
205 upb::Arena arena;
206 const XdsApiContext context = {client_, tracer_, def_pool_->ptr(),
207 arena.ptr()};
208 // Create a request.
209 envoy_service_discovery_v3_DiscoveryRequest* request =
210 envoy_service_discovery_v3_DiscoveryRequest_new(arena.ptr());
211 // Set type_url.
212 std::string type_url_str = absl::StrCat("type.googleapis.com/", type_url);
213 envoy_service_discovery_v3_DiscoveryRequest_set_type_url(
214 request, StdStringToUpbString(type_url_str));
215 // Set version_info.
216 if (!version.empty()) {
217 envoy_service_discovery_v3_DiscoveryRequest_set_version_info(
218 request, StdStringToUpbString(version));
219 }
220 // Set nonce.
221 if (!nonce.empty()) {
222 envoy_service_discovery_v3_DiscoveryRequest_set_response_nonce(
223 request, StdStringToUpbString(nonce));
224 }
225 // Set error_detail if it's a NACK.
226 std::string error_string_storage;
227 if (!status.ok()) {
228 google_rpc_Status* error_detail =
229 envoy_service_discovery_v3_DiscoveryRequest_mutable_error_detail(
230 request, arena.ptr());
231 // Hard-code INVALID_ARGUMENT as the status code.
232 // TODO(roth): If at some point we decide we care about this value,
233 // we could attach a status code to the individual errors where we
234 // generate them in the parsing code, and then use that here.
235 google_rpc_Status_set_code(error_detail, GRPC_STATUS_INVALID_ARGUMENT);
236 // Error description comes from the status that was passed in.
237 error_string_storage = std::string(status.message());
238 upb_StringView error_description =
239 StdStringToUpbString(error_string_storage);
240 google_rpc_Status_set_message(error_detail, error_description);
241 }
242 // Populate node.
243 if (populate_node) {
244 envoy_config_core_v3_Node* node_msg =
245 envoy_service_discovery_v3_DiscoveryRequest_mutable_node(request,
246 arena.ptr());
247 PopulateNode(node_msg, arena.ptr());
248 envoy_config_core_v3_Node_add_client_features(
249 node_msg, upb_StringView_FromString("xds.config.resource-in-sotw"),
250 context.arena);
251 }
252 // Add resource_names.
253 for (const std::string& resource_name : resource_names) {
254 envoy_service_discovery_v3_DiscoveryRequest_add_resource_names(
255 request, StdStringToUpbString(resource_name), arena.ptr());
256 }
257 MaybeLogDiscoveryRequest(context, request);
258 return SerializeDiscoveryRequest(context, request);
259 }
260
261 namespace {
262
MaybeLogDiscoveryResponse(const XdsApiContext & context,const envoy_service_discovery_v3_DiscoveryResponse * response)263 void MaybeLogDiscoveryResponse(
264 const XdsApiContext& context,
265 const envoy_service_discovery_v3_DiscoveryResponse* response) {
266 if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) &&
267 gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
268 const upb_MessageDef* msg_type =
269 envoy_service_discovery_v3_DiscoveryResponse_getmsgdef(
270 context.def_pool);
271 char buf[10240];
272 upb_TextEncode(reinterpret_cast<const upb_Message*>(response), msg_type,
273 nullptr, 0, buf, sizeof(buf));
274 gpr_log(GPR_DEBUG, "[xds_client %p] received response: %s", context.client,
275 buf);
276 }
277 }
278
279 } // namespace
280
ParseAdsResponse(absl::string_view encoded_response,AdsResponseParserInterface * parser)281 absl::Status XdsApi::ParseAdsResponse(absl::string_view encoded_response,
282 AdsResponseParserInterface* parser) {
283 upb::Arena arena;
284 const XdsApiContext context = {client_, tracer_, def_pool_->ptr(),
285 arena.ptr()};
286 // Decode the response.
287 const envoy_service_discovery_v3_DiscoveryResponse* response =
288 envoy_service_discovery_v3_DiscoveryResponse_parse(
289 encoded_response.data(), encoded_response.size(), arena.ptr());
290 // If decoding fails, report a fatal error and return.
291 if (response == nullptr) {
292 return absl::InvalidArgumentError("Can't decode DiscoveryResponse.");
293 }
294 MaybeLogDiscoveryResponse(context, response);
295 // Report the type_url, version, nonce, and number of resources to the parser.
296 AdsResponseParserInterface::AdsResponseFields fields;
297 fields.type_url = std::string(absl::StripPrefix(
298 UpbStringToAbsl(
299 envoy_service_discovery_v3_DiscoveryResponse_type_url(response)),
300 "type.googleapis.com/"));
301 fields.version = UpbStringToStdString(
302 envoy_service_discovery_v3_DiscoveryResponse_version_info(response));
303 fields.nonce = UpbStringToStdString(
304 envoy_service_discovery_v3_DiscoveryResponse_nonce(response));
305 size_t num_resources;
306 const google_protobuf_Any* const* resources =
307 envoy_service_discovery_v3_DiscoveryResponse_resources(response,
308 &num_resources);
309 fields.num_resources = num_resources;
310 absl::Status status = parser->ProcessAdsResponseFields(std::move(fields));
311 if (!status.ok()) return status;
312 // Process each resource.
313 for (size_t i = 0; i < num_resources; ++i) {
314 absl::string_view type_url = absl::StripPrefix(
315 UpbStringToAbsl(google_protobuf_Any_type_url(resources[i])),
316 "type.googleapis.com/");
317 absl::string_view serialized_resource =
318 UpbStringToAbsl(google_protobuf_Any_value(resources[i]));
319 // Unwrap Resource messages, if so wrapped.
320 absl::string_view resource_name;
321 if (type_url == "envoy.service.discovery.v3.Resource") {
322 const auto* resource_wrapper = envoy_service_discovery_v3_Resource_parse(
323 serialized_resource.data(), serialized_resource.size(), arena.ptr());
324 if (resource_wrapper == nullptr) {
325 parser->ResourceWrapperParsingFailed(
326 i, "Can't decode Resource proto wrapper");
327 continue;
328 }
329 const auto* resource =
330 envoy_service_discovery_v3_Resource_resource(resource_wrapper);
331 if (resource == nullptr) {
332 parser->ResourceWrapperParsingFailed(
333 i, "No resource present in Resource proto wrapper");
334 continue;
335 }
336 type_url = absl::StripPrefix(
337 UpbStringToAbsl(google_protobuf_Any_type_url(resource)),
338 "type.googleapis.com/");
339 serialized_resource =
340 UpbStringToAbsl(google_protobuf_Any_value(resource));
341 resource_name = UpbStringToAbsl(
342 envoy_service_discovery_v3_Resource_name(resource_wrapper));
343 }
344 parser->ParseResource(context.arena, i, type_url, resource_name,
345 serialized_resource);
346 }
347 return absl::OkStatus();
348 }
349
350 namespace {
351
MaybeLogLrsRequest(const XdsApiContext & context,const envoy_service_load_stats_v3_LoadStatsRequest * request)352 void MaybeLogLrsRequest(
353 const XdsApiContext& context,
354 const envoy_service_load_stats_v3_LoadStatsRequest* request) {
355 if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) &&
356 gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
357 const upb_MessageDef* msg_type =
358 envoy_service_load_stats_v3_LoadStatsRequest_getmsgdef(
359 context.def_pool);
360 char buf[10240];
361 upb_TextEncode(reinterpret_cast<const upb_Message*>(request), msg_type,
362 nullptr, 0, buf, sizeof(buf));
363 gpr_log(GPR_DEBUG, "[xds_client %p] constructed LRS request: %s",
364 context.client, buf);
365 }
366 }
367
SerializeLrsRequest(const XdsApiContext & context,const envoy_service_load_stats_v3_LoadStatsRequest * request)368 std::string SerializeLrsRequest(
369 const XdsApiContext& context,
370 const envoy_service_load_stats_v3_LoadStatsRequest* request) {
371 size_t output_length;
372 char* output = envoy_service_load_stats_v3_LoadStatsRequest_serialize(
373 request, context.arena, &output_length);
374 return std::string(output, output_length);
375 }
376
377 } // namespace
378
CreateLrsInitialRequest()379 std::string XdsApi::CreateLrsInitialRequest() {
380 upb::Arena arena;
381 const XdsApiContext context = {client_, tracer_, def_pool_->ptr(),
382 arena.ptr()};
383 // Create a request.
384 envoy_service_load_stats_v3_LoadStatsRequest* request =
385 envoy_service_load_stats_v3_LoadStatsRequest_new(arena.ptr());
386 // Populate node.
387 envoy_config_core_v3_Node* node_msg =
388 envoy_service_load_stats_v3_LoadStatsRequest_mutable_node(request,
389 arena.ptr());
390 PopulateNode(node_msg, arena.ptr());
391 envoy_config_core_v3_Node_add_client_features(
392 node_msg,
393 upb_StringView_FromString("envoy.lrs.supports_send_all_clusters"),
394 arena.ptr());
395 MaybeLogLrsRequest(context, request);
396 return SerializeLrsRequest(context, request);
397 }
398
399 namespace {
400
LocalityStatsPopulate(const XdsApiContext & context,envoy_config_endpoint_v3_UpstreamLocalityStats * output,const XdsLocalityName & locality_name,const XdsClusterLocalityStats::Snapshot & snapshot)401 void LocalityStatsPopulate(
402 const XdsApiContext& context,
403 envoy_config_endpoint_v3_UpstreamLocalityStats* output,
404 const XdsLocalityName& locality_name,
405 const XdsClusterLocalityStats::Snapshot& snapshot) {
406 // Set locality.
407 envoy_config_core_v3_Locality* locality =
408 envoy_config_endpoint_v3_UpstreamLocalityStats_mutable_locality(
409 output, context.arena);
410 if (!locality_name.region().empty()) {
411 envoy_config_core_v3_Locality_set_region(
412 locality, StdStringToUpbString(locality_name.region()));
413 }
414 if (!locality_name.zone().empty()) {
415 envoy_config_core_v3_Locality_set_zone(
416 locality, StdStringToUpbString(locality_name.zone()));
417 }
418 if (!locality_name.sub_zone().empty()) {
419 envoy_config_core_v3_Locality_set_sub_zone(
420 locality, StdStringToUpbString(locality_name.sub_zone()));
421 }
422 // Set total counts.
423 envoy_config_endpoint_v3_UpstreamLocalityStats_set_total_successful_requests(
424 output, snapshot.total_successful_requests);
425 envoy_config_endpoint_v3_UpstreamLocalityStats_set_total_requests_in_progress(
426 output, snapshot.total_requests_in_progress);
427 envoy_config_endpoint_v3_UpstreamLocalityStats_set_total_error_requests(
428 output, snapshot.total_error_requests);
429 envoy_config_endpoint_v3_UpstreamLocalityStats_set_total_issued_requests(
430 output, snapshot.total_issued_requests);
431 // Add backend metrics.
432 for (const auto& p : snapshot.backend_metrics) {
433 const std::string& metric_name = p.first;
434 const XdsClusterLocalityStats::BackendMetric& metric_value = p.second;
435 envoy_config_endpoint_v3_EndpointLoadMetricStats* load_metric =
436 envoy_config_endpoint_v3_UpstreamLocalityStats_add_load_metric_stats(
437 output, context.arena);
438 envoy_config_endpoint_v3_EndpointLoadMetricStats_set_metric_name(
439 load_metric, StdStringToUpbString(metric_name));
440 envoy_config_endpoint_v3_EndpointLoadMetricStats_set_num_requests_finished_with_metric(
441 load_metric, metric_value.num_requests_finished_with_metric);
442 envoy_config_endpoint_v3_EndpointLoadMetricStats_set_total_metric_value(
443 load_metric, metric_value.total_metric_value);
444 }
445 }
446
447 } // namespace
448
CreateLrsRequest(ClusterLoadReportMap cluster_load_report_map)449 std::string XdsApi::CreateLrsRequest(
450 ClusterLoadReportMap cluster_load_report_map) {
451 upb::Arena arena;
452 const XdsApiContext context = {client_, tracer_, def_pool_->ptr(),
453 arena.ptr()};
454 // Create a request.
455 envoy_service_load_stats_v3_LoadStatsRequest* request =
456 envoy_service_load_stats_v3_LoadStatsRequest_new(arena.ptr());
457 for (auto& p : cluster_load_report_map) {
458 const std::string& cluster_name = p.first.first;
459 const std::string& eds_service_name = p.first.second;
460 const ClusterLoadReport& load_report = p.second;
461 // Add cluster stats.
462 envoy_config_endpoint_v3_ClusterStats* cluster_stats =
463 envoy_service_load_stats_v3_LoadStatsRequest_add_cluster_stats(
464 request, arena.ptr());
465 // Set the cluster name.
466 envoy_config_endpoint_v3_ClusterStats_set_cluster_name(
467 cluster_stats, StdStringToUpbString(cluster_name));
468 // Set EDS service name, if non-empty.
469 if (!eds_service_name.empty()) {
470 envoy_config_endpoint_v3_ClusterStats_set_cluster_service_name(
471 cluster_stats, StdStringToUpbString(eds_service_name));
472 }
473 // Add locality stats.
474 for (const auto& p : load_report.locality_stats) {
475 const XdsLocalityName& locality_name = *p.first;
476 const auto& snapshot = p.second;
477 envoy_config_endpoint_v3_UpstreamLocalityStats* locality_stats =
478 envoy_config_endpoint_v3_ClusterStats_add_upstream_locality_stats(
479 cluster_stats, arena.ptr());
480 LocalityStatsPopulate(context, locality_stats, locality_name, snapshot);
481 }
482 // Add dropped requests.
483 uint64_t total_dropped_requests = 0;
484 for (const auto& p : load_report.dropped_requests.categorized_drops) {
485 const std::string& category = p.first;
486 const uint64_t count = p.second;
487 envoy_config_endpoint_v3_ClusterStats_DroppedRequests* dropped_requests =
488 envoy_config_endpoint_v3_ClusterStats_add_dropped_requests(
489 cluster_stats, arena.ptr());
490 envoy_config_endpoint_v3_ClusterStats_DroppedRequests_set_category(
491 dropped_requests, StdStringToUpbString(category));
492 envoy_config_endpoint_v3_ClusterStats_DroppedRequests_set_dropped_count(
493 dropped_requests, count);
494 total_dropped_requests += count;
495 }
496 total_dropped_requests += load_report.dropped_requests.uncategorized_drops;
497 // Set total dropped requests.
498 envoy_config_endpoint_v3_ClusterStats_set_total_dropped_requests(
499 cluster_stats, total_dropped_requests);
500 // Set real load report interval.
501 gpr_timespec timespec = load_report.load_report_interval.as_timespec();
502 google_protobuf_Duration* load_report_interval =
503 envoy_config_endpoint_v3_ClusterStats_mutable_load_report_interval(
504 cluster_stats, arena.ptr());
505 google_protobuf_Duration_set_seconds(load_report_interval, timespec.tv_sec);
506 google_protobuf_Duration_set_nanos(load_report_interval, timespec.tv_nsec);
507 }
508 MaybeLogLrsRequest(context, request);
509 return SerializeLrsRequest(context, request);
510 }
511
512 namespace {
513
MaybeLogLrsResponse(const XdsApiContext & context,const envoy_service_load_stats_v3_LoadStatsResponse * response)514 void MaybeLogLrsResponse(
515 const XdsApiContext& context,
516 const envoy_service_load_stats_v3_LoadStatsResponse* response) {
517 if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) &&
518 gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
519 const upb_MessageDef* msg_type =
520 envoy_service_load_stats_v3_LoadStatsResponse_getmsgdef(
521 context.def_pool);
522 char buf[10240];
523 upb_TextEncode(reinterpret_cast<const upb_Message*>(response), msg_type,
524 nullptr, 0, buf, sizeof(buf));
525 gpr_log(GPR_DEBUG, "[xds_client %p] received LRS response: %s",
526 context.client, buf);
527 }
528 }
529
530 } // namespace
531
ParseLrsResponse(absl::string_view encoded_response,bool * send_all_clusters,std::set<std::string> * cluster_names,Duration * load_reporting_interval)532 absl::Status XdsApi::ParseLrsResponse(absl::string_view encoded_response,
533 bool* send_all_clusters,
534 std::set<std::string>* cluster_names,
535 Duration* load_reporting_interval) {
536 upb::Arena arena;
537 // Decode the response.
538 const envoy_service_load_stats_v3_LoadStatsResponse* decoded_response =
539 envoy_service_load_stats_v3_LoadStatsResponse_parse(
540 encoded_response.data(), encoded_response.size(), arena.ptr());
541 // Parse the response.
542 if (decoded_response == nullptr) {
543 return absl::UnavailableError("Can't decode response.");
544 }
545 const XdsApiContext context = {client_, tracer_, def_pool_->ptr(),
546 arena.ptr()};
547 MaybeLogLrsResponse(context, decoded_response);
548 // Check send_all_clusters.
549 if (envoy_service_load_stats_v3_LoadStatsResponse_send_all_clusters(
550 decoded_response)) {
551 *send_all_clusters = true;
552 } else {
553 // Store the cluster names.
554 size_t size;
555 const upb_StringView* clusters =
556 envoy_service_load_stats_v3_LoadStatsResponse_clusters(decoded_response,
557 &size);
558 for (size_t i = 0; i < size; ++i) {
559 cluster_names->emplace(UpbStringToStdString(clusters[i]));
560 }
561 }
562 // Get the load report interval.
563 const google_protobuf_Duration* load_reporting_interval_duration =
564 envoy_service_load_stats_v3_LoadStatsResponse_load_reporting_interval(
565 decoded_response);
566 *load_reporting_interval = Duration::FromSecondsAndNanoseconds(
567 google_protobuf_Duration_seconds(load_reporting_interval_duration),
568 google_protobuf_Duration_nanos(load_reporting_interval_duration));
569 return absl::OkStatus();
570 }
571
572 } // namespace grpc_core
573