Revert "XdsClient: convert xDS bootstrap code to use new JSON parsing API (#30431)" (#30924)

This reverts commit 04ddf3d0b7.
pull/30899/head^2
Craig Tiller 3 years ago committed by GitHub
parent 89de6312cd
commit 3332e7e48d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      BUILD
  2. 4
      src/core/ext/filters/client_channel/lb_policy/xds/cds.cc
  3. 31
      src/core/ext/filters/client_channel/lb_policy/xds/xds_cluster_impl.cc
  4. 21
      src/core/ext/filters/client_channel/lb_policy/xds/xds_cluster_resolver.cc
  5. 5
      src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc
  6. 57
      src/core/ext/xds/certificate_provider_store.cc
  7. 6
      src/core/ext/xds/certificate_provider_store.h
  8. 28
      src/core/ext/xds/xds_api.cc
  9. 48
      src/core/ext/xds/xds_bootstrap.cc
  10. 80
      src/core/ext/xds/xds_bootstrap.h
  11. 621
      src/core/ext/xds/xds_bootstrap_grpc.cc
  12. 127
      src/core/ext/xds/xds_bootstrap_grpc.h
  13. 105
      src/core/ext/xds/xds_client.cc
  14. 14
      src/core/ext/xds/xds_client.h
  15. 18
      src/core/ext/xds/xds_client_grpc.cc
  16. 4
      src/core/ext/xds/xds_client_grpc.h
  17. 8
      src/core/ext/xds/xds_client_stats.cc
  18. 23
      src/core/ext/xds/xds_cluster.cc
  19. 4
      src/core/ext/xds/xds_cluster.h
  20. 11
      src/core/ext/xds/xds_transport_grpc.cc
  21. 423
      test/core/xds/xds_bootstrap_test.cc
  22. 4
      test/core/xds/xds_lb_policy_registry_test.cc

@ -4581,7 +4581,6 @@ grpc_cc_library(
"grpc_transport_chttp2_client_connector", "grpc_transport_chttp2_client_connector",
"iomgr_timer", "iomgr_timer",
"json", "json",
"json_object_loader",
"json_util", "json_util",
"lb_policy_registry", "lb_policy_registry",
"match", "match",
@ -4824,7 +4823,6 @@ grpc_cc_library(
"grpc_trace", "grpc_trace",
"grpc_xds_client", "grpc_xds_client",
"json", "json",
"json_object_loader",
"lb_policy", "lb_policy",
"lb_policy_factory", "lb_policy_factory",
"lb_policy_registry", "lb_policy_registry",
@ -4866,7 +4864,6 @@ grpc_cc_library(
"grpc_trace", "grpc_trace",
"grpc_xds_client", "grpc_xds_client",
"json", "json",
"json_object_loader",
"lb_policy", "lb_policy",
"lb_policy_factory", "lb_policy_factory",
"lb_policy_registry", "lb_policy_registry",

@ -445,8 +445,8 @@ absl::StatusOr<bool> CdsLb::GenerateDiscoveryMechanismForCluster(
break; break;
} }
if (state.update->lrs_load_reporting_server.has_value()) { if (state.update->lrs_load_reporting_server.has_value()) {
mechanism["lrsLoadReportingServer"] = mechanism["lrsLoadReportingServer"] = GrpcXdsBootstrap::XdsServerToJson(
state.update->lrs_load_reporting_server->ToJson(); *state.update->lrs_load_reporting_server);
} }
discovery_mechanisms->emplace_back(std::move(mechanism)); discovery_mechanisms->emplace_back(std::move(mechanism));
return true; return true;

@ -43,6 +43,7 @@
#include "src/core/ext/filters/client_channel/lb_policy/child_policy_handler.h" #include "src/core/ext/filters/client_channel/lb_policy/child_policy_handler.h"
#include "src/core/ext/filters/client_channel/lb_policy/xds/xds.h" #include "src/core/ext/filters/client_channel/lb_policy/xds/xds.h"
#include "src/core/ext/filters/client_channel/lb_policy/xds/xds_channel_args.h" #include "src/core/ext/filters/client_channel/lb_policy/xds/xds_channel_args.h"
#include "src/core/ext/xds/xds_bootstrap.h"
#include "src/core/ext/xds/xds_bootstrap_grpc.h" #include "src/core/ext/xds/xds_bootstrap_grpc.h"
#include "src/core/ext/xds/xds_client.h" #include "src/core/ext/xds/xds_client.h"
#include "src/core/ext/xds/xds_client_grpc.h" #include "src/core/ext/xds/xds_client_grpc.h"
@ -57,9 +58,9 @@
#include "src/core/lib/gprpp/ref_counted.h" #include "src/core/lib/gprpp/ref_counted.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h" #include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/gprpp/sync.h" #include "src/core/lib/gprpp/sync.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/iomgr/pollset_set.h" #include "src/core/lib/iomgr/pollset_set.h"
#include "src/core/lib/json/json.h" #include "src/core/lib/json/json.h"
#include "src/core/lib/json/json_object_loader.h"
#include "src/core/lib/load_balancing/lb_policy.h" #include "src/core/lib/load_balancing/lb_policy.h"
#include "src/core/lib/load_balancing/lb_policy_factory.h" #include "src/core/lib/load_balancing/lb_policy_factory.h"
#include "src/core/lib/load_balancing/lb_policy_registry.h" #include "src/core/lib/load_balancing/lb_policy_registry.h"
@ -148,7 +149,7 @@ class XdsClusterImplLbConfig : public LoadBalancingPolicy::Config {
XdsClusterImplLbConfig( XdsClusterImplLbConfig(
RefCountedPtr<LoadBalancingPolicy::Config> child_policy, RefCountedPtr<LoadBalancingPolicy::Config> child_policy,
std::string cluster_name, std::string eds_service_name, std::string cluster_name, std::string eds_service_name,
absl::optional<GrpcXdsBootstrap::GrpcXdsServer> lrs_load_reporting_server, absl::optional<XdsBootstrap::XdsServer> lrs_load_reporting_server,
uint32_t max_concurrent_requests, uint32_t max_concurrent_requests,
RefCountedPtr<XdsEndpointResource::DropConfig> drop_config) RefCountedPtr<XdsEndpointResource::DropConfig> drop_config)
: child_policy_(std::move(child_policy)), : child_policy_(std::move(child_policy)),
@ -165,8 +166,8 @@ class XdsClusterImplLbConfig : public LoadBalancingPolicy::Config {
} }
const std::string& cluster_name() const { return cluster_name_; } const std::string& cluster_name() const { return cluster_name_; }
const std::string& eds_service_name() const { return eds_service_name_; } const std::string& eds_service_name() const { return eds_service_name_; }
const absl::optional<GrpcXdsBootstrap::GrpcXdsServer>& const absl::optional<XdsBootstrap::XdsServer>& lrs_load_reporting_server()
lrs_load_reporting_server() const { const {
return lrs_load_reporting_server_; return lrs_load_reporting_server_;
}; };
uint32_t max_concurrent_requests() const { return max_concurrent_requests_; } uint32_t max_concurrent_requests() const { return max_concurrent_requests_; }
@ -178,7 +179,7 @@ class XdsClusterImplLbConfig : public LoadBalancingPolicy::Config {
RefCountedPtr<LoadBalancingPolicy::Config> child_policy_; RefCountedPtr<LoadBalancingPolicy::Config> child_policy_;
std::string cluster_name_; std::string cluster_name_;
std::string eds_service_name_; std::string eds_service_name_;
absl::optional<GrpcXdsBootstrap::GrpcXdsServer> lrs_load_reporting_server_; absl::optional<XdsBootstrap::XdsServer> lrs_load_reporting_server_;
uint32_t max_concurrent_requests_; uint32_t max_concurrent_requests_;
RefCountedPtr<XdsEndpointResource::DropConfig> drop_config_; RefCountedPtr<XdsEndpointResource::DropConfig> drop_config_;
}; };
@ -501,8 +502,7 @@ void XdsClusterImplLb::UpdateLocked(UpdateArgs args) {
"[xds_cluster_impl_lb %p] Failed to get cluster drop stats for " "[xds_cluster_impl_lb %p] Failed to get cluster drop stats for "
"LRS server %s, cluster %s, EDS service name %s, load " "LRS server %s, cluster %s, EDS service name %s, load "
"reporting for drops will not be done.", "reporting for drops will not be done.",
this, this, config_->lrs_load_reporting_server()->server_uri.c_str(),
config_->lrs_load_reporting_server()->server_uri().c_str(),
config_->cluster_name().c_str(), config_->cluster_name().c_str(),
config_->eds_service_name().c_str()); config_->eds_service_name().c_str());
} }
@ -641,8 +641,7 @@ RefCountedPtr<SubchannelInterface> XdsClusterImplLb::Helper::CreateSubchannel(
"not be generated (not wrapping subchannel)", "not be generated (not wrapping subchannel)",
this, this,
xds_cluster_impl_policy_->config_->lrs_load_reporting_server() xds_cluster_impl_policy_->config_->lrs_load_reporting_server()
->server_uri() ->server_uri.c_str(),
.c_str(),
xds_cluster_impl_policy_->config_->cluster_name().c_str(), xds_cluster_impl_policy_->config_->cluster_name().c_str(),
xds_cluster_impl_policy_->config_->eds_service_name().c_str()); xds_cluster_impl_policy_->config_->eds_service_name().c_str());
} }
@ -758,21 +757,21 @@ class XdsClusterImplLbFactory : public LoadBalancingPolicyFactory {
} }
} }
// LRS load reporting server name. // LRS load reporting server name.
absl::optional<GrpcXdsBootstrap::GrpcXdsServer> lrs_load_reporting_server; absl::optional<XdsBootstrap::XdsServer> lrs_load_reporting_server;
it = json.object_value().find("lrsLoadReportingServer"); it = json.object_value().find("lrsLoadReportingServer");
if (it != json.object_value().end()) { if (it != json.object_value().end()) {
if (it->second.type() != Json::Type::OBJECT) { if (it->second.type() != Json::Type::OBJECT) {
errors.emplace_back( errors.emplace_back(
"field:lrsLoadReportingServer error:type should be object"); "field:lrsLoadReportingServer error:type should be object");
} else { } else {
auto xds_server = grpc_error_handle parser_error;
LoadFromJson<GrpcXdsBootstrap::GrpcXdsServer>(it->second); lrs_load_reporting_server = GrpcXdsBootstrap::XdsServerParse(
if (!xds_server.ok()) { it->second.object_value(), &parser_error);
if (!GRPC_ERROR_IS_NONE(parser_error)) {
errors.emplace_back( errors.emplace_back(
absl::StrCat("error parsing lrs_load_reporting_server: ", absl::StrCat("error parsing lrs_load_reporting_server: ",
xds_server.status().ToString())); grpc_error_std_string(parser_error)));
} else { GRPC_ERROR_UNREF(parser_error);
lrs_load_reporting_server = std::move(*xds_server);
} }
} }
} }

@ -46,6 +46,7 @@
#include "src/core/ext/filters/client_channel/lb_policy/xds/xds.h" #include "src/core/ext/filters/client_channel/lb_policy/xds/xds.h"
#include "src/core/ext/filters/client_channel/lb_policy/xds/xds_channel_args.h" #include "src/core/ext/filters/client_channel/lb_policy/xds/xds_channel_args.h"
#include "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h" #include "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h"
#include "src/core/ext/xds/xds_bootstrap.h"
#include "src/core/ext/xds/xds_bootstrap_grpc.h" #include "src/core/ext/xds/xds_bootstrap_grpc.h"
#include "src/core/ext/xds/xds_client.h" #include "src/core/ext/xds/xds_client.h"
#include "src/core/ext/xds/xds_client_grpc.h" #include "src/core/ext/xds/xds_client_grpc.h"
@ -63,7 +64,6 @@
#include "src/core/lib/iomgr/error.h" #include "src/core/lib/iomgr/error.h"
#include "src/core/lib/iomgr/pollset_set.h" #include "src/core/lib/iomgr/pollset_set.h"
#include "src/core/lib/json/json.h" #include "src/core/lib/json/json.h"
#include "src/core/lib/json/json_object_loader.h"
#include "src/core/lib/load_balancing/lb_policy.h" #include "src/core/lib/load_balancing/lb_policy.h"
#include "src/core/lib/load_balancing/lb_policy_factory.h" #include "src/core/lib/load_balancing/lb_policy_factory.h"
#include "src/core/lib/load_balancing/lb_policy_registry.h" #include "src/core/lib/load_balancing/lb_policy_registry.h"
@ -92,7 +92,7 @@ class XdsClusterResolverLbConfig : public LoadBalancingPolicy::Config {
public: public:
struct DiscoveryMechanism { struct DiscoveryMechanism {
std::string cluster_name; std::string cluster_name;
absl::optional<GrpcXdsBootstrap::GrpcXdsServer> lrs_load_reporting_server; absl::optional<XdsBootstrap::XdsServer> lrs_load_reporting_server;
uint32_t max_concurrent_requests; uint32_t max_concurrent_requests;
enum DiscoveryMechanismType { enum DiscoveryMechanismType {
EDS, EDS,
@ -926,7 +926,8 @@ XdsClusterResolverLb::CreateChildPolicyConfigLocked() {
} }
if (discovery_config.lrs_load_reporting_server.has_value()) { if (discovery_config.lrs_load_reporting_server.has_value()) {
xds_cluster_impl_config["lrsLoadReportingServer"] = xds_cluster_impl_config["lrsLoadReportingServer"] =
discovery_config.lrs_load_reporting_server->ToJson(); GrpcXdsBootstrap::XdsServerToJson(
*discovery_config.lrs_load_reporting_server);
} }
Json locality_picking_policy; Json locality_picking_policy;
if (XdsOutlierDetectionEnabled()) { if (XdsOutlierDetectionEnabled()) {
@ -1192,15 +1193,13 @@ class XdsClusterResolverLbFactory : public LoadBalancingPolicyFactory {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:lrsLoadReportingServer error:type should be object")); "field:lrsLoadReportingServer error:type should be object"));
} else { } else {
auto xds_server = grpc_error_handle parse_error;
LoadFromJson<GrpcXdsBootstrap::GrpcXdsServer>(it->second);
if (!xds_server.ok()) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_CPP_STRING(
absl::StrCat("error parsing lrs_load_reporting_server: ",
xds_server.status().ToString())));
} else {
discovery_mechanism->lrs_load_reporting_server.emplace( discovery_mechanism->lrs_load_reporting_server.emplace(
std::move(*xds_server)); GrpcXdsBootstrap::XdsServerParse(it->second, &parse_error));
if (!GRPC_ERROR_IS_NONE(parse_error)) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_CPP_STRING(
absl::StrCat("errors parsing lrs_load_reporting_server")));
error_list.push_back(parse_error);
} }
} }
} }

@ -814,8 +814,7 @@ void XdsResolver::StartLocked() {
if (!uri_.authority().empty()) { if (!uri_.authority().empty()) {
// target_uri.authority is set case // target_uri.authority is set case
const auto* authority_config = const auto* authority_config =
static_cast<const GrpcXdsBootstrap::GrpcAuthority*>( xds_client_->bootstrap().LookupAuthority(uri_.authority());
xds_client_->bootstrap().LookupAuthority(uri_.authority()));
if (authority_config == nullptr) { if (authority_config == nullptr) {
absl::Status status = absl::UnavailableError( absl::Status status = absl::UnavailableError(
absl::StrCat("Invalid target URI -- authority not found for ", absl::StrCat("Invalid target URI -- authority not found for ",
@ -828,7 +827,7 @@ void XdsResolver::StartLocked() {
return; return;
} }
std::string name_template = std::string name_template =
authority_config->client_listener_resource_name_template(); authority_config->client_listener_resource_name_template;
if (name_template.empty()) { if (name_template.empty()) {
name_template = absl::StrCat( name_template = absl::StrCat(
"xdstp://", URI::PercentEncodeAuthority(uri_.authority()), "xdstp://", URI::PercentEncodeAuthority(uri_.authority()),

@ -20,70 +20,13 @@
#include "src/core/ext/xds/certificate_provider_store.h" #include "src/core/ext/xds/certificate_provider_store.h"
#include "absl/strings/str_cat.h"
#include <grpc/support/log.h> #include <grpc/support/log.h>
#include "src/core/lib/config/core_configuration.h" #include "src/core/lib/config/core_configuration.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/security/certificate_provider/certificate_provider_registry.h" #include "src/core/lib/security/certificate_provider/certificate_provider_registry.h"
namespace grpc_core { namespace grpc_core {
//
// CertificateProviderStore::PluginDefinition
//
const JsonLoaderInterface*
CertificateProviderStore::PluginDefinition::JsonLoader(const JsonArgs&) {
static const auto* loader =
JsonObjectLoader<PluginDefinition>()
.Field("plugin_name", &PluginDefinition::plugin_name)
.Finish();
return loader;
}
void CertificateProviderStore::PluginDefinition::JsonPostLoad(
const Json& json, const JsonArgs&, ErrorList* errors) {
// Check that plugin is supported.
CertificateProviderFactory* factory = nullptr;
if (!plugin_name.empty()) {
ScopedField field(errors, ".plugin_name");
factory = CoreConfiguration::Get()
.certificate_provider_registry()
.LookupCertificateProviderFactory(plugin_name);
if (factory == nullptr) {
errors->AddError(absl::StrCat("Unrecognized plugin name: ", plugin_name));
return; // No point checking config.
}
}
// Parse the config field.
{
ScopedField field(errors, ".config");
auto it = json.object_value().find("config");
// The config field is optional; if not present, we use an empty JSON
// object.
Json::Object config_json;
if (it != json.object_value().end()) {
if (it->second.type() != Json::Type::OBJECT) {
errors->AddError("is not an object");
return; // No point parsing config.
} else {
config_json = it->second.object_value();
}
}
if (factory == nullptr) return;
// Use plugin to validate and parse config.
grpc_error_handle parse_error = GRPC_ERROR_NONE;
config =
factory->CreateCertificateProviderConfig(config_json, &parse_error);
if (!GRPC_ERROR_IS_NONE(parse_error)) {
errors->AddError(grpc_error_std_string(parse_error));
GRPC_ERROR_UNREF(parse_error);
}
}
}
// //
// CertificateProviderStore::CertificateProviderWrapper // CertificateProviderStore::CertificateProviderWrapper
// //

@ -36,9 +36,6 @@
#include "src/core/lib/gprpp/sync.h" #include "src/core/lib/gprpp/sync.h"
#include "src/core/lib/gprpp/unique_type_name.h" #include "src/core/lib/gprpp/unique_type_name.h"
#include "src/core/lib/iomgr/iomgr_fwd.h" #include "src/core/lib/iomgr/iomgr_fwd.h"
#include "src/core/lib/json/json.h"
#include "src/core/lib/json/json_args.h"
#include "src/core/lib/json/json_object_loader.h"
#include "src/core/lib/security/certificate_provider/certificate_provider_factory.h" #include "src/core/lib/security/certificate_provider/certificate_provider_factory.h"
#include "src/core/lib/security/credentials/tls/grpc_tls_certificate_distributor.h" #include "src/core/lib/security/credentials/tls/grpc_tls_certificate_distributor.h"
#include "src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.h" #include "src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.h"
@ -52,9 +49,6 @@ class CertificateProviderStore
struct PluginDefinition { struct PluginDefinition {
std::string plugin_name; std::string plugin_name;
RefCountedPtr<CertificateProviderFactory::Config> config; RefCountedPtr<CertificateProviderFactory::Config> config;
static const JsonLoaderInterface* JsonLoader(const JsonArgs&);
void JsonPostLoad(const Json& json, const JsonArgs&, ErrorList* errors);
}; };
// Maps plugin instance (opaque) name to plugin defition. // Maps plugin instance (opaque) name to plugin defition.

@ -201,34 +201,34 @@ void PopulateNode(const XdsApiContext& context, const XdsBootstrap::Node* node,
const std::string& user_agent_version, const std::string& user_agent_version,
envoy_config_core_v3_Node* node_msg) { envoy_config_core_v3_Node* node_msg) {
if (node != nullptr) { if (node != nullptr) {
if (!node->id().empty()) { if (!node->id.empty()) {
envoy_config_core_v3_Node_set_id(node_msg, envoy_config_core_v3_Node_set_id(node_msg,
StdStringToUpbString(node->id())); StdStringToUpbString(node->id));
} }
if (!node->cluster().empty()) { if (!node->cluster.empty()) {
envoy_config_core_v3_Node_set_cluster( envoy_config_core_v3_Node_set_cluster(
node_msg, StdStringToUpbString(node->cluster())); node_msg, StdStringToUpbString(node->cluster));
} }
if (!node->metadata().empty()) { if (!node->metadata.object_value().empty()) {
google_protobuf_Struct* metadata = google_protobuf_Struct* metadata =
envoy_config_core_v3_Node_mutable_metadata(node_msg, context.arena); envoy_config_core_v3_Node_mutable_metadata(node_msg, context.arena);
PopulateMetadata(context, metadata, node->metadata()); PopulateMetadata(context, metadata, node->metadata.object_value());
} }
if (!node->locality_region().empty() || !node->locality_zone().empty() || if (!node->locality_region.empty() || !node->locality_zone.empty() ||
!node->locality_sub_zone().empty()) { !node->locality_sub_zone.empty()) {
envoy_config_core_v3_Locality* locality = envoy_config_core_v3_Locality* locality =
envoy_config_core_v3_Node_mutable_locality(node_msg, context.arena); envoy_config_core_v3_Node_mutable_locality(node_msg, context.arena);
if (!node->locality_region().empty()) { if (!node->locality_region.empty()) {
envoy_config_core_v3_Locality_set_region( envoy_config_core_v3_Locality_set_region(
locality, StdStringToUpbString(node->locality_region())); locality, StdStringToUpbString(node->locality_region));
} }
if (!node->locality_zone().empty()) { if (!node->locality_zone.empty()) {
envoy_config_core_v3_Locality_set_zone( envoy_config_core_v3_Locality_set_zone(
locality, StdStringToUpbString(node->locality_zone())); locality, StdStringToUpbString(node->locality_zone));
} }
if (!node->locality_sub_zone().empty()) { if (!node->locality_sub_zone.empty()) {
envoy_config_core_v3_Locality_set_sub_zone( envoy_config_core_v3_Locality_set_sub_zone(
locality, StdStringToUpbString(node->locality_sub_zone())); locality, StdStringToUpbString(node->locality_sub_zone));
} }
} }
} }

@ -18,6 +18,12 @@
#include "src/core/ext/xds/xds_bootstrap.h" #include "src/core/ext/xds/xds_bootstrap.h"
#include <set>
#include <utility>
#include <vector>
#include "absl/strings/string_view.h"
#include <grpc/support/alloc.h> #include <grpc/support/alloc.h>
#include "src/core/lib/gpr/env.h" #include "src/core/lib/gpr/env.h"
@ -35,4 +41,46 @@ bool XdsFederationEnabled() {
return parse_succeeded && parsed_value; return parse_succeeded && parsed_value;
} }
//
// XdsBootstrap::XdsServer
//
constexpr absl::string_view XdsBootstrap::XdsServer::kServerFeatureXdsV3;
constexpr absl::string_view
XdsBootstrap::XdsServer::kServerFeatureIgnoreResourceDeletion;
bool XdsBootstrap::XdsServer::ShouldUseV3() const {
return server_features.find(std::string(kServerFeatureXdsV3)) !=
server_features.end();
}
bool XdsBootstrap::XdsServer::IgnoreResourceDeletion() const {
return server_features.find(std::string(
kServerFeatureIgnoreResourceDeletion)) != server_features.end();
}
//
// XdsBootstrap
//
const XdsBootstrap::Authority* XdsBootstrap::LookupAuthority(
const std::string& name) const {
auto it = authorities().find(name);
if (it != authorities().end()) {
return &it->second;
}
return nullptr;
}
bool XdsBootstrap::XdsServerExists(
const XdsBootstrap::XdsServer& server) const {
if (server == this->server()) return true;
for (auto& authority : authorities()) {
for (auto& xds_server : authority.second.xds_servers) {
if (server == xds_server) return true;
}
}
return false;
}
} // namespace grpc_core } // namespace grpc_core

@ -19,7 +19,12 @@
#include <grpc/support/port_platform.h> #include <grpc/support/port_platform.h>
#include <map>
#include <set>
#include <string> #include <string>
#include <vector>
#include "absl/strings/string_view.h"
#include "src/core/lib/json/json.h" #include "src/core/lib/json/json.h"
@ -29,34 +34,49 @@ bool XdsFederationEnabled();
class XdsBootstrap { class XdsBootstrap {
public: public:
class Node { struct Node {
public: std::string id;
virtual ~Node() = default; std::string cluster;
std::string locality_region;
virtual const std::string& id() const = 0; std::string locality_zone;
virtual const std::string& cluster() const = 0; std::string locality_sub_zone;
virtual const std::string& locality_region() const = 0; Json metadata;
virtual const std::string& locality_zone() const = 0;
virtual const std::string& locality_sub_zone() const = 0;
virtual const Json::Object& metadata() const = 0;
}; };
class XdsServer { struct XdsServer {
public: static constexpr absl::string_view kServerFeatureXdsV3 = "xds_v3";
virtual ~XdsServer() = default; static constexpr absl::string_view kServerFeatureIgnoreResourceDeletion =
"ignore_resource_deletion";
virtual const std::string& server_uri() const = 0;
virtual bool ShouldUseV3() const = 0; std::string server_uri;
virtual bool IgnoreResourceDeletion() const = 0; std::string channel_creds_type;
Json channel_creds_config;
virtual bool operator==(const XdsServer& other) const = 0; std::set<std::string> server_features;
bool operator==(const XdsServer& other) const {
return (server_uri == other.server_uri &&
channel_creds_type == other.channel_creds_type &&
channel_creds_config == other.channel_creds_config &&
server_features == other.server_features);
}
bool operator<(const XdsServer& other) const {
if (server_uri < other.server_uri) return true;
if (channel_creds_type < other.channel_creds_type) return true;
if (channel_creds_config.Dump() < other.channel_creds_config.Dump()) {
return true;
}
if (server_features < other.server_features) return true;
return false;
}
bool ShouldUseV3() const;
bool IgnoreResourceDeletion() const;
}; };
class Authority { struct Authority {
public: std::string client_listener_resource_name_template;
virtual ~Authority() = default; std::vector<XdsServer> xds_servers;
virtual const XdsServer* server() const = 0;
}; };
virtual ~XdsBootstrap() = default; virtual ~XdsBootstrap() = default;
@ -66,18 +86,16 @@ class XdsBootstrap {
// TODO(roth): We currently support only one server. Fix this when we // TODO(roth): We currently support only one server. Fix this when we
// add support for fallback for the xds channel. // add support for fallback for the xds channel.
virtual const XdsServer& server() const = 0; virtual const XdsServer& server() const = 0;
// Returns the node information, or null if not present in the bootstrap
// config.
virtual const Node* node() const = 0; virtual const Node* node() const = 0;
virtual const std::map<std::string, Authority>& authorities() const = 0;
// Returns a pointer to the specified authority, or null if it does // Returns a pointer to the specified authority, or null if it does
// not exist in this bootstrap config. // not exist in this bootstrap config.
virtual const Authority* LookupAuthority(const std::string& name) const = 0; const Authority* LookupAuthority(const std::string& name) const;
// If the server exists in the bootstrap config, returns a pointer to // A utility method to check that an xDS server exists in this
// the XdsServer instance in the config. Otherwise, returns null. // bootstrap config.
virtual const XdsServer* FindXdsServer(const XdsServer& server) const = 0; bool XdsServerExists(const XdsServer& server) const;
}; };
} // namespace grpc_core } // namespace grpc_core

@ -38,256 +38,382 @@
#include "src/core/lib/config/core_configuration.h" #include "src/core/lib/config/core_configuration.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h" #include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/json/json.h" #include "src/core/lib/json/json.h"
#include "src/core/lib/json/json_object_loader.h" #include "src/core/lib/json/json_util.h"
#include "src/core/lib/security/certificate_provider/certificate_provider_factory.h" #include "src/core/lib/security/certificate_provider/certificate_provider_factory.h"
#include "src/core/lib/security/certificate_provider/certificate_provider_registry.h"
#include "src/core/lib/security/credentials/channel_creds_registry.h" #include "src/core/lib/security/credentials/channel_creds_registry.h"
namespace grpc_core { namespace grpc_core {
// namespace {
// GrpcXdsBootstrap::GrpcNode::Locality
//
const JsonLoaderInterface* GrpcXdsBootstrap::GrpcNode::Locality::JsonLoader( grpc_error_handle ParseChannelCreds(const Json::Object& json, size_t idx,
const JsonArgs&) { XdsBootstrap::XdsServer* server) {
static const auto* loader = std::vector<grpc_error_handle> error_list;
JsonObjectLoader<Locality>() std::string type;
.OptionalField("region", &Locality::region) ParseJsonObjectField(json, "type", &type, &error_list);
.OptionalField("zone", &Locality::zone) const Json::Object* config_ptr = nullptr;
.OptionalField("sub_zone", &Locality::sub_zone) ParseJsonObjectField(json, "config", &config_ptr, &error_list,
.Finish(); /*required=*/false);
return loader; // Select the first channel creds type that we support.
if (server->channel_creds_type.empty() &&
CoreConfiguration::Get().channel_creds_registry().IsSupported(type)) {
Json config;
if (config_ptr != nullptr) config = *config_ptr;
if (!CoreConfiguration::Get().channel_creds_registry().IsValidConfig(
type, config)) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_CPP_STRING(absl::StrCat(
"invalid config for channel creds type \"", type, "\"")));
}
server->channel_creds_type = std::move(type);
server->channel_creds_config = std::move(config);
}
return GRPC_ERROR_CREATE_FROM_VECTOR_AND_CPP_STRING(
absl::StrCat("errors parsing index ", idx), &error_list);
} }
// grpc_error_handle ParseChannelCredsArray(const Json::Array& json,
// GrpcXdsBootstrap::GrpcNode XdsBootstrap::XdsServer* server) {
// std::vector<grpc_error_handle> error_list;
for (size_t i = 0; i < json.size(); ++i) {
const JsonLoaderInterface* GrpcXdsBootstrap::GrpcNode::JsonLoader( const Json& child = json.at(i);
const JsonArgs&) { if (child.type() != Json::Type::OBJECT) {
static const auto* loader = error_list.push_back(GRPC_ERROR_CREATE_FROM_CPP_STRING(
JsonObjectLoader<GrpcNode>() absl::StrCat("array element ", i, " is not an object")));
.OptionalField("id", &GrpcNode::id_) } else {
.OptionalField("cluster", &GrpcNode::cluster_) grpc_error_handle parse_error =
.OptionalField("locality", &GrpcNode::locality_) ParseChannelCreds(child.object_value(), i, server);
.OptionalField("metadata", &GrpcNode::metadata_) if (!GRPC_ERROR_IS_NONE(parse_error)) error_list.push_back(parse_error);
.Finish(); }
return loader; }
if (server->channel_creds_type.empty()) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"no known creds type found in \"channel_creds\""));
}
return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing \"channel_creds\" array",
&error_list);
} }
// } // namespace
// GrpcXdsBootstrap::GrpcXdsServer::ChannelCreds
//
const JsonLoaderInterface*
GrpcXdsBootstrap::GrpcXdsServer::ChannelCreds::JsonLoader(const JsonArgs&) {
static const auto* loader =
JsonObjectLoader<ChannelCreds>()
.Field("type", &ChannelCreds::type)
.OptionalField("config", &ChannelCreds::config)
.Finish();
return loader;
}
// //
// GrpcXdsBootstrap::GrpcXdsServer // GrpcXdsBootstrap
// //
namespace { std::unique_ptr<GrpcXdsBootstrap> GrpcXdsBootstrap::Create(
absl::string_view json_string, grpc_error_handle* error) {
constexpr absl::string_view kServerFeatureXdsV3 = "xds_v3"; auto json = Json::Parse(json_string);
constexpr absl::string_view kServerFeatureIgnoreResourceDeletion = if (!json.ok()) {
"ignore_resource_deletion"; *error = GRPC_ERROR_CREATE_FROM_CPP_STRING(absl::StrCat(
"Failed to parse bootstrap JSON string: ", json.status().ToString()));
} // namespace return nullptr;
}
bool GrpcXdsBootstrap::GrpcXdsServer::ShouldUseV3() const { return absl::make_unique<GrpcXdsBootstrap>(std::move(*json), error);
return server_features_.find(std::string(kServerFeatureXdsV3)) !=
server_features_.end();
}
bool GrpcXdsBootstrap::GrpcXdsServer::IgnoreResourceDeletion() const {
return server_features_.find(std::string(
kServerFeatureIgnoreResourceDeletion)) != server_features_.end();
}
bool GrpcXdsBootstrap::GrpcXdsServer::operator==(const XdsServer& other) const {
const auto& o = static_cast<const GrpcXdsServer&>(other);
return (server_uri_ == o.server_uri_ &&
channel_creds_.type == o.channel_creds_.type &&
channel_creds_.config == o.channel_creds_.config &&
server_features_ == o.server_features_);
}
const JsonLoaderInterface* GrpcXdsBootstrap::GrpcXdsServer::JsonLoader(
const JsonArgs&) {
static const auto* loader =
JsonObjectLoader<GrpcXdsServer>()
.Field("server_uri", &GrpcXdsServer::server_uri_)
.Finish();
return loader;
} }
void GrpcXdsBootstrap::GrpcXdsServer::JsonPostLoad(const Json& json, GrpcXdsBootstrap::GrpcXdsBootstrap(Json json, grpc_error_handle* error) {
const JsonArgs& args, if (json.type() != Json::Type::OBJECT) {
ErrorList* errors) { *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
// Parse "channel_creds". "malformed JSON in bootstrap file");
auto channel_creds_list = LoadJsonObjectField<std::vector<ChannelCreds>>( return;
json.object_value(), args, "channel_creds", errors);
if (channel_creds_list.has_value()) {
ScopedField field(errors, ".channel_creds");
for (size_t i = 0; i < channel_creds_list->size(); ++i) {
ScopedField field(errors, absl::StrCat("[", i, "]"));
auto& creds = (*channel_creds_list)[i];
// Select the first channel creds type that we support.
if (channel_creds_.type.empty() &&
CoreConfiguration::Get().channel_creds_registry().IsSupported(
creds.type)) {
if (!CoreConfiguration::Get().channel_creds_registry().IsValidConfig(
creds.type, creds.config)) {
errors->AddError(absl::StrCat(
"invalid config for channel creds type \"", creds.type, "\""));
continue;
} }
channel_creds_.type = std::move(creds.type); std::vector<grpc_error_handle> error_list;
channel_creds_.config = std::move(creds.config); auto it = json.mutable_object()->find("xds_servers");
if (it == json.mutable_object()->end()) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"\"xds_servers\" field not present"));
} else if (it->second.type() != Json::Type::ARRAY) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"\"xds_servers\" field is not an array"));
} else {
grpc_error_handle parse_error = ParseXdsServerList(&it->second, &servers_);
if (!GRPC_ERROR_IS_NONE(parse_error)) error_list.push_back(parse_error);
} }
it = json.mutable_object()->find("node");
if (it != json.mutable_object()->end()) {
if (it->second.type() != Json::Type::OBJECT) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"\"node\" field is not an object"));
} else {
grpc_error_handle parse_error = ParseNode(&it->second);
if (!GRPC_ERROR_IS_NONE(parse_error)) error_list.push_back(parse_error);
} }
if (channel_creds_.type.empty()) {
errors->AddError("no known creds type found");
} }
if (XdsFederationEnabled()) {
it = json.mutable_object()->find("authorities");
if (it != json.mutable_object()->end()) {
if (it->second.type() != Json::Type::OBJECT) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"\"authorities\" field is not an object"));
} else {
grpc_error_handle parse_error = ParseAuthorities(&it->second);
if (!GRPC_ERROR_IS_NONE(parse_error)) error_list.push_back(parse_error);
} }
// Parse "server_features". }
{ it = json.mutable_object()->find(
ScopedField field(errors, ".server_features"); "client_default_listener_resource_name_template");
auto it = json.object_value().find("server_features"); if (it != json.mutable_object()->end()) {
if (it != json.object_value().end()) { if (it->second.type() != Json::Type::STRING) {
if (it->second.type() != Json::Type::ARRAY) { error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
errors->AddError("is not an array"); "\"client_default_listener_resource_name_template\" field is not a "
"string"));
} else { } else {
const Json::Array& array = it->second.array_value(); client_default_listener_resource_name_template_ =
for (const Json& feature_json : array) { std::move(*it->second.mutable_string_value());
if (feature_json.type() == Json::Type::STRING && }
(feature_json.string_value() == kServerFeatureXdsV3 ||
feature_json.string_value() ==
kServerFeatureIgnoreResourceDeletion)) {
server_features_.insert(feature_json.string_value());
} }
} }
it = json.mutable_object()->find("server_listener_resource_name_template");
if (it != json.mutable_object()->end()) {
if (it->second.type() != Json::Type::STRING) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"\"server_listener_resource_name_template\" field is not a string"));
} else {
server_listener_resource_name_template_ =
std::move(*it->second.mutable_string_value());
}
} }
it = json.mutable_object()->find("certificate_providers");
if (it != json.mutable_object()->end()) {
if (it->second.type() != Json::Type::OBJECT) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"\"certificate_providers\" field is not an object"));
} else {
grpc_error_handle parse_error = ParseCertificateProviders(&it->second);
if (!GRPC_ERROR_IS_NONE(parse_error)) error_list.push_back(parse_error);
} }
} }
*error = GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing xds bootstrap file",
&error_list);
} }
Json GrpcXdsBootstrap::GrpcXdsServer::ToJson() const { grpc_error_handle GrpcXdsBootstrap::ParseXdsServerList(
Json::Object channel_creds_json{{"type", channel_creds_.type}}; Json* json, std::vector<XdsServer>* servers) {
if (!channel_creds_.config.empty()) { std::vector<grpc_error_handle> error_list;
channel_creds_json["config"] = channel_creds_.config; for (size_t i = 0; i < json->mutable_array()->size(); ++i) {
Json& child = json->mutable_array()->at(i);
if (child.type() != Json::Type::OBJECT) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_CPP_STRING(
absl::StrCat("array element ", i, " is not an object")));
} else {
grpc_error_handle parse_error;
servers->emplace_back(XdsServerParse(child, &parse_error));
if (!GRPC_ERROR_IS_NONE(parse_error)) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_CPP_STRING(
absl::StrCat("errors parsing index ", i)));
error_list.push_back(parse_error);
} }
Json::Object json{
{"server_uri", server_uri_},
{"channel_creds", Json::Array{std::move(channel_creds_json)}},
};
if (!server_features_.empty()) {
Json::Array server_features_json;
for (auto& feature : server_features_) {
server_features_json.emplace_back(feature);
} }
json["server_features"] = std::move(server_features_json);
} }
return json; return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing \"xds_servers\" array",
&error_list);
} }
// grpc_error_handle GrpcXdsBootstrap::ParseAuthorities(Json* json) {
// GrpcXdsBootstrap::GrpcAuthority std::vector<grpc_error_handle> error_list;
// for (auto& p : *(json->mutable_object())) {
if (p.second.type() != Json::Type::OBJECT) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_CPP_STRING(
"field:authorities element error: element is not a object"));
continue;
}
grpc_error_handle parse_error = ParseAuthority(&p.second, p.first);
if (!GRPC_ERROR_IS_NONE(parse_error)) error_list.push_back(parse_error);
}
return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing \"authorities\"",
&error_list);
}
const JsonLoaderInterface* GrpcXdsBootstrap::GrpcAuthority::JsonLoader( grpc_error_handle GrpcXdsBootstrap::ParseAuthority(Json* json,
const JsonArgs&) { const std::string& name) {
static const auto* loader = std::vector<grpc_error_handle> error_list;
JsonObjectLoader<GrpcAuthority>() Authority authority;
.OptionalField( auto it =
"client_listener_resource_name_template", json->mutable_object()->find("client_listener_resource_name_template");
&GrpcAuthority::client_listener_resource_name_template_) if (it != json->mutable_object()->end()) {
.OptionalField("xds_servers", &GrpcAuthority::servers_) if (it->second.type() != Json::Type::STRING) {
.Finish(); error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
return loader; "\"client_listener_resource_name_template\" field is not a string"));
} else {
std::string expected_prefix = absl::StrCat("xdstp://", name, "/");
if (!absl::StartsWith(it->second.string_value(), expected_prefix)) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_CPP_STRING(
absl::StrCat("\"client_listener_resource_name_template\" field "
"must begin with \"",
expected_prefix, "\"")));
} else {
authority.client_listener_resource_name_template =
std::move(*it->second.mutable_string_value());
}
}
}
it = json->mutable_object()->find("xds_servers");
if (it != json->mutable_object()->end()) {
if (it->second.type() != Json::Type::ARRAY) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"\"xds_servers\" field is not an array"));
} else {
grpc_error_handle parse_error =
ParseXdsServerList(&it->second, &authority.xds_servers);
if (!GRPC_ERROR_IS_NONE(parse_error)) error_list.push_back(parse_error);
}
}
if (error_list.empty()) {
authorities_[name] = std::move(authority);
}
return GRPC_ERROR_CREATE_FROM_VECTOR_AND_CPP_STRING(
absl::StrCat("errors parsing authority ", name), &error_list);
} }
// grpc_error_handle GrpcXdsBootstrap::ParseNode(Json* json) {
// GrpcXdsBootstrap std::vector<grpc_error_handle> error_list;
// node_ = absl::make_unique<Node>();
auto it = json->mutable_object()->find("id");
if (it != json->mutable_object()->end()) {
if (it->second.type() != Json::Type::STRING) {
error_list.push_back(
GRPC_ERROR_CREATE_FROM_STATIC_STRING("\"id\" field is not a string"));
} else {
node_->id = std::move(*it->second.mutable_string_value());
}
}
it = json->mutable_object()->find("cluster");
if (it != json->mutable_object()->end()) {
if (it->second.type() != Json::Type::STRING) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"\"cluster\" field is not a string"));
} else {
node_->cluster = std::move(*it->second.mutable_string_value());
}
}
it = json->mutable_object()->find("locality");
if (it != json->mutable_object()->end()) {
if (it->second.type() != Json::Type::OBJECT) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"\"locality\" field is not an object"));
} else {
grpc_error_handle parse_error = ParseLocality(&it->second);
if (!GRPC_ERROR_IS_NONE(parse_error)) error_list.push_back(parse_error);
}
}
it = json->mutable_object()->find("metadata");
if (it != json->mutable_object()->end()) {
if (it->second.type() != Json::Type::OBJECT) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"\"metadata\" field is not an object"));
} else {
node_->metadata = std::move(it->second);
}
}
return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing \"node\" object",
&error_list);
}
absl::StatusOr<std::unique_ptr<GrpcXdsBootstrap>> GrpcXdsBootstrap::Create( grpc_error_handle GrpcXdsBootstrap::ParseLocality(Json* json) {
absl::string_view json_string) { std::vector<grpc_error_handle> error_list;
auto json = Json::Parse(json_string); auto it = json->mutable_object()->find("region");
if (!json.ok()) { if (it != json->mutable_object()->end()) {
return absl::InvalidArgumentError(absl::StrCat( if (it->second.type() != Json::Type::STRING) {
"Failed to parse bootstrap JSON string: ", json.status().ToString())); error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"\"region\" field is not a string"));
} else {
node_->locality_region = std::move(*it->second.mutable_string_value());
} }
// Validate JSON.
class XdsJsonArgs : public JsonArgs {
public:
bool IsEnabled(absl::string_view key) const override {
if (key == "federation") return XdsFederationEnabled();
return true;
} }
}; it = json->mutable_object()->find("zone");
auto bootstrap = LoadFromJson<GrpcXdsBootstrap>(*json, XdsJsonArgs()); if (it != json->mutable_object()->end()) {
if (!bootstrap.ok()) return bootstrap.status(); if (it->second.type() != Json::Type::STRING) {
return absl::make_unique<GrpcXdsBootstrap>(std::move(*bootstrap)); error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"\"zone\" field is not a string"));
} else {
node_->locality_zone = std::move(*it->second.mutable_string_value());
}
}
it = json->mutable_object()->find("sub_zone");
if (it != json->mutable_object()->end()) {
if (it->second.type() != Json::Type::STRING) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"\"sub_zone\" field is not a string"));
} else {
node_->locality_sub_zone = std::move(*it->second.mutable_string_value());
}
}
return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing \"locality\" object",
&error_list);
} }
const JsonLoaderInterface* GrpcXdsBootstrap::JsonLoader(const JsonArgs&) { grpc_error_handle GrpcXdsBootstrap::ParseCertificateProviders(Json* json) {
static const auto* loader = std::vector<grpc_error_handle> error_list;
JsonObjectLoader<GrpcXdsBootstrap>() for (auto& certificate_provider : *(json->mutable_object())) {
.Field("xds_servers", &GrpcXdsBootstrap::servers_) if (certificate_provider.second.type() != Json::Type::OBJECT) {
.OptionalField("node", &GrpcXdsBootstrap::node_) error_list.push_back(GRPC_ERROR_CREATE_FROM_CPP_STRING(absl::StrCat(
.OptionalField("certificate_providers", "element \"", certificate_provider.first, "\" is not an object")));
&GrpcXdsBootstrap::certificate_providers_) } else {
.OptionalField( grpc_error_handle parse_error = ParseCertificateProvider(
"server_listener_resource_name_template", certificate_provider.first, &certificate_provider.second);
&GrpcXdsBootstrap::server_listener_resource_name_template_) if (!GRPC_ERROR_IS_NONE(parse_error)) error_list.push_back(parse_error);
.OptionalField("authorities", &GrpcXdsBootstrap::authorities_, }
"federation") }
.OptionalField("client_default_listener_resource_name_template", return GRPC_ERROR_CREATE_FROM_VECTOR(
&GrpcXdsBootstrap:: "errors parsing \"certificate_providers\" object", &error_list);
client_default_listener_resource_name_template_,
"federation")
.Finish();
return loader;
} }
void GrpcXdsBootstrap::JsonPostLoad(const Json& /*json*/, grpc_error_handle GrpcXdsBootstrap::ParseCertificateProvider(
const JsonArgs& /*args*/, const std::string& instance_name, Json* certificate_provider_json) {
ErrorList* errors) { std::vector<grpc_error_handle> error_list;
// Verify that each authority has the right prefix in the auto it = certificate_provider_json->mutable_object()->find("plugin_name");
// client_listener_resource_name_template field. if (it == certificate_provider_json->mutable_object()->end()) {
{ error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
ScopedField field(errors, ".authorities"); "\"plugin_name\" field not present"));
for (const auto& p : authorities_) { } else if (it->second.type() != Json::Type::STRING) {
const std::string& name = p.first; error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
const GrpcAuthority& authority = "\"plugin_name\" field is not a string"));
static_cast<const GrpcAuthority&>(p.second); } else {
ScopedField field( std::string plugin_name = std::move(*(it->second.mutable_string_value()));
errors, absl::StrCat("[\"", name, // Find config JSON.
"\"].client_listener_resource_name_template")); absl::optional<Json> config_json;
std::string expected_prefix = absl::StrCat("xdstp://", name, "/"); it = certificate_provider_json->mutable_object()->find("config");
if (!authority.client_listener_resource_name_template().empty() && if (it != certificate_provider_json->mutable_object()->end()) {
!absl::StartsWith(authority.client_listener_resource_name_template(), if (it->second.type() != Json::Type::OBJECT) {
expected_prefix)) { error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
errors->AddError( "\"config\" field is not an object"));
absl::StrCat("field must begin with \"", expected_prefix, "\"")); } else {
config_json = it->second;
} }
} else {
// "config" is an optional field, so default to an empty JSON object.
config_json = Json::Object();
}
// Try to instantiate the provider.
CertificateProviderFactory* factory =
CoreConfiguration::Get()
.certificate_provider_registry()
.LookupCertificateProviderFactory(plugin_name);
if (factory == nullptr) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_CPP_STRING(
absl::StrCat("Unrecognized plugin name: ", plugin_name)));
} else if (config_json.has_value()) {
grpc_error_handle parse_error = GRPC_ERROR_NONE;
RefCountedPtr<CertificateProviderFactory::Config> config =
factory->CreateCertificateProviderConfig(*config_json, &parse_error);
if (!GRPC_ERROR_IS_NONE(parse_error)) {
error_list.push_back(parse_error);
} else {
certificate_providers_.insert(
{instance_name, {std::move(plugin_name), std::move(config)}});
} }
} }
}
return GRPC_ERROR_CREATE_FROM_VECTOR_AND_CPP_STRING(
absl::StrCat("errors parsing element \"", instance_name, "\""),
&error_list);
} }
std::string GrpcXdsBootstrap::ToString() const { std::string GrpcXdsBootstrap::ToString() const {
std::vector<std::string> parts; std::vector<std::string> parts;
if (node_.has_value()) { if (node_ != nullptr) {
parts.push_back( parts.push_back(absl::StrFormat(
absl::StrFormat("node={\n" "node={\n"
" id=\"%s\",\n" " id=\"%s\",\n"
" cluster=\"%s\",\n" " cluster=\"%s\",\n"
" locality={\n" " locality={\n"
@ -297,12 +423,25 @@ std::string GrpcXdsBootstrap::ToString() const {
" },\n" " },\n"
" metadata=%s,\n" " metadata=%s,\n"
"},\n", "},\n",
node_->id(), node_->cluster(), node_->locality_region(), node_->id, node_->cluster, node_->locality_region, node_->locality_zone,
node_->locality_zone(), node_->locality_sub_zone(), node_->locality_sub_zone, node_->metadata.Dump()));
Json{node_->metadata()}.Dump()));
} }
parts.push_back( parts.push_back(
absl::StrFormat("servers=[\n%s\n],\n", servers_[0].ToJson().Dump())); absl::StrFormat("servers=[\n"
" {\n"
" uri=\"%s\",\n"
" creds_type=%s,\n",
server().server_uri, server().channel_creds_type));
if (server().channel_creds_config.type() != Json::Type::JSON_NULL) {
parts.push_back(absl::StrFormat(" creds_config=%s,",
server().channel_creds_config.Dump()));
}
if (!server().server_features.empty()) {
parts.push_back(absl::StrCat(" server_features=[",
absl::StrJoin(server().server_features, ", "),
"],\n"));
}
parts.push_back(" }\n],\n");
if (!client_default_listener_resource_name_template_.empty()) { if (!client_default_listener_resource_name_template_.empty()) {
parts.push_back(absl::StrFormat( parts.push_back(absl::StrFormat(
"client_default_listener_resource_name_template=\"%s\",\n", "client_default_listener_resource_name_template=\"%s\",\n",
@ -318,14 +457,14 @@ std::string GrpcXdsBootstrap::ToString() const {
parts.push_back(absl::StrFormat(" %s={\n", entry.first)); parts.push_back(absl::StrFormat(" %s={\n", entry.first));
parts.push_back( parts.push_back(
absl::StrFormat(" client_listener_resource_name_template=\"%s\",\n", absl::StrFormat(" client_listener_resource_name_template=\"%s\",\n",
entry.second.client_listener_resource_name_template())); entry.second.client_listener_resource_name_template));
if (entry.second.server() != nullptr) { parts.push_back(
parts.push_back(absl::StrFormat( absl::StrFormat(" servers=[\n"
" servers=[\n%s\n],\n", " {\n"
static_cast<const GrpcXdsServer*>(entry.second.server()) " uri=\"%s\",\n"
->ToJson() " creds_type=%s,\n",
.Dump())); entry.second.xds_servers[0].server_uri,
} entry.second.xds_servers[0].channel_creds_type));
parts.push_back(" },\n"); parts.push_back(" },\n");
} }
parts.push_back("}\n"); parts.push_back("}\n");
@ -343,28 +482,56 @@ std::string GrpcXdsBootstrap::ToString() const {
return absl::StrJoin(parts, ""); return absl::StrJoin(parts, "");
} }
const XdsBootstrap::Authority* GrpcXdsBootstrap::LookupAuthority( XdsBootstrap::XdsServer GrpcXdsBootstrap::XdsServerParse(
const std::string& name) const { const Json& json, grpc_error_handle* error) {
auto it = authorities_.find(name); std::vector<grpc_error_handle> error_list;
if (it != authorities_.end()) { XdsServer server;
return &it->second; ParseJsonObjectField(json.object_value(), "server_uri", &server.server_uri,
&error_list);
const Json::Array* creds_array = nullptr;
ParseJsonObjectField(json.object_value(), "channel_creds", &creds_array,
&error_list);
if (creds_array != nullptr) {
grpc_error_handle parse_error =
ParseChannelCredsArray(*creds_array, &server);
if (!GRPC_ERROR_IS_NONE(parse_error)) error_list.push_back(parse_error);
} }
return nullptr; const Json::Array* server_features_array = nullptr;
ParseJsonObjectField(json.object_value(), "server_features",
&server_features_array, &error_list, /*required=*/false);
if (server_features_array != nullptr) {
for (const Json& feature_json : *server_features_array) {
if (feature_json.type() == Json::Type::STRING &&
(feature_json.string_value() ==
XdsBootstrap::XdsServer::kServerFeatureXdsV3 ||
feature_json.string_value() ==
XdsBootstrap::XdsServer::kServerFeatureIgnoreResourceDeletion)) {
server.server_features.insert(feature_json.string_value());
}
}
}
*error = GRPC_ERROR_CREATE_FROM_VECTOR_AND_CPP_STRING(
"errors parsing xds server", &error_list);
return server;
} }
const XdsBootstrap::XdsServer* GrpcXdsBootstrap::FindXdsServer( Json::Object GrpcXdsBootstrap::XdsServerToJson(const XdsServer& server) {
const XdsBootstrap::XdsServer& server) const { Json::Object channel_creds_json{{"type", server.channel_creds_type}};
if (static_cast<const GrpcXdsServer&>(server) == servers_[0]) { if (server.channel_creds_config.type() != Json::Type::JSON_NULL) {
return &servers_[0]; channel_creds_json["config"] = server.channel_creds_config;
} }
for (auto& p : authorities_) { Json::Object json{
const auto* authority_server = {"server_uri", server.server_uri},
static_cast<const GrpcXdsServer*>(p.second.server()); {"channel_creds", Json::Array{std::move(channel_creds_json)}},
if (authority_server != nullptr && *authority_server == server) { };
return authority_server; if (!server.server_features.empty()) {
Json::Array server_features_json;
for (auto& feature : server.server_features) {
server_features_json.emplace_back(feature);
} }
json["server_features"] = std::move(server_features_json);
} }
return nullptr; return json;
} }
} // namespace grpc_core } // namespace grpc_core

@ -21,121 +21,36 @@
#include <map> #include <map>
#include <memory> #include <memory>
#include <set>
#include <string> #include <string>
#include <vector> #include <vector>
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "src/core/ext/xds/certificate_provider_store.h" #include "src/core/ext/xds/certificate_provider_store.h"
#include "src/core/ext/xds/xds_bootstrap.h" #include "src/core/ext/xds/xds_bootstrap.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/json/json.h" #include "src/core/lib/json/json.h"
#include "src/core/lib/json/json_args.h"
#include "src/core/lib/json/json_object_loader.h"
namespace grpc_core { namespace grpc_core {
class GrpcXdsBootstrap : public XdsBootstrap { class GrpcXdsBootstrap : public XdsBootstrap {
public: public:
class GrpcNode : public Node {
public:
const std::string& id() const override { return id_; }
const std::string& cluster() const override { return cluster_; }
const std::string& locality_region() const override {
return locality_.region;
}
const std::string& locality_zone() const override { return locality_.zone; }
const std::string& locality_sub_zone() const override {
return locality_.sub_zone;
}
const Json::Object& metadata() const override { return metadata_; }
static const JsonLoaderInterface* JsonLoader(const JsonArgs&);
private:
struct Locality {
std::string region;
std::string zone;
std::string sub_zone;
static const JsonLoaderInterface* JsonLoader(const JsonArgs&);
};
std::string id_;
std::string cluster_;
Locality locality_;
Json::Object metadata_;
};
class GrpcXdsServer : public XdsServer {
public:
const std::string& server_uri() const override { return server_uri_; }
bool ShouldUseV3() const override;
bool IgnoreResourceDeletion() const override;
bool operator==(const XdsServer& other) const override;
const std::string& channel_creds_type() const {
return channel_creds_.type;
}
const Json::Object& channel_creds_config() const {
return channel_creds_.config;
}
static const JsonLoaderInterface* JsonLoader(const JsonArgs&);
void JsonPostLoad(const Json& json, const JsonArgs& args,
ErrorList* errors);
Json ToJson() const;
private:
struct ChannelCreds {
std::string type;
Json::Object config;
static const JsonLoaderInterface* JsonLoader(const JsonArgs&);
};
std::string server_uri_;
ChannelCreds channel_creds_;
std::set<std::string> server_features_;
};
class GrpcAuthority : public Authority {
public:
const XdsServer* server() const override {
return servers_.empty() ? nullptr : &servers_[0];
}
const std::string& client_listener_resource_name_template() const {
return client_listener_resource_name_template_;
}
static const JsonLoaderInterface* JsonLoader(const JsonArgs&);
private:
std::vector<GrpcXdsServer> servers_;
std::string client_listener_resource_name_template_;
};
// Creates bootstrap object from json_string. // Creates bootstrap object from json_string.
static absl::StatusOr<std::unique_ptr<GrpcXdsBootstrap>> Create( // If *error is not GRPC_ERROR_NONE after returning, then there was an
absl::string_view json_string); // error parsing the contents.
static std::unique_ptr<GrpcXdsBootstrap> Create(absl::string_view json_string,
grpc_error_handle* error);
static const JsonLoaderInterface* JsonLoader(const JsonArgs&); // Do not instantiate directly -- use Create() above instead.
void JsonPostLoad(const Json& json, const JsonArgs& args, ErrorList* errors); GrpcXdsBootstrap(Json json, grpc_error_handle* error);
std::string ToString() const override; std::string ToString() const override;
const XdsServer& server() const override { return servers_[0]; } const XdsServer& server() const override { return servers_[0]; }
const Node* node() const override { const Node* node() const override { return node_.get(); }
return node_.has_value() ? &*node_ : nullptr; const std::map<std::string, Authority>& authorities() const override {
return authorities_;
} }
const Authority* LookupAuthority(const std::string& name) const override;
const XdsServer* FindXdsServer(const XdsServer& server) const override;
const std::string& client_default_listener_resource_name_template() const { const std::string& client_default_listener_resource_name_template() const {
return client_default_listener_resource_name_template_; return client_default_listener_resource_name_template_;
@ -148,17 +63,25 @@ class GrpcXdsBootstrap : public XdsBootstrap {
return certificate_providers_; return certificate_providers_;
} }
// Exposed for testing purposes only. static XdsServer XdsServerParse(const Json& json, grpc_error_handle* error);
const std::map<std::string, GrpcAuthority>& authorities() const { static Json::Object XdsServerToJson(const XdsServer& server);
return authorities_;
}
private: private:
std::vector<GrpcXdsServer> servers_; grpc_error_handle ParseXdsServerList(Json* json,
absl::optional<GrpcNode> node_; std::vector<XdsServer>* servers);
grpc_error_handle ParseAuthorities(Json* json);
grpc_error_handle ParseAuthority(Json* json, const std::string& name);
grpc_error_handle ParseNode(Json* json);
grpc_error_handle ParseLocality(Json* json);
grpc_error_handle ParseCertificateProviders(Json* json);
grpc_error_handle ParseCertificateProvider(const std::string& instance_name,
Json* certificate_provider_json);
std::vector<XdsServer> servers_;
std::unique_ptr<Node> node_;
std::string client_default_listener_resource_name_template_; std::string client_default_listener_resource_name_template_;
std::string server_listener_resource_name_template_; std::string server_listener_resource_name_template_;
std::map<std::string, GrpcAuthority> authorities_; std::map<std::string, Authority> authorities_;
CertificateProviderStore::PluginDefinitionMap certificate_providers_; CertificateProviderStore::PluginDefinitionMap certificate_providers_;
}; };

@ -233,7 +233,7 @@ class XdsClient::ChannelState::AdsCallState
"[xds_client %p] xds server %s: timeout obtaining resource " "[xds_client %p] xds server %s: timeout obtaining resource "
"{type=%s name=%s} from xds server", "{type=%s name=%s} from xds server",
ads_calld_->xds_client(), ads_calld_->xds_client(),
ads_calld_->chand()->server_.server_uri().c_str(), ads_calld_->chand()->server_.server_uri.c_str(),
std::string(type_->type_url()).c_str(), std::string(type_->type_url()).c_str(),
XdsClient::ConstructFullXdsResourceName( XdsClient::ConstructFullXdsResourceName(
name_.authority, type_->type_url(), name_.key) name_.authority, type_->type_url(), name_.key)
@ -426,7 +426,7 @@ XdsClient::ChannelState::ChannelState(WeakRefCountedPtr<XdsClient> xds_client,
server_(server) { server_(server) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) { if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
gpr_log(GPR_INFO, "[xds_client %p] creating channel to %s", gpr_log(GPR_INFO, "[xds_client %p] creating channel to %s",
xds_client_.get(), server.server_uri().c_str()); xds_client_.get(), server.server_uri.c_str());
} }
absl::Status status; absl::Status status;
transport_ = xds_client_->transport_factory_->Create( transport_ = xds_client_->transport_factory_->Create(
@ -443,7 +443,7 @@ XdsClient::ChannelState::ChannelState(WeakRefCountedPtr<XdsClient> xds_client,
XdsClient::ChannelState::~ChannelState() { XdsClient::ChannelState::~ChannelState() {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) { if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
gpr_log(GPR_INFO, "[xds_client %p] destroying xds channel %p for server %s", gpr_log(GPR_INFO, "[xds_client %p] destroying xds channel %p for server %s",
xds_client(), this, server_.server_uri().c_str()); xds_client(), this, server_.server_uri.c_str());
} }
xds_client_.reset(DEBUG_LOCATION, "ChannelState"); xds_client_.reset(DEBUG_LOCATION, "ChannelState");
} }
@ -458,7 +458,7 @@ void XdsClient::ChannelState::Orphan() ABSL_NO_THREAD_SAFETY_ANALYSIS {
// At this time, all strong refs are removed, remove from channel map to // At this time, all strong refs are removed, remove from channel map to
// prevent subsequent subscription from trying to use this ChannelState as // prevent subsequent subscription from trying to use this ChannelState as
// it is shutting down. // it is shutting down.
xds_client_->xds_server_channel_map_.erase(&server_); xds_client_->xds_server_channel_map_.erase(server_);
ads_calld_.reset(); ads_calld_.reset();
lrs_calld_.reset(); lrs_calld_.reset();
} }
@ -486,7 +486,7 @@ void XdsClient::ChannelState::MaybeStartLrsCall() {
} }
void XdsClient::ChannelState::StopLrsCallLocked() { void XdsClient::ChannelState::StopLrsCallLocked() {
xds_client_->xds_load_report_server_map_.erase(&server_); xds_client_->xds_load_report_server_map_.erase(server_);
lrs_calld_.reset(); lrs_calld_.reset();
} }
@ -537,7 +537,7 @@ void XdsClient::ChannelState::OnConnectivityStateChangeLocked(
gpr_log(GPR_INFO, gpr_log(GPR_INFO,
"[xds_client %p] xds channel for server %s in " "[xds_client %p] xds channel for server %s in "
"state TRANSIENT_FAILURE: %s", "state TRANSIENT_FAILURE: %s",
xds_client(), server_.server_uri().c_str(), xds_client(), server_.server_uri.c_str(),
status.ToString().c_str()); status.ToString().c_str());
xds_client_->NotifyOnErrorLocked(absl::UnavailableError( xds_client_->NotifyOnErrorLocked(absl::UnavailableError(
absl::StrCat("xds channel in TRANSIENT_FAILURE, connectivity error: ", absl::StrCat("xds channel in TRANSIENT_FAILURE, connectivity error: ",
@ -592,7 +592,7 @@ void XdsClient::ChannelState::RetryableCall<T>::StartNewCallLocked() {
gpr_log(GPR_INFO, gpr_log(GPR_INFO,
"[xds_client %p] xds server %s: start new call from retryable " "[xds_client %p] xds server %s: start new call from retryable "
"call %p", "call %p",
chand()->xds_client(), chand()->server_.server_uri().c_str(), this); chand()->xds_client(), chand()->server_.server_uri.c_str(), this);
} }
calld_ = MakeOrphanable<T>( calld_ = MakeOrphanable<T>(
this->Ref(DEBUG_LOCATION, "RetryableCall+start_new_call")); this->Ref(DEBUG_LOCATION, "RetryableCall+start_new_call"));
@ -608,7 +608,7 @@ void XdsClient::ChannelState::RetryableCall<T>::StartRetryTimerLocked() {
gpr_log(GPR_INFO, gpr_log(GPR_INFO,
"[xds_client %p] xds server %s: call attempt failed; " "[xds_client %p] xds server %s: call attempt failed; "
"retry timer will fire in %" PRId64 "ms.", "retry timer will fire in %" PRId64 "ms.",
chand()->xds_client(), chand()->server_.server_uri().c_str(), chand()->xds_client(), chand()->server_.server_uri.c_str(),
timeout.millis()); timeout.millis());
} }
timer_handle_ = GetDefaultEventEngine()->RunAfter( timer_handle_ = GetDefaultEventEngine()->RunAfter(
@ -630,8 +630,7 @@ void XdsClient::ChannelState::RetryableCall<T>::OnRetryTimer() {
gpr_log(GPR_INFO, gpr_log(GPR_INFO,
"[xds_client %p] xds server %s: retry timer fired (retryable " "[xds_client %p] xds server %s: retry timer fired (retryable "
"call: %p)", "call: %p)",
chand()->xds_client(), chand()->server_.server_uri().c_str(), chand()->xds_client(), chand()->server_.server_uri.c_str(), this);
this);
} }
StartNewCallLocked(); StartNewCallLocked();
} }
@ -649,7 +648,7 @@ absl::Status XdsClient::ChannelState::AdsCallState::AdsResponseParser::
"[xds_client %p] xds server %s: received ADS response: type_url=%s, " "[xds_client %p] xds server %s: received ADS response: type_url=%s, "
"version=%s, nonce=%s, num_resources=%" PRIuPTR, "version=%s, nonce=%s, num_resources=%" PRIuPTR,
ads_call_state_->xds_client(), ads_call_state_->xds_client(),
ads_call_state_->chand()->server_.server_uri().c_str(), ads_call_state_->chand()->server_.server_uri.c_str(),
fields.type_url.c_str(), fields.version.c_str(), fields.nonce.c_str(), fields.type_url.c_str(), fields.version.c_str(), fields.nonce.c_str(),
fields.num_resources); fields.num_resources);
} }
@ -772,8 +771,7 @@ void XdsClient::ChannelState::AdsCallState::AdsResponseParser::ParseResource(
"[xds_client %p] xds server %s: server returned new version of " "[xds_client %p] xds server %s: server returned new version of "
"resource for which we previously ignored a deletion: type %s " "resource for which we previously ignored a deletion: type %s "
"name %s", "name %s",
xds_client(), xds_client(), ads_call_state_->chand()->server_.server_uri.c_str(),
ads_call_state_->chand()->server_.server_uri().c_str(),
std::string(type_url).c_str(), std::string(resource_name).c_str()); std::string(type_url).c_str(), std::string(resource_name).c_str());
resource_state.ignored_deletion = false; resource_state.ignored_deletion = false;
} }
@ -860,7 +858,7 @@ XdsClient::ChannelState::AdsCallState::AdsCallState(
gpr_log(GPR_INFO, gpr_log(GPR_INFO,
"[xds_client %p] xds server %s: starting ADS call " "[xds_client %p] xds server %s: starting ADS call "
"(calld: %p, call: %p)", "(calld: %p, call: %p)",
xds_client(), chand()->server_.server_uri().c_str(), this, xds_client(), chand()->server_.server_uri.c_str(), this,
call_.get()); call_.get());
} }
// If this is a reconnect, add any necessary subscriptions from what's // If this is a reconnect, add any necessary subscriptions from what's
@ -910,7 +908,7 @@ void XdsClient::ChannelState::AdsCallState::SendMessageLocked(
gpr_log(GPR_INFO, gpr_log(GPR_INFO,
"[xds_client %p] xds server %s: sending ADS request: type=%s " "[xds_client %p] xds server %s: sending ADS request: type=%s "
"version=%s nonce=%s error=%s", "version=%s nonce=%s error=%s",
xds_client(), chand()->server_.server_uri().c_str(), xds_client(), chand()->server_.server_uri.c_str(),
std::string(type->type_url()).c_str(), std::string(type->type_url()).c_str(),
chand()->resource_type_version_map_[type].c_str(), chand()->resource_type_version_map_[type].c_str(),
state.nonce.c_str(), state.status.ToString().c_str()); state.nonce.c_str(), state.status.ToString().c_str());
@ -983,7 +981,7 @@ void XdsClient::ChannelState::AdsCallState::OnRecvMessage(
gpr_log(GPR_ERROR, gpr_log(GPR_ERROR,
"[xds_client %p] xds server %s: error parsing ADS response (%s) " "[xds_client %p] xds server %s: error parsing ADS response (%s) "
"-- ignoring", "-- ignoring",
xds_client(), chand()->server_.server_uri().c_str(), xds_client(), chand()->server_.server_uri.c_str(),
status.ToString().c_str()); status.ToString().c_str());
} else { } else {
seen_response_ = true; seen_response_ = true;
@ -1000,7 +998,7 @@ void XdsClient::ChannelState::AdsCallState::OnRecvMessage(
"[xds_client %p] xds server %s: ADS response invalid for " "[xds_client %p] xds server %s: ADS response invalid for "
"resource " "resource "
"type %s version %s, will NACK: nonce=%s status=%s", "type %s version %s, will NACK: nonce=%s status=%s",
xds_client(), chand()->server_.server_uri().c_str(), xds_client(), chand()->server_.server_uri.c_str(),
result.type_url.c_str(), result.version.c_str(), result.type_url.c_str(), result.version.c_str(),
state.nonce.c_str(), state.status.ToString().c_str()); state.nonce.c_str(), state.status.ToString().c_str());
} }
@ -1035,7 +1033,7 @@ void XdsClient::ChannelState::AdsCallState::OnRecvMessage(
gpr_log(GPR_ERROR, gpr_log(GPR_ERROR,
"[xds_client %p] xds server %s: ignoring deletion " "[xds_client %p] xds server %s: ignoring deletion "
"for resource type %s name %s", "for resource type %s name %s",
xds_client(), chand()->server_.server_uri().c_str(), xds_client(), chand()->server_.server_uri.c_str(),
result.type_url.c_str(), result.type_url.c_str(),
XdsClient::ConstructFullXdsResourceName( XdsClient::ConstructFullXdsResourceName(
authority, result.type_url.c_str(), resource_key) authority, result.type_url.c_str(), resource_key)
@ -1077,8 +1075,8 @@ void XdsClient::ChannelState::AdsCallState::OnStatusReceived(
gpr_log(GPR_INFO, gpr_log(GPR_INFO,
"[xds_client %p] xds server %s: ADS call status received " "[xds_client %p] xds server %s: ADS call status received "
"(chand=%p, ads_calld=%p, call=%p): %s", "(chand=%p, ads_calld=%p, call=%p): %s",
xds_client(), chand()->server_.server_uri().c_str(), chand(), xds_client(), chand()->server_.server_uri.c_str(), chand(), this,
this, call_.get(), status.ToString().c_str()); call_.get(), status.ToString().c_str());
} }
// Ignore status from a stale call. // Ignore status from a stale call.
if (IsCurrentCallOnChannel()) { if (IsCurrentCallOnChannel()) {
@ -1087,7 +1085,7 @@ void XdsClient::ChannelState::AdsCallState::OnStatusReceived(
// Send error to all watchers. // Send error to all watchers.
xds_client()->NotifyOnErrorLocked(absl::UnavailableError(absl::StrFormat( xds_client()->NotifyOnErrorLocked(absl::UnavailableError(absl::StrFormat(
"xDS call failed: xDS server: %s, ADS call status: %s", "xDS call failed: xDS server: %s, ADS call status: %s",
chand()->server_.server_uri(), status.ToString().c_str()))); chand()->server_.server_uri, status.ToString().c_str())));
} }
} }
xds_client()->work_serializer_.DrainQueue(); xds_client()->work_serializer_.DrainQueue();
@ -1179,7 +1177,7 @@ bool XdsClient::ChannelState::LrsCallState::Reporter::SendReportLocked() {
last_report_counters_were_zero_ = LoadReportCountersAreZero(snapshot); last_report_counters_were_zero_ = LoadReportCountersAreZero(snapshot);
if (old_val && last_report_counters_were_zero_) { if (old_val && last_report_counters_were_zero_) {
auto it = xds_client()->xds_load_report_server_map_.find( auto it = xds_client()->xds_load_report_server_map_.find(
&parent_->chand()->server_); parent_->chand()->server_);
if (it == xds_client()->xds_load_report_server_map_.end() || if (it == xds_client()->xds_load_report_server_map_.end() ||
it->second.load_report_map.empty()) { it->second.load_report_map.empty()) {
it->second.channel_state->StopLrsCallLocked(); it->second.channel_state->StopLrsCallLocked();
@ -1205,8 +1203,8 @@ void XdsClient::ChannelState::LrsCallState::Reporter::OnReportDoneLocked() {
// we just ignore the completion and wait for the timer to fire. // we just ignore the completion and wait for the timer to fire.
if (timer_handle_.has_value()) return; if (timer_handle_.has_value()) return;
// If there are no more registered stats to report, cancel the call. // If there are no more registered stats to report, cancel the call.
auto it = xds_client()->xds_load_report_server_map_.find( auto it =
&parent_->chand()->server_); xds_client()->xds_load_report_server_map_.find(parent_->chand()->server_);
if (it == xds_client()->xds_load_report_server_map_.end()) return; if (it == xds_client()->xds_load_report_server_map_.end()) return;
if (it->second.load_report_map.empty()) { if (it->second.load_report_map.empty()) {
if (it->second.channel_state != nullptr) { if (it->second.channel_state != nullptr) {
@ -1249,7 +1247,7 @@ XdsClient::ChannelState::LrsCallState::LrsCallState(
gpr_log(GPR_INFO, gpr_log(GPR_INFO,
"[xds_client %p] xds server %s: starting LRS call (calld=%p, " "[xds_client %p] xds server %s: starting LRS call (calld=%p, "
"call=%p)", "call=%p)",
xds_client(), chand()->server_.server_uri().c_str(), this, xds_client(), chand()->server_.server_uri.c_str(), this,
call_.get()); call_.get());
} }
// Send the initial request. // Send the initial request.
@ -1313,7 +1311,7 @@ void XdsClient::ChannelState::LrsCallState::OnRecvMessage(
if (!status.ok()) { if (!status.ok()) {
gpr_log(GPR_ERROR, gpr_log(GPR_ERROR,
"[xds_client %p] xds server %s: LRS response parsing failed: %s", "[xds_client %p] xds server %s: LRS response parsing failed: %s",
xds_client(), chand()->server_.server_uri().c_str(), xds_client(), chand()->server_.server_uri.c_str(),
status.ToString().c_str()); status.ToString().c_str());
return; return;
} }
@ -1324,7 +1322,7 @@ void XdsClient::ChannelState::LrsCallState::OnRecvMessage(
"[xds_client %p] xds server %s: LRS response received, %" PRIuPTR "[xds_client %p] xds server %s: LRS response received, %" PRIuPTR
" cluster names, send_all_clusters=%d, load_report_interval=%" PRId64 " cluster names, send_all_clusters=%d, load_report_interval=%" PRId64
"ms", "ms",
xds_client(), chand()->server_.server_uri().c_str(), xds_client(), chand()->server_.server_uri.c_str(),
new_cluster_names.size(), send_all_clusters, new_cluster_names.size(), send_all_clusters,
new_load_reporting_interval.millis()); new_load_reporting_interval.millis());
size_t i = 0; size_t i = 0;
@ -1341,7 +1339,7 @@ void XdsClient::ChannelState::LrsCallState::OnRecvMessage(
gpr_log(GPR_INFO, gpr_log(GPR_INFO,
"[xds_client %p] xds server %s: increased load_report_interval " "[xds_client %p] xds server %s: increased load_report_interval "
"to minimum value %dms", "to minimum value %dms",
xds_client(), chand()->server_.server_uri().c_str(), xds_client(), chand()->server_.server_uri.c_str(),
GRPC_XDS_MIN_CLIENT_LOAD_REPORTING_INTERVAL_MS); GRPC_XDS_MIN_CLIENT_LOAD_REPORTING_INTERVAL_MS);
} }
} }
@ -1353,7 +1351,7 @@ void XdsClient::ChannelState::LrsCallState::OnRecvMessage(
gpr_log(GPR_INFO, gpr_log(GPR_INFO,
"[xds_client %p] xds server %s: incoming LRS response identical " "[xds_client %p] xds server %s: incoming LRS response identical "
"to current, ignoring.", "to current, ignoring.",
xds_client(), chand()->server_.server_uri().c_str()); xds_client(), chand()->server_.server_uri.c_str());
} }
return; return;
} }
@ -1374,7 +1372,7 @@ void XdsClient::ChannelState::LrsCallState::OnStatusReceived(
gpr_log(GPR_INFO, gpr_log(GPR_INFO,
"[xds_client %p] xds server %s: LRS call status received " "[xds_client %p] xds server %s: LRS call status received "
"(chand=%p, calld=%p, call=%p): %s", "(chand=%p, calld=%p, call=%p): %s",
xds_client(), chand()->server_.server_uri().c_str(), chand(), this, xds_client(), chand()->server_.server_uri.c_str(), chand(), this,
call_.get(), status.ToString().c_str()); call_.get(), status.ToString().c_str());
} }
// Ignore status from a stale call. // Ignore status from a stale call.
@ -1409,7 +1407,6 @@ XdsClient::XdsClient(std::unique_ptr<XdsBootstrap> bootstrap,
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) { if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
gpr_log(GPR_INFO, "[xds_client %p] creating xds client", this); gpr_log(GPR_INFO, "[xds_client %p] creating xds client", this);
} }
GPR_ASSERT(bootstrap_ != nullptr);
} }
XdsClient::~XdsClient() { XdsClient::~XdsClient() {
@ -1438,14 +1435,14 @@ void XdsClient::Orphan() {
RefCountedPtr<XdsClient::ChannelState> XdsClient::GetOrCreateChannelStateLocked( RefCountedPtr<XdsClient::ChannelState> XdsClient::GetOrCreateChannelStateLocked(
const XdsBootstrap::XdsServer& server, const char* reason) { const XdsBootstrap::XdsServer& server, const char* reason) {
auto it = xds_server_channel_map_.find(&server); auto it = xds_server_channel_map_.find(server);
if (it != xds_server_channel_map_.end()) { if (it != xds_server_channel_map_.end()) {
return it->second->Ref(DEBUG_LOCATION, reason); return it->second->Ref(DEBUG_LOCATION, reason);
} }
// Channel not found, so create a new one. // Channel not found, so create a new one.
auto channel_state = MakeRefCounted<ChannelState>( auto channel_state = MakeRefCounted<ChannelState>(
WeakRef(DEBUG_LOCATION, "ChannelState"), server); WeakRef(DEBUG_LOCATION, "ChannelState"), server);
xds_server_channel_map_[&server] = channel_state.get(); xds_server_channel_map_[server] = channel_state.get();
return channel_state; return channel_state;
} }
@ -1485,7 +1482,9 @@ void XdsClient::WatchResource(const XdsResourceType* type,
"\" not present in bootstrap config"))); "\" not present in bootstrap config")));
return; return;
} }
xds_server = authority->server(); if (!authority->xds_servers.empty()) {
xds_server = &authority->xds_servers[0];
}
} }
if (xds_server == nullptr) xds_server = &bootstrap_->server(); if (xds_server == nullptr) xds_server = &bootstrap_->server();
{ {
@ -1635,8 +1634,7 @@ std::string XdsClient::ConstructFullXdsResourceName(
RefCountedPtr<XdsClusterDropStats> XdsClient::AddClusterDropStats( RefCountedPtr<XdsClusterDropStats> XdsClient::AddClusterDropStats(
const XdsBootstrap::XdsServer& xds_server, absl::string_view cluster_name, const XdsBootstrap::XdsServer& xds_server, absl::string_view cluster_name,
absl::string_view eds_service_name) { absl::string_view eds_service_name) {
const auto* server = bootstrap_->FindXdsServer(xds_server); if (!bootstrap_->XdsServerExists(xds_server)) return nullptr;
if (server == nullptr) return nullptr;
auto key = auto key =
std::make_pair(std::string(cluster_name), std::string(eds_service_name)); std::make_pair(std::string(cluster_name), std::string(eds_service_name));
RefCountedPtr<XdsClusterDropStats> cluster_drop_stats; RefCountedPtr<XdsClusterDropStats> cluster_drop_stats;
@ -1648,10 +1646,11 @@ RefCountedPtr<XdsClusterDropStats> XdsClient::AddClusterDropStats(
// XdsBootstrap::XdsServer and strings // XdsBootstrap::XdsServer and strings
// in the load_report_map_ key, so that they have the same lifetime. // in the load_report_map_ key, so that they have the same lifetime.
auto server_it = auto server_it =
xds_load_report_server_map_.emplace(server, LoadReportServer()).first; xds_load_report_server_map_.emplace(xds_server, LoadReportServer())
.first;
if (server_it->second.channel_state == nullptr) { if (server_it->second.channel_state == nullptr) {
server_it->second.channel_state = GetOrCreateChannelStateLocked( server_it->second.channel_state = GetOrCreateChannelStateLocked(
*server, "load report map (drop stats)"); xds_server, "load report map (drop stats)");
} }
auto load_report_it = server_it->second.load_report_map auto load_report_it = server_it->second.load_report_map
.emplace(std::move(key), LoadReportState()) .emplace(std::move(key), LoadReportState())
@ -1666,7 +1665,7 @@ RefCountedPtr<XdsClusterDropStats> XdsClient::AddClusterDropStats(
load_report_state.drop_stats->GetSnapshotAndReset(); load_report_state.drop_stats->GetSnapshotAndReset();
} }
cluster_drop_stats = MakeRefCounted<XdsClusterDropStats>( cluster_drop_stats = MakeRefCounted<XdsClusterDropStats>(
Ref(DEBUG_LOCATION, "DropStats"), *server, Ref(DEBUG_LOCATION, "DropStats"), server_it->first,
load_report_it->first.first /*cluster_name*/, load_report_it->first.first /*cluster_name*/,
load_report_it->first.second /*eds_service_name*/); load_report_it->first.second /*eds_service_name*/);
load_report_state.drop_stats = cluster_drop_stats.get(); load_report_state.drop_stats = cluster_drop_stats.get();
@ -1681,10 +1680,8 @@ void XdsClient::RemoveClusterDropStats(
const XdsBootstrap::XdsServer& xds_server, absl::string_view cluster_name, const XdsBootstrap::XdsServer& xds_server, absl::string_view cluster_name,
absl::string_view eds_service_name, absl::string_view eds_service_name,
XdsClusterDropStats* cluster_drop_stats) { XdsClusterDropStats* cluster_drop_stats) {
const auto* server = bootstrap_->FindXdsServer(xds_server);
if (server == nullptr) return;
MutexLock lock(&mu_); MutexLock lock(&mu_);
auto server_it = xds_load_report_server_map_.find(server); auto server_it = xds_load_report_server_map_.find(xds_server);
if (server_it == xds_load_report_server_map_.end()) return; if (server_it == xds_load_report_server_map_.end()) return;
auto load_report_it = server_it->second.load_report_map.find( auto load_report_it = server_it->second.load_report_map.find(
std::make_pair(std::string(cluster_name), std::string(eds_service_name))); std::make_pair(std::string(cluster_name), std::string(eds_service_name)));
@ -1703,8 +1700,7 @@ RefCountedPtr<XdsClusterLocalityStats> XdsClient::AddClusterLocalityStats(
const XdsBootstrap::XdsServer& xds_server, absl::string_view cluster_name, const XdsBootstrap::XdsServer& xds_server, absl::string_view cluster_name,
absl::string_view eds_service_name, absl::string_view eds_service_name,
RefCountedPtr<XdsLocalityName> locality) { RefCountedPtr<XdsLocalityName> locality) {
const auto* server = bootstrap_->FindXdsServer(xds_server); if (!bootstrap_->XdsServerExists(xds_server)) return nullptr;
if (server == nullptr) return nullptr;
auto key = auto key =
std::make_pair(std::string(cluster_name), std::string(eds_service_name)); std::make_pair(std::string(cluster_name), std::string(eds_service_name));
RefCountedPtr<XdsClusterLocalityStats> cluster_locality_stats; RefCountedPtr<XdsClusterLocalityStats> cluster_locality_stats;
@ -1716,10 +1712,11 @@ RefCountedPtr<XdsClusterLocalityStats> XdsClient::AddClusterLocalityStats(
// XdsBootstrap::XdsServer and strings // XdsBootstrap::XdsServer and strings
// in the load_report_map_ key, so that they have the same lifetime. // in the load_report_map_ key, so that they have the same lifetime.
auto server_it = auto server_it =
xds_load_report_server_map_.emplace(server, LoadReportServer()).first; xds_load_report_server_map_.emplace(xds_server, LoadReportServer())
.first;
if (server_it->second.channel_state == nullptr) { if (server_it->second.channel_state == nullptr) {
server_it->second.channel_state = GetOrCreateChannelStateLocked( server_it->second.channel_state = GetOrCreateChannelStateLocked(
*server, "load report map (locality stats)"); xds_server, "load report map (locality stats)");
} }
auto load_report_it = server_it->second.load_report_map auto load_report_it = server_it->second.load_report_map
.emplace(std::move(key), LoadReportState()) .emplace(std::move(key), LoadReportState())
@ -1736,7 +1733,7 @@ RefCountedPtr<XdsClusterLocalityStats> XdsClient::AddClusterLocalityStats(
locality_state.locality_stats->GetSnapshotAndReset(); locality_state.locality_stats->GetSnapshotAndReset();
} }
cluster_locality_stats = MakeRefCounted<XdsClusterLocalityStats>( cluster_locality_stats = MakeRefCounted<XdsClusterLocalityStats>(
Ref(DEBUG_LOCATION, "LocalityStats"), *server, Ref(DEBUG_LOCATION, "LocalityStats"), server_it->first,
load_report_it->first.first /*cluster_name*/, load_report_it->first.first /*cluster_name*/,
load_report_it->first.second /*eds_service_name*/, load_report_it->first.second /*eds_service_name*/,
std::move(locality)); std::move(locality));
@ -1753,10 +1750,8 @@ void XdsClient::RemoveClusterLocalityStats(
absl::string_view eds_service_name, absl::string_view eds_service_name,
const RefCountedPtr<XdsLocalityName>& locality, const RefCountedPtr<XdsLocalityName>& locality,
XdsClusterLocalityStats* cluster_locality_stats) { XdsClusterLocalityStats* cluster_locality_stats) {
const auto* server = bootstrap_->FindXdsServer(xds_server);
if (server == nullptr) return;
MutexLock lock(&mu_); MutexLock lock(&mu_);
auto server_it = xds_load_report_server_map_.find(server); auto server_it = xds_load_report_server_map_.find(xds_server);
if (server_it == xds_load_report_server_map_.end()) return; if (server_it == xds_load_report_server_map_.end()) return;
auto load_report_it = server_it->second.load_report_map.find( auto load_report_it = server_it->second.load_report_map.find(
std::make_pair(std::string(cluster_name), std::string(eds_service_name))); std::make_pair(std::string(cluster_name), std::string(eds_service_name)));
@ -1785,8 +1780,8 @@ void XdsClient::NotifyOnErrorLocked(absl::Status status) {
const auto* node = bootstrap_->node(); const auto* node = bootstrap_->node();
if (node != nullptr) { if (node != nullptr) {
status = absl::Status( status = absl::Status(
status.code(), status.code(), absl::StrCat(status.message(),
absl::StrCat(status.message(), " (node ID:", node->id(), ")")); " (node ID:", bootstrap_->node()->id, ")"));
} }
std::set<RefCountedPtr<ResourceWatcherInterface>> watchers; std::set<RefCountedPtr<ResourceWatcherInterface>> watchers;
for (const auto& a : authority_state_map_) { // authority for (const auto& a : authority_state_map_) { // authority
@ -1816,8 +1811,8 @@ void XdsClient::NotifyWatchersOnErrorLocked(
const auto* node = bootstrap_->node(); const auto* node = bootstrap_->node();
if (node != nullptr) { if (node != nullptr) {
status = absl::Status( status = absl::Status(
status.code(), status.code(), absl::StrCat(status.message(),
absl::StrCat(status.message(), " (node ID:", node->id(), ")")); " (node ID:", bootstrap_->node()->id, ")"));
} }
work_serializer_.Schedule( work_serializer_.Schedule(
[watchers, status]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(&work_serializer_) { [watchers, status]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(&work_serializer_) {
@ -1847,7 +1842,7 @@ XdsApi::ClusterLoadReportMap XdsClient::BuildLoadReportSnapshotLocked(
gpr_log(GPR_INFO, "[xds_client %p] start building load report", this); gpr_log(GPR_INFO, "[xds_client %p] start building load report", this);
} }
XdsApi::ClusterLoadReportMap snapshot_map; XdsApi::ClusterLoadReportMap snapshot_map;
auto server_it = xds_load_report_server_map_.find(&xds_server); auto server_it = xds_load_report_server_map_.find(xds_server);
if (server_it == xds_load_report_server_map_.end()) return snapshot_map; if (server_it == xds_load_report_server_map_.end()) return snapshot_map;
auto& load_report_map = server_it->second.load_report_map; auto& load_report_map = server_it->second.load_report_map;
for (auto load_report_it = load_report_map.begin(); for (auto load_report_it = load_report_map.begin();

@ -76,7 +76,9 @@ class XdsClient : public DualRefCounted<XdsClient> {
~XdsClient() override; ~XdsClient() override;
const XdsBootstrap& bootstrap() const { const XdsBootstrap& bootstrap() const {
return *bootstrap_; // ctor asserts that it is non-null // bootstrap_ is guaranteed to be non-null since XdsClient::GetOrCreate()
// would return a null object if bootstrap_ was null.
return *bootstrap_;
} }
XdsTransportFactory* transport_factory() const { XdsTransportFactory* transport_factory() const {
@ -205,7 +207,7 @@ class XdsClient : public DualRefCounted<XdsClient> {
// The owning xds client. // The owning xds client.
WeakRefCountedPtr<XdsClient> xds_client_; WeakRefCountedPtr<XdsClient> xds_client_;
const XdsBootstrap::XdsServer& server_; // Owned by bootstrap. const XdsBootstrap::XdsServer& server_;
OrphanablePtr<XdsTransportFactory::XdsTransport> transport_; OrphanablePtr<XdsTransportFactory::XdsTransport> transport_;
@ -310,15 +312,13 @@ class XdsClient : public DualRefCounted<XdsClient> {
upb::SymbolTable symtab_ ABSL_GUARDED_BY(mu_); upb::SymbolTable symtab_ ABSL_GUARDED_BY(mu_);
// Map of existing xDS server channels. // Map of existing xDS server channels.
// Key is owned by the bootstrap config. std::map<XdsBootstrap::XdsServer, ChannelState*> xds_server_channel_map_
std::map<const XdsBootstrap::XdsServer*, ChannelState*> ABSL_GUARDED_BY(mu_);
xds_server_channel_map_ ABSL_GUARDED_BY(mu_);
std::map<std::string /*authority*/, AuthorityState> authority_state_map_ std::map<std::string /*authority*/, AuthorityState> authority_state_map_
ABSL_GUARDED_BY(mu_); ABSL_GUARDED_BY(mu_);
// Key is owned by the bootstrap config. std::map<XdsBootstrap::XdsServer, LoadReportServer>
std::map<const XdsBootstrap::XdsServer*, LoadReportServer>
xds_load_report_server_map_ ABSL_GUARDED_BY(mu_); xds_load_report_server_map_ ABSL_GUARDED_BY(mu_);
// Stores started watchers whose resource name was not parsed successfully, // Stores started watchers whose resource name was not parsed successfully,

@ -141,11 +141,13 @@ absl::StatusOr<RefCountedPtr<GrpcXdsClient>> GrpcXdsClient::GetOrCreate(
absl::optional<absl::string_view> bootstrap_config = args.GetString( absl::optional<absl::string_view> bootstrap_config = args.GetString(
GRPC_ARG_TEST_ONLY_DO_NOT_USE_IN_PROD_XDS_BOOTSTRAP_CONFIG); GRPC_ARG_TEST_ONLY_DO_NOT_USE_IN_PROD_XDS_BOOTSTRAP_CONFIG);
if (bootstrap_config.has_value()) { if (bootstrap_config.has_value()) {
auto bootstrap = GrpcXdsBootstrap::Create(*bootstrap_config); grpc_error_handle error = GRPC_ERROR_NONE;
if (!bootstrap.ok()) return bootstrap.status(); std::unique_ptr<GrpcXdsBootstrap> bootstrap =
GrpcXdsBootstrap::Create(*bootstrap_config, &error);
if (!GRPC_ERROR_IS_NONE(error)) return grpc_error_to_absl_status(error);
grpc_channel_args* xds_channel_args = args.GetPointer<grpc_channel_args>( grpc_channel_args* xds_channel_args = args.GetPointer<grpc_channel_args>(
GRPC_ARG_TEST_ONLY_DO_NOT_USE_IN_PROD_XDS_CLIENT_CHANNEL_ARGS); GRPC_ARG_TEST_ONLY_DO_NOT_USE_IN_PROD_XDS_CLIENT_CHANNEL_ARGS);
return MakeRefCounted<GrpcXdsClient>(std::move(*bootstrap), return MakeRefCounted<GrpcXdsClient>(std::move(bootstrap),
ChannelArgs::FromC(xds_channel_args)); ChannelArgs::FromC(xds_channel_args));
} }
// Otherwise, use the global instance. // Otherwise, use the global instance.
@ -162,16 +164,18 @@ absl::StatusOr<RefCountedPtr<GrpcXdsClient>> GrpcXdsClient::GetOrCreate(
bootstrap_contents->c_str()); bootstrap_contents->c_str());
} }
// Parse bootstrap. // Parse bootstrap.
auto bootstrap = GrpcXdsBootstrap::Create(*bootstrap_contents); grpc_error_handle error = GRPC_ERROR_NONE;
if (!bootstrap.ok()) return bootstrap.status(); std::unique_ptr<GrpcXdsBootstrap> bootstrap =
GrpcXdsBootstrap::Create(*bootstrap_contents, &error);
if (!GRPC_ERROR_IS_NONE(error)) return grpc_error_to_absl_status(error);
// Instantiate XdsClient. // Instantiate XdsClient.
auto xds_client = MakeRefCounted<GrpcXdsClient>( auto xds_client = MakeRefCounted<GrpcXdsClient>(
std::move(*bootstrap), ChannelArgs::FromC(g_channel_args)); std::move(bootstrap), ChannelArgs::FromC(g_channel_args));
g_xds_client = xds_client.get(); g_xds_client = xds_client.get();
return xds_client; return xds_client;
} }
GrpcXdsClient::GrpcXdsClient(std::unique_ptr<GrpcXdsBootstrap> bootstrap, GrpcXdsClient::GrpcXdsClient(std::unique_ptr<XdsBootstrap> bootstrap,
const ChannelArgs& args) const ChannelArgs& args)
: XdsClient( : XdsClient(
std::move(bootstrap), MakeOrphanable<GrpcXdsTransportFactory>(args), std::move(bootstrap), MakeOrphanable<GrpcXdsTransportFactory>(args),

@ -27,7 +27,7 @@
#include <grpc/impl/codegen/grpc_types.h> #include <grpc/impl/codegen/grpc_types.h>
#include "src/core/ext/xds/certificate_provider_store.h" #include "src/core/ext/xds/certificate_provider_store.h"
#include "src/core/ext/xds/xds_bootstrap_grpc.h" #include "src/core/ext/xds/xds_bootstrap.h"
#include "src/core/ext/xds/xds_client.h" #include "src/core/ext/xds/xds_client.h"
#include "src/core/lib/channel/channel_args.h" #include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/gpr/useful.h" #include "src/core/lib/gpr/useful.h"
@ -44,7 +44,7 @@ class GrpcXdsClient : public XdsClient {
const ChannelArgs& args, const char* reason); const ChannelArgs& args, const char* reason);
// Do not instantiate directly -- use GetOrCreate() instead. // Do not instantiate directly -- use GetOrCreate() instead.
GrpcXdsClient(std::unique_ptr<GrpcXdsBootstrap> bootstrap, GrpcXdsClient(std::unique_ptr<XdsBootstrap> bootstrap,
const ChannelArgs& args); const ChannelArgs& args);
~GrpcXdsClient() override; ~GrpcXdsClient() override;

@ -53,7 +53,7 @@ XdsClusterDropStats::XdsClusterDropStats(
eds_service_name_(eds_service_name) { eds_service_name_(eds_service_name) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) { if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
gpr_log(GPR_INFO, "[xds_client %p] created drop stats %p for {%s, %s, %s}", gpr_log(GPR_INFO, "[xds_client %p] created drop stats %p for {%s, %s, %s}",
xds_client_.get(), this, lrs_server_.server_uri().c_str(), xds_client_.get(), this, lrs_server_.server_uri.c_str(),
std::string(cluster_name_).c_str(), std::string(cluster_name_).c_str(),
std::string(eds_service_name_).c_str()); std::string(eds_service_name_).c_str());
} }
@ -63,7 +63,7 @@ XdsClusterDropStats::~XdsClusterDropStats() {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) { if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
gpr_log(GPR_INFO, gpr_log(GPR_INFO,
"[xds_client %p] destroying drop stats %p for {%s, %s, %s}", "[xds_client %p] destroying drop stats %p for {%s, %s, %s}",
xds_client_.get(), this, lrs_server_.server_uri().c_str(), xds_client_.get(), this, lrs_server_.server_uri.c_str(),
std::string(cluster_name_).c_str(), std::string(cluster_name_).c_str(),
std::string(eds_service_name_).c_str()); std::string(eds_service_name_).c_str());
} }
@ -108,7 +108,7 @@ XdsClusterLocalityStats::XdsClusterLocalityStats(
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) { if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
gpr_log(GPR_INFO, gpr_log(GPR_INFO,
"[xds_client %p] created locality stats %p for {%s, %s, %s, %s}", "[xds_client %p] created locality stats %p for {%s, %s, %s, %s}",
xds_client_.get(), this, lrs_server_.server_uri().c_str(), xds_client_.get(), this, lrs_server_.server_uri.c_str(),
std::string(cluster_name_).c_str(), std::string(cluster_name_).c_str(),
std::string(eds_service_name_).c_str(), std::string(eds_service_name_).c_str(),
name_->AsHumanReadableString().c_str()); name_->AsHumanReadableString().c_str());
@ -119,7 +119,7 @@ XdsClusterLocalityStats::~XdsClusterLocalityStats() {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) { if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
gpr_log(GPR_INFO, gpr_log(GPR_INFO,
"[xds_client %p] destroying locality stats %p for {%s, %s, %s, %s}", "[xds_client %p] destroying locality stats %p for {%s, %s, %s, %s}",
xds_client_.get(), this, lrs_server_.server_uri().c_str(), xds_client_.get(), this, lrs_server_.server_uri.c_str(),
std::string(cluster_name_).c_str(), std::string(cluster_name_).c_str(),
std::string(eds_service_name_).c_str(), std::string(eds_service_name_).c_str(),
name_->AsHumanReadableString().c_str()); name_->AsHumanReadableString().c_str());

@ -25,6 +25,7 @@
#include "absl/memory/memory.h" #include "absl/memory/memory.h"
#include "absl/status/status.h" #include "absl/status/status.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_join.h" #include "absl/strings/str_join.h"
#include "envoy/config/cluster/v3/circuit_breaker.upb.h" #include "envoy/config/cluster/v3/circuit_breaker.upb.h"
#include "envoy/config/cluster/v3/cluster.upb.h" #include "envoy/config/cluster/v3/cluster.upb.h"
@ -64,26 +65,27 @@ std::string XdsClusterResource::ToString() const {
case EDS: case EDS:
contents.push_back("cluster_type=EDS"); contents.push_back("cluster_type=EDS");
if (!eds_service_name.empty()) { if (!eds_service_name.empty()) {
contents.push_back(absl::StrCat("eds_service_name=", eds_service_name)); contents.push_back(
absl::StrFormat("eds_service_name=%s", eds_service_name));
} }
break; break;
case LOGICAL_DNS: case LOGICAL_DNS:
contents.push_back("cluster_type=LOGICAL_DNS"); contents.push_back("cluster_type=LOGICAL_DNS");
contents.push_back(absl::StrCat("dns_hostname=", dns_hostname)); contents.push_back(absl::StrFormat("dns_hostname=%s", dns_hostname));
break; break;
case AGGREGATE: case AGGREGATE:
contents.push_back("cluster_type=AGGREGATE"); contents.push_back("cluster_type=AGGREGATE");
contents.push_back( contents.push_back(
absl::StrCat("prioritized_cluster_names=[", absl::StrFormat("prioritized_cluster_names=[%s]",
absl::StrJoin(prioritized_cluster_names, ", "), "]")); absl::StrJoin(prioritized_cluster_names, ", ")));
} }
if (!common_tls_context.Empty()) { if (!common_tls_context.Empty()) {
contents.push_back( contents.push_back(absl::StrFormat("common_tls_context=%s",
absl::StrCat("common_tls_context=", common_tls_context.ToString())); common_tls_context.ToString()));
} }
if (lrs_load_reporting_server.has_value()) { if (lrs_load_reporting_server.has_value()) {
contents.push_back(absl::StrCat("lrs_load_reporting_server_name=", contents.push_back(absl::StrFormat("lrs_load_reporting_server_name=%s",
lrs_load_reporting_server->server_uri())); lrs_load_reporting_server->server_uri));
} }
contents.push_back(absl::StrCat("lb_policy=", lb_policy)); contents.push_back(absl::StrCat("lb_policy=", lb_policy));
if (lb_policy == "RING_HASH") { if (lb_policy == "RING_HASH") {
@ -91,7 +93,7 @@ std::string XdsClusterResource::ToString() const {
contents.push_back(absl::StrCat("max_ring_size=", max_ring_size)); contents.push_back(absl::StrCat("max_ring_size=", max_ring_size));
} }
contents.push_back( contents.push_back(
absl::StrCat("max_concurrent_requests=", max_concurrent_requests)); absl::StrFormat("max_concurrent_requests=%d", max_concurrent_requests));
return absl::StrCat("{", absl::StrJoin(contents, ", "), "}"); return absl::StrCat("{", absl::StrJoin(contents, ", "), "}");
} }
@ -351,8 +353,7 @@ absl::StatusOr<XdsClusterResource> CdsResourceParse(
if (!envoy_config_core_v3_ConfigSource_has_self(lrs_server)) { if (!envoy_config_core_v3_ConfigSource_has_self(lrs_server)) {
errors.emplace_back("LRS ConfigSource is not self."); errors.emplace_back("LRS ConfigSource is not self.");
} }
cds_update.lrs_load_reporting_server.emplace( cds_update.lrs_load_reporting_server.emplace(context.server);
static_cast<const GrpcXdsBootstrap::GrpcXdsServer&>(context.server));
} }
// The Cluster resource encodes the circuit breaking parameters in a list of // The Cluster resource encodes the circuit breaking parameters in a list of
// Thresholds messages, where each message specifies the parameters for a // Thresholds messages, where each message specifies the parameters for a

@ -35,7 +35,7 @@
#include "upb/def.h" #include "upb/def.h"
#include "src/core/ext/filters/client_channel/lb_policy/outlier_detection/outlier_detection.h" #include "src/core/ext/filters/client_channel/lb_policy/outlier_detection/outlier_detection.h"
#include "src/core/ext/xds/xds_bootstrap_grpc.h" #include "src/core/ext/xds/xds_bootstrap.h"
#include "src/core/ext/xds/xds_common_types.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_resource_type.h"
#include "src/core/ext/xds/xds_resource_type_impl.h" #include "src/core/ext/xds/xds_resource_type_impl.h"
@ -61,7 +61,7 @@ struct XdsClusterResource {
// The LRS server to use for load reporting. // The LRS server to use for load reporting.
// If not set, load reporting will be disabled. // If not set, load reporting will be disabled.
absl::optional<GrpcXdsBootstrap::GrpcXdsServer> lrs_load_reporting_server; absl::optional<XdsBootstrap::XdsServer> lrs_load_reporting_server;
// The LB policy to use (e.g., "ROUND_ROBIN" or "RING_HASH"). // The LB policy to use (e.g., "ROUND_ROBIN" or "RING_HASH").
std::string lb_policy; std::string lb_policy;

@ -34,7 +34,6 @@
#include "src/core/ext/filters/client_channel/client_channel.h" #include "src/core/ext/filters/client_channel/client_channel.h"
#include "src/core/ext/xds/xds_bootstrap.h" #include "src/core/ext/xds/xds_bootstrap.h"
#include "src/core/ext/xds/xds_bootstrap_grpc.h"
#include "src/core/lib/channel/channel_args.h" #include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/channel/channel_fwd.h" #include "src/core/lib/channel/channel_fwd.h"
#include "src/core/lib/channel/channel_stack.h" #include "src/core/lib/channel/channel_stack.h"
@ -248,11 +247,11 @@ class GrpcXdsTransportFactory::GrpcXdsTransport::StateWatcher
namespace { namespace {
grpc_channel* CreateXdsChannel(const ChannelArgs& args, grpc_channel* CreateXdsChannel(const ChannelArgs& args,
const GrpcXdsBootstrap::GrpcXdsServer& server) { const XdsBootstrap::XdsServer& server) {
RefCountedPtr<grpc_channel_credentials> channel_creds = RefCountedPtr<grpc_channel_credentials> channel_creds =
CoreConfiguration::Get().channel_creds_registry().CreateChannelCreds( CoreConfiguration::Get().channel_creds_registry().CreateChannelCreds(
server.channel_creds_type(), server.channel_creds_config()); server.channel_creds_type, server.channel_creds_config);
return grpc_channel_create(server.server_uri().c_str(), channel_creds.get(), return grpc_channel_create(server.server_uri.c_str(), channel_creds.get(),
args.ToC().get()); args.ToC().get());
} }
@ -269,9 +268,7 @@ GrpcXdsTransportFactory::GrpcXdsTransport::GrpcXdsTransport(
std::function<void(absl::Status)> on_connectivity_failure, std::function<void(absl::Status)> on_connectivity_failure,
absl::Status* status) absl::Status* status)
: factory_(factory) { : factory_(factory) {
channel_ = CreateXdsChannel( channel_ = CreateXdsChannel(factory->args_, server);
factory->args_,
static_cast<const GrpcXdsBootstrap::GrpcXdsServer&>(server));
GPR_ASSERT(channel_ != nullptr); GPR_ASSERT(channel_ != nullptr);
if (IsLameChannel(channel_)) { if (IsLameChannel(channel_)) {
*status = absl::UnavailableError("xds client has a lame channel"); *status = absl::UnavailableError("xds client has a lame channel");

@ -113,49 +113,46 @@ TEST(XdsBootstrapTest, Basic) {
" \"server_listener_resource_name_template\": \"example/resource\"," " \"server_listener_resource_name_template\": \"example/resource\","
" \"ignore\": {}" " \"ignore\": {}"
"}"; "}";
auto bootstrap_or = GrpcXdsBootstrap::Create(json_str); auto json = Json::Parse(json_str);
ASSERT_TRUE(bootstrap_or.ok()) << bootstrap_or.status(); ASSERT_TRUE(json.ok()) << json.status();
auto bootstrap = std::move(*bootstrap_or); grpc_error_handle error = GRPC_ERROR_NONE;
auto* server = GrpcXdsBootstrap bootstrap(std::move(*json), &error);
&static_cast<const GrpcXdsBootstrap::GrpcXdsServer&>(bootstrap->server()); EXPECT_EQ(error, GRPC_ERROR_NONE) << grpc_error_std_string(error);
EXPECT_EQ(server->server_uri(), "fake:///lb"); EXPECT_EQ(bootstrap.server().server_uri, "fake:///lb");
EXPECT_EQ(server->channel_creds_type(), "fake"); EXPECT_EQ(bootstrap.server().channel_creds_type, "fake");
EXPECT_TRUE(server->channel_creds_config().empty()) EXPECT_EQ(bootstrap.server().channel_creds_config.type(),
<< Json{server->channel_creds_config()}.Dump(); Json::Type::JSON_NULL);
EXPECT_EQ(bootstrap->authorities().size(), 2); EXPECT_EQ(bootstrap.authorities().size(), 2);
auto* authority = static_cast<const GrpcXdsBootstrap::GrpcAuthority*>( const XdsBootstrap::Authority* authority1 =
bootstrap->LookupAuthority("xds.example.com")); bootstrap.LookupAuthority("xds.example.com");
ASSERT_NE(authority, nullptr); ASSERT_NE(authority1, nullptr);
EXPECT_EQ(authority->client_listener_resource_name_template(), EXPECT_EQ(authority1->client_listener_resource_name_template,
"xdstp://xds.example.com/envoy.config.listener.v3.Listener/grpc/" "xdstp://xds.example.com/envoy.config.listener.v3.Listener/grpc/"
"server/%s"); "server/%s");
server = EXPECT_EQ(authority1->xds_servers.size(), 1);
static_cast<const GrpcXdsBootstrap::GrpcXdsServer*>(authority->server()); EXPECT_EQ(authority1->xds_servers[0].server_uri, "fake:///xds_server");
ASSERT_NE(server, nullptr); EXPECT_EQ(authority1->xds_servers[0].channel_creds_type, "fake");
EXPECT_EQ(server->server_uri(), "fake:///xds_server"); EXPECT_EQ(authority1->xds_servers[0].channel_creds_config.type(),
EXPECT_EQ(server->channel_creds_type(), "fake"); Json::Type::JSON_NULL);
EXPECT_TRUE(server->channel_creds_config().empty()) const XdsBootstrap::Authority* authority2 =
<< Json{server->channel_creds_config()}.Dump(); bootstrap.LookupAuthority("xds.example2.com");
authority = static_cast<const GrpcXdsBootstrap::GrpcAuthority*>( ASSERT_NE(authority2, nullptr);
bootstrap->LookupAuthority("xds.example2.com")); EXPECT_EQ(authority2->client_listener_resource_name_template,
ASSERT_NE(authority, nullptr);
EXPECT_EQ(authority->client_listener_resource_name_template(),
"xdstp://xds.example2.com/envoy.config.listener.v3.Listener/grpc/" "xdstp://xds.example2.com/envoy.config.listener.v3.Listener/grpc/"
"server/%s"); "server/%s");
server = EXPECT_EQ(authority2->xds_servers.size(), 1);
static_cast<const GrpcXdsBootstrap::GrpcXdsServer*>(authority->server()); EXPECT_EQ(authority2->xds_servers[0].server_uri, "fake:///xds_server2");
ASSERT_NE(server, nullptr); EXPECT_EQ(authority2->xds_servers[0].channel_creds_type, "fake");
EXPECT_EQ(server->server_uri(), "fake:///xds_server2"); EXPECT_EQ(authority2->xds_servers[0].channel_creds_config.type(),
EXPECT_EQ(server->channel_creds_type(), "fake"); Json::Type::JSON_NULL);
EXPECT_TRUE(server->channel_creds_config().empty()) ASSERT_NE(bootstrap.node(), nullptr);
<< Json{server->channel_creds_config()}.Dump(); EXPECT_EQ(bootstrap.node()->id, "foo");
ASSERT_NE(bootstrap->node(), nullptr); EXPECT_EQ(bootstrap.node()->cluster, "bar");
EXPECT_EQ(bootstrap->node()->id(), "foo"); EXPECT_EQ(bootstrap.node()->locality_region, "milky_way");
EXPECT_EQ(bootstrap->node()->cluster(), "bar"); EXPECT_EQ(bootstrap.node()->locality_zone, "sol_system");
EXPECT_EQ(bootstrap->node()->locality_region(), "milky_way"); EXPECT_EQ(bootstrap.node()->locality_sub_zone, "earth");
EXPECT_EQ(bootstrap->node()->locality_zone(), "sol_system"); ASSERT_EQ(bootstrap.node()->metadata.type(), Json::Type::OBJECT);
EXPECT_EQ(bootstrap->node()->locality_sub_zone(), "earth"); EXPECT_THAT(bootstrap.node()->metadata.object_value(),
EXPECT_THAT(bootstrap->node()->metadata(),
::testing::ElementsAre( ::testing::ElementsAre(
::testing::Pair( ::testing::Pair(
::testing::Eq("bar"), ::testing::Eq("bar"),
@ -167,7 +164,7 @@ TEST(XdsBootstrapTest, Basic) {
::testing::AllOf( ::testing::AllOf(
::testing::Property(&Json::type, Json::Type::NUMBER), ::testing::Property(&Json::type, Json::Type::NUMBER),
::testing::Property(&Json::string_value, "1"))))); ::testing::Property(&Json::string_value, "1")))));
EXPECT_EQ(bootstrap->server_listener_resource_name_template(), EXPECT_EQ(bootstrap.server_listener_resource_name_template(),
"example/resource"); "example/resource");
gpr_unsetenv("GRPC_EXPERIMENTAL_XDS_FEDERATION"); gpr_unsetenv("GRPC_EXPERIMENTAL_XDS_FEDERATION");
} }
@ -182,14 +179,14 @@ TEST(XdsBootstrapTest, ValidWithoutNode) {
" }" " }"
" ]" " ]"
"}"; "}";
auto bootstrap_or = GrpcXdsBootstrap::Create(json_str); auto json = Json::Parse(json_str);
ASSERT_TRUE(bootstrap_or.ok()) << bootstrap_or.status(); ASSERT_TRUE(json.ok()) << json.status();
auto bootstrap = std::move(*bootstrap_or); grpc_error_handle error = GRPC_ERROR_NONE;
auto* server = GrpcXdsBootstrap bootstrap(std::move(*json), &error);
&static_cast<const GrpcXdsBootstrap::GrpcXdsServer&>(bootstrap->server()); EXPECT_EQ(error, GRPC_ERROR_NONE) << grpc_error_std_string(error);
EXPECT_EQ(server->server_uri(), "fake:///lb"); EXPECT_EQ(bootstrap.server().server_uri, "fake:///lb");
EXPECT_EQ(server->channel_creds_type(), "fake"); EXPECT_EQ(bootstrap.server().channel_creds_type, "fake");
EXPECT_EQ(bootstrap->node(), nullptr); EXPECT_EQ(bootstrap.node(), nullptr);
} }
TEST(XdsBootstrapTest, InsecureCreds) { TEST(XdsBootstrapTest, InsecureCreds) {
@ -202,14 +199,14 @@ TEST(XdsBootstrapTest, InsecureCreds) {
" }" " }"
" ]" " ]"
"}"; "}";
auto bootstrap_or = GrpcXdsBootstrap::Create(json_str); auto json = Json::Parse(json_str);
ASSERT_TRUE(bootstrap_or.ok()) << bootstrap_or.status(); ASSERT_TRUE(json.ok()) << json.status();
auto bootstrap = std::move(*bootstrap_or); grpc_error_handle error = GRPC_ERROR_NONE;
auto* server = GrpcXdsBootstrap bootstrap(std::move(*json), &error);
&static_cast<const GrpcXdsBootstrap::GrpcXdsServer&>(bootstrap->server()); EXPECT_EQ(error, GRPC_ERROR_NONE) << grpc_error_std_string(error);
EXPECT_EQ(server->server_uri(), "fake:///lb"); EXPECT_EQ(bootstrap.server().server_uri, "fake:///lb");
EXPECT_EQ(server->channel_creds_type(), "insecure"); EXPECT_EQ(bootstrap.server().channel_creds_type, "insecure");
EXPECT_EQ(bootstrap->node(), nullptr); EXPECT_EQ(bootstrap.node(), nullptr);
} }
TEST(XdsBootstrapTest, GoogleDefaultCreds) { TEST(XdsBootstrapTest, GoogleDefaultCreds) {
@ -238,14 +235,14 @@ TEST(XdsBootstrapTest, GoogleDefaultCreds) {
" }" " }"
" ]" " ]"
"}"; "}";
auto bootstrap_or = GrpcXdsBootstrap::Create(json_str); auto json = Json::Parse(json_str);
ASSERT_TRUE(bootstrap_or.ok()) << bootstrap_or.status(); ASSERT_TRUE(json.ok()) << json.status();
auto bootstrap = std::move(*bootstrap_or); grpc_error_handle error = GRPC_ERROR_NONE;
auto* server = GrpcXdsBootstrap bootstrap(std::move(*json), &error);
&static_cast<const GrpcXdsBootstrap::GrpcXdsServer&>(bootstrap->server()); EXPECT_EQ(error, GRPC_ERROR_NONE) << grpc_error_std_string(error);
EXPECT_EQ(server->server_uri(), "fake:///lb"); EXPECT_EQ(bootstrap.server().server_uri, "fake:///lb");
EXPECT_EQ(server->channel_creds_type(), "google_default"); EXPECT_EQ(bootstrap.server().channel_creds_type, "google_default");
EXPECT_EQ(bootstrap->node(), nullptr); EXPECT_EQ(bootstrap.node(), nullptr);
} }
TEST(XdsBootstrapTest, MissingChannelCreds) { TEST(XdsBootstrapTest, MissingChannelCreds) {
@ -257,11 +254,14 @@ TEST(XdsBootstrapTest, MissingChannelCreds) {
" }" " }"
" ]" " ]"
"}"; "}";
auto bootstrap = GrpcXdsBootstrap::Create(json_str); auto json = Json::Parse(json_str);
EXPECT_EQ(bootstrap.status().message(), ASSERT_TRUE(json.ok()) << json.status();
"errors validating JSON: [" grpc_error_handle error = GRPC_ERROR_NONE;
"field:xds_servers[0].channel_creds error:field not present]") GrpcXdsBootstrap bootstrap(std::move(*json), &error);
<< bootstrap.status(); EXPECT_THAT(
grpc_error_std_string(error),
::testing::ContainsRegex("field:channel_creds error:does not exist."));
GRPC_ERROR_UNREF(error);
} }
TEST(XdsBootstrapTest, NoKnownChannelCreds) { TEST(XdsBootstrapTest, NoKnownChannelCreds) {
@ -274,20 +274,24 @@ TEST(XdsBootstrapTest, NoKnownChannelCreds) {
" }" " }"
" ]" " ]"
"}"; "}";
auto bootstrap = GrpcXdsBootstrap::Create(json_str); auto json = Json::Parse(json_str);
EXPECT_EQ(bootstrap.status().message(), ASSERT_TRUE(json.ok()) << json.status();
"errors validating JSON: [" grpc_error_handle error = GRPC_ERROR_NONE;
"field:xds_servers[0].channel_creds " GrpcXdsBootstrap bootstrap(std::move(*json), &error);
"error:no known creds type found]") EXPECT_THAT(grpc_error_std_string(error),
<< bootstrap.status(); ::testing::ContainsRegex(
"no known creds type found in \"channel_creds\""));
GRPC_ERROR_UNREF(error);
} }
TEST(XdsBootstrapTest, MissingXdsServers) { TEST(XdsBootstrapTest, MissingXdsServers) {
auto bootstrap = GrpcXdsBootstrap::Create("{}"); auto json = Json::Parse("{}");
EXPECT_EQ( ASSERT_TRUE(json.ok()) << json.status();
bootstrap.status().message(), grpc_error_handle error = GRPC_ERROR_NONE;
"errors validating JSON: [field:xds_servers error:field not present]") GrpcXdsBootstrap bootstrap(std::move(*json), &error);
<< bootstrap.status(); EXPECT_THAT(grpc_error_std_string(error),
::testing::ContainsRegex("\"xds_servers\" field not present"));
GRPC_ERROR_UNREF(error);
} }
TEST(XdsBootstrapTest, TopFieldsWrongTypes) { TEST(XdsBootstrapTest, TopFieldsWrongTypes) {
@ -298,28 +302,37 @@ TEST(XdsBootstrapTest, TopFieldsWrongTypes) {
" \"server_listener_resource_name_template\":1," " \"server_listener_resource_name_template\":1,"
" \"certificate_providers\":1" " \"certificate_providers\":1"
"}"; "}";
auto bootstrap = GrpcXdsBootstrap::Create(json_str); auto json = Json::Parse(json_str);
EXPECT_EQ( ASSERT_TRUE(json.ok()) << json.status();
bootstrap.status().message(), grpc_error_handle error = GRPC_ERROR_NONE;
"errors validating JSON: [" GrpcXdsBootstrap bootstrap(std::move(*json), &error);
"field:certificate_providers error:is not an object; " EXPECT_THAT(grpc_error_std_string(error),
"field:node error:is not an object; " ::testing::ContainsRegex("\"xds_servers\" field is not an array.*"
"field:server_listener_resource_name_template error:is not a string; " "\"node\" field is not an object.*"
"field:xds_servers error:is not an array]") "\"server_listener_resource_name_"
<< bootstrap.status(); "template\" field is not a string.*"));
EXPECT_THAT(grpc_error_std_string(error),
::testing::ContainsRegex(
"\"certificate_providers\" field is not an object"));
GRPC_ERROR_UNREF(error);
} }
TEST(XdsBootstrapTest, XdsServerMissingFields) { TEST(XdsBootstrapTest, XdsServerMissingServerUri) {
const char* json_str = const char* json_str =
"{" "{"
" \"xds_servers\":[{}]" " \"xds_servers\":[{}]"
"}"; "}";
auto bootstrap = GrpcXdsBootstrap::Create(json_str); auto json = Json::Parse(json_str);
EXPECT_EQ(bootstrap.status().message(), ASSERT_TRUE(json.ok()) << json.status();
"errors validating JSON: [" grpc_error_handle error = GRPC_ERROR_NONE;
"field:xds_servers[0].channel_creds error:field not present; " GrpcXdsBootstrap bootstrap(std::move(*json), &error);
"field:xds_servers[0].server_uri error:field not present]") EXPECT_THAT(
<< bootstrap.status(); grpc_error_std_string(error),
::testing::ContainsRegex("errors parsing \"xds_servers\" array.*"
"errors parsing index 0.*"
"errors parsing xds server.*"
"field:server_uri error:does not exist."));
GRPC_ERROR_UNREF(error);
} }
TEST(XdsBootstrapTest, XdsServerUriAndCredsWrongTypes) { TEST(XdsBootstrapTest, XdsServerUriAndCredsWrongTypes) {
@ -332,12 +345,18 @@ TEST(XdsBootstrapTest, XdsServerUriAndCredsWrongTypes) {
" }" " }"
" ]" " ]"
"}"; "}";
auto bootstrap = GrpcXdsBootstrap::Create(json_str); auto json = Json::Parse(json_str);
EXPECT_EQ(bootstrap.status().message(), ASSERT_TRUE(json.ok()) << json.status();
"errors validating JSON: [" grpc_error_handle error = GRPC_ERROR_NONE;
"field:xds_servers[0].channel_creds error:is not an array; " GrpcXdsBootstrap bootstrap(std::move(*json), &error);
"field:xds_servers[0].server_uri error:is not a string]") EXPECT_THAT(grpc_error_std_string(error),
<< bootstrap.status(); ::testing::ContainsRegex(
"errors parsing \"xds_servers\" array.*"
"errors parsing index 0.*"
"errors parsing xds server.*"
"field:server_uri error:type should be STRING.*"
"field:channel_creds error:type should be ARRAY"));
GRPC_ERROR_UNREF(error);
} }
TEST(XdsBootstrapTest, ChannelCredsFieldsWrongTypes) { TEST(XdsBootstrapTest, ChannelCredsFieldsWrongTypes) {
@ -355,13 +374,20 @@ TEST(XdsBootstrapTest, ChannelCredsFieldsWrongTypes) {
" }" " }"
" ]" " ]"
"}"; "}";
auto bootstrap = GrpcXdsBootstrap::Create(json_str); auto json = Json::Parse(json_str);
EXPECT_EQ( ASSERT_TRUE(json.ok()) << json.status();
bootstrap.status().message(), grpc_error_handle error = GRPC_ERROR_NONE;
"errors validating JSON: [" GrpcXdsBootstrap bootstrap(std::move(*json), &error);
"field:xds_servers[0].channel_creds[0].config error:is not an object; " EXPECT_THAT(
"field:xds_servers[0].channel_creds[0].type error:is not a string]") grpc_error_std_string(error),
<< bootstrap.status(); ::testing::ContainsRegex("errors parsing \"xds_servers\" array.*"
"errors parsing index 0.*"
"errors parsing xds server.*"
"errors parsing \"channel_creds\" array.*"
"errors parsing index 0.*"
"field:type error:type should be STRING.*"
"field:config error:type should be OBJECT"));
GRPC_ERROR_UNREF(error);
} }
TEST(XdsBootstrapTest, NodeFieldsWrongTypes) { TEST(XdsBootstrapTest, NodeFieldsWrongTypes) {
@ -374,15 +400,17 @@ TEST(XdsBootstrapTest, NodeFieldsWrongTypes) {
" \"metadata\":0" " \"metadata\":0"
" }" " }"
"}"; "}";
auto bootstrap = GrpcXdsBootstrap::Create(json_str); auto json = Json::Parse(json_str);
EXPECT_EQ(bootstrap.status().message(), ASSERT_TRUE(json.ok()) << json.status();
"errors validating JSON: [" grpc_error_handle error = GRPC_ERROR_NONE;
"field:node.cluster error:is not a string; " GrpcXdsBootstrap bootstrap(std::move(*json), &error);
"field:node.id error:is not a string; " EXPECT_THAT(grpc_error_std_string(error),
"field:node.locality error:is not an object; " ::testing::ContainsRegex("errors parsing \"node\" object.*"
"field:node.metadata error:is not an object; " "\"id\" field is not a string.*"
"field:xds_servers error:field not present]") "\"cluster\" field is not a string.*"
<< bootstrap.status(); "\"locality\" field is not an object.*"
"\"metadata\" field is not an object"));
GRPC_ERROR_UNREF(error);
} }
TEST(XdsBootstrapTest, LocalityFieldsWrongType) { TEST(XdsBootstrapTest, LocalityFieldsWrongType) {
@ -396,14 +424,17 @@ TEST(XdsBootstrapTest, LocalityFieldsWrongType) {
" }" " }"
" }" " }"
"}"; "}";
auto bootstrap = GrpcXdsBootstrap::Create(json_str); auto json = Json::Parse(json_str);
EXPECT_EQ(bootstrap.status().message(), ASSERT_TRUE(json.ok()) << json.status();
"errors validating JSON: [" grpc_error_handle error = GRPC_ERROR_NONE;
"field:node.locality.region error:is not a string; " GrpcXdsBootstrap bootstrap(std::move(*json), &error);
"field:node.locality.sub_zone error:is not a string; " EXPECT_THAT(grpc_error_std_string(error),
"field:node.locality.zone error:is not a string; " ::testing::ContainsRegex("errors parsing \"node\" object.*"
"field:xds_servers error:field not present]") "errors parsing \"locality\" object.*"
<< bootstrap.status(); "\"region\" field is not a string.*"
"\"zone\" field is not a string.*"
"\"sub_zone\" field is not a string"));
GRPC_ERROR_UNREF(error);
} }
TEST(XdsBootstrapTest, CertificateProvidersElementWrongType) { TEST(XdsBootstrapTest, CertificateProvidersElementWrongType) {
@ -419,11 +450,15 @@ TEST(XdsBootstrapTest, CertificateProvidersElementWrongType) {
" \"plugin\":1" " \"plugin\":1"
" }" " }"
"}"; "}";
auto bootstrap = GrpcXdsBootstrap::Create(json_str); auto json = Json::Parse(json_str);
EXPECT_EQ(bootstrap.status().message(), ASSERT_TRUE(json.ok()) << json.status();
"errors validating JSON: [" grpc_error_handle error = GRPC_ERROR_NONE;
"field:certificate_providers[\"plugin\"] error:is not an object]") GrpcXdsBootstrap bootstrap(std::move(*json), &error);
<< bootstrap.status(); EXPECT_THAT(grpc_error_std_string(error),
::testing::ContainsRegex(
"errors parsing \"certificate_providers\" object.*"
"element \"plugin\" is not an object"));
GRPC_ERROR_UNREF(error);
} }
TEST(XdsBootstrapTest, CertificateProvidersPluginNameWrongType) { TEST(XdsBootstrapTest, CertificateProvidersPluginNameWrongType) {
@ -441,12 +476,16 @@ TEST(XdsBootstrapTest, CertificateProvidersPluginNameWrongType) {
" }" " }"
" }" " }"
"}"; "}";
auto bootstrap = GrpcXdsBootstrap::Create(json_str); auto json = Json::Parse(json_str);
EXPECT_EQ(bootstrap.status().message(), ASSERT_TRUE(json.ok()) << json.status();
"errors validating JSON: [" grpc_error_handle error = GRPC_ERROR_NONE;
"field:certificate_providers[\"plugin\"].plugin_name error:" GrpcXdsBootstrap bootstrap(std::move(*json), &error);
"is not a string]") EXPECT_THAT(grpc_error_std_string(error),
<< bootstrap.status(); ::testing::ContainsRegex(
"errors parsing \"certificate_providers\" object.*"
"errors parsing element \"plugin\".*"
"\"plugin_name\" field is not a string"));
GRPC_ERROR_UNREF(error);
} }
TEST(XdsBootstrapTest, CertificateProvidersUnrecognizedPluginName) { TEST(XdsBootstrapTest, CertificateProvidersUnrecognizedPluginName) {
@ -464,12 +503,16 @@ TEST(XdsBootstrapTest, CertificateProvidersUnrecognizedPluginName) {
" }" " }"
" }" " }"
"}"; "}";
auto bootstrap = GrpcXdsBootstrap::Create(json_str); auto json = Json::Parse(json_str);
EXPECT_EQ(bootstrap.status().message(), ASSERT_TRUE(json.ok()) << json.status();
"errors validating JSON: [" grpc_error_handle error = GRPC_ERROR_NONE;
"field:certificate_providers[\"plugin\"].plugin_name error:" GrpcXdsBootstrap bootstrap(std::move(*json), &error);
"Unrecognized plugin name: unknown]") EXPECT_THAT(grpc_error_std_string(error),
<< bootstrap.status(); ::testing::ContainsRegex(
"errors parsing \"certificate_providers\" object.*"
"errors parsing element \"plugin\".*"
"Unrecognized plugin name: unknown"));
GRPC_ERROR_UNREF(error);
} }
TEST(XdsBootstrapTest, AuthorityXdsServerInvalidResourceTemplate) { TEST(XdsBootstrapTest, AuthorityXdsServerInvalidResourceTemplate) {
@ -501,13 +544,16 @@ TEST(XdsBootstrapTest, AuthorityXdsServerInvalidResourceTemplate) {
" }" " }"
" }" " }"
"}"; "}";
auto bootstrap = GrpcXdsBootstrap::Create(json_str); auto json = Json::Parse(json_str);
EXPECT_EQ(bootstrap.status().message(), ASSERT_TRUE(json.ok()) << json.status();
"errors validating JSON: [" grpc_error_handle error = GRPC_ERROR_NONE;
"field:authorities[\"xds.example.com\"]" GrpcXdsBootstrap bootstrap(std::move(*json), &error);
".client_listener_resource_name_template error:" EXPECT_THAT(grpc_error_std_string(error),
"field must begin with \"xdstp://xds.example.com/\"]") ::testing::ContainsRegex(
<< bootstrap.status(); "errors parsing \"authorities\".*"
"errors parsing authority xds.example.com.*"
"field must begin with \"xdstp://xds.example.com/\""));
GRPC_ERROR_UNREF(error);
gpr_unsetenv("GRPC_EXPERIMENTAL_XDS_FEDERATION"); gpr_unsetenv("GRPC_EXPERIMENTAL_XDS_FEDERATION");
} }
@ -530,15 +576,19 @@ TEST(XdsBootstrapTest, AuthorityXdsServerMissingServerUri) {
" }" " }"
" }" " }"
"}"; "}";
auto bootstrap = GrpcXdsBootstrap::Create(json_str); auto json = Json::Parse(json_str);
EXPECT_EQ( ASSERT_TRUE(json.ok()) << json.status();
bootstrap.status().message(), grpc_error_handle error = GRPC_ERROR_NONE;
"errors validating JSON: [" GrpcXdsBootstrap bootstrap(std::move(*json), &error);
"field:authorities[\"xds.example.com\"].xds_servers[0].channel_creds " EXPECT_THAT(
"error:field not present; " grpc_error_std_string(error),
"field:authorities[\"xds.example.com\"].xds_servers[0].server_uri " ::testing::ContainsRegex("errors parsing \"authorities\".*"
"error:field not present]") "errors parsing authority xds.example.com.*"
<< bootstrap.status(); "errors parsing \"xds_servers\" array.*"
"errors parsing index 0.*"
"errors parsing xds server.*"
"field:server_uri error:does not exist."));
GRPC_ERROR_UNREF(error);
gpr_unsetenv("GRPC_EXPERIMENTAL_XDS_FEDERATION"); gpr_unsetenv("GRPC_EXPERIMENTAL_XDS_FEDERATION");
} }
@ -609,16 +659,16 @@ TEST(XdsBootstrapTest, CertificateProvidersFakePluginParsingError) {
" }" " }"
" }" " }"
"}"; "}";
auto bootstrap = GrpcXdsBootstrap::Create(json_str); auto json = Json::Parse(json_str);
EXPECT_THAT( ASSERT_TRUE(json.ok()) << json.status();
// Explicit conversion to std::string to work around grpc_error_handle error = GRPC_ERROR_NONE;
// https://github.com/google/googletest/issues/3949. GrpcXdsBootstrap bootstrap(std::move(*json), &error);
std::string(bootstrap.status().message()), EXPECT_THAT(grpc_error_std_string(error),
::testing::MatchesRegex( ::testing::ContainsRegex(
"errors validating JSON: \\[" "errors parsing \"certificate_providers\" object.*"
"field:certificate_providers\\[\"fake_plugin\"\\].config " "errors parsing element \"fake_plugin\".*"
"error:UNKNOWN:field:config field:value not of type number.*\\]")) "field:config field:value not of type number"));
<< bootstrap.status(); GRPC_ERROR_UNREF(error);
} }
TEST(XdsBootstrapTest, CertificateProvidersFakePluginParsingSuccess) { TEST(XdsBootstrapTest, CertificateProvidersFakePluginParsingSuccess) {
@ -639,11 +689,13 @@ TEST(XdsBootstrapTest, CertificateProvidersFakePluginParsingSuccess) {
" }" " }"
" }" " }"
"}"; "}";
auto bootstrap_or = GrpcXdsBootstrap::Create(json_str); auto json = Json::Parse(json_str);
ASSERT_TRUE(bootstrap_or.ok()) << bootstrap_or.status(); ASSERT_TRUE(json.ok()) << json.status();
auto bootstrap = std::move(*bootstrap_or); grpc_error_handle error = GRPC_ERROR_NONE;
GrpcXdsBootstrap bootstrap(std::move(*json), &error);
ASSERT_TRUE(GRPC_ERROR_IS_NONE(error)) << grpc_error_std_string(error);
const CertificateProviderStore::PluginDefinition& fake_plugin = const CertificateProviderStore::PluginDefinition& fake_plugin =
bootstrap->certificate_providers().at("fake_plugin"); bootstrap.certificate_providers().at("fake_plugin");
ASSERT_EQ(fake_plugin.plugin_name, "fake"); ASSERT_EQ(fake_plugin.plugin_name, "fake");
ASSERT_STREQ(fake_plugin.config->name(), "fake"); ASSERT_STREQ(fake_plugin.config->name(), "fake");
ASSERT_EQ(static_cast<RefCountedPtr<FakeCertificateProviderFactory::Config>>( ASSERT_EQ(static_cast<RefCountedPtr<FakeCertificateProviderFactory::Config>>(
@ -667,11 +719,13 @@ TEST(XdsBootstrapTest, CertificateProvidersFakePluginEmptyConfig) {
" }" " }"
" }" " }"
"}"; "}";
auto bootstrap_or = GrpcXdsBootstrap::Create(json_str); auto json = Json::Parse(json_str);
ASSERT_TRUE(bootstrap_or.ok()) << bootstrap_or.status(); ASSERT_TRUE(json.ok()) << json.status();
auto bootstrap = std::move(*bootstrap_or); grpc_error_handle error = GRPC_ERROR_NONE;
GrpcXdsBootstrap bootstrap(std::move(*json), &error);
ASSERT_TRUE(GRPC_ERROR_IS_NONE(error)) << grpc_error_std_string(error);
const CertificateProviderStore::PluginDefinition& fake_plugin = const CertificateProviderStore::PluginDefinition& fake_plugin =
bootstrap->certificate_providers().at("fake_plugin"); bootstrap.certificate_providers().at("fake_plugin");
ASSERT_EQ(fake_plugin.plugin_name, "fake"); ASSERT_EQ(fake_plugin.plugin_name, "fake");
ASSERT_STREQ(fake_plugin.config->name(), "fake"); ASSERT_STREQ(fake_plugin.config->name(), "fake");
ASSERT_EQ(static_cast<RefCountedPtr<FakeCertificateProviderFactory::Config>>( ASSERT_EQ(static_cast<RefCountedPtr<FakeCertificateProviderFactory::Config>>(
@ -695,13 +749,14 @@ TEST(XdsBootstrapTest, XdsServerToJsonAndParse) {
" }"; " }";
auto json = Json::Parse(json_str); auto json = Json::Parse(json_str);
ASSERT_TRUE(json.ok()) << json.status(); ASSERT_TRUE(json.ok()) << json.status();
auto xds_server = LoadFromJson<GrpcXdsBootstrap::GrpcXdsServer>(*json); grpc_error_handle error = GRPC_ERROR_NONE;
ASSERT_TRUE(xds_server.ok()) << xds_server.status(); XdsBootstrap::XdsServer xds_server =
Json output = xds_server->ToJson(); GrpcXdsBootstrap::XdsServerParse(*json, &error);
auto output_xds_server = ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_std_string(error);
LoadFromJson<GrpcXdsBootstrap::GrpcXdsServer>(output); Json::Object output = GrpcXdsBootstrap::XdsServerToJson(xds_server);
ASSERT_TRUE(output_xds_server.ok()) << output_xds_server.status(); XdsBootstrap::XdsServer output_xds_server =
EXPECT_EQ(*xds_server, *output_xds_server); GrpcXdsBootstrap::XdsServerParse(output, &error);
ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_std_string(error);
gpr_unsetenv("GRPC_EXPERIMENTAL_XDS_FEDERATION"); gpr_unsetenv("GRPC_EXPERIMENTAL_XDS_FEDERATION");
} }

@ -28,7 +28,6 @@
#include <grpc/grpc.h> #include <grpc/grpc.h>
#include "src/core/ext/xds/xds_bootstrap_grpc.h"
#include "src/core/lib/config/core_configuration.h" #include "src/core/lib/config/core_configuration.h"
#include "src/core/lib/load_balancing/lb_policy_factory.h" #include "src/core/lib/load_balancing/lb_policy_factory.h"
#include "src/core/lib/load_balancing/lb_policy_registry.h" #include "src/core/lib/load_balancing/lb_policy_registry.h"
@ -59,8 +58,7 @@ absl::StatusOr<Json::Array> ConvertXdsPolicy(LoadBalancingPolicyProto policy) {
std::string serialized_policy = policy.SerializeAsString(); std::string serialized_policy = policy.SerializeAsString();
upb::Arena arena; upb::Arena arena;
upb::SymbolTable symtab; upb::SymbolTable symtab;
XdsResourceType::DecodeContext context = {nullptr, XdsResourceType::DecodeContext context = {nullptr, XdsBootstrap::XdsServer(),
GrpcXdsBootstrap::GrpcXdsServer(),
nullptr, symtab.ptr(), arena.ptr()}; nullptr, symtab.ptr(), arena.ptr()};
auto* upb_policy = envoy_config_cluster_v3_LoadBalancingPolicy_parse( auto* upb_policy = envoy_config_cluster_v3_LoadBalancingPolicy_parse(
serialized_policy.data(), serialized_policy.size(), arena.ptr()); serialized_policy.data(), serialized_policy.size(), arena.ptr());

Loading…
Cancel
Save