// // Copyright 2018 gRPC authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include #include "src/core/ext/xds/xds_api.h" #include #include #include #include #include #include #include "absl/strings/str_cat.h" #include "absl/strings/strip.h" #include "envoy/admin/v3/config_dump.upb.h" #include "envoy/config/core/v3/base.upb.h" #include "envoy/config/endpoint/v3/load_report.upb.h" #include "envoy/service/discovery/v3/discovery.upb.h" #include "envoy/service/discovery/v3/discovery.upbdefs.h" #include "envoy/service/load_stats/v3/lrs.upb.h" #include "envoy/service/load_stats/v3/lrs.upbdefs.h" #include "envoy/service/status/v3/csds.upb.h" #include "google/protobuf/any.upb.h" #include "google/protobuf/duration.upb.h" #include "google/protobuf/struct.upb.h" #include "google/protobuf/timestamp.upb.h" #include "google/rpc/status.upb.h" #include "upb/def.h" #include "upb/text_encode.h" #include "upb/upb.h" #include "upb/upb.hpp" #include #include #include #include #include "src/core/ext/xds/upb_utils.h" #include "src/core/ext/xds/xds_client.h" #include "src/core/lib/iomgr/error.h" #include "src/core/lib/json/json.h" // IWYU pragma: no_include "upb/msg_internal.h" namespace grpc_core { // If gRPC is built with -DGRPC_XDS_USER_AGENT_NAME_SUFFIX="...", that string // will be appended to the user agent name reported to the xDS server. #ifdef GRPC_XDS_USER_AGENT_NAME_SUFFIX #define GRPC_XDS_USER_AGENT_NAME_SUFFIX_STRING \ " " GRPC_XDS_USER_AGENT_NAME_SUFFIX #else #define GRPC_XDS_USER_AGENT_NAME_SUFFIX_STRING "" #endif // If gRPC is built with -DGRPC_XDS_USER_AGENT_VERSION_SUFFIX="...", that string // will be appended to the user agent version reported to the xDS server. #ifdef GRPC_XDS_USER_AGENT_VERSION_SUFFIX #define GRPC_XDS_USER_AGENT_VERSION_SUFFIX_STRING \ " " GRPC_XDS_USER_AGENT_VERSION_SUFFIX #else #define GRPC_XDS_USER_AGENT_VERSION_SUFFIX_STRING "" #endif XdsApi::XdsApi(XdsClient* client, TraceFlag* tracer, const XdsBootstrap::Node* node, const CertificateProviderStore::PluginDefinitionMap* certificate_provider_definition_map, upb::SymbolTable* symtab) : client_(client), tracer_(tracer), node_(node), certificate_provider_definition_map_(certificate_provider_definition_map), symtab_(symtab), build_version_(absl::StrCat("gRPC C-core ", GPR_PLATFORM_STRING, " ", grpc_version_string(), GRPC_XDS_USER_AGENT_NAME_SUFFIX_STRING, GRPC_XDS_USER_AGENT_VERSION_SUFFIX_STRING)), user_agent_name_(absl::StrCat("gRPC C-core ", GPR_PLATFORM_STRING, GRPC_XDS_USER_AGENT_NAME_SUFFIX_STRING)), user_agent_version_( absl::StrCat("C-core ", grpc_version_string(), GRPC_XDS_USER_AGENT_NAME_SUFFIX_STRING, GRPC_XDS_USER_AGENT_VERSION_SUFFIX_STRING)) {} namespace { void PopulateMetadataValue(const XdsEncodingContext& context, google_protobuf_Value* value_pb, const Json& value); void PopulateListValue(const XdsEncodingContext& context, google_protobuf_ListValue* list_value, const Json::Array& values) { for (const auto& value : values) { auto* value_pb = google_protobuf_ListValue_add_values(list_value, context.arena); PopulateMetadataValue(context, value_pb, value); } } void PopulateMetadata(const XdsEncodingContext& context, google_protobuf_Struct* metadata_pb, const Json::Object& metadata) { for (const auto& p : metadata) { google_protobuf_Value* value = google_protobuf_Value_new(context.arena); PopulateMetadataValue(context, value, p.second); google_protobuf_Struct_fields_set( metadata_pb, StdStringToUpbString(p.first), value, context.arena); } } void PopulateMetadataValue(const XdsEncodingContext& context, google_protobuf_Value* value_pb, const Json& value) { switch (value.type()) { case Json::Type::JSON_NULL: google_protobuf_Value_set_null_value(value_pb, 0); break; case Json::Type::NUMBER: google_protobuf_Value_set_number_value( value_pb, strtod(value.string_value().c_str(), nullptr)); break; case Json::Type::STRING: google_protobuf_Value_set_string_value( value_pb, StdStringToUpbString(value.string_value())); break; case Json::Type::JSON_TRUE: google_protobuf_Value_set_bool_value(value_pb, true); break; case Json::Type::JSON_FALSE: google_protobuf_Value_set_bool_value(value_pb, false); break; case Json::Type::OBJECT: { google_protobuf_Struct* struct_value = google_protobuf_Value_mutable_struct_value(value_pb, context.arena); PopulateMetadata(context, struct_value, value.object_value()); break; } case Json::Type::ARRAY: { google_protobuf_ListValue* list_value = google_protobuf_Value_mutable_list_value(value_pb, context.arena); PopulateListValue(context, list_value, value.array_value()); break; } } } // Helper functions to manually do protobuf string encoding, so that we // can populate the node build_version field that was removed in v3. std::string EncodeVarint(uint64_t val) { std::string data; do { uint8_t byte = val & 0x7fU; val >>= 7; if (val) byte |= 0x80U; data += byte; } while (val); return data; } std::string EncodeTag(uint32_t field_number, uint8_t wire_type) { return EncodeVarint((field_number << 3) | wire_type); } std::string EncodeStringField(uint32_t field_number, const std::string& str) { static const uint8_t kDelimitedWireType = 2; return EncodeTag(field_number, kDelimitedWireType) + EncodeVarint(str.size()) + str; } void PopulateBuildVersion(const XdsEncodingContext& context, envoy_config_core_v3_Node* node_msg, const std::string& build_version) { std::string encoded_build_version = EncodeStringField(5, build_version); // TODO(roth): This should use upb_Message_AddUnknown(), but that API is // broken in the current version of upb, so we're using the internal // API for now. Change this once we upgrade to a version of upb that // fixes this bug. _upb_Message_AddUnknown(node_msg, encoded_build_version.data(), encoded_build_version.size(), context.arena); } void PopulateNode(const XdsEncodingContext& context, const XdsBootstrap::Node* node, const std::string& build_version, const std::string& user_agent_name, const std::string& user_agent_version, envoy_config_core_v3_Node* node_msg) { if (node != nullptr) { if (!node->id.empty()) { envoy_config_core_v3_Node_set_id(node_msg, StdStringToUpbString(node->id)); } if (!node->cluster.empty()) { envoy_config_core_v3_Node_set_cluster( node_msg, StdStringToUpbString(node->cluster)); } if (!node->metadata.object_value().empty()) { google_protobuf_Struct* metadata = envoy_config_core_v3_Node_mutable_metadata(node_msg, context.arena); PopulateMetadata(context, metadata, node->metadata.object_value()); } if (!node->locality_region.empty() || !node->locality_zone.empty() || !node->locality_sub_zone.empty()) { envoy_config_core_v3_Locality* locality = envoy_config_core_v3_Node_mutable_locality(node_msg, context.arena); if (!node->locality_region.empty()) { envoy_config_core_v3_Locality_set_region( locality, StdStringToUpbString(node->locality_region)); } if (!node->locality_zone.empty()) { envoy_config_core_v3_Locality_set_zone( locality, StdStringToUpbString(node->locality_zone)); } if (!node->locality_sub_zone.empty()) { envoy_config_core_v3_Locality_set_sub_zone( locality, StdStringToUpbString(node->locality_sub_zone)); } } } if (!context.use_v3) { PopulateBuildVersion(context, node_msg, build_version); } envoy_config_core_v3_Node_set_user_agent_name( node_msg, StdStringToUpbString(user_agent_name)); envoy_config_core_v3_Node_set_user_agent_version( node_msg, StdStringToUpbString(user_agent_version)); envoy_config_core_v3_Node_add_client_features( node_msg, upb_StringView_FromString("envoy.lb.does_not_support_overprovisioning"), context.arena); } void MaybeLogDiscoveryRequest( const XdsEncodingContext& context, const envoy_service_discovery_v3_DiscoveryRequest* request) { if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) && gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) { const upb_MessageDef* msg_type = envoy_service_discovery_v3_DiscoveryRequest_getmsgdef(context.symtab); char buf[10240]; upb_TextEncode(request, msg_type, nullptr, 0, buf, sizeof(buf)); gpr_log(GPR_DEBUG, "[xds_client %p] constructed ADS request: %s", context.client, buf); } } grpc_slice SerializeDiscoveryRequest( const XdsEncodingContext& context, envoy_service_discovery_v3_DiscoveryRequest* request) { size_t output_length; char* output = envoy_service_discovery_v3_DiscoveryRequest_serialize( request, context.arena, &output_length); return grpc_slice_from_copied_buffer(output, output_length); } } // namespace grpc_slice XdsApi::CreateAdsRequest( const XdsBootstrap::XdsServer& server, absl::string_view type_url, absl::string_view version, absl::string_view nonce, const std::vector& resource_names, grpc_error_handle error, bool populate_node) { upb::Arena arena; const XdsEncodingContext context = {client_, server, tracer_, symtab_->ptr(), arena.ptr(), server.ShouldUseV3(), certificate_provider_definition_map_}; // Create a request. envoy_service_discovery_v3_DiscoveryRequest* request = envoy_service_discovery_v3_DiscoveryRequest_new(arena.ptr()); // Set type_url. std::string type_url_str = absl::StrCat("type.googleapis.com/", type_url); envoy_service_discovery_v3_DiscoveryRequest_set_type_url( request, StdStringToUpbString(type_url_str)); // Set version_info. if (!version.empty()) { envoy_service_discovery_v3_DiscoveryRequest_set_version_info( request, StdStringToUpbString(version)); } // Set nonce. if (!nonce.empty()) { envoy_service_discovery_v3_DiscoveryRequest_set_response_nonce( request, StdStringToUpbString(nonce)); } // Set error_detail if it's a NACK. std::string error_string_storage; if (!GRPC_ERROR_IS_NONE(error)) { google_rpc_Status* error_detail = envoy_service_discovery_v3_DiscoveryRequest_mutable_error_detail( request, arena.ptr()); // Hard-code INVALID_ARGUMENT as the status code. // TODO(roth): If at some point we decide we care about this value, // we could attach a status code to the individual errors where we // generate them in the parsing code, and then use that here. google_rpc_Status_set_code(error_detail, GRPC_STATUS_INVALID_ARGUMENT); // Error description comes from the error that was passed in. error_string_storage = grpc_error_std_string(error); upb_StringView error_description = StdStringToUpbString(error_string_storage); google_rpc_Status_set_message(error_detail, error_description); GRPC_ERROR_UNREF(error); } // Populate node. if (populate_node) { envoy_config_core_v3_Node* node_msg = envoy_service_discovery_v3_DiscoveryRequest_mutable_node(request, arena.ptr()); PopulateNode(context, node_, build_version_, user_agent_name_, user_agent_version_, node_msg); envoy_config_core_v3_Node_add_client_features( node_msg, upb_StringView_FromString("xds.config.resource-in-sotw"), context.arena); } // Add resource_names. for (const std::string& resource_name : resource_names) { envoy_service_discovery_v3_DiscoveryRequest_add_resource_names( request, StdStringToUpbString(resource_name), arena.ptr()); } MaybeLogDiscoveryRequest(context, request); return SerializeDiscoveryRequest(context, request); } namespace { void MaybeLogDiscoveryResponse( const XdsEncodingContext& context, const envoy_service_discovery_v3_DiscoveryResponse* response) { if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) && gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) { const upb_MessageDef* msg_type = envoy_service_discovery_v3_DiscoveryResponse_getmsgdef(context.symtab); char buf[10240]; upb_TextEncode(response, msg_type, nullptr, 0, buf, sizeof(buf)); gpr_log(GPR_DEBUG, "[xds_client %p] received response: %s", context.client, buf); } } } // namespace absl::Status XdsApi::ParseAdsResponse(const XdsBootstrap::XdsServer& server, const grpc_slice& encoded_response, AdsResponseParserInterface* parser) { upb::Arena arena; const XdsEncodingContext context = {client_, server, tracer_, symtab_->ptr(), arena.ptr(), server.ShouldUseV3(), certificate_provider_definition_map_}; // Decode the response. const envoy_service_discovery_v3_DiscoveryResponse* response = envoy_service_discovery_v3_DiscoveryResponse_parse( reinterpret_cast(GRPC_SLICE_START_PTR(encoded_response)), GRPC_SLICE_LENGTH(encoded_response), arena.ptr()); // If decoding fails, report a fatal error and return. if (response == nullptr) { return absl::InvalidArgumentError("Can't decode DiscoveryResponse."); } MaybeLogDiscoveryResponse(context, response); // Report the type_url, version, nonce, and number of resources to the parser. AdsResponseParserInterface::AdsResponseFields fields; fields.type_url = std::string(absl::StripPrefix( UpbStringToAbsl( envoy_service_discovery_v3_DiscoveryResponse_type_url(response)), "type.googleapis.com/")); fields.version = UpbStringToStdString( envoy_service_discovery_v3_DiscoveryResponse_version_info(response)); fields.nonce = UpbStringToStdString( envoy_service_discovery_v3_DiscoveryResponse_nonce(response)); size_t num_resources; const google_protobuf_Any* const* resources = envoy_service_discovery_v3_DiscoveryResponse_resources(response, &num_resources); fields.num_resources = num_resources; absl::Status status = parser->ProcessAdsResponseFields(std::move(fields)); if (!status.ok()) return status; // Process each resource. for (size_t i = 0; i < num_resources; ++i) { absl::string_view type_url = absl::StripPrefix( UpbStringToAbsl(google_protobuf_Any_type_url(resources[i])), "type.googleapis.com/"); absl::string_view serialized_resource = UpbStringToAbsl(google_protobuf_Any_value(resources[i])); // Unwrap Resource messages, if so wrapped. if (type_url == "envoy.api.v2.Resource" || type_url == "envoy.service.discovery.v3.Resource") { const auto* resource_wrapper = envoy_service_discovery_v3_Resource_parse( serialized_resource.data(), serialized_resource.size(), arena.ptr()); if (resource_wrapper == nullptr) { return absl::InvalidArgumentError( "Can't decode Resource proto wrapper"); } const auto* resource = envoy_service_discovery_v3_Resource_resource(resource_wrapper); type_url = absl::StripPrefix( UpbStringToAbsl(google_protobuf_Any_type_url(resource)), "type.googleapis.com/"); serialized_resource = UpbStringToAbsl(google_protobuf_Any_value(resource)); } parser->ParseResource(context, i, type_url, serialized_resource); } return absl::OkStatus(); } namespace { void MaybeLogLrsRequest( const XdsEncodingContext& context, const envoy_service_load_stats_v3_LoadStatsRequest* request) { if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) && gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) { const upb_MessageDef* msg_type = envoy_service_load_stats_v3_LoadStatsRequest_getmsgdef(context.symtab); char buf[10240]; upb_TextEncode(request, msg_type, nullptr, 0, buf, sizeof(buf)); gpr_log(GPR_DEBUG, "[xds_client %p] constructed LRS request: %s", context.client, buf); } } grpc_slice SerializeLrsRequest( const XdsEncodingContext& context, const envoy_service_load_stats_v3_LoadStatsRequest* request) { size_t output_length; char* output = envoy_service_load_stats_v3_LoadStatsRequest_serialize( request, context.arena, &output_length); return grpc_slice_from_copied_buffer(output, output_length); } } // namespace grpc_slice XdsApi::CreateLrsInitialRequest( const XdsBootstrap::XdsServer& server) { upb::Arena arena; const XdsEncodingContext context = {client_, server, tracer_, symtab_->ptr(), arena.ptr(), server.ShouldUseV3(), certificate_provider_definition_map_}; // Create a request. envoy_service_load_stats_v3_LoadStatsRequest* request = envoy_service_load_stats_v3_LoadStatsRequest_new(arena.ptr()); // Populate node. envoy_config_core_v3_Node* node_msg = envoy_service_load_stats_v3_LoadStatsRequest_mutable_node(request, arena.ptr()); PopulateNode(context, node_, build_version_, user_agent_name_, user_agent_version_, node_msg); envoy_config_core_v3_Node_add_client_features( node_msg, upb_StringView_FromString("envoy.lrs.supports_send_all_clusters"), arena.ptr()); MaybeLogLrsRequest(context, request); return SerializeLrsRequest(context, request); } namespace { void LocalityStatsPopulate( const XdsEncodingContext& context, envoy_config_endpoint_v3_UpstreamLocalityStats* output, const XdsLocalityName& locality_name, const XdsClusterLocalityStats::Snapshot& snapshot) { // Set locality. envoy_config_core_v3_Locality* locality = envoy_config_endpoint_v3_UpstreamLocalityStats_mutable_locality( output, context.arena); if (!locality_name.region().empty()) { envoy_config_core_v3_Locality_set_region( locality, StdStringToUpbString(locality_name.region())); } if (!locality_name.zone().empty()) { envoy_config_core_v3_Locality_set_zone( locality, StdStringToUpbString(locality_name.zone())); } if (!locality_name.sub_zone().empty()) { envoy_config_core_v3_Locality_set_sub_zone( locality, StdStringToUpbString(locality_name.sub_zone())); } // Set total counts. envoy_config_endpoint_v3_UpstreamLocalityStats_set_total_successful_requests( output, snapshot.total_successful_requests); envoy_config_endpoint_v3_UpstreamLocalityStats_set_total_requests_in_progress( output, snapshot.total_requests_in_progress); envoy_config_endpoint_v3_UpstreamLocalityStats_set_total_error_requests( output, snapshot.total_error_requests); envoy_config_endpoint_v3_UpstreamLocalityStats_set_total_issued_requests( output, snapshot.total_issued_requests); // Add backend metrics. for (const auto& p : snapshot.backend_metrics) { const std::string& metric_name = p.first; const XdsClusterLocalityStats::BackendMetric& metric_value = p.second; envoy_config_endpoint_v3_EndpointLoadMetricStats* load_metric = envoy_config_endpoint_v3_UpstreamLocalityStats_add_load_metric_stats( output, context.arena); envoy_config_endpoint_v3_EndpointLoadMetricStats_set_metric_name( load_metric, StdStringToUpbString(metric_name)); envoy_config_endpoint_v3_EndpointLoadMetricStats_set_num_requests_finished_with_metric( load_metric, metric_value.num_requests_finished_with_metric); envoy_config_endpoint_v3_EndpointLoadMetricStats_set_total_metric_value( load_metric, metric_value.total_metric_value); } } } // namespace grpc_slice XdsApi::CreateLrsRequest( ClusterLoadReportMap cluster_load_report_map) { upb::Arena arena; // The xDS server info is not actually needed here, so we seed it with an // empty value. XdsBootstrap::XdsServer empty_server; const XdsEncodingContext context = {client_, empty_server, tracer_, symtab_->ptr(), arena.ptr(), false, certificate_provider_definition_map_}; // Create a request. envoy_service_load_stats_v3_LoadStatsRequest* request = envoy_service_load_stats_v3_LoadStatsRequest_new(arena.ptr()); for (auto& p : cluster_load_report_map) { const std::string& cluster_name = p.first.first; const std::string& eds_service_name = p.first.second; const ClusterLoadReport& load_report = p.second; // Add cluster stats. envoy_config_endpoint_v3_ClusterStats* cluster_stats = envoy_service_load_stats_v3_LoadStatsRequest_add_cluster_stats( request, arena.ptr()); // Set the cluster name. envoy_config_endpoint_v3_ClusterStats_set_cluster_name( cluster_stats, StdStringToUpbString(cluster_name)); // Set EDS service name, if non-empty. if (!eds_service_name.empty()) { envoy_config_endpoint_v3_ClusterStats_set_cluster_service_name( cluster_stats, StdStringToUpbString(eds_service_name)); } // Add locality stats. for (const auto& p : load_report.locality_stats) { const XdsLocalityName& locality_name = *p.first; const auto& snapshot = p.second; envoy_config_endpoint_v3_UpstreamLocalityStats* locality_stats = envoy_config_endpoint_v3_ClusterStats_add_upstream_locality_stats( cluster_stats, arena.ptr()); LocalityStatsPopulate(context, locality_stats, locality_name, snapshot); } // Add dropped requests. uint64_t total_dropped_requests = 0; for (const auto& p : load_report.dropped_requests.categorized_drops) { const std::string& category = p.first; const uint64_t count = p.second; envoy_config_endpoint_v3_ClusterStats_DroppedRequests* dropped_requests = envoy_config_endpoint_v3_ClusterStats_add_dropped_requests( cluster_stats, arena.ptr()); envoy_config_endpoint_v3_ClusterStats_DroppedRequests_set_category( dropped_requests, StdStringToUpbString(category)); envoy_config_endpoint_v3_ClusterStats_DroppedRequests_set_dropped_count( dropped_requests, count); total_dropped_requests += count; } total_dropped_requests += load_report.dropped_requests.uncategorized_drops; // Set total dropped requests. envoy_config_endpoint_v3_ClusterStats_set_total_dropped_requests( cluster_stats, total_dropped_requests); // Set real load report interval. gpr_timespec timespec = load_report.load_report_interval.as_timespec(); google_protobuf_Duration* load_report_interval = envoy_config_endpoint_v3_ClusterStats_mutable_load_report_interval( cluster_stats, arena.ptr()); google_protobuf_Duration_set_seconds(load_report_interval, timespec.tv_sec); google_protobuf_Duration_set_nanos(load_report_interval, timespec.tv_nsec); } MaybeLogLrsRequest(context, request); return SerializeLrsRequest(context, request); } grpc_error_handle XdsApi::ParseLrsResponse(const grpc_slice& encoded_response, bool* send_all_clusters, std::set* cluster_names, Duration* load_reporting_interval) { upb::Arena arena; // Decode the response. const envoy_service_load_stats_v3_LoadStatsResponse* decoded_response = envoy_service_load_stats_v3_LoadStatsResponse_parse( reinterpret_cast(GRPC_SLICE_START_PTR(encoded_response)), GRPC_SLICE_LENGTH(encoded_response), arena.ptr()); // Parse the response. if (decoded_response == nullptr) { return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Can't decode response."); } // Check send_all_clusters. if (envoy_service_load_stats_v3_LoadStatsResponse_send_all_clusters( decoded_response)) { *send_all_clusters = true; } else { // Store the cluster names. size_t size; const upb_StringView* clusters = envoy_service_load_stats_v3_LoadStatsResponse_clusters(decoded_response, &size); for (size_t i = 0; i < size; ++i) { cluster_names->emplace(UpbStringToStdString(clusters[i])); } } // Get the load report interval. const google_protobuf_Duration* load_reporting_interval_duration = envoy_service_load_stats_v3_LoadStatsResponse_load_reporting_interval( decoded_response); *load_reporting_interval = Duration::FromSecondsAndNanoseconds( google_protobuf_Duration_seconds(load_reporting_interval_duration), google_protobuf_Duration_nanos(load_reporting_interval_duration)); return GRPC_ERROR_NONE; } namespace { google_protobuf_Timestamp* EncodeTimestamp(const XdsEncodingContext& context, Timestamp value) { google_protobuf_Timestamp* timestamp = google_protobuf_Timestamp_new(context.arena); gpr_timespec timespec = value.as_timespec(GPR_CLOCK_REALTIME); google_protobuf_Timestamp_set_seconds(timestamp, timespec.tv_sec); google_protobuf_Timestamp_set_nanos(timestamp, timespec.tv_nsec); return timestamp; } } // namespace std::string XdsApi::AssembleClientConfig( const ResourceTypeMetadataMap& resource_type_metadata_map) { upb::Arena arena; // Create the ClientConfig for resource metadata from XdsClient auto* client_config = envoy_service_status_v3_ClientConfig_new(arena.ptr()); // Fill-in the node information auto* node = envoy_service_status_v3_ClientConfig_mutable_node(client_config, arena.ptr()); // The xDS server info is not actually needed here, so we seed it with an // empty value. XdsBootstrap::XdsServer empty_server; const XdsEncodingContext context = {client_, empty_server, tracer_, symtab_->ptr(), arena.ptr(), true, certificate_provider_definition_map_}; PopulateNode(context, node_, build_version_, user_agent_name_, user_agent_version_, node); // Dump each resource. std::vector type_url_storage; for (const auto& p : resource_type_metadata_map) { absl::string_view type_url = p.first; const ResourceMetadataMap& resource_metadata_map = p.second; type_url_storage.emplace_back( absl::StrCat("type.googleapis.com/", type_url)); for (const auto& q : resource_metadata_map) { absl::string_view resource_name = q.first; const ResourceMetadata& metadata = *q.second; auto* entry = envoy_service_status_v3_ClientConfig_add_generic_xds_configs( client_config, context.arena); envoy_service_status_v3_ClientConfig_GenericXdsConfig_set_type_url( entry, StdStringToUpbString(type_url_storage.back())); envoy_service_status_v3_ClientConfig_GenericXdsConfig_set_name( entry, StdStringToUpbString(resource_name)); envoy_service_status_v3_ClientConfig_GenericXdsConfig_set_client_status( entry, metadata.client_status); if (!metadata.serialized_proto.empty()) { envoy_service_status_v3_ClientConfig_GenericXdsConfig_set_version_info( entry, StdStringToUpbString(metadata.version)); envoy_service_status_v3_ClientConfig_GenericXdsConfig_set_last_updated( entry, EncodeTimestamp(context, metadata.update_time)); auto* any_field = envoy_service_status_v3_ClientConfig_GenericXdsConfig_mutable_xds_config( entry, context.arena); google_protobuf_Any_set_type_url( any_field, StdStringToUpbString(type_url_storage.back())); google_protobuf_Any_set_value( any_field, StdStringToUpbString(metadata.serialized_proto)); } if (metadata.client_status == XdsApi::ResourceMetadata::NACKED) { auto* update_failure_state = envoy_admin_v3_UpdateFailureState_new(context.arena); envoy_admin_v3_UpdateFailureState_set_details( update_failure_state, StdStringToUpbString(metadata.failed_details)); envoy_admin_v3_UpdateFailureState_set_version_info( update_failure_state, StdStringToUpbString(metadata.failed_version)); envoy_admin_v3_UpdateFailureState_set_last_update_attempt( update_failure_state, EncodeTimestamp(context, metadata.failed_update_time)); envoy_service_status_v3_ClientConfig_GenericXdsConfig_set_error_state( entry, update_failure_state); } } } // Serialize the upb message to bytes size_t output_length; char* output = envoy_service_status_v3_ClientConfig_serialize( client_config, arena.ptr(), &output_length); return std::string(output, output_length); } } // namespace grpc_core