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/gpr/string.h"
23 #include "src/core/lib/http/httpcli.h"
24 #include "src/core/lib/iomgr/polling_entity.h"
25 #include "src/core/lib/security/credentials/alts/check_gcp_environment.h"
31 class GoogleCloud2ProdResolver : public Resolver {
33 explicit GoogleCloud2ProdResolver(ResolverArgs args);
35 void StartLocked() override;
36 void RequestReresolutionLocked() override;
37 void ResetBackoffLocked() override;
38 void ShutdownLocked() override;
41 // Represents an HTTP request to the metadata server.
42 class MetadataQuery : public InternallyRefCounted<MetadataQuery> {
44 MetadataQuery(RefCountedPtr<GoogleCloud2ProdResolver> resolver,
45 const char* path, grpc_polling_entity* pollent);
46 ~MetadataQuery() override;
48 void Orphan() override;
51 static void OnHttpRequestDone(void* arg, grpc_error* error);
53 // Calls OnDone() if not already called. Releases a ref.
54 void MaybeCallOnDone(grpc_error* error);
56 // If error is not GRPC_ERROR_NONE, then it's not safe to look at response.
57 virtual void OnDone(GoogleCloud2ProdResolver* resolver,
58 const grpc_http_response* response,
59 grpc_error* error) = 0;
61 RefCountedPtr<GoogleCloud2ProdResolver> resolver_;
62 grpc_httpcli_context context_;
63 grpc_httpcli_response response_;
64 grpc_closure on_done_;
65 Atomic<bool> on_done_called_{false};
68 // A metadata server query to get the zone.
69 class ZoneQuery : public MetadataQuery {
71 ZoneQuery(RefCountedPtr<GoogleCloud2ProdResolver> resolver,
72 grpc_polling_entity* pollent);
75 void OnDone(GoogleCloud2ProdResolver* resolver,
76 const grpc_http_response* response, grpc_error* error) override;
79 // A metadata server query to get the IPv6 address.
80 class IPv6Query : public MetadataQuery {
82 IPv6Query(RefCountedPtr<GoogleCloud2ProdResolver> resolver,
83 grpc_polling_entity* pollent);
86 void OnDone(GoogleCloud2ProdResolver* resolver,
87 const grpc_http_response* response, grpc_error* error) override;
90 void ZoneQueryDone(std::string zone);
91 void IPv6QueryDone(bool ipv6_supported);
92 void StartXdsResolver();
94 std::shared_ptr<WorkSerializer> work_serializer_;
95 grpc_polling_entity pollent_;
96 bool using_dns_ = false;
97 OrphanablePtr<Resolver> child_resolver_;
99 OrphanablePtr<ZoneQuery> zone_query_;
100 absl::optional<std::string> zone_;
102 OrphanablePtr<IPv6Query> ipv6_query_;
103 absl::optional<bool> supports_ipv6_;
107 // GoogleCloud2ProdResolver::MetadataQuery
110 GoogleCloud2ProdResolver::MetadataQuery::MetadataQuery(
111 RefCountedPtr<GoogleCloud2ProdResolver> resolver, const char* path,
112 grpc_polling_entity* pollent)
113 : resolver_(std::move(resolver)) {
114 grpc_httpcli_context_init(&context_);
115 // Start HTTP request.
116 GRPC_CLOSURE_INIT(&on_done_, OnHttpRequestDone, this, nullptr);
117 Ref().release(); // Ref held by callback.
118 grpc_httpcli_request request;
119 memset(&request, 0, sizeof(grpc_httpcli_request));
120 grpc_http_header header = {const_cast<char*>("Metadata-Flavor"),
121 const_cast<char*>("Google")};
122 request.host = const_cast<char*>("metadata.google.internal");
123 request.http.path = const_cast<char*>(path);
124 request.http.hdr_count = 1;
125 request.http.hdrs = &header;
126 grpc_resource_quota* resource_quota =
127 grpc_resource_quota_create("c2p_resolver");
128 grpc_httpcli_get(&context_, pollent, resource_quota, &request,
129 ExecCtx::Get()->Now() + 10000, // 10s timeout
130 &on_done_, &response_);
131 grpc_resource_quota_unref_internal(resource_quota);
134 GoogleCloud2ProdResolver::MetadataQuery::~MetadataQuery() {
135 grpc_httpcli_context_destroy(&context_);
136 grpc_http_response_destroy(&response_);
139 void GoogleCloud2ProdResolver::MetadataQuery::Orphan() {
140 // TODO(roth): Once the HTTP client library supports cancellation,
142 MaybeCallOnDone(GRPC_ERROR_CANCELLED);
145 void GoogleCloud2ProdResolver::MetadataQuery::OnHttpRequestDone(
146 void* arg, grpc_error* error) {
147 auto* self = static_cast<MetadataQuery*>(arg);
148 self->MaybeCallOnDone(GRPC_ERROR_REF(error));
151 void GoogleCloud2ProdResolver::MetadataQuery::MaybeCallOnDone(
153 bool expected = false;
154 if (!on_done_called_.CompareExchangeStrong(
155 &expected, true, MemoryOrder::RELAXED, MemoryOrder::RELAXED)) {
156 // We've already called OnDone(), so just clean up.
157 GRPC_ERROR_UNREF(error);
161 // Hop back into WorkSerializer to call OnDone().
162 // Note: We implicitly pass our ref to the callback here.
163 resolver_->work_serializer_->Run(
165 OnDone(resolver_.get(), &response_, error);
172 // GoogleCloud2ProdResolver::ZoneQuery
175 GoogleCloud2ProdResolver::ZoneQuery::ZoneQuery(
176 RefCountedPtr<GoogleCloud2ProdResolver> resolver,
177 grpc_polling_entity* pollent)
178 : MetadataQuery(std::move(resolver), "/computeMetadata/v1/instance/zone",
181 void GoogleCloud2ProdResolver::ZoneQuery::OnDone(
182 GoogleCloud2ProdResolver* resolver, const grpc_http_response* response,
184 if (error != GRPC_ERROR_NONE) {
185 gpr_log(GPR_ERROR, "error fetching zone from metadata server: %s",
186 grpc_error_string(error));
189 if (error == GRPC_ERROR_NONE && response->status == 200) {
190 absl::string_view body(response->body, response->body_length);
191 size_t i = body.find_last_of('/');
192 if (i == body.npos) {
193 gpr_log(GPR_ERROR, "could not parse zone from metadata server: %s",
194 std::string(body).c_str());
196 zone = std::string(body.substr(i));
199 resolver->ZoneQueryDone(std::move(zone));
200 GRPC_ERROR_UNREF(error);
204 // GoogleCloud2ProdResolver::IPv6Query
207 GoogleCloud2ProdResolver::IPv6Query::IPv6Query(
208 RefCountedPtr<GoogleCloud2ProdResolver> resolver,
209 grpc_polling_entity* pollent)
210 : MetadataQuery(std::move(resolver),
211 "/computeMetadata/v1/instance/network-interfaces/0/ipv6s",
214 void GoogleCloud2ProdResolver::IPv6Query::OnDone(
215 GoogleCloud2ProdResolver* resolver, const grpc_http_response* response,
217 if (error != GRPC_ERROR_NONE) {
218 gpr_log(GPR_ERROR, "error fetching IPv6 address from metadata server: %s",
219 grpc_error_string(error));
221 resolver->IPv6QueryDone(error == GRPC_ERROR_NONE && response->status == 200);
222 GRPC_ERROR_UNREF(error);
226 // GoogleCloud2ProdResolver
229 GoogleCloud2ProdResolver::GoogleCloud2ProdResolver(ResolverArgs args)
230 : work_serializer_(std::move(args.work_serializer)),
231 pollent_(grpc_polling_entity_create_from_pollset_set(args.pollset_set)) {
232 absl::string_view name_to_resolve = absl::StripPrefix(args.uri.path(), "/");
233 // If we're not running on GCP, we can't use DirectPath, so delegate
234 // to the DNS resolver.
235 if (!grpc_alts_is_running_on_gcp() ||
236 // If the client is already using xDS, we can't use it here, because
237 // they may be talking to a completely different xDS server than we
239 // TODO(roth): When we implement xDS federation, remove this constraint.
240 UniquePtr<char>(gpr_getenv("GRPC_XDS_BOOTSTRAP")) != nullptr ||
241 UniquePtr<char>(gpr_getenv("GRPC_XDS_BOOTSTRAP_CONFIG")) != nullptr) {
243 child_resolver_ = ResolverRegistry::CreateResolver(
244 absl::StrCat("dns:", name_to_resolve).c_str(), args.args,
245 args.pollset_set, work_serializer_, std::move(args.result_handler));
246 GPR_ASSERT(child_resolver_ != nullptr);
249 // Create xds resolver.
250 child_resolver_ = ResolverRegistry::CreateResolver(
251 absl::StrCat("xds:", name_to_resolve).c_str(), args.args,
252 args.pollset_set, work_serializer_, std::move(args.result_handler));
253 GPR_ASSERT(child_resolver_ != nullptr);
256 void GoogleCloud2ProdResolver::StartLocked() {
258 child_resolver_->StartLocked();
261 // Using xDS. Start metadata server queries.
262 zone_query_ = MakeOrphanable<ZoneQuery>(Ref(), &pollent_);
263 ipv6_query_ = MakeOrphanable<IPv6Query>(Ref(), &pollent_);
266 void GoogleCloud2ProdResolver::RequestReresolutionLocked() {
267 if (child_resolver_ != nullptr) {
268 child_resolver_->RequestReresolutionLocked();
272 void GoogleCloud2ProdResolver::ResetBackoffLocked() {
273 if (child_resolver_ != nullptr) {
274 child_resolver_->ResetBackoffLocked();
278 void GoogleCloud2ProdResolver::ShutdownLocked() {
281 child_resolver_.reset();
284 void GoogleCloud2ProdResolver::ZoneQueryDone(std::string zone) {
286 zone_ = std::move(zone);
287 if (supports_ipv6_.has_value()) StartXdsResolver();
290 void GoogleCloud2ProdResolver::IPv6QueryDone(bool ipv6_supported) {
292 supports_ipv6_ = ipv6_supported;
293 if (zone_.has_value()) StartXdsResolver();
296 void GoogleCloud2ProdResolver::StartXdsResolver() {
297 // Construct bootstrap JSON.
298 Json::Object node = {
301 if (!zone_->empty()) {
302 node["locality"] = Json::Object{
306 if (*supports_ipv6_) {
307 node["metadata"] = Json::Object{
308 {"TRAFFICDIRECTOR_DIRECTPATH_C2P_IPV6_CAPABLE", true},
311 Json bootstrap = Json::Object{
315 {"server_uri", "directpath-trafficdirector.googleapis.com"},
319 {"type", "google_default"},
324 {"node", std::move(node)},
326 // Inject bootstrap JSON as fallback config.
327 internal::SetXdsFallbackBootstrapConfig(bootstrap.Dump().c_str());
328 // Now start xDS resolver.
329 child_resolver_->StartLocked();
336 class GoogleCloud2ProdResolverFactory : public ResolverFactory {
338 bool IsValidUri(const URI& uri) const override {
339 if (GPR_UNLIKELY(!uri.authority().empty())) {
340 gpr_log(GPR_ERROR, "google-c2p URI scheme does not support authorities");
346 OrphanablePtr<Resolver> CreateResolver(ResolverArgs args) const override {
347 if (!IsValidUri(args.uri)) return nullptr;
348 return MakeOrphanable<GoogleCloud2ProdResolver>(std::move(args));
351 const char* scheme() const override { return "google-c2p"; }
356 void GoogleCloud2ProdResolverInit() {
357 // TODO(roth): Remove env var protection once this code is proven stable.
358 UniquePtr<char> value(gpr_getenv("GRPC_EXPERIMENTAL_GOOGLE_C2P_RESOLVER"));
360 bool parse_succeeded = gpr_parse_bool_value(value.get(), &parsed_value);
361 if (parse_succeeded && parsed_value) {
362 ResolverRegistry::Builder::RegisterResolverFactory(
363 absl::make_unique<GoogleCloud2ProdResolverFactory>());
367 void GoogleCloud2ProdResolverShutdown() {}
369 } // namespace grpc_core