diff --git a/CMakeLists.txt b/CMakeLists.txt
index bfe81db86d2..c09a313f0ab 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1782,6 +1782,7 @@ add_library(grpc
src/core/ext/filters/server_config_selector/server_config_selector_filter.cc
src/core/ext/filters/stateful_session/stateful_session_filter.cc
src/core/ext/filters/stateful_session/stateful_session_service_config_parser.cc
+ src/core/ext/gcp/metadata_query.cc
src/core/ext/transport/chttp2/alpn/alpn.cc
src/core/ext/transport/chttp2/client/chttp2_connector.cc
src/core/ext/transport/chttp2/server/chttp2_server.cc
diff --git a/Makefile b/Makefile
index de6eefa8291..dfd1178b40b 100644
--- a/Makefile
+++ b/Makefile
@@ -1036,6 +1036,7 @@ LIBGRPC_SRC = \
src/core/ext/filters/server_config_selector/server_config_selector_filter.cc \
src/core/ext/filters/stateful_session/stateful_session_filter.cc \
src/core/ext/filters/stateful_session/stateful_session_service_config_parser.cc \
+ src/core/ext/gcp/metadata_query.cc \
src/core/ext/transport/chttp2/alpn/alpn.cc \
src/core/ext/transport/chttp2/client/chttp2_connector.cc \
src/core/ext/transport/chttp2/server/chttp2_server.cc \
@@ -2987,6 +2988,7 @@ src/core/ext/filters/rbac/rbac_service_config_parser.cc: $(OPENSSL_DEP)
src/core/ext/filters/server_config_selector/server_config_selector_filter.cc: $(OPENSSL_DEP)
src/core/ext/filters/stateful_session/stateful_session_filter.cc: $(OPENSSL_DEP)
src/core/ext/filters/stateful_session/stateful_session_service_config_parser.cc: $(OPENSSL_DEP)
+src/core/ext/gcp/metadata_query.cc: $(OPENSSL_DEP)
src/core/ext/transport/chttp2/alpn/alpn.cc: $(OPENSSL_DEP)
src/core/ext/upb-generated/envoy/admin/v3/certs.upb.c: $(OPENSSL_DEP)
src/core/ext/upb-generated/envoy/admin/v3/clusters.upb.c: $(OPENSSL_DEP)
diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml
index a4d503379a0..ead6d3e1f53 100644
--- a/build_autogenerated.yaml
+++ b/build_autogenerated.yaml
@@ -388,6 +388,7 @@ libs:
- src/core/ext/filters/server_config_selector/server_config_selector_filter.h
- src/core/ext/filters/stateful_session/stateful_session_filter.h
- src/core/ext/filters/stateful_session/stateful_session_service_config_parser.h
+ - src/core/ext/gcp/metadata_query.h
- src/core/ext/transport/chttp2/alpn/alpn.h
- src/core/ext/transport/chttp2/client/chttp2_connector.h
- src/core/ext/transport/chttp2/server/chttp2_server.h
@@ -1175,6 +1176,7 @@ libs:
- src/core/ext/filters/server_config_selector/server_config_selector_filter.cc
- src/core/ext/filters/stateful_session/stateful_session_filter.cc
- src/core/ext/filters/stateful_session/stateful_session_service_config_parser.cc
+ - src/core/ext/gcp/metadata_query.cc
- src/core/ext/transport/chttp2/alpn/alpn.cc
- src/core/ext/transport/chttp2/client/chttp2_connector.cc
- src/core/ext/transport/chttp2/server/chttp2_server.cc
diff --git a/config.m4 b/config.m4
index 98b27744058..75bdab9a2c7 100644
--- a/config.m4
+++ b/config.m4
@@ -117,6 +117,7 @@ if test "$PHP_GRPC" != "no"; then
src/core/ext/filters/server_config_selector/server_config_selector_filter.cc \
src/core/ext/filters/stateful_session/stateful_session_filter.cc \
src/core/ext/filters/stateful_session/stateful_session_service_config_parser.cc \
+ src/core/ext/gcp/metadata_query.cc \
src/core/ext/transport/chttp2/alpn/alpn.cc \
src/core/ext/transport/chttp2/client/chttp2_connector.cc \
src/core/ext/transport/chttp2/server/chttp2_server.cc \
@@ -1305,6 +1306,7 @@ if test "$PHP_GRPC" != "no"; then
PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/filters/rbac)
PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/filters/server_config_selector)
PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/filters/stateful_session)
+ PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/gcp)
PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/transport/chttp2/alpn)
PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/transport/chttp2/client)
PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/transport/chttp2/server)
diff --git a/config.w32 b/config.w32
index 12bf9850cc3..92803b84e70 100644
--- a/config.w32
+++ b/config.w32
@@ -83,6 +83,7 @@ if (PHP_GRPC != "no") {
"src\\core\\ext\\filters\\server_config_selector\\server_config_selector_filter.cc " +
"src\\core\\ext\\filters\\stateful_session\\stateful_session_filter.cc " +
"src\\core\\ext\\filters\\stateful_session\\stateful_session_service_config_parser.cc " +
+ "src\\core\\ext\\gcp\\metadata_query.cc " +
"src\\core\\ext\\transport\\chttp2\\alpn\\alpn.cc " +
"src\\core\\ext\\transport\\chttp2\\client\\chttp2_connector.cc " +
"src\\core\\ext\\transport\\chttp2\\server\\chttp2_server.cc " +
@@ -1303,6 +1304,7 @@ if (PHP_GRPC != "no") {
FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\ext\\filters\\rbac");
FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\ext\\filters\\server_config_selector");
FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\ext\\filters\\stateful_session");
+ FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\ext\\gcp");
FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\ext\\transport");
FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\ext\\transport\\chttp2");
FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\ext\\transport\\chttp2\\alpn");
diff --git a/gRPC-C++.podspec b/gRPC-C++.podspec
index 123435c836c..7ad05b739ec 100644
--- a/gRPC-C++.podspec
+++ b/gRPC-C++.podspec
@@ -301,6 +301,7 @@ Pod::Spec.new do |s|
'src/core/ext/filters/server_config_selector/server_config_selector_filter.h',
'src/core/ext/filters/stateful_session/stateful_session_filter.h',
'src/core/ext/filters/stateful_session/stateful_session_service_config_parser.h',
+ 'src/core/ext/gcp/metadata_query.h',
'src/core/ext/transport/binder/client/binder_connector.cc',
'src/core/ext/transport/binder/client/binder_connector.h',
'src/core/ext/transport/binder/client/channel_create.cc',
@@ -1251,6 +1252,7 @@ Pod::Spec.new do |s|
'src/core/ext/filters/server_config_selector/server_config_selector_filter.h',
'src/core/ext/filters/stateful_session/stateful_session_filter.h',
'src/core/ext/filters/stateful_session/stateful_session_service_config_parser.h',
+ 'src/core/ext/gcp/metadata_query.h',
'src/core/ext/transport/binder/client/binder_connector.h',
'src/core/ext/transport/binder/client/channel_create_impl.h',
'src/core/ext/transport/binder/client/connection_id_generator.h',
diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec
index b9ff4fd4d1d..2b76e3974a0 100644
--- a/gRPC-Core.podspec
+++ b/gRPC-Core.podspec
@@ -345,6 +345,8 @@ Pod::Spec.new do |s|
'src/core/ext/filters/stateful_session/stateful_session_filter.h',
'src/core/ext/filters/stateful_session/stateful_session_service_config_parser.cc',
'src/core/ext/filters/stateful_session/stateful_session_service_config_parser.h',
+ 'src/core/ext/gcp/metadata_query.cc',
+ 'src/core/ext/gcp/metadata_query.h',
'src/core/ext/transport/chttp2/alpn/alpn.cc',
'src/core/ext/transport/chttp2/alpn/alpn.h',
'src/core/ext/transport/chttp2/client/chttp2_connector.cc',
@@ -1956,6 +1958,7 @@ Pod::Spec.new do |s|
'src/core/ext/filters/server_config_selector/server_config_selector_filter.h',
'src/core/ext/filters/stateful_session/stateful_session_filter.h',
'src/core/ext/filters/stateful_session/stateful_session_service_config_parser.h',
+ 'src/core/ext/gcp/metadata_query.h',
'src/core/ext/transport/chttp2/alpn/alpn.h',
'src/core/ext/transport/chttp2/client/chttp2_connector.h',
'src/core/ext/transport/chttp2/server/chttp2_server.h',
diff --git a/grpc.gemspec b/grpc.gemspec
index 9adc8350126..9238063974c 100644
--- a/grpc.gemspec
+++ b/grpc.gemspec
@@ -254,6 +254,8 @@ Gem::Specification.new do |s|
s.files += %w( src/core/ext/filters/stateful_session/stateful_session_filter.h )
s.files += %w( src/core/ext/filters/stateful_session/stateful_session_service_config_parser.cc )
s.files += %w( src/core/ext/filters/stateful_session/stateful_session_service_config_parser.h )
+ s.files += %w( src/core/ext/gcp/metadata_query.cc )
+ s.files += %w( src/core/ext/gcp/metadata_query.h )
s.files += %w( src/core/ext/transport/chttp2/alpn/alpn.cc )
s.files += %w( src/core/ext/transport/chttp2/alpn/alpn.h )
s.files += %w( src/core/ext/transport/chttp2/client/chttp2_connector.cc )
diff --git a/grpc.gyp b/grpc.gyp
index dd13cf0eecf..5ed7ecf0c1d 100644
--- a/grpc.gyp
+++ b/grpc.gyp
@@ -449,6 +449,7 @@
'src/core/ext/filters/server_config_selector/server_config_selector_filter.cc',
'src/core/ext/filters/stateful_session/stateful_session_filter.cc',
'src/core/ext/filters/stateful_session/stateful_session_service_config_parser.cc',
+ 'src/core/ext/gcp/metadata_query.cc',
'src/core/ext/transport/chttp2/alpn/alpn.cc',
'src/core/ext/transport/chttp2/client/chttp2_connector.cc',
'src/core/ext/transport/chttp2/server/chttp2_server.cc',
diff --git a/package.xml b/package.xml
index a6385b21344..7a1a2948ead 100644
--- a/package.xml
+++ b/package.xml
@@ -236,6 +236,8 @@
+
+
diff --git a/src/core/BUILD b/src/core/BUILD
index cfdb023546d..276612691da 100644
--- a/src/core/BUILD
+++ b/src/core/BUILD
@@ -4995,22 +4995,18 @@ grpc_cc_library(
"ext/filters/client_channel/resolver/google_c2p/google_c2p_resolver.cc",
],
external_deps = [
- "absl/status",
"absl/status:statusor",
"absl/strings",
- "absl/strings:str_format",
"absl/types:optional",
],
language = "c++",
deps = [
"channel_args",
- "closure",
"env",
- "error",
+ "gcp_metadata_query",
"grpc_xds_client",
"json",
"resource_quota",
- "status_helper",
"time",
"//:alts_util",
"//:config",
@@ -5018,8 +5014,6 @@ grpc_cc_library(
"//:gpr",
"//:grpc_base",
"//:grpc_resolver",
- "//:grpc_security_base",
- "//:httpcli",
"//:orphanable",
"//:ref_counted_ptr",
"//:uri_parser",
@@ -5321,6 +5315,38 @@ grpc_cc_library(
],
)
+grpc_cc_library(
+ name = "gcp_metadata_query",
+ srcs = [
+ "ext/gcp/metadata_query.cc",
+ ],
+ hdrs = [
+ "ext/gcp/metadata_query.h",
+ ],
+ external_deps = [
+ "absl/functional:any_invocable",
+ "absl/status",
+ "absl/status:statusor",
+ "absl/strings",
+ "absl/strings:str_format",
+ ],
+ deps = [
+ "closure",
+ "error",
+ "status_helper",
+ "time",
+ "//:gpr",
+ "//:gpr_platform",
+ "//:grpc_base",
+ "//:grpc_security_base",
+ "//:grpc_trace",
+ "//:httpcli",
+ "//:orphanable",
+ "//:ref_counted_ptr",
+ "//:uri_parser",
+ ],
+)
+
### UPB Targets
grpc_upb_proto_library(
diff --git a/src/core/ext/filters/client_channel/resolver/google_c2p/google_c2p_resolver.cc b/src/core/ext/filters/client_channel/resolver/google_c2p/google_c2p_resolver.cc
index 9ebc49285f7..234d7568cce 100644
--- a/src/core/ext/filters/client_channel/resolver/google_c2p/google_c2p_resolver.cc
+++ b/src/core/ext/filters/client_channel/resolver/google_c2p/google_c2p_resolver.cc
@@ -16,27 +16,22 @@
#include
-#include
-
#include
-#include
#include
#include
#include
+#include
#include
-#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
-#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "absl/strings/strip.h"
#include "absl/types/optional.h"
-#include
-#include
#include
+#include "src/core/ext/gcp/metadata_query.h"
#include "src/core/ext/xds/xds_bootstrap.h"
#include "src/core/ext/xds/xds_client_grpc.h"
#include "src/core/lib/channel/channel_args.h"
@@ -45,13 +40,8 @@
#include "src/core/lib/gprpp/env.h"
#include "src/core/lib/gprpp/orphanable.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
-#include "src/core/lib/gprpp/status_helper.h"
#include "src/core/lib/gprpp/time.h"
#include "src/core/lib/gprpp/work_serializer.h"
-#include "src/core/lib/http/httpcli.h"
-#include "src/core/lib/http/parser.h"
-#include "src/core/lib/iomgr/closure.h"
-#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/iomgr/polling_entity.h"
#include "src/core/lib/json/json.h"
#include "src/core/lib/resolver/resolver.h"
@@ -59,7 +49,6 @@
#include "src/core/lib/resolver/resolver_registry.h"
#include "src/core/lib/resource_quota/resource_quota.h"
#include "src/core/lib/security/credentials/alts/check_gcp_environment.h"
-#include "src/core/lib/security/credentials/credentials.h"
#include "src/core/lib/uri/uri_parser.h"
namespace grpc_core {
@@ -78,53 +67,6 @@ class GoogleCloud2ProdResolver : public Resolver {
void ShutdownLocked() override;
private:
- // Represents an HTTP request to the metadata server.
- class MetadataQuery : public InternallyRefCounted {
- public:
- MetadataQuery(RefCountedPtr resolver,
- const char* path, grpc_polling_entity* pollent);
- ~MetadataQuery() override;
-
- void Orphan() override;
-
- private:
- static void OnHttpRequestDone(void* arg, grpc_error_handle error);
-
- // If error is not absl::OkStatus(), then it's not safe to look at response.
- virtual void OnDone(GoogleCloud2ProdResolver* resolver,
- const grpc_http_response* response,
- grpc_error_handle error) = 0;
-
- RefCountedPtr resolver_;
- OrphanablePtr http_request_;
- grpc_http_response response_;
- grpc_closure on_done_;
- };
-
- // A metadata server query to get the zone.
- class ZoneQuery : public MetadataQuery {
- public:
- ZoneQuery(RefCountedPtr resolver,
- grpc_polling_entity* pollent);
-
- private:
- void OnDone(GoogleCloud2ProdResolver* resolver,
- const grpc_http_response* response,
- grpc_error_handle error) override;
- };
-
- // A metadata server query to get the IPv6 address.
- class IPv6Query : public MetadataQuery {
- public:
- IPv6Query(RefCountedPtr resolver,
- grpc_polling_entity* pollent);
-
- private:
- void OnDone(GoogleCloud2ProdResolver* resolver,
- const grpc_http_response* response,
- grpc_error_handle error) override;
- };
-
void ZoneQueryDone(std::string zone);
void IPv6QueryDone(bool ipv6_supported);
void StartXdsResolver();
@@ -137,128 +79,13 @@ class GoogleCloud2ProdResolver : public Resolver {
std::string metadata_server_name_ = "metadata.google.internal.";
bool shutdown_ = false;
- OrphanablePtr zone_query_;
+ OrphanablePtr zone_query_;
absl::optional zone_;
- OrphanablePtr ipv6_query_;
+ OrphanablePtr ipv6_query_;
absl::optional supports_ipv6_;
};
-//
-// GoogleCloud2ProdResolver::MetadataQuery
-//
-
-GoogleCloud2ProdResolver::MetadataQuery::MetadataQuery(
- RefCountedPtr resolver, const char* path,
- grpc_polling_entity* pollent)
- : resolver_(std::move(resolver)) {
- // Start HTTP request.
- GRPC_CLOSURE_INIT(&on_done_, OnHttpRequestDone, this, nullptr);
- Ref().release(); // Ref held by callback.
- grpc_http_request request;
- memset(&request, 0, sizeof(grpc_http_request));
- grpc_http_header header = {const_cast("Metadata-Flavor"),
- const_cast("Google")};
- request.hdr_count = 1;
- request.hdrs = &header;
- auto uri = URI::Create("http", resolver_->metadata_server_name_, path,
- {} /* query params */, "" /* fragment */);
- GPR_ASSERT(uri.ok()); // params are hardcoded
- grpc_arg resource_quota_arg = grpc_channel_arg_pointer_create(
- const_cast(GRPC_ARG_RESOURCE_QUOTA),
- resolver_->resource_quota_.get(), grpc_resource_quota_arg_vtable());
- grpc_channel_args args = {1, &resource_quota_arg};
- http_request_ =
- HttpRequest::Get(std::move(*uri), &args, pollent, &request,
- Timestamp::Now() + Duration::Seconds(10), // 10s timeout
- &on_done_, &response_,
- RefCountedPtr(
- grpc_insecure_credentials_create()));
- http_request_->Start();
-}
-
-GoogleCloud2ProdResolver::MetadataQuery::~MetadataQuery() {
- grpc_http_response_destroy(&response_);
-}
-
-void GoogleCloud2ProdResolver::MetadataQuery::Orphan() {
- http_request_.reset();
- Unref();
-}
-
-void GoogleCloud2ProdResolver::MetadataQuery::OnHttpRequestDone(
- void* arg, grpc_error_handle error) {
- auto* self = static_cast(arg);
- // Hop back into WorkSerializer to call OnDone().
- // Note: We implicitly pass our ref to the callback here.
- self->resolver_->work_serializer_->Run(
- [self, error]() {
- self->OnDone(self->resolver_.get(), &self->response_, error);
- self->Unref();
- },
- DEBUG_LOCATION);
-}
-
-//
-// GoogleCloud2ProdResolver::ZoneQuery
-//
-
-GoogleCloud2ProdResolver::ZoneQuery::ZoneQuery(
- RefCountedPtr 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_handle error) {
- absl::StatusOr zone;
- if (!error.ok()) {
- zone = absl::UnknownError(absl::StrCat(
- "error fetching zone from metadata server: ", StatusToString(error)));
- } else if (response->status != 200) {
- zone = absl::UnknownError(absl::StrFormat(
- "zone query received non-200 status: %d", response->status));
- } else {
- absl::string_view body(response->body, response->body_length);
- size_t i = body.find_last_of('/');
- if (i == body.npos) {
- zone = absl::UnknownError(
- absl::StrCat("could not parse zone from metadata server: ", body));
- } else {
- zone = std::string(body.substr(i + 1));
- }
- }
- if (!zone.ok()) {
- gpr_log(GPR_ERROR, "zone query failed: %s",
- zone.status().ToString().c_str());
- resolver->ZoneQueryDone("");
- } else {
- resolver->ZoneQueryDone(std::move(*zone));
- }
-}
-
-//
-// GoogleCloud2ProdResolver::IPv6Query
-//
-
-GoogleCloud2ProdResolver::IPv6Query::IPv6Query(
- RefCountedPtr 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_handle error) {
- if (!error.ok()) {
- gpr_log(GPR_ERROR, "error fetching IPv6 address from metadata server: %s",
- StatusToString(error).c_str());
- }
- resolver->IPv6QueryDone(error.ok() && response->status == 200);
-}
-
//
// GoogleCloud2ProdResolver
//
@@ -322,8 +149,31 @@ void GoogleCloud2ProdResolver::StartLocked() {
return;
}
// Using xDS. Start metadata server queries.
- zone_query_ = MakeOrphanable(Ref(), &pollent_);
- ipv6_query_ = MakeOrphanable(Ref(), &pollent_);
+ zone_query_ = MakeOrphanable(
+ std::string(MetadataQuery::kZoneAttribute), &pollent_,
+ [resolver = static_cast>(Ref())](
+ std::string /* attribute */,
+ absl::StatusOr result) mutable {
+ resolver->work_serializer_->Run(
+ [resolver, result = std::move(result)]() mutable {
+ resolver->ZoneQueryDone(result.ok() ? std::move(result).value()
+ : "");
+ },
+ DEBUG_LOCATION);
+ },
+ Duration::Seconds(10));
+ ipv6_query_ = MakeOrphanable(
+ std::string(MetadataQuery::kIPv6Attribute), &pollent_,
+ [resolver = static_cast>(Ref())](
+ std::string /* attribute */,
+ absl::StatusOr result) mutable {
+ resolver->work_serializer_->Run(
+ [resolver, result = std::move(result)]() {
+ resolver->IPv6QueryDone(result.ok());
+ },
+ DEBUG_LOCATION);
+ },
+ Duration::Seconds(10));
}
void GoogleCloud2ProdResolver::RequestReresolutionLocked() {
diff --git a/src/core/ext/gcp/metadata_query.cc b/src/core/ext/gcp/metadata_query.cc
new file mode 100644
index 00000000000..51dc4cc0bb2
--- /dev/null
+++ b/src/core/ext/gcp/metadata_query.cc
@@ -0,0 +1,132 @@
+//
+//
+// Copyright 2023 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
+
+#include "src/core/ext/gcp/metadata_query.h"
+
+#include
+
+#include
+#include
+#include
+
+#include "absl/status/status.h"
+#include "absl/status/statusor.h"
+#include "absl/strings/str_format.h"
+#include "absl/strings/string_view.h"
+
+#include
+#include
+#include
+
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/gprpp/status_helper.h"
+#include "src/core/lib/gprpp/time.h"
+#include "src/core/lib/security/credentials/credentials.h"
+#include "src/core/lib/uri/uri_parser.h"
+
+namespace grpc_core {
+
+TraceFlag grpc_metadata_query_trace(false, "metadata_query");
+
+constexpr const char MetadataQuery::kZoneAttribute[] =
+ "/computeMetadata/v1/instance/zone";
+constexpr const char MetadataQuery::kClusterNameAttribute[] =
+ "/computeMetadata/v1/instance/attributes/cluster-name";
+constexpr const char MetadataQuery::kRegionAttribute[] =
+ "/computeMetadata/v1/instance/region";
+constexpr const char MetadataQuery::kInstanceIdAttribute[] =
+ "/computeMetadata/v1/instance/id";
+constexpr const char MetadataQuery::kIPv6Attribute[] =
+ "/computeMetadata/v1/instance/network-interfaces/0/ipv6s";
+
+MetadataQuery::MetadataQuery(
+ std::string attribute, grpc_polling_entity* pollent,
+ absl::AnyInvocable /* result */)>
+ callback,
+ Duration timeout)
+ : InternallyRefCounted(nullptr, 2),
+ attribute_(std::move(attribute)),
+ callback_(std::move(callback)) {
+ GRPC_CLOSURE_INIT(&on_done_, OnDone, this, nullptr);
+ auto uri = URI::Create("http", "metadata.google.internal.", attribute_,
+ {} /* query params */, "" /* fragment */);
+ GPR_ASSERT(uri.ok()); // params are hardcoded
+ grpc_http_request request;
+ memset(&request, 0, sizeof(grpc_http_request));
+ grpc_http_header header = {const_cast("Metadata-Flavor"),
+ const_cast("Google")};
+ request.hdr_count = 1;
+ request.hdrs = &header;
+ http_request_ = HttpRequest::Get(
+ std::move(*uri), nullptr /* channel args */, pollent, &request,
+ Timestamp::Now() + timeout, &on_done_, &response_,
+ RefCountedPtr(
+ grpc_insecure_credentials_create()));
+ http_request_->Start();
+}
+
+MetadataQuery::~MetadataQuery() { grpc_http_response_destroy(&response_); }
+
+void MetadataQuery::Orphan() {
+ http_request_.reset();
+ Unref();
+}
+
+void MetadataQuery::OnDone(void* arg, grpc_error_handle error) {
+ auto* self = static_cast(arg);
+ if (GRPC_TRACE_FLAG_ENABLED(grpc_metadata_query_trace)) {
+ gpr_log(GPR_INFO, "MetadataServer Query for %s: HTTP status: %d, error: %s",
+ self->attribute_.c_str(), self->response_.status,
+ StatusToString(error).c_str());
+ }
+ absl::StatusOr result;
+ if (!error.ok()) {
+ result = absl::UnavailableError(absl::StrFormat(
+ "MetadataServer Query failed for %s: %s", self->attribute_.c_str(),
+ StatusToString(error).c_str()));
+ } else if (self->response_.status != 200) {
+ result = absl::UnavailableError(absl::StrFormat(
+ "MetadataServer Query received non-200 status for %s: %s",
+ self->attribute_.c_str(), StatusToString(error).c_str()));
+ } else if (self->attribute_ == kZoneAttribute) {
+ absl::string_view body(self->response_.body, self->response_.body_length);
+ size_t pos = body.find_last_of('/');
+ if (pos == body.npos) {
+ result = absl::UnavailableError(
+ absl::StrFormat("MetadataServer Could not parse zone: %s",
+ std::string(body).c_str()));
+ if (GRPC_TRACE_FLAG_ENABLED(grpc_metadata_query_trace)) {
+ gpr_log(GPR_INFO, "%s", result.status().ToString().c_str());
+ }
+ } else {
+ result = std::string(body.substr(pos + 1));
+ }
+ } else {
+ result = std::string(self->response_.body, self->response_.body_length);
+ }
+ auto callback = std::move(self->callback_);
+ auto attribute = std::move(self->attribute_);
+ self->Unref();
+ return callback(std::move(attribute), std::move(result));
+}
+
+} // namespace grpc_core
diff --git a/src/core/ext/gcp/metadata_query.h b/src/core/ext/gcp/metadata_query.h
new file mode 100644
index 00000000000..e679cfac8f1
--- /dev/null
+++ b/src/core/ext/gcp/metadata_query.h
@@ -0,0 +1,72 @@
+//
+// Copyright 2023 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.
+//
+
+#ifndef GRPC_SRC_CORE_EXT_GCP_METADATA_QUERY_H
+#define GRPC_SRC_CORE_EXT_GCP_METADATA_QUERY_H
+
+#include
+
+#include
+
+#include "absl/functional/any_invocable.h"
+#include "absl/status/statusor.h"
+
+#include "src/core/lib/gprpp/orphanable.h"
+#include "src/core/lib/gprpp/time.h"
+#include "src/core/lib/http/httpcli.h"
+#include "src/core/lib/http/parser.h"
+#include "src/core/lib/iomgr/closure.h"
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/iomgr/polling_entity.h"
+
+namespace grpc_core {
+
+// Fetches the value of an attribute from the MetadataServer on a GCP
+// environment.
+class MetadataQuery : public InternallyRefCounted {
+ public:
+ static const char kZoneAttribute[];
+ static const char kClusterNameAttribute[];
+ static const char kRegionAttribute[];
+ static const char kInstanceIdAttribute[];
+ static const char kIPv6Attribute[];
+
+ MetadataQuery(
+ std::string attribute, grpc_polling_entity* pollent,
+ absl::AnyInvocable /* result */)>
+ callback,
+ Duration timeout);
+
+ ~MetadataQuery() override;
+
+ void Orphan() override;
+
+ private:
+ static void OnDone(void* arg, grpc_error_handle error);
+
+ grpc_closure on_done_;
+ std::string attribute_;
+ absl::AnyInvocable /* result */)>
+ callback_;
+ OrphanablePtr http_request_;
+ grpc_http_response response_;
+};
+
+} // namespace grpc_core
+
+#endif // GRPC_SRC_CORE_EXT_GCP_METADATA_QUERY_H
diff --git a/src/cpp/ext/gcp/BUILD b/src/cpp/ext/gcp/BUILD
index a4203b9ca9c..a94cfed4cc5 100644
--- a/src/cpp/ext/gcp/BUILD
+++ b/src/cpp/ext/gcp/BUILD
@@ -123,3 +123,46 @@ grpc_cc_library(
"//src/cpp/ext/filters/logging:logging_sink",
],
)
+
+grpc_cc_library(
+ name = "environment_autodetect",
+ srcs = [
+ "environment_autodetect.cc",
+ ],
+ hdrs = [
+ "environment_autodetect.h",
+ ],
+ external_deps = [
+ "absl/base:core_headers",
+ "absl/container:flat_hash_map",
+ "absl/functional:any_invocable",
+ "absl/meta:type_traits",
+ "absl/status",
+ "absl/status:statusor",
+ "absl/types:optional",
+ ],
+ language = "c++",
+ visibility = [
+ "//src/cpp/ext/gcp:__subpackages__",
+ "//test:__subpackages__",
+ ],
+ deps = [
+ "//:exec_ctx",
+ "//:gpr",
+ "//:gpr_platform",
+ "//:grpc++",
+ "//:grpc_base",
+ "//:grpc_trace",
+ "//:orphanable",
+ "//src/core:closure",
+ "//src/core:default_event_engine",
+ "//src/core:env",
+ "//src/core:error",
+ "//src/core:gcp_metadata_query",
+ "//src/core:iomgr_fwd",
+ "//src/core:load_file",
+ "//src/core:slice",
+ "//src/core:status_helper",
+ "//src/core:time",
+ ],
+)
diff --git a/src/cpp/ext/gcp/environment_autodetect.cc b/src/cpp/ext/gcp/environment_autodetect.cc
new file mode 100644
index 00000000000..f641331f801
--- /dev/null
+++ b/src/cpp/ext/gcp/environment_autodetect.cc
@@ -0,0 +1,368 @@
+//
+//
+// Copyright 2023 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
+
+#include "src/cpp/ext/gcp/environment_autodetect.h"
+
+#include
+#include
+#include
+
+#include "absl/container/flat_hash_map.h"
+#include "absl/meta/type_traits.h"
+#include "absl/status/status.h"
+#include "absl/status/statusor.h"
+#include "absl/types/optional.h"
+
+#include
+#include
+#include
+#include
+
+#include "src/core/ext/gcp/metadata_query.h"
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/event_engine/default_event_engine.h"
+#include "src/core/lib/gprpp/crash.h"
+#include "src/core/lib/gprpp/env.h"
+#include "src/core/lib/gprpp/load_file.h"
+#include "src/core/lib/gprpp/orphanable.h"
+#include "src/core/lib/gprpp/status_helper.h"
+#include "src/core/lib/gprpp/time.h"
+#include "src/core/lib/iomgr/closure.h"
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/iomgr/iomgr_fwd.h"
+#include "src/core/lib/iomgr/polling_entity.h"
+#include "src/core/lib/iomgr/pollset.h"
+#include "src/core/lib/slice/slice.h"
+
+namespace grpc {
+namespace internal {
+
+namespace {
+
+grpc_core::TraceFlag grpc_environment_autodetect_trace(
+ false, "environment_autodetect");
+
+// This is not a definite method to get the namespace name for GKE, but it is
+// the best we have.
+std::string GetNamespaceName() {
+ // Read the root file.
+ const char* filename =
+ "/var/run/secrets/kubernetes.io/serviceaccount/namespace";
+ auto namespace_name = grpc_core::LoadFile(filename, false);
+ if (!namespace_name.ok()) {
+ if (GRPC_TRACE_FLAG_ENABLED(grpc_environment_autodetect_trace)) {
+ gpr_log(GPR_DEBUG, "Reading file %s failed: %s", filename,
+ grpc_core::StatusToString(namespace_name.status()).c_str());
+ }
+ // Fallback on an environment variable
+ return grpc_core::GetEnv("NAMESPACE_NAME").value_or("");
+ }
+ return std::string(reinterpret_cast((*namespace_name).begin()),
+ (*namespace_name).length());
+}
+
+// Get pod name for GKE
+std::string GetPodName() {
+ auto pod_name = grpc_core::GetEnv("POD_NAME");
+ if (pod_name.has_value()) {
+ return pod_name.value();
+ }
+ return grpc_core::GetEnv("HOSTNAME").value_or("");
+}
+
+// Get container name for GKE
+std::string GetContainerName() {
+ return grpc_core::GetEnv("HOSTNAME").value_or("");
+}
+
+// Get function name for Cloud Functions
+std::string GetFunctionName() {
+ auto k_service = grpc_core::GetEnv("K_SERVICE");
+ if (k_service.has_value()) {
+ return k_service.value();
+ }
+ return grpc_core::GetEnv("FUNCTION_NAME").value_or("");
+}
+
+// Get revision name for Cloud run
+std::string GetRevisionName() {
+ return grpc_core::GetEnv("K_REVISION").value_or("");
+}
+
+// Get service name for Cloud run
+std::string GetServiceName() {
+ return grpc_core::GetEnv("K_SERVICE").value_or("");
+}
+
+// Get configuration name for Cloud run
+std::string GetConfiguratioName() {
+ return grpc_core::GetEnv("K_CONFIGURATION").value_or("");
+}
+
+// Get module ID for App Engine
+std::string GetModuleId() {
+ return grpc_core::GetEnv("GAE_SERVICE").value_or("");
+}
+
+// Get version ID for App Engine
+std::string GetVersionId() {
+ return grpc_core::GetEnv("GAE_VERSION").value_or("");
+}
+
+// Fire and forget class
+class EnvironmentAutoDetectHelper
+ : public grpc_core::InternallyRefCounted,
+ private internal::GrpcLibrary {
+ public:
+ EnvironmentAutoDetectHelper(
+ std::string project_id,
+ absl::AnyInvocable on_done,
+ std::shared_ptr
+ event_engine)
+ : InternallyRefCounted(/*trace=*/nullptr, /*initial_refcount=*/2),
+ project_id_(std::move(project_id)),
+ on_done_(std::move(on_done)),
+ event_engine_(std::move(event_engine)) {
+ grpc_core::ExecCtx exec_ctx;
+ // TODO(yashykt): The pollset stuff should go away once the HTTP library is
+ // ported over to use EventEngine.
+ pollset_ = static_cast(gpr_zalloc(grpc_pollset_size()));
+ grpc_pollset_init(pollset_, &mu_poll_);
+ pollent_ = grpc_polling_entity_create_from_pollset(pollset_);
+ // TODO(yashykt): Note that using EventEngine::Run is not fork-safe. If we
+ // want to make this fork-safe, we might need some re-work here.
+ event_engine_->Run([this] { PollLoop(); });
+ AutoDetect();
+ }
+
+ ~EnvironmentAutoDetectHelper() override {
+ grpc_core::ExecCtx exec_ctx;
+ grpc_pollset_shutdown(
+ pollset_, GRPC_CLOSURE_CREATE(
+ [](void* arg, absl::Status /* status */) {
+ grpc_pollset_destroy(static_cast(arg));
+ gpr_free(arg);
+ },
+ pollset_, nullptr));
+ }
+
+ void Orphan() override {
+ grpc_core::Crash("Illegal Orphan() call on EnvironmentAutoDetectHelper.");
+ }
+
+ private:
+ struct Attribute {
+ std::string resource_attribute;
+ std::string metadata_server_atttribute;
+ };
+
+ void PollLoop() {
+ grpc_core::ExecCtx exec_ctx;
+ bool done = false;
+ gpr_mu_lock(mu_poll_);
+ grpc_pollset_worker* worker = nullptr;
+ if (!GRPC_LOG_IF_ERROR(
+ "pollset_work",
+ grpc_pollset_work(grpc_polling_entity_pollset(&pollent_), &worker,
+ grpc_core::Timestamp::InfPast()))) {
+ notify_poller_ = true;
+ }
+ done = notify_poller_;
+ gpr_mu_unlock(mu_poll_);
+ if (!done) {
+ event_engine_->RunAfter(grpc_core::Duration::Milliseconds(100),
+ [this] { PollLoop(); });
+ } else {
+ Unref();
+ }
+ }
+
+ void AutoDetect() {
+ grpc_core::MutexLock lock(&mu_);
+ // GKE
+ resource_.labels.emplace("project_id", project_id_);
+ if (grpc_core::GetEnv("KUBERNETES_SERVICE_HOST").has_value()) {
+ resource_.resource_type = "k8s_container";
+ resource_.labels.emplace("namespace_name", GetNamespaceName());
+ resource_.labels.emplace("pod_name", GetPodName());
+ resource_.labels.emplace("container_name", GetContainerName());
+ attributes_to_fetch_.emplace(grpc_core::MetadataQuery::kZoneAttribute,
+ "location");
+ attributes_to_fetch_.emplace(
+ grpc_core::MetadataQuery::kClusterNameAttribute, "cluster_name");
+ }
+ // Cloud Functions
+ else if (grpc_core::GetEnv("FUNCTION_NAME").has_value() ||
+ grpc_core::GetEnv("FUNCTION_TARGET").has_value()) {
+ resource_.resource_type = "cloud_function";
+ resource_.labels.emplace("function_name", GetFunctionName());
+ attributes_to_fetch_.emplace(grpc_core::MetadataQuery::kRegionAttribute,
+ "region");
+ }
+ // Cloud Run
+ else if (grpc_core::GetEnv("K_CONFIGURATION").has_value()) {
+ resource_.resource_type = "cloud_run_revision";
+ resource_.labels.emplace("revision_name", GetRevisionName());
+ resource_.labels.emplace("service_name", GetServiceName());
+ resource_.labels.emplace("configuration_name", GetConfiguratioName());
+ attributes_to_fetch_.emplace(grpc_core::MetadataQuery::kRegionAttribute,
+ "location");
+ }
+ // App Engine
+ else if (grpc_core::GetEnv("GAE_SERVICE").has_value()) {
+ resource_.resource_type = "gae_app";
+ resource_.labels.emplace("module_id", GetModuleId());
+ resource_.labels.emplace("version_id", GetVersionId());
+ attributes_to_fetch_.emplace(grpc_core::MetadataQuery::kZoneAttribute,
+ "zone");
+ }
+ // Assume GCE
+ else {
+ assuming_gce_ = true;
+ resource_.resource_type = "gce_instance";
+ attributes_to_fetch_.emplace(
+ grpc_core::MetadataQuery::kInstanceIdAttribute, "instance_id");
+ attributes_to_fetch_.emplace(grpc_core::MetadataQuery::kZoneAttribute,
+ "zone");
+ }
+ FetchMetadataServerAttributesAsynchronouslyLocked();
+ }
+
+ void FetchMetadataServerAttributesAsynchronouslyLocked()
+ ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_) {
+ GPR_ASSERT(!attributes_to_fetch_.empty());
+ for (auto& element : attributes_to_fetch_) {
+ queries_.push_back(grpc_core::MakeOrphanable(
+ element.first, &pollent_,
+ [this](std::string attribute, absl::StatusOr result) {
+ if (GRPC_TRACE_FLAG_ENABLED(grpc_environment_autodetect_trace)) {
+ gpr_log(
+ GPR_INFO,
+ "Environment AutoDetect: Attribute: \"%s\" Result: \"%s\"",
+ attribute.c_str(),
+ result.ok()
+ ? result.value().c_str()
+ : grpc_core::StatusToString(result.status()).c_str());
+ }
+ absl::optional resource;
+ {
+ grpc_core::MutexLock lock(&mu_);
+ auto it = attributes_to_fetch_.find(attribute);
+ if (it != attributes_to_fetch_.end()) {
+ if (result.ok()) {
+ resource_.labels.emplace(std::move(it->second),
+ std::move(result).value());
+ }
+ // If fetching from the MetadataServer failed and we were
+ // assuming a GCE environment, fallback to "global".
+ else if (assuming_gce_) {
+ assuming_gce_ = false;
+ resource_.resource_type = "global";
+ }
+ attributes_to_fetch_.erase(it);
+ } else {
+ // This should not happen
+ gpr_log(GPR_ERROR,
+ "An unexpected attribute was seen from the "
+ "MetadataServer: %s",
+ attribute.c_str());
+ }
+ if (attributes_to_fetch_.empty()) {
+ resource = std::move(resource_);
+ }
+ }
+ if (resource.has_value()) {
+ gpr_mu_lock(mu_poll_);
+ notify_poller_ = true;
+ gpr_mu_unlock(mu_poll_);
+ auto on_done = std::move(on_done_);
+ Unref();
+ on_done(std::move(resource).value());
+ }
+ },
+ grpc_core::Duration::Seconds(1)));
+ }
+ }
+
+ const std::string project_id_;
+ grpc_pollset* pollset_ = nullptr;
+ grpc_polling_entity pollent_;
+ gpr_mu* mu_poll_ = nullptr;
+ absl::AnyInvocable on_done_;
+ std::shared_ptr event_engine_;
+ grpc_core::Mutex mu_;
+ bool notify_poller_ = false;
+ absl::flat_hash_map
+ attributes_to_fetch_ ABSL_GUARDED_BY(mu_);
+ std::vector> queries_
+ ABSL_GUARDED_BY(mu_);
+ EnvironmentAutoDetect::ResourceType resource_ ABSL_GUARDED_BY(mu_);
+ // This would be true if we are assuming the resource to be GCE. In this case,
+ // there is a chance that it will fail and we should instead just use
+ // "global".
+ bool assuming_gce_ ABSL_GUARDED_BY(mu_) = false;
+};
+
+EnvironmentAutoDetect* g_autodetect = nullptr;
+
+} // namespace
+
+void EnvironmentAutoDetect::Create(std::string project_id) {
+ GPR_ASSERT(g_autodetect == nullptr && !project_id.empty());
+ g_autodetect = new EnvironmentAutoDetect(project_id);
+}
+
+EnvironmentAutoDetect& EnvironmentAutoDetect::Get() { return *g_autodetect; }
+
+EnvironmentAutoDetect::EnvironmentAutoDetect(std::string project_id)
+ : project_id_(std::move(project_id)),
+ event_engine_(grpc_event_engine::experimental::GetDefaultEventEngine()) {
+ GPR_ASSERT(!project_id_.empty());
+ new EnvironmentAutoDetectHelper(
+ project_id_,
+ [this](EnvironmentAutoDetect::ResourceType resource) {
+ std::vector> callbacks;
+ {
+ grpc_core::MutexLock lock(&mu_);
+ resource_ = std::make_unique(
+ std::move(resource));
+ callbacks = std::move(callbacks_);
+ }
+ for (auto& callback : callbacks) {
+ callback();
+ }
+ },
+ event_engine_);
+}
+
+void EnvironmentAutoDetect::NotifyOnDone(absl::AnyInvocable callback) {
+ grpc_core::MutexLock lock(&mu_);
+ // Environment has already been detected
+ if (resource_ != nullptr) {
+ // Execute on the event engine to avoid deadlocks.
+ return event_engine_->Run(std::move(callback));
+ }
+ callbacks_.push_back(std::move(callback));
+}
+
+} // namespace internal
+} // namespace grpc
diff --git a/src/cpp/ext/gcp/environment_autodetect.h b/src/cpp/ext/gcp/environment_autodetect.h
new file mode 100644
index 00000000000..3dcfd81ff48
--- /dev/null
+++ b/src/cpp/ext/gcp/environment_autodetect.h
@@ -0,0 +1,83 @@
+//
+// Copyright 2023 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.
+//
+
+#ifndef GRPC_SRC_CPP_EXT_GCP_ENVIRONMENT_AUTODETECT_H
+#define GRPC_SRC_CPP_EXT_GCP_ENVIRONMENT_AUTODETECT_H
+
+#include
+
+#include