blob: 6da587ed9aa5c0e1d25f58fb2763457307911427 [file] [log] [blame]
//
// 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 <stdint.h>
#include <stdlib.h>
#include <algorithm>
#include <set>
#include <string>
#include <vector>
#include "absl/strings/str_cat.h"
#include "absl/strings/strip.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/base/string_view.h"
#include "upb/reflection/def.h"
#include "upb/text/encode.h"
#include "upb/upb.hpp"
#include <grpc/status.h>
#include <grpc/support/log.h>
#include <grpc/support/time.h>
#include "src/core/ext/xds/upb_utils.h"
#include "src/core/ext/xds/xds_client.h"
#include "src/core/lib/json/json.h"
// IWYU pragma: no_include "upb/msg_internal.h"
namespace grpc_core {
XdsApi::XdsApi(XdsClient* client, TraceFlag* tracer,
const XdsBootstrap::Node* node, upb::SymbolTable* symtab,
std::string user_agent_name, std::string user_agent_version)
: client_(client),
tracer_(tracer),
node_(node),
symtab_(symtab),
user_agent_name_(std::move(user_agent_name)),
user_agent_version_(std::move(user_agent_version)) {}
namespace {
struct XdsApiContext {
XdsClient* client;
TraceFlag* tracer;
upb_DefPool* symtab;
upb_Arena* arena;
};
void PopulateMetadataValue(const XdsApiContext& context,
google_protobuf_Value* value_pb, const Json& value);
void PopulateListValue(const XdsApiContext& 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 XdsApiContext& 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 XdsApiContext& context,
google_protobuf_Value* value_pb, const Json& value) {
switch (value.type()) {
case Json::Type::kNull:
google_protobuf_Value_set_null_value(value_pb, 0);
break;
case Json::Type::kNumber:
google_protobuf_Value_set_number_value(
value_pb, strtod(value.string().c_str(), nullptr));
break;
case Json::Type::kString:
google_protobuf_Value_set_string_value(
value_pb, StdStringToUpbString(value.string()));
break;
case Json::Type::kBoolean:
google_protobuf_Value_set_bool_value(value_pb, value.boolean());
break;
case Json::Type::kObject: {
google_protobuf_Struct* struct_value =
google_protobuf_Value_mutable_struct_value(value_pb, context.arena);
PopulateMetadata(context, struct_value, value.object());
break;
}
case Json::Type::kArray: {
google_protobuf_ListValue* list_value =
google_protobuf_Value_mutable_list_value(value_pb, context.arena);
PopulateListValue(context, list_value, value.array());
break;
}
}
}
void 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) {
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().empty()) {
google_protobuf_Struct* metadata =
envoy_config_core_v3_Node_mutable_metadata(node_msg, context.arena);
PopulateMetadata(context, metadata, node->metadata());
}
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()));
}
}
}
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 XdsApiContext& 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);
}
}
std::string SerializeDiscoveryRequest(
const XdsApiContext& 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 std::string(output, output_length);
}
} // namespace
std::string XdsApi::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) {
upb::Arena arena;
const XdsApiContext context = {client_, tracer_, symtab_->ptr(), arena.ptr()};
// 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 (!status.ok()) {
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 status that was passed in.
error_string_storage = std::string(status.message());
upb_StringView error_description =
StdStringToUpbString(error_string_storage);
google_rpc_Status_set_message(error_detail, error_description);
}
// 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_, 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 XdsApiContext& 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(absl::string_view encoded_response,
AdsResponseParserInterface* parser) {
upb::Arena arena;
const XdsApiContext context = {client_, tracer_, symtab_->ptr(), arena.ptr()};
// Decode the response.
const envoy_service_discovery_v3_DiscoveryResponse* response =
envoy_service_discovery_v3_DiscoveryResponse_parse(
encoded_response.data(), encoded_response.size(), 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.
absl::string_view resource_name;
if (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) {
parser->ResourceWrapperParsingFailed(
i, "Can't decode Resource proto wrapper");
continue;
}
const auto* resource =
envoy_service_discovery_v3_Resource_resource(resource_wrapper);
if (resource == nullptr) {
parser->ResourceWrapperParsingFailed(
i, "No resource present in Resource proto wrapper");
continue;
}
type_url = absl::StripPrefix(
UpbStringToAbsl(google_protobuf_Any_type_url(resource)),
"type.googleapis.com/");
serialized_resource =
UpbStringToAbsl(google_protobuf_Any_value(resource));
resource_name = UpbStringToAbsl(
envoy_service_discovery_v3_Resource_name(resource_wrapper));
}
parser->ParseResource(context.arena, i, type_url, resource_name,
serialized_resource);
}
return absl::OkStatus();
}
namespace {
void MaybeLogLrsRequest(
const XdsApiContext& 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);
}
}
std::string SerializeLrsRequest(
const XdsApiContext& 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 std::string(output, output_length);
}
} // namespace
std::string XdsApi::CreateLrsInitialRequest() {
upb::Arena arena;
const XdsApiContext context = {client_, tracer_, symtab_->ptr(), arena.ptr()};
// 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_, 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 XdsApiContext& 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
std::string XdsApi::CreateLrsRequest(
ClusterLoadReportMap cluster_load_report_map) {
upb::Arena arena;
const XdsApiContext context = {client_, tracer_, symtab_->ptr(), arena.ptr()};
// 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);
}
namespace {
void MaybeLogLrsResponse(
const XdsApiContext& context,
const envoy_service_load_stats_v3_LoadStatsResponse* response) {
if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) &&
gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
const upb_MessageDef* msg_type =
envoy_service_load_stats_v3_LoadStatsResponse_getmsgdef(context.symtab);
char buf[10240];
upb_TextEncode(response, msg_type, nullptr, 0, buf, sizeof(buf));
gpr_log(GPR_DEBUG, "[xds_client %p] received LRS response: %s",
context.client, buf);
}
}
} // namespace
absl::Status XdsApi::ParseLrsResponse(absl::string_view 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(
encoded_response.data(), encoded_response.size(), arena.ptr());
// Parse the response.
if (decoded_response == nullptr) {
return absl::UnavailableError("Can't decode response.");
}
const XdsApiContext context = {client_, tracer_, symtab_->ptr(), arena.ptr()};
MaybeLogLrsResponse(context, decoded_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 absl::OkStatus();
}
namespace {
google_protobuf_Timestamp* EncodeTimestamp(const XdsApiContext& 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());
const XdsApiContext context = {client_, tracer_, symtab_->ptr(), arena.ptr()};
PopulateNode(context, node_, 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