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 +#include +#include +#include + +#include "absl/base/thread_annotations.h" +#include "absl/functional/any_invocable.h" +#include "absl/status/status.h" + +#include + +#include "src/core/lib/gprpp/sync.h" + +namespace grpc { + +namespace experimental { +// Forward declaration for GcpObservabilityInit +absl::Status GcpObservabilityInit(); +} // namespace experimental + +namespace internal { + +class EnvironmentAutoDetect { + public: + struct ResourceType { + // For example, "gce_instance", "gke_container", etc. + std::string resource_type; + // Values for all the labels listed in the associated resource type. + std::map labels; + }; + + EnvironmentAutoDetect& Get(); + + // Exposed for testing purposes only + explicit EnvironmentAutoDetect(std::string project_id); + + // \a callback will be invoked once the environment is done being detected. + void NotifyOnDone(absl::AnyInvocable callback); + + const ResourceType* resource() { + grpc_core::MutexLock lock(&mu_); + return resource_.get(); + } + + private: + friend absl::Status grpc::experimental::GcpObservabilityInit(); + + // GcpObservabilityInit() is responsible for setting up the singleton with the + // project_id. + void Create(std::string project_id); + + const std::string project_id_; + std::shared_ptr event_engine_; + grpc_core::Mutex mu_; + std::unique_ptr resource_ ABSL_GUARDED_BY(mu_); + std::vector> callbacks_ ABSL_GUARDED_BY(mu_); +}; + +} // namespace internal +} // namespace grpc + +#endif // GRPC_SRC_CPP_EXT_GCP_ENVIRONMENT_AUTODETECT_H diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py index 6f474ac2029..bfb3834fa52 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -92,6 +92,7 @@ CORE_SOURCE_FILES = [ '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/test/cpp/ext/gcp/BUILD b/test/cpp/ext/gcp/BUILD index 77ebac5785f..4dce17e2fb6 100644 --- a/test/cpp/ext/gcp/BUILD +++ b/test/cpp/ext/gcp/BUILD @@ -65,3 +65,21 @@ grpc_cc_test( "//test/cpp/util:test_util", ], ) + +grpc_cc_test( + name = "environment_autodetect_test", + srcs = [ + "environment_autodetect_test.cc", + ], + external_deps = [ + "gtest", + ], + language = "C++", + linkstatic = True, + deps = [ + "//src/cpp/ext/gcp:environment_autodetect", + "//test/core/util:grpc_test_util", + "//test/cpp/util:test_config", + "//test/cpp/util:test_util", + ], +) diff --git a/test/cpp/ext/gcp/environment_autodetect_test.cc b/test/cpp/ext/gcp/environment_autodetect_test.cc new file mode 100644 index 00000000000..188b5638938 --- /dev/null +++ b/test/cpp/ext/gcp/environment_autodetect_test.cc @@ -0,0 +1,146 @@ +// +// +// 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 "src/cpp/ext/gcp/environment_autodetect.h" + +#include +#include // NOLINT +#include + +#include "absl/strings/string_view.h" +#include "absl/synchronization/notification.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include + +#include "src/core/lib/gprpp/env.h" +#include "src/core/lib/gprpp/notification.h" +#include "test/core/util/test_config.h" + +namespace grpc { +namespace testing { + +namespace { + +class EnvironmentAutoDetectTest : public ::testing::Test { + protected: + void GetNotifiedOnEnvironmentDetection( + grpc::internal::EnvironmentAutoDetect* env, + grpc_core::Notification* notify) { + env->NotifyOnDone([notify]() { notify->Notify(); }); + } +}; + +// TODO(yashykt): We could create a mock MetadataServer to test this more end to +// end, but given that that should be covered by our integration testing so +// deferring to that. + +TEST_F(EnvironmentAutoDetectTest, Basic) { + grpc::internal::EnvironmentAutoDetect env("project"); + + grpc_core::Notification notify; + GetNotifiedOnEnvironmentDetection(&env, ¬ify); + notify.WaitForNotification(); + + // Unless we test in a specific GCP resource, we should get "global" here. + // EXPECT_EQ(env.resource()->resource_type, "global"); + EXPECT_EQ((env.resource()->labels).at("project_id"), "project"); +} + +TEST_F(EnvironmentAutoDetectTest, GkeEnvironment) { + grpc_core::SetEnv("KUBERNETES_SERVICE_HOST", "k8s_service_host"); + grpc::internal::EnvironmentAutoDetect env("project"); + + grpc_core::Notification notify; + GetNotifiedOnEnvironmentDetection(&env, ¬ify); + notify.WaitForNotification(); + + EXPECT_EQ(env.resource()->resource_type, "k8s_container"); + EXPECT_EQ((env.resource()->labels).at("project_id"), "project"); + grpc_core::UnsetEnv("KUBERNETES_SERVICE_HOST"); +} + +TEST_F(EnvironmentAutoDetectTest, CloudFunctions) { + grpc_core::SetEnv("FUNCTION_NAME", "function_name"); + grpc::internal::EnvironmentAutoDetect env("project"); + + grpc_core::Notification notify; + GetNotifiedOnEnvironmentDetection(&env, ¬ify); + notify.WaitForNotification(); + + EXPECT_EQ(env.resource()->resource_type, "cloud_function"); + EXPECT_EQ((env.resource()->labels).at("project_id"), "project"); + grpc_core::UnsetEnv("FUNCTION_NAME"); +} + +TEST_F(EnvironmentAutoDetectTest, CloudRun) { + grpc_core::SetEnv("K_CONFIGURATION", "config"); + grpc::internal::EnvironmentAutoDetect env("project"); + + grpc_core::Notification notify; + GetNotifiedOnEnvironmentDetection(&env, ¬ify); + notify.WaitForNotification(); + + EXPECT_EQ(env.resource()->resource_type, "cloud_run_revision"); + EXPECT_EQ((env.resource()->labels).at("project_id"), "project"); + grpc_core::UnsetEnv("K_CONFIGURATION"); +} + +TEST_F(EnvironmentAutoDetectTest, AppEngine) { + grpc_core::SetEnv("K_CONFIGURATION", "config"); + grpc::internal::EnvironmentAutoDetect env("project"); + + grpc_core::Notification notify; + GetNotifiedOnEnvironmentDetection(&env, ¬ify); + notify.WaitForNotification(); + + EXPECT_EQ(env.resource()->resource_type, "cloud_run_revision"); + EXPECT_EQ((env.resource()->labels).at("project_id"), "project"); + grpc_core::UnsetEnv("K_CONFIGURATION"); +} + +TEST_F(EnvironmentAutoDetectTest, MultipleNotifyWaiters) { + grpc::internal::EnvironmentAutoDetect env("project"); + + grpc_core::Notification notify[10]; + for (int i = 0; i < 10; ++i) { + GetNotifiedOnEnvironmentDetection(&env, ¬ify[i]); + } + for (int i = 0; i < 10; ++i) { + notify[i].WaitForNotification(); + } + + // Unless we test in a specific GCP resource, we should get "global" here. + // EXPECT_EQ(env.resource()->resource_type, "global"); + EXPECT_EQ((env.resource()->labels).at("project_id"), "project"); +} + +} // namespace + +} // namespace testing +} // namespace grpc + +int main(int argc, char** argv) { + grpc::testing::TestEnvironment env(&argc, argv); + ::testing::InitGoogleTest(&argc, argv); + grpc_init(); + int ret_val = RUN_ALL_TESTS(); + grpc_shutdown(); + return ret_val; +} diff --git a/tools/distrib/fix_build_deps.py b/tools/distrib/fix_build_deps.py index ec7e310684a..5e1fb4703a6 100755 --- a/tools/distrib/fix_build_deps.py +++ b/tools/distrib/fix_build_deps.py @@ -287,7 +287,10 @@ def _get_filename(name, parsing_path): (parsing_path + '/' if (parsing_path and not name.startswith('//')) else ''), name) filename = filename.replace('//:', '') - return filename.replace('//src/core:', 'src/core/') + filename = filename.replace('//src/core:', 'src/core/') + filename = filename.replace('//src/cpp/ext/filters/census:', + 'src/cpp/ext/filters/census/') + return filename def grpc_cc_library(name, diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal index 00b8aec67f0..62d6d82ed80 100644 --- a/tools/doxygen/Doxyfile.c++.internal +++ b/tools/doxygen/Doxyfile.c++.internal @@ -1211,6 +1211,8 @@ src/core/ext/filters/stateful_session/stateful_session_filter.cc \ 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/binder/client/binder_connector.cc \ src/core/ext/transport/binder/client/binder_connector.h \ src/core/ext/transport/binder/client/channel_create.cc \ diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal index 9f561b23be5..676f971734e 100644 --- a/tools/doxygen/Doxyfile.core.internal +++ b/tools/doxygen/Doxyfile.core.internal @@ -1021,6 +1021,8 @@ src/core/ext/filters/stateful_session/stateful_session_filter.cc \ 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/README.md \ src/core/ext/transport/binder/README.md \ src/core/ext/transport/chttp2/README.md \