|  | @@ -0,0 +1,360 @@
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +// Copyright 2021 gRPC authors.
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +// Licensed under the Apache License, Version 2.0 (the "License");
 | 
	
		
			
				|  |  | +// you may not use this file except in compliance with the License.
 | 
	
		
			
				|  |  | +// You may obtain a copy of the License at
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +//     http://www.apache.org/licenses/LICENSE-2.0
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +// Unless required by applicable law or agreed to in writing, software
 | 
	
		
			
				|  |  | +// distributed under the License is distributed on an "AS IS" BASIS,
 | 
	
		
			
				|  |  | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
	
		
			
				|  |  | +// See the License for the specific language governing permissions and
 | 
	
		
			
				|  |  | +// limitations under the License.
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#include <grpc/support/port_platform.h>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#include "src/core/ext/filters/client_channel/resolver_registry.h"
 | 
	
		
			
				|  |  | +#include "src/core/ext/xds/xds_client.h"
 | 
	
		
			
				|  |  | +#include "src/core/lib/gpr/env.h"
 | 
	
		
			
				|  |  | +#include "src/core/lib/http/httpcli.h"
 | 
	
		
			
				|  |  | +#include "src/core/lib/iomgr/polling_entity.h"
 | 
	
		
			
				|  |  | +#include "src/core/lib/security/credentials/alts/check_gcp_environment.h"
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +namespace grpc_core {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +namespace {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class GoogleCloud2ProdResolver : public Resolver {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  explicit GoogleCloud2ProdResolver(ResolverArgs args);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void StartLocked() override;
 | 
	
		
			
				|  |  | +  void RequestReresolutionLocked() override;
 | 
	
		
			
				|  |  | +  void ResetBackoffLocked() override;
 | 
	
		
			
				|  |  | +  void ShutdownLocked() override;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + private:
 | 
	
		
			
				|  |  | +  // Represents an HTTP request to the metadata server.
 | 
	
		
			
				|  |  | +  class MetadataQuery : public InternallyRefCounted<MetadataQuery> {
 | 
	
		
			
				|  |  | +   public:
 | 
	
		
			
				|  |  | +    MetadataQuery(RefCountedPtr<GoogleCloud2ProdResolver> resolver,
 | 
	
		
			
				|  |  | +                  const char* path, grpc_polling_entity* pollent);
 | 
	
		
			
				|  |  | +    ~MetadataQuery() override;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    void Orphan() override;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +   private:
 | 
	
		
			
				|  |  | +    static void OnHttpRequestDone(void* arg, grpc_error* error);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    void MaybeCallOnDone(grpc_error* error);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // If error is not GRPC_ERROR_NONE, then it's not safe to look at response.
 | 
	
		
			
				|  |  | +    virtual void OnDone(GoogleCloud2ProdResolver* resolver,
 | 
	
		
			
				|  |  | +                        const grpc_http_response* response,
 | 
	
		
			
				|  |  | +                        grpc_error* error) = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    RefCountedPtr<GoogleCloud2ProdResolver> resolver_;
 | 
	
		
			
				|  |  | +    grpc_httpcli_context context_;
 | 
	
		
			
				|  |  | +    grpc_httpcli_response response_;
 | 
	
		
			
				|  |  | +    grpc_closure on_done_;
 | 
	
		
			
				|  |  | +    Atomic<bool> on_done_called_{false};
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // A metadata server query to get the zone.
 | 
	
		
			
				|  |  | +  class ZoneQuery : public MetadataQuery {
 | 
	
		
			
				|  |  | +   public:
 | 
	
		
			
				|  |  | +    ZoneQuery(RefCountedPtr<GoogleCloud2ProdResolver> resolver,
 | 
	
		
			
				|  |  | +              grpc_polling_entity* pollent);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +   private:
 | 
	
		
			
				|  |  | +    void OnDone(GoogleCloud2ProdResolver* resolver,
 | 
	
		
			
				|  |  | +                const grpc_http_response* response, grpc_error* error) override;
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // A metadata server query to get the IPv6 address.
 | 
	
		
			
				|  |  | +  class IPv6Query : public MetadataQuery {
 | 
	
		
			
				|  |  | +   public:
 | 
	
		
			
				|  |  | +    IPv6Query(RefCountedPtr<GoogleCloud2ProdResolver> resolver,
 | 
	
		
			
				|  |  | +              grpc_polling_entity* pollent);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +   private:
 | 
	
		
			
				|  |  | +    void OnDone(GoogleCloud2ProdResolver* resolver,
 | 
	
		
			
				|  |  | +                const grpc_http_response* response, grpc_error* error) override;
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void ZoneQueryDone(std::string zone);
 | 
	
		
			
				|  |  | +  void IPv6QueryDone(bool ipv6_supported);
 | 
	
		
			
				|  |  | +  void StartXdsResolver();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  std::shared_ptr<WorkSerializer> work_serializer_;
 | 
	
		
			
				|  |  | +  grpc_polling_entity pollent_;
 | 
	
		
			
				|  |  | +  bool using_dns_ = false;
 | 
	
		
			
				|  |  | +  OrphanablePtr<Resolver> child_resolver_;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  OrphanablePtr<ZoneQuery> zone_query_;
 | 
	
		
			
				|  |  | +  absl::optional<std::string> zone_;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  OrphanablePtr<IPv6Query> ipv6_query_;
 | 
	
		
			
				|  |  | +  absl::optional<bool> supports_ipv6_;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +// GoogleCloud2ProdResolver::MetadataQuery
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +GoogleCloud2ProdResolver::MetadataQuery::MetadataQuery(
 | 
	
		
			
				|  |  | +    RefCountedPtr<GoogleCloud2ProdResolver> resolver, const char* path,
 | 
	
		
			
				|  |  | +    grpc_polling_entity* pollent)
 | 
	
		
			
				|  |  | +    : resolver_(std::move(resolver)) {
 | 
	
		
			
				|  |  | +  grpc_httpcli_context_init(&context_);
 | 
	
		
			
				|  |  | +  // Start HTTP request.
 | 
	
		
			
				|  |  | +  GRPC_CLOSURE_INIT(&on_done_, OnHttpRequestDone, this, nullptr);
 | 
	
		
			
				|  |  | +  Ref().release();  // Ref held by callback.
 | 
	
		
			
				|  |  | +  grpc_httpcli_request request;
 | 
	
		
			
				|  |  | +  memset(&request, 0, sizeof(grpc_httpcli_request));
 | 
	
		
			
				|  |  | +  grpc_http_header header = {const_cast<char*>("Metadata-Flavor"),
 | 
	
		
			
				|  |  | +                             const_cast<char*>("Google")};
 | 
	
		
			
				|  |  | +  request.host = const_cast<char*>("metadata.google.internal");
 | 
	
		
			
				|  |  | +  request.http.path = const_cast<char*>(path);
 | 
	
		
			
				|  |  | +  request.http.hdr_count = 1;
 | 
	
		
			
				|  |  | +  request.http.hdrs = &header;
 | 
	
		
			
				|  |  | +  grpc_resource_quota* resource_quota =
 | 
	
		
			
				|  |  | +      grpc_resource_quota_create("c2p_resolver");
 | 
	
		
			
				|  |  | +  grpc_httpcli_get(&context_, pollent, resource_quota, &request,
 | 
	
		
			
				|  |  | +                   ExecCtx::Get()->Now() + 10000,  // 10s timeout
 | 
	
		
			
				|  |  | +                   &on_done_, &response_);
 | 
	
		
			
				|  |  | +  grpc_resource_quota_unref_internal(resource_quota);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +GoogleCloud2ProdResolver::MetadataQuery::~MetadataQuery() {
 | 
	
		
			
				|  |  | +  grpc_httpcli_context_destroy(&context_);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void GoogleCloud2ProdResolver::MetadataQuery::Orphan() {
 | 
	
		
			
				|  |  | +  // TODO(roth): Once the HTTP client library supports cancellation,
 | 
	
		
			
				|  |  | +  // use that here.
 | 
	
		
			
				|  |  | +  MaybeCallOnDone(GRPC_ERROR_CANCELLED);
 | 
	
		
			
				|  |  | +  Unref();
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void GoogleCloud2ProdResolver::MetadataQuery::OnHttpRequestDone(
 | 
	
		
			
				|  |  | +    void* arg, grpc_error* error) {
 | 
	
		
			
				|  |  | +  auto* self = static_cast<MetadataQuery*>(arg);
 | 
	
		
			
				|  |  | +  self->MaybeCallOnDone(error);
 | 
	
		
			
				|  |  | +  grpc_http_response_destroy(&self->response_);
 | 
	
		
			
				|  |  | +  self->Unref();
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void GoogleCloud2ProdResolver::MetadataQuery::MaybeCallOnDone(
 | 
	
		
			
				|  |  | +    grpc_error* error) {
 | 
	
		
			
				|  |  | +  bool expected = false;
 | 
	
		
			
				|  |  | +  if (on_done_called_.CompareExchangeStrong(
 | 
	
		
			
				|  |  | +          &expected, true, MemoryOrder::RELAXED, MemoryOrder::RELAXED)) {
 | 
	
		
			
				|  |  | +    // Hop back into WorkSerializer.
 | 
	
		
			
				|  |  | +    Ref().release();  // Ref held by callback.
 | 
	
		
			
				|  |  | +    resolver_->work_serializer_->Run(
 | 
	
		
			
				|  |  | +        [this, error]() {
 | 
	
		
			
				|  |  | +          OnDone(resolver_.get(), &response_, error);
 | 
	
		
			
				|  |  | +          Unref();
 | 
	
		
			
				|  |  | +        },
 | 
	
		
			
				|  |  | +        DEBUG_LOCATION);
 | 
	
		
			
				|  |  | +  } else {
 | 
	
		
			
				|  |  | +    GRPC_ERROR_UNREF(error);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +// GoogleCloud2ProdResolver::ZoneQuery
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +GoogleCloud2ProdResolver::ZoneQuery::ZoneQuery(
 | 
	
		
			
				|  |  | +    RefCountedPtr<GoogleCloud2ProdResolver> resolver,
 | 
	
		
			
				|  |  | +    grpc_polling_entity* pollent)
 | 
	
		
			
				|  |  | +    : MetadataQuery(std::move(resolver), "/computeMetadata/v1/instance/zone",
 | 
	
		
			
				|  |  | +                    pollent) {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void GoogleCloud2ProdResolver::ZoneQuery::OnDone(
 | 
	
		
			
				|  |  | +    GoogleCloud2ProdResolver* resolver, const grpc_http_response* response,
 | 
	
		
			
				|  |  | +    grpc_error* error) {
 | 
	
		
			
				|  |  | +  if (error != GRPC_ERROR_NONE) {
 | 
	
		
			
				|  |  | +    gpr_log(GPR_ERROR, "error fetching zone from metadata server: %s",
 | 
	
		
			
				|  |  | +            grpc_error_string(error));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  std::string zone;
 | 
	
		
			
				|  |  | +  if (error == GRPC_ERROR_NONE && response->status == 200) {
 | 
	
		
			
				|  |  | +    absl::string_view body(response->body, response->body_length);
 | 
	
		
			
				|  |  | +    size_t i = body.find_last_of('/');
 | 
	
		
			
				|  |  | +    if (i == body.npos) {
 | 
	
		
			
				|  |  | +      gpr_log(GPR_ERROR, "could not parse zone from metadata server: %s",
 | 
	
		
			
				|  |  | +              std::string(body).c_str());
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      zone = std::string(body.substr(i));
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  resolver->ZoneQueryDone(std::move(zone));
 | 
	
		
			
				|  |  | +  GRPC_ERROR_UNREF(error);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +// GoogleCloud2ProdResolver::IPv6Query
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +GoogleCloud2ProdResolver::IPv6Query::IPv6Query(
 | 
	
		
			
				|  |  | +    RefCountedPtr<GoogleCloud2ProdResolver> resolver,
 | 
	
		
			
				|  |  | +    grpc_polling_entity* pollent)
 | 
	
		
			
				|  |  | +    : MetadataQuery(std::move(resolver),
 | 
	
		
			
				|  |  | +                    "/computeMetadata/v1/instance/network-interfaces/0/ipv6s",
 | 
	
		
			
				|  |  | +                    pollent) {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void GoogleCloud2ProdResolver::IPv6Query::OnDone(
 | 
	
		
			
				|  |  | +    GoogleCloud2ProdResolver* resolver, const grpc_http_response* response,
 | 
	
		
			
				|  |  | +    grpc_error* error) {
 | 
	
		
			
				|  |  | +  if (error != GRPC_ERROR_NONE) {
 | 
	
		
			
				|  |  | +    gpr_log(GPR_ERROR, "error fetching IPv6 address from metadata server: %s",
 | 
	
		
			
				|  |  | +            grpc_error_string(error));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  resolver->IPv6QueryDone(error == GRPC_ERROR_NONE && response->status == 200);
 | 
	
		
			
				|  |  | +  GRPC_ERROR_UNREF(error);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +// GoogleCloud2ProdResolver
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +GoogleCloud2ProdResolver::GoogleCloud2ProdResolver(ResolverArgs args)
 | 
	
		
			
				|  |  | +    : work_serializer_(std::move(args.work_serializer)),
 | 
	
		
			
				|  |  | +      pollent_(grpc_polling_entity_create_from_pollset_set(args.pollset_set)) {
 | 
	
		
			
				|  |  | +  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.
 | 
	
		
			
				|  |  | +  if (!grpc_alts_is_running_on_gcp() ||
 | 
	
		
			
				|  |  | +      // If the client is already using xDS, we can't use it here, because
 | 
	
		
			
				|  |  | +      // they may be talking to a completely different xDS server than we
 | 
	
		
			
				|  |  | +      // want to.
 | 
	
		
			
				|  |  | +      // TODO(roth): When we implement xDS federation, remove this constraint.
 | 
	
		
			
				|  |  | +      UniquePtr<char>(gpr_getenv("GRPC_XDS_BOOTSTRAP")) != nullptr ||
 | 
	
		
			
				|  |  | +      UniquePtr<char>(gpr_getenv("GRPC_XDS_BOOTSTRAP_CONFIG")) != nullptr) {
 | 
	
		
			
				|  |  | +    using_dns_ = true;
 | 
	
		
			
				|  |  | +    child_resolver_ = ResolverRegistry::CreateResolver(
 | 
	
		
			
				|  |  | +        absl::StrCat("dns:", name_to_resolve).c_str(), args.args,
 | 
	
		
			
				|  |  | +        args.pollset_set, work_serializer_, std::move(args.result_handler));
 | 
	
		
			
				|  |  | +    GPR_ASSERT(child_resolver_ != nullptr);
 | 
	
		
			
				|  |  | +    return;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  // Create xds resolver.
 | 
	
		
			
				|  |  | +  child_resolver_ = ResolverRegistry::CreateResolver(
 | 
	
		
			
				|  |  | +      absl::StrCat("xds:", name_to_resolve).c_str(), args.args,
 | 
	
		
			
				|  |  | +      args.pollset_set, work_serializer_, std::move(args.result_handler));
 | 
	
		
			
				|  |  | +  GPR_ASSERT(child_resolver_ != nullptr);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void GoogleCloud2ProdResolver::StartLocked() {
 | 
	
		
			
				|  |  | +  if (using_dns_) {
 | 
	
		
			
				|  |  | +    child_resolver_->StartLocked();
 | 
	
		
			
				|  |  | +    return;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  // Using xDS.  Start metadata server queries.
 | 
	
		
			
				|  |  | +  zone_query_ = MakeOrphanable<ZoneQuery>(Ref(), &pollent_);
 | 
	
		
			
				|  |  | +  ipv6_query_ = MakeOrphanable<IPv6Query>(Ref(), &pollent_);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void GoogleCloud2ProdResolver::RequestReresolutionLocked() {
 | 
	
		
			
				|  |  | +  if (child_resolver_ != nullptr) {
 | 
	
		
			
				|  |  | +    child_resolver_->RequestReresolutionLocked();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void GoogleCloud2ProdResolver::ResetBackoffLocked() {
 | 
	
		
			
				|  |  | +  if (child_resolver_ != nullptr) {
 | 
	
		
			
				|  |  | +    child_resolver_->ResetBackoffLocked();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void GoogleCloud2ProdResolver::ShutdownLocked() {
 | 
	
		
			
				|  |  | +  zone_query_.reset();
 | 
	
		
			
				|  |  | +  ipv6_query_.reset();
 | 
	
		
			
				|  |  | +  child_resolver_.reset();
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void GoogleCloud2ProdResolver::ZoneQueryDone(std::string zone) {
 | 
	
		
			
				|  |  | +  zone_query_.reset();
 | 
	
		
			
				|  |  | +  zone_ = std::move(zone);
 | 
	
		
			
				|  |  | +  if (supports_ipv6_.has_value()) StartXdsResolver();
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void GoogleCloud2ProdResolver::IPv6QueryDone(bool ipv6_supported) {
 | 
	
		
			
				|  |  | +  ipv6_query_.reset();
 | 
	
		
			
				|  |  | +  supports_ipv6_ = ipv6_supported;
 | 
	
		
			
				|  |  | +  if (zone_.has_value()) StartXdsResolver();
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void GoogleCloud2ProdResolver::StartXdsResolver() {
 | 
	
		
			
				|  |  | +  // Construct bootstrap JSON.
 | 
	
		
			
				|  |  | +  Json::Object node = {
 | 
	
		
			
				|  |  | +      {"id", "C2P"},
 | 
	
		
			
				|  |  | +      {"locality",
 | 
	
		
			
				|  |  | +       Json::Object{
 | 
	
		
			
				|  |  | +           {"zone", *zone_},
 | 
	
		
			
				|  |  | +       }},
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +  if (*supports_ipv6_) {
 | 
	
		
			
				|  |  | +    node["metadata"] = Json::Object{
 | 
	
		
			
				|  |  | +        {"TRAFFICDIRECTOR_DIRECTPATH_C2P_IPV6_CAPABLE", true},
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  Json bootstrap = Json::Object{
 | 
	
		
			
				|  |  | +      {"xds_servers",
 | 
	
		
			
				|  |  | +       Json::Array{
 | 
	
		
			
				|  |  | +           Json::Object{
 | 
	
		
			
				|  |  | +               {"server_uri", "directpath-trafficdirector.googleapis.com"},
 | 
	
		
			
				|  |  | +               {"channel_creds",
 | 
	
		
			
				|  |  | +                Json::Array{
 | 
	
		
			
				|  |  | +                    Json::Object{
 | 
	
		
			
				|  |  | +                        {"type", "google_default"},
 | 
	
		
			
				|  |  | +                    },
 | 
	
		
			
				|  |  | +                }},
 | 
	
		
			
				|  |  | +           },
 | 
	
		
			
				|  |  | +       }},
 | 
	
		
			
				|  |  | +      {"node", std::move(node)},
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +  // Inject bootstrap JSON as fallback config.
 | 
	
		
			
				|  |  | +  internal::SetXdsFallbackBootstrapConfig(bootstrap.Dump().c_str());
 | 
	
		
			
				|  |  | +  // Now start xDS resolver.
 | 
	
		
			
				|  |  | +  child_resolver_->StartLocked();
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +// Factory
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class GoogleCloud2ProdResolverFactory : public ResolverFactory {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  bool IsValidUri(const URI& uri) const override {
 | 
	
		
			
				|  |  | +    if (GPR_UNLIKELY(!uri.authority().empty())) {
 | 
	
		
			
				|  |  | +      gpr_log(GPR_ERROR, "google-c2p URI scheme does not support authorities");
 | 
	
		
			
				|  |  | +      return false;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    return true;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  OrphanablePtr<Resolver> CreateResolver(ResolverArgs args) const override {
 | 
	
		
			
				|  |  | +    if (!IsValidUri(args.uri)) return nullptr;
 | 
	
		
			
				|  |  | +    return MakeOrphanable<GoogleCloud2ProdResolver>(std::move(args));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  const char* scheme() const override { return "google-c2p"; }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +}  // namespace
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void GoogleCloud2ProdResolverInit() {
 | 
	
		
			
				|  |  | +  ResolverRegistry::Builder::RegisterResolverFactory(
 | 
	
		
			
				|  |  | +      absl::make_unique<GoogleCloud2ProdResolverFactory>());
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void GoogleCloud2ProdResolverShutdown() {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +}  // namespace grpc_core
 |