2 // Copyright 2021 gRPC authors.
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
8 // http://www.apache.org/licenses/LICENSE-2.0
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
17 #include <grpc/support/port_platform.h>
19 #include "src/core/ext/filters/client_channel/resolver_registry.h"
20 #include "src/core/ext/xds/xds_client.h"
21 #include "src/core/lib/gpr/env.h"
22 #include "src/core/lib/http/httpcli.h"
23 #include "src/core/lib/iomgr/polling_entity.h"
24 #include "src/core/lib/security/credentials/alts/check_gcp_environment.h"
30 class GoogleCloud2ProdResolver : public Resolver {
32 explicit GoogleCloud2ProdResolver(ResolverArgs args);
34 void StartLocked() override;
35 void RequestReresolutionLocked() override;
36 void ResetBackoffLocked() override;
37 void ShutdownLocked() override;
40 // Represents an HTTP request to the metadata server.
41 class MetadataQuery : public InternallyRefCounted<MetadataQuery> {
43 MetadataQuery(RefCountedPtr<GoogleCloud2ProdResolver> resolver,
44 const char* path, grpc_polling_entity* pollent);
45 ~MetadataQuery() override;
47 void Orphan() override;
50 static void OnHttpRequestDone(void* arg, grpc_error* error);
52 // Calls OnDone() if not already called. Releases a ref.
53 void MaybeCallOnDone(grpc_error* error);
55 // If error is not GRPC_ERROR_NONE, then it's not safe to look at response.
56 virtual void OnDone(GoogleCloud2ProdResolver* resolver,
57 const grpc_http_response* response,
58 grpc_error* error) = 0;
60 RefCountedPtr<GoogleCloud2ProdResolver> resolver_;
61 grpc_httpcli_context context_;
62 grpc_httpcli_response response_;
63 grpc_closure on_done_;
64 Atomic<bool> on_done_called_{false};
67 // A metadata server query to get the zone.
68 class ZoneQuery : public MetadataQuery {
70 ZoneQuery(RefCountedPtr<GoogleCloud2ProdResolver> resolver,
71 grpc_polling_entity* pollent);
74 void OnDone(GoogleCloud2ProdResolver* resolver,
75 const grpc_http_response* response, grpc_error* error) override;
78 // A metadata server query to get the IPv6 address.
79 class IPv6Query : public MetadataQuery {
81 IPv6Query(RefCountedPtr<GoogleCloud2ProdResolver> resolver,
82 grpc_polling_entity* pollent);
85 void OnDone(GoogleCloud2ProdResolver* resolver,
86 const grpc_http_response* response, grpc_error* error) override;
89 void ZoneQueryDone(std::string zone);
90 void IPv6QueryDone(bool ipv6_supported);
91 void StartXdsResolver();
93 std::shared_ptr<WorkSerializer> work_serializer_;
94 grpc_polling_entity pollent_;
95 bool using_dns_ = false;
96 OrphanablePtr<Resolver> child_resolver_;
98 OrphanablePtr<ZoneQuery> zone_query_;
99 absl::optional<std::string> zone_;
101 OrphanablePtr<IPv6Query> ipv6_query_;
102 absl::optional<bool> supports_ipv6_;
106 // GoogleCloud2ProdResolver::MetadataQuery
109 GoogleCloud2ProdResolver::MetadataQuery::MetadataQuery(
110 RefCountedPtr<GoogleCloud2ProdResolver> resolver, const char* path,
111 grpc_polling_entity* pollent)
112 : resolver_(std::move(resolver)) {
113 grpc_httpcli_context_init(&context_);
114 // Start HTTP request.
115 GRPC_CLOSURE_INIT(&on_done_, OnHttpRequestDone, this, nullptr);
116 Ref().release(); // Ref held by callback.
117 grpc_httpcli_request request;
118 memset(&request, 0, sizeof(grpc_httpcli_request));
119 grpc_http_header header = {const_cast<char*>("Metadata-Flavor"),
120 const_cast<char*>("Google")};
121 request.host = const_cast<char*>("metadata.google.internal");
122 request.http.path = const_cast<char*>(path);
123 request.http.hdr_count = 1;
124 request.http.hdrs = &header;
125 grpc_resource_quota* resource_quota =
126 grpc_resource_quota_create("c2p_resolver");
127 grpc_httpcli_get(&context_, pollent, resource_quota, &request,
128 ExecCtx::Get()->Now() + 10000, // 10s timeout
129 &on_done_, &response_);
130 grpc_resource_quota_unref_internal(resource_quota);
133 GoogleCloud2ProdResolver::MetadataQuery::~MetadataQuery() {
134 grpc_httpcli_context_destroy(&context_);
135 grpc_http_response_destroy(&response_);
138 void GoogleCloud2ProdResolver::MetadataQuery::Orphan() {
139 // TODO(roth): Once the HTTP client library supports cancellation,
141 MaybeCallOnDone(GRPC_ERROR_CANCELLED);
144 void GoogleCloud2ProdResolver::MetadataQuery::OnHttpRequestDone(
145 void* arg, grpc_error* error) {
146 auto* self = static_cast<MetadataQuery*>(arg);
147 self->MaybeCallOnDone(GRPC_ERROR_REF(error));
150 void GoogleCloud2ProdResolver::MetadataQuery::MaybeCallOnDone(
152 bool expected = false;
153 if (!on_done_called_.CompareExchangeStrong(
154 &expected, true, MemoryOrder::RELAXED, MemoryOrder::RELAXED)) {
155 // We've already called OnDone(), so just clean up.
156 GRPC_ERROR_UNREF(error);
160 // Hop back into WorkSerializer to call OnDone().
161 // Note: We implicitly pass our ref to the callback here.
162 resolver_->work_serializer_->Run(
164 OnDone(resolver_.get(), &response_, error);
171 // GoogleCloud2ProdResolver::ZoneQuery
174 GoogleCloud2ProdResolver::ZoneQuery::ZoneQuery(
175 RefCountedPtr<GoogleCloud2ProdResolver> resolver,
176 grpc_polling_entity* pollent)
177 : MetadataQuery(std::move(resolver), "/computeMetadata/v1/instance/zone",
180 void GoogleCloud2ProdResolver::ZoneQuery::OnDone(
181 GoogleCloud2ProdResolver* resolver, const grpc_http_response* response,
183 if (error != GRPC_ERROR_NONE) {
184 gpr_log(GPR_ERROR, "error fetching zone from metadata server: %s",
185 grpc_error_string(error));
188 if (error == GRPC_ERROR_NONE && response->status == 200) {
189 absl::string_view body(response->body, response->body_length);
190 size_t i = body.find_last_of('/');
191 if (i == body.npos) {
192 gpr_log(GPR_ERROR, "could not parse zone from metadata server: %s",
193 std::string(body).c_str());
195 zone = std::string(body.substr(i));
198 resolver->ZoneQueryDone(std::move(zone));
199 GRPC_ERROR_UNREF(error);
203 // GoogleCloud2ProdResolver::IPv6Query
206 GoogleCloud2ProdResolver::IPv6Query::IPv6Query(
207 RefCountedPtr<GoogleCloud2ProdResolver> resolver,
208 grpc_polling_entity* pollent)
209 : MetadataQuery(std::move(resolver),
210 "/computeMetadata/v1/instance/network-interfaces/0/ipv6s",
213 void GoogleCloud2ProdResolver::IPv6Query::OnDone(
214 GoogleCloud2ProdResolver* resolver, const grpc_http_response* response,
216 if (error != GRPC_ERROR_NONE) {
217 gpr_log(GPR_ERROR, "error fetching IPv6 address from metadata server: %s",
218 grpc_error_string(error));
220 resolver->IPv6QueryDone(error == GRPC_ERROR_NONE && response->status == 200);
221 GRPC_ERROR_UNREF(error);
225 // GoogleCloud2ProdResolver
228 GoogleCloud2ProdResolver::GoogleCloud2ProdResolver(ResolverArgs args)
229 : work_serializer_(std::move(args.work_serializer)),
230 pollent_(grpc_polling_entity_create_from_pollset_set(args.pollset_set)) {
231 absl::string_view name_to_resolve = absl::StripPrefix(args.uri.path(), "/");
232 // If we're not running on GCP, we can't use DirectPath, so delegate
233 // to the DNS resolver.
234 if (!grpc_alts_is_running_on_gcp() ||
235 // If the client is already using xDS, we can't use it here, because
236 // they may be talking to a completely different xDS server than we
238 // TODO(roth): When we implement xDS federation, remove this constraint.
239 UniquePtr<char>(gpr_getenv("GRPC_XDS_BOOTSTRAP")) != nullptr ||
240 UniquePtr<char>(gpr_getenv("GRPC_XDS_BOOTSTRAP_CONFIG")) != nullptr) {
242 child_resolver_ = ResolverRegistry::CreateResolver(
243 absl::StrCat("dns:", name_to_resolve).c_str(), args.args,
244 args.pollset_set, work_serializer_, std::move(args.result_handler));
245 GPR_ASSERT(child_resolver_ != nullptr);
248 // Create xds resolver.
249 child_resolver_ = ResolverRegistry::CreateResolver(
250 absl::StrCat("xds:", name_to_resolve).c_str(), args.args,
251 args.pollset_set, work_serializer_, std::move(args.result_handler));
252 GPR_ASSERT(child_resolver_ != nullptr);
255 void GoogleCloud2ProdResolver::StartLocked() {
257 child_resolver_->StartLocked();
260 // Using xDS. Start metadata server queries.
261 zone_query_ = MakeOrphanable<ZoneQuery>(Ref(), &pollent_);
262 ipv6_query_ = MakeOrphanable<IPv6Query>(Ref(), &pollent_);
265 void GoogleCloud2ProdResolver::RequestReresolutionLocked() {
266 if (child_resolver_ != nullptr) {
267 child_resolver_->RequestReresolutionLocked();
271 void GoogleCloud2ProdResolver::ResetBackoffLocked() {
272 if (child_resolver_ != nullptr) {
273 child_resolver_->ResetBackoffLocked();
277 void GoogleCloud2ProdResolver::ShutdownLocked() {
280 child_resolver_.reset();
283 void GoogleCloud2ProdResolver::ZoneQueryDone(std::string zone) {
285 zone_ = std::move(zone);
286 if (supports_ipv6_.has_value()) StartXdsResolver();
289 void GoogleCloud2ProdResolver::IPv6QueryDone(bool ipv6_supported) {
291 supports_ipv6_ = ipv6_supported;
292 if (zone_.has_value()) StartXdsResolver();
295 void GoogleCloud2ProdResolver::StartXdsResolver() {
296 // Construct bootstrap JSON.
297 Json::Object node = {
300 if (!zone_->empty()) {
301 node["locality"] = Json::Object{
305 if (*supports_ipv6_) {
306 node["metadata"] = Json::Object{
307 {"TRAFFICDIRECTOR_DIRECTPATH_C2P_IPV6_CAPABLE", true},
310 Json bootstrap = Json::Object{
314 {"server_uri", "directpath-trafficdirector.googleapis.com"},
318 {"type", "google_default"},
323 {"node", std::move(node)},
325 // Inject bootstrap JSON as fallback config.
326 internal::SetXdsFallbackBootstrapConfig(bootstrap.Dump().c_str());
327 // Now start xDS resolver.
328 child_resolver_->StartLocked();
335 class GoogleCloud2ProdResolverFactory : public ResolverFactory {
337 bool IsValidUri(const URI& uri) const override {
338 if (GPR_UNLIKELY(!uri.authority().empty())) {
339 gpr_log(GPR_ERROR, "google-c2p URI scheme does not support authorities");
345 OrphanablePtr<Resolver> CreateResolver(ResolverArgs args) const override {
346 if (!IsValidUri(args.uri)) return nullptr;
347 return MakeOrphanable<GoogleCloud2ProdResolver>(std::move(args));
350 const char* scheme() const override { return "google-c2p"; }
355 void GoogleCloud2ProdResolverInit() {
356 ResolverRegistry::Builder::RegisterResolverFactory(
357 absl::make_unique<GoogleCloud2ProdResolverFactory>());
360 void GoogleCloud2ProdResolverShutdown() {}
362 } // namespace grpc_core