| // |
| // 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 <grpc/support/port_platform.h> |
| |
| #include "src/core/ext/xds/xds_api.h" |
| |
| #include <set> |
| #include <string> |
| #include <vector> |
| |
| #include "absl/strings/str_cat.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 "envoy/service/status/v3/csds.upbdefs.h" |
| #include "google/protobuf/any.upb.h" |
| #include "google/protobuf/struct.upb.h" |
| #include "google/protobuf/timestamp.upb.h" |
| #include "google/protobuf/wrappers.upb.h" |
| #include "google/rpc/status.upb.h" |
| #include "upb/text_encode.h" |
| #include "upb/upb.h" |
| #include "upb/upb.hpp" |
| |
| #include <grpc/impl/codegen/log.h> |
| #include <grpc/support/alloc.h> |
| #include <grpc/support/string_util.h> |
| |
| #include "src/core/ext/xds/upb_utils.h" |
| #include "src/core/ext/xds/xds_common_types.h" |
| #include "src/core/ext/xds/xds_resource_type.h" |
| #include "src/core/ext/xds/xds_routing.h" |
| #include "src/core/lib/address_utils/parse_address.h" |
| #include "src/core/lib/address_utils/sockaddr_utils.h" |
| #include "src/core/lib/gpr/env.h" |
| #include "src/core/lib/gpr/string.h" |
| #include "src/core/lib/gprpp/host_port.h" |
| #include "src/core/lib/iomgr/error.h" |
| #include "src/core/lib/iomgr/sockaddr.h" |
| #include "src/core/lib/iomgr/socket_utils.h" |
| #include "src/core/lib/slice/slice_internal.h" |
| #include "src/core/lib/uri/uri_parser.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<std::string>& 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 (error != GRPC_ERROR_NONE) { |
| 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); |
| } |
| // 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<const char*>(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])); |
| 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<std::string>* 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<const char*>(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<std::string> 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 |