[xDS] allow C2P resolver to explicitly set the bootstrap config (#40623)

This avoids the need for the TD bootstrap generator (or a user-provided bootstrap config) to include the C2P authority.  The bootstrap for C2P channels now always comes from the C2P resolver.

b/442819521

Closes #40623

COPYBARA_INTEGRATE_REVIEW=https://github.com/grpc/grpc/pull/40623 from markdroth:xds_bootstrap_override 5e5d3e22bc2792f9df47acca234243ef4619b9f8
PiperOrigin-RevId: 805559420
diff --git a/src/core/resolver/google_c2p/google_c2p_resolver.cc b/src/core/resolver/google_c2p/google_c2p_resolver.cc
index f017c66..8b24124 100644
--- a/src/core/resolver/google_c2p/google_c2p_resolver.cc
+++ b/src/core/resolver/google_c2p/google_c2p_resolver.cc
@@ -71,6 +71,7 @@
   void ZoneQueryDone(std::string zone);
   void IPv6QueryDone(bool ipv6_supported);
   void StartXdsResolver();
+  std::shared_ptr<GrpcXdsBootstrap> ConstructBootstrap() const;
 
   ResourceQuotaRefPtr resource_quota_;
   std::shared_ptr<WorkSerializer> work_serializer_;
@@ -80,6 +81,10 @@
   std::string metadata_server_name_ = "metadata.google.internal.";
   bool shutdown_ = false;
 
+  // Used to hold state between ctor and StartLocked().
+  std::string xds_uri_;
+  ChannelArgs args_;
+
   OrphanablePtr<GcpMetadataQuery> zone_query_;
   std::string zone_;
 };
@@ -96,13 +101,13 @@
 GoogleCloud2ProdResolver::GoogleCloud2ProdResolver(ResolverArgs args)
     : resource_quota_(args.args.GetObjectRef<ResourceQuota>()),
       work_serializer_(std::move(args.work_serializer)),
-      pollent_(grpc_polling_entity_create_from_pollset_set(args.pollset_set)) {
+      pollent_(grpc_polling_entity_create_from_pollset_set(args.pollset_set)),
+      args_(std::move(args.args)) {
   absl::string_view name_to_resolve = absl::StripPrefix(args.uri.path(), "/");
   // If we're not running on GCP, we can't use DirectPath, so delegate
   // to the DNS resolver.
   const bool test_only_pretend_running_on_gcp =
-      args.args
-          .GetBool("grpc.testing.google_c2p_resolver_pretend_running_on_gcp")
+      args_.GetBool("grpc.testing.google_c2p_resolver_pretend_running_on_gcp")
           .value_or(false);
   const bool running_on_gcp =
       test_only_pretend_running_on_gcp || grpc_alts_is_running_on_gcp();
@@ -117,26 +122,25 @@
     using_dns_ = true;
     child_resolver_ =
         CoreConfiguration::Get().resolver_registry().CreateResolver(
-            absl::StrCat("dns:", name_to_resolve), args.args, args.pollset_set,
+            absl::StrCat("dns:", name_to_resolve), args_, args.pollset_set,
             work_serializer_, std::move(args.result_handler));
     CHECK(child_resolver_ != nullptr);
     return;
   }
   // Maybe override metadata server name for testing
   std::optional<std::string> test_only_metadata_server_override =
-      args.args.GetOwnedString(
+      args_.GetOwnedString(
           "grpc.testing.google_c2p_resolver_metadata_server_override");
   if (test_only_metadata_server_override.has_value() &&
       !test_only_metadata_server_override->empty()) {
     metadata_server_name_ = std::move(*test_only_metadata_server_override);
   }
   // Create xds resolver.
-  std::string xds_uri =
-      federation_enabled
-          ? absl::StrCat("xds://", kC2PAuthority, "/", name_to_resolve)
-          : absl::StrCat("xds:", name_to_resolve);
+  xds_uri_ = federation_enabled
+                 ? absl::StrCat("xds://", kC2PAuthority, "/", name_to_resolve)
+                 : absl::StrCat("xds:", name_to_resolve);
   child_resolver_ = CoreConfiguration::Get().resolver_registry().CreateResolver(
-      xds_uri, args.args, args.pollset_set, work_serializer_,
+      xds_uri_, args_, args.pollset_set, work_serializer_,
       std::move(args.result_handler));
   CHECK(child_resolver_ != nullptr);
 }
@@ -185,10 +189,8 @@
   StartXdsResolver();
 }
 
-void GoogleCloud2ProdResolver::StartXdsResolver() {
-  if (shutdown_) {
-    return;
-  }
+std::shared_ptr<GrpcXdsBootstrap> GoogleCloud2ProdResolver::ConstructBootstrap()
+    const {
   // Construct bootstrap JSON.
   std::random_device rd;
   std::mt19937 mt(rd());
@@ -235,8 +237,20 @@
        })},
       {"node", Json::FromObject(std::move(node))},
   });
-  // Inject bootstrap JSON as fallback config.
-  internal::SetXdsFallbackBootstrapConfig(JsonDump(bootstrap).c_str());
+  return GrpcXdsBootstrap::Create(JsonDump(bootstrap)).value();
+}
+
+void GoogleCloud2ProdResolver::StartXdsResolver() {
+  if (shutdown_) return;
+  // Create XdsClient here with a custom bootstrap for C2P.  When the
+  // xds resolver calls GrpcXdsClient::GetOrCreate(), it will get back
+  // the same instance rather than creating one with the normal
+  // bootstrap config from env vars.
+  static NoDestruct<std::shared_ptr<GrpcXdsBootstrap>> bootstrap(
+      ConstructBootstrap());
+  auto xds_client = GrpcXdsClient::GetOrCreate(
+      xds_uri_, args_, "google-c2p resolver", *bootstrap);
+  args_ = ChannelArgs();  // Don't need the args anymore.
   // Now start xDS resolver.
   child_resolver_->StartLocked();
 }
diff --git a/src/core/xds/grpc/xds_client_grpc.cc b/src/core/xds/grpc/xds_client_grpc.cc
index 2a814a5..1b7f601 100644
--- a/src/core/xds/grpc/xds_client_grpc.cc
+++ b/src/core/xds/grpc/xds_client_grpc.cc
@@ -228,6 +228,22 @@
       "not defined");
 }
 
+absl::StatusOr<std::shared_ptr<GrpcXdsBootstrap>> GetOrCreateGlobalBootstrap()
+    ABSL_EXCLUSIVE_LOCKS_REQUIRED(*g_mu) {
+  if (*g_parsed_bootstrap == nullptr) {
+    // First, find bootstrap contents.
+    auto bootstrap_contents = FindBootstrapContents();
+    if (!bootstrap_contents.ok()) return bootstrap_contents.status();
+    GRPC_TRACE_LOG(xds_client, INFO)
+        << "xDS bootstrap contents: " << *bootstrap_contents;
+    // Parse bootstrap.
+    auto bootstrap = GrpcXdsBootstrap::Create(*bootstrap_contents);
+    if (!bootstrap.ok()) return bootstrap.status();
+    *g_parsed_bootstrap = std::move(*bootstrap);
+  }
+  return *g_parsed_bootstrap;
+}
+
 std::shared_ptr<GlobalStatsPluginRegistry::StatsPluginGroup>
 GetStatsPluginGroupForKeyAndChannelArgs(absl::string_view key,
                                         const ChannelArgs& channel_args) {
@@ -248,7 +264,8 @@
 }  // namespace
 
 absl::StatusOr<RefCountedPtr<GrpcXdsClient>> GrpcXdsClient::GetOrCreate(
-    absl::string_view key, const ChannelArgs& args, const char* reason) {
+    absl::string_view key, const ChannelArgs& args, const char* reason,
+    std::shared_ptr<GrpcXdsBootstrap> bootstrap_override) {
   // If getting bootstrap from channel args, create a local XdsClient
   // instance for the channel or server instead of using the global instance.
   std::optional<absl::string_view> bootstrap_config = args.GetString(
@@ -277,22 +294,15 @@
     }
   }
   // The XdsClient doesn't exist, so we'll create it.
-  // If the bootstrap hasn't already been parsed, do that.
-  if (*g_parsed_bootstrap == nullptr) {
-    // First, find bootstrap contents.
-    auto bootstrap_contents = FindBootstrapContents();
-    if (!bootstrap_contents.ok()) return bootstrap_contents.status();
-    GRPC_TRACE_LOG(xds_client, INFO)
-        << "xDS bootstrap contents: " << *bootstrap_contents;
-    // Parse bootstrap.
-    auto bootstrap = GrpcXdsBootstrap::Create(*bootstrap_contents);
-    if (!bootstrap.ok()) return bootstrap.status();
-    *g_parsed_bootstrap = std::move(*bootstrap);
+  std::shared_ptr<GrpcXdsBootstrap> bootstrap = std::move(bootstrap_override);
+  if (bootstrap == nullptr) {
+    auto global_bootstrap = GetOrCreateGlobalBootstrap();
+    if (!global_bootstrap.ok()) return global_bootstrap.status();
+    bootstrap = std::move(*global_bootstrap);
   }
-  // Instantiate XdsClient.
   auto channel_args = ChannelArgs::FromC(g_channel_args);
   auto xds_client = MakeRefCounted<GrpcXdsClient>(
-      key, *g_parsed_bootstrap, channel_args,
+      key, std::move(bootstrap), channel_args,
       MakeRefCounted<GrpcXdsTransportFactory>(channel_args),
       GetStatsPluginGroupForKeyAndChannelArgs(key, args));
   g_xds_client_map->emplace(xds_client->key(), xds_client.get());
diff --git a/src/core/xds/grpc/xds_client_grpc.h b/src/core/xds/grpc/xds_client_grpc.h
index f412495..2bb00a1 100644
--- a/src/core/xds/grpc/xds_client_grpc.h
+++ b/src/core/xds/grpc/xds_client_grpc.h
@@ -45,8 +45,11 @@
   static constexpr absl::string_view kServerKey = "#server";
 
   // Factory function to get or create the global XdsClient instance.
+  // If bootstrap_override is null, the default bootstrap is used based
+  // on environment variables, channel args, etc.
   static absl::StatusOr<RefCountedPtr<GrpcXdsClient>> GetOrCreate(
-      absl::string_view key, const ChannelArgs& args, const char* reason);
+      absl::string_view key, const ChannelArgs& args, const char* reason,
+      std::shared_ptr<GrpcXdsBootstrap> bootstrap_override = nullptr);
 
   // Do not instantiate directly -- use GetOrCreate() instead.
   // TODO(roth): The transport factory is injectable here to support