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>
21 #include "src/core/ext/filters/client_channel/resolver_registry.h"
22 #include "src/core/ext/xds/xds_client.h"
23 #include "src/core/lib/gpr/env.h"
24 #include "src/core/lib/gpr/string.h"
25 #include "src/core/lib/http/httpcli.h"
26 #include "src/core/lib/iomgr/polling_entity.h"
27 #include "src/core/lib/security/credentials/alts/check_gcp_environment.h"
33 class GoogleCloud2ProdResolver : public Resolver {
35 explicit GoogleCloud2ProdResolver(ResolverArgs args);
37 void StartLocked() override;
38 void RequestReresolutionLocked() override;
39 void ResetBackoffLocked() override;
40 void ShutdownLocked() override;
43 // Represents an HTTP request to the metadata server.
44 class MetadataQuery : public InternallyRefCounted<MetadataQuery> {
46 MetadataQuery(RefCountedPtr<GoogleCloud2ProdResolver> resolver,
47 const char* path, grpc_polling_entity* pollent);
48 ~MetadataQuery() override;
50 void Orphan() override;
53 static void OnHttpRequestDone(void* arg, grpc_error_handle error);
55 // Calls OnDone() if not already called. Releases a ref.
56 void MaybeCallOnDone(grpc_error_handle error);
58 // If error is not GRPC_ERROR_NONE, then it's not safe to look at response.
59 virtual void OnDone(GoogleCloud2ProdResolver* resolver,
60 const grpc_http_response* response,
61 grpc_error_handle error) = 0;
63 RefCountedPtr<GoogleCloud2ProdResolver> resolver_;
64 grpc_httpcli_context context_;
65 grpc_httpcli_response response_;
66 grpc_closure on_done_;
67 Atomic<bool> on_done_called_{false};
70 // A metadata server query to get the zone.
71 class ZoneQuery : public MetadataQuery {
73 ZoneQuery(RefCountedPtr<GoogleCloud2ProdResolver> resolver,
74 grpc_polling_entity* pollent);
77 void OnDone(GoogleCloud2ProdResolver* resolver,
78 const grpc_http_response* response,
79 grpc_error_handle error) override;
82 // A metadata server query to get the IPv6 address.
83 class IPv6Query : public MetadataQuery {
85 IPv6Query(RefCountedPtr<GoogleCloud2ProdResolver> resolver,
86 grpc_polling_entity* pollent);
89 void OnDone(GoogleCloud2ProdResolver* resolver,
90 const grpc_http_response* response,
91 grpc_error_handle error) override;
94 void ZoneQueryDone(std::string zone);
95 void IPv6QueryDone(bool ipv6_supported);
96 void StartXdsResolver();
98 std::shared_ptr<WorkSerializer> work_serializer_;
99 grpc_polling_entity pollent_;
100 bool using_dns_ = false;
101 OrphanablePtr<Resolver> child_resolver_;
103 OrphanablePtr<ZoneQuery> zone_query_;
104 absl::optional<std::string> zone_;
106 OrphanablePtr<IPv6Query> ipv6_query_;
107 absl::optional<bool> supports_ipv6_;
111 // GoogleCloud2ProdResolver::MetadataQuery
114 GoogleCloud2ProdResolver::MetadataQuery::MetadataQuery(
115 RefCountedPtr<GoogleCloud2ProdResolver> resolver, const char* path,
116 grpc_polling_entity* pollent)
117 : resolver_(std::move(resolver)) {
118 grpc_httpcli_context_init(&context_);
119 // Start HTTP request.
120 GRPC_CLOSURE_INIT(&on_done_, OnHttpRequestDone, this, nullptr);
121 Ref().release(); // Ref held by callback.
122 grpc_httpcli_request request;
123 memset(&request, 0, sizeof(grpc_httpcli_request));
124 grpc_http_header header = {const_cast<char*>("Metadata-Flavor"),
125 const_cast<char*>("Google")};
126 request.host = const_cast<char*>("metadata.google.internal");
127 request.http.path = const_cast<char*>(path);
128 request.http.hdr_count = 1;
129 request.http.hdrs = &header;
130 grpc_resource_quota* resource_quota =
131 grpc_resource_quota_create("c2p_resolver");
132 grpc_httpcli_get(&context_, pollent, resource_quota, &request,
133 ExecCtx::Get()->Now() + 10000, // 10s timeout
134 &on_done_, &response_);
135 grpc_resource_quota_unref_internal(resource_quota);
138 GoogleCloud2ProdResolver::MetadataQuery::~MetadataQuery() {
139 grpc_httpcli_context_destroy(&context_);
140 grpc_http_response_destroy(&response_);
143 void GoogleCloud2ProdResolver::MetadataQuery::Orphan() {
144 // TODO(roth): Once the HTTP client library supports cancellation,
146 MaybeCallOnDone(GRPC_ERROR_CANCELLED);
149 void GoogleCloud2ProdResolver::MetadataQuery::OnHttpRequestDone(
150 void* arg, grpc_error_handle error) {
151 auto* self = static_cast<MetadataQuery*>(arg);
152 self->MaybeCallOnDone(GRPC_ERROR_REF(error));
155 void GoogleCloud2ProdResolver::MetadataQuery::MaybeCallOnDone(
156 grpc_error_handle error) {
157 bool expected = false;
158 if (!on_done_called_.CompareExchangeStrong(
159 &expected, true, MemoryOrder::RELAXED, MemoryOrder::RELAXED)) {
160 // We've already called OnDone(), so just clean up.
161 GRPC_ERROR_UNREF(error);
165 // Hop back into WorkSerializer to call OnDone().
166 // Note: We implicitly pass our ref to the callback here.
167 resolver_->work_serializer_->Run(
169 OnDone(resolver_.get(), &response_, error);
176 // GoogleCloud2ProdResolver::ZoneQuery
179 GoogleCloud2ProdResolver::ZoneQuery::ZoneQuery(
180 RefCountedPtr<GoogleCloud2ProdResolver> resolver,
181 grpc_polling_entity* pollent)
182 : MetadataQuery(std::move(resolver), "/computeMetadata/v1/instance/zone",
185 void GoogleCloud2ProdResolver::ZoneQuery::OnDone(
186 GoogleCloud2ProdResolver* resolver, const grpc_http_response* response,
187 grpc_error_handle error) {
188 if (error != GRPC_ERROR_NONE) {
189 gpr_log(GPR_ERROR, "error fetching zone from metadata server: %s",
190 grpc_error_std_string(error).c_str());
193 if (error == GRPC_ERROR_NONE && response->status == 200) {
194 absl::string_view body(response->body, response->body_length);
195 size_t i = body.find_last_of('/');
196 if (i == body.npos) {
197 gpr_log(GPR_ERROR, "could not parse zone from metadata server: %s",
198 std::string(body).c_str());
200 zone = std::string(body.substr(i + 1));
203 resolver->ZoneQueryDone(std::move(zone));
204 GRPC_ERROR_UNREF(error);
208 // GoogleCloud2ProdResolver::IPv6Query
211 GoogleCloud2ProdResolver::IPv6Query::IPv6Query(
212 RefCountedPtr<GoogleCloud2ProdResolver> resolver,
213 grpc_polling_entity* pollent)
214 : MetadataQuery(std::move(resolver),
215 "/computeMetadata/v1/instance/network-interfaces/0/ipv6s",
218 void GoogleCloud2ProdResolver::IPv6Query::OnDone(
219 GoogleCloud2ProdResolver* resolver, const grpc_http_response* response,
220 grpc_error_handle error) {
221 if (error != GRPC_ERROR_NONE) {
222 gpr_log(GPR_ERROR, "error fetching IPv6 address from metadata server: %s",
223 grpc_error_std_string(error).c_str());
225 resolver->IPv6QueryDone(error == GRPC_ERROR_NONE && response->status == 200);
226 GRPC_ERROR_UNREF(error);
230 // GoogleCloud2ProdResolver
233 GoogleCloud2ProdResolver::GoogleCloud2ProdResolver(ResolverArgs args)
234 : work_serializer_(std::move(args.work_serializer)),
235 pollent_(grpc_polling_entity_create_from_pollset_set(args.pollset_set)) {
236 absl::string_view name_to_resolve = absl::StripPrefix(args.uri.path(), "/");
237 // If we're not running on GCP, we can't use DirectPath, so delegate
238 // to the DNS resolver.
239 if (!grpc_alts_is_running_on_gcp() ||
240 // If the client is already using xDS, we can't use it here, because
241 // they may be talking to a completely different xDS server than we
243 // TODO(roth): When we implement xDS federation, remove this constraint.
244 UniquePtr<char>(gpr_getenv("GRPC_XDS_BOOTSTRAP")) != nullptr ||
245 UniquePtr<char>(gpr_getenv("GRPC_XDS_BOOTSTRAP_CONFIG")) != nullptr) {
247 child_resolver_ = ResolverRegistry::CreateResolver(
248 absl::StrCat("dns:", name_to_resolve).c_str(), args.args,
249 args.pollset_set, work_serializer_, std::move(args.result_handler));
250 GPR_ASSERT(child_resolver_ != nullptr);
253 // Create xds resolver.
254 child_resolver_ = ResolverRegistry::CreateResolver(
255 absl::StrCat("xds:", name_to_resolve).c_str(), args.args,
256 args.pollset_set, work_serializer_, std::move(args.result_handler));
257 GPR_ASSERT(child_resolver_ != nullptr);
260 void GoogleCloud2ProdResolver::StartLocked() {
262 child_resolver_->StartLocked();
265 // Using xDS. Start metadata server queries.
266 zone_query_ = MakeOrphanable<ZoneQuery>(Ref(), &pollent_);
267 ipv6_query_ = MakeOrphanable<IPv6Query>(Ref(), &pollent_);
270 void GoogleCloud2ProdResolver::RequestReresolutionLocked() {
271 if (child_resolver_ != nullptr) {
272 child_resolver_->RequestReresolutionLocked();
276 void GoogleCloud2ProdResolver::ResetBackoffLocked() {
277 if (child_resolver_ != nullptr) {
278 child_resolver_->ResetBackoffLocked();
282 void GoogleCloud2ProdResolver::ShutdownLocked() {
285 child_resolver_.reset();
288 void GoogleCloud2ProdResolver::ZoneQueryDone(std::string zone) {
290 zone_ = std::move(zone);
291 if (supports_ipv6_.has_value()) StartXdsResolver();
294 void GoogleCloud2ProdResolver::IPv6QueryDone(bool ipv6_supported) {
296 supports_ipv6_ = ipv6_supported;
297 if (zone_.has_value()) StartXdsResolver();
300 void GoogleCloud2ProdResolver::StartXdsResolver() {
301 // Construct bootstrap JSON.
302 std::random_device rd;
303 std::mt19937 mt(rd());
304 std::uniform_int_distribution<uint64_t> dist(1, UINT64_MAX);
305 Json::Object node = {
306 {"id", absl::StrCat("C2P-", dist(mt))},
308 if (!zone_->empty()) {
309 node["locality"] = Json::Object{
313 if (*supports_ipv6_) {
314 node["metadata"] = Json::Object{
315 {"TRAFFICDIRECTOR_DIRECTPATH_C2P_IPV6_CAPABLE", true},
318 // Allow the TD server uri to be overridden for testing purposes.
319 UniquePtr<char> override_server(
320 gpr_getenv("GRPC_TEST_ONLY_GOOGLE_C2P_RESOLVER_TRAFFIC_DIRECTOR_URI"));
321 const char* server_uri =
322 override_server != nullptr && strlen(override_server.get()) > 0
323 ? override_server.get()
324 : "directpath-trafficdirector.googleapis.com";
325 Json bootstrap = Json::Object{
329 {"server_uri", server_uri},
333 {"type", "google_default"},
336 {"server_features", Json::Array{"xds_v3"}},
339 {"node", std::move(node)},
341 // Inject bootstrap JSON as fallback config.
342 internal::SetXdsFallbackBootstrapConfig(bootstrap.Dump().c_str());
343 // Now start xDS resolver.
344 child_resolver_->StartLocked();
351 class GoogleCloud2ProdResolverFactory : public ResolverFactory {
353 bool IsValidUri(const URI& uri) const override {
354 if (GPR_UNLIKELY(!uri.authority().empty())) {
355 gpr_log(GPR_ERROR, "google-c2p URI scheme does not support authorities");
361 OrphanablePtr<Resolver> CreateResolver(ResolverArgs args) const override {
362 if (!IsValidUri(args.uri)) return nullptr;
363 return MakeOrphanable<GoogleCloud2ProdResolver>(std::move(args));
366 const char* scheme() const override { return "google-c2p"; }
371 void GoogleCloud2ProdResolverInit() {
372 // TODO(roth): Remove env var protection once this code is proven stable.
373 UniquePtr<char> value(gpr_getenv("GRPC_EXPERIMENTAL_GOOGLE_C2P_RESOLVER"));
375 bool parse_succeeded = gpr_parse_bool_value(value.get(), &parsed_value);
376 if (parse_succeeded && parsed_value) {
377 ResolverRegistry::Builder::RegisterResolverFactory(
378 absl::make_unique<GoogleCloud2ProdResolverFactory>());
382 void GoogleCloud2ProdResolverShutdown() {}
384 } // namespace grpc_core