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