GCP Observability : Framework for detecting the environment (#32294)

This code is not plumbed through yet, but it provides the core
infrastructure needed to detect the proper GCP environment resources
needed to set up the labels/attributes/resources for stats, tracing and
logging.

Details on how the various environment resources are setup has been
derived by looking at java's cloud logging library and OpenTelemetry's
future plans. (Could be better explained in an offline review since some
links are internal).

Requesting @veblush for a full review and @markdroth for a structural
review.

<!--

If you know who should review your pull request, please assign it to
that
person, otherwise the pull request would get assigned randomly.

If your pull request is for a specific language, please add the
appropriate
lang label.

-->
pull/32426/head
Yash Tibrewal 2 years ago committed by GitHub
parent 3013c7e9b3
commit 04e3a8e73d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CMakeLists.txt
  2. 2
      Makefile
  3. 2
      build_autogenerated.yaml
  4. 2
      config.m4
  5. 2
      config.w32
  6. 2
      gRPC-C++.podspec
  7. 3
      gRPC-Core.podspec
  8. 2
      grpc.gemspec
  9. 1
      grpc.gyp
  10. 2
      package.xml
  11. 40
      src/core/BUILD
  12. 208
      src/core/ext/filters/client_channel/resolver/google_c2p/google_c2p_resolver.cc
  13. 132
      src/core/ext/gcp/metadata_query.cc
  14. 72
      src/core/ext/gcp/metadata_query.h
  15. 43
      src/cpp/ext/gcp/BUILD
  16. 368
      src/cpp/ext/gcp/environment_autodetect.cc
  17. 83
      src/cpp/ext/gcp/environment_autodetect.h
  18. 1
      src/python/grpcio/grpc_core_dependencies.py
  19. 18
      test/cpp/ext/gcp/BUILD
  20. 146
      test/cpp/ext/gcp/environment_autodetect_test.cc
  21. 5
      tools/distrib/fix_build_deps.py
  22. 2
      tools/doxygen/Doxyfile.c++.internal
  23. 2
      tools/doxygen/Doxyfile.core.internal

1
CMakeLists.txt generated

@ -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

2
Makefile generated

@ -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)

@ -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

2
config.m4 generated

@ -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)

2
config.w32 generated

@ -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");

2
gRPC-C++.podspec generated

@ -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',

3
gRPC-Core.podspec generated

@ -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',

2
grpc.gemspec generated

@ -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 )

1
grpc.gyp generated

@ -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',

2
package.xml generated

@ -236,6 +236,8 @@
<file baseinstalldir="/" name="src/core/ext/filters/stateful_session/stateful_session_filter.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/stateful_session/stateful_session_service_config_parser.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/stateful_session/stateful_session_service_config_parser.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/gcp/metadata_query.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/gcp/metadata_query.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/alpn/alpn.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/alpn/alpn.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/transport/chttp2/client/chttp2_connector.cc" role="src" />

@ -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(

@ -16,27 +16,22 @@
#include <grpc/support/port_platform.h>
#include <string.h>
#include <cstdint>
#include <initializer_list>
#include <memory>
#include <random>
#include <string>
#include <type_traits>
#include <utility>
#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 <grpc/grpc.h>
#include <grpc/grpc_security.h>
#include <grpc/support/log.h>
#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<MetadataQuery> {
public:
MetadataQuery(RefCountedPtr<GoogleCloud2ProdResolver> 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<GoogleCloud2ProdResolver> resolver_;
OrphanablePtr<HttpRequest> 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<GoogleCloud2ProdResolver> 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<GoogleCloud2ProdResolver> 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<ZoneQuery> zone_query_;
OrphanablePtr<MetadataQuery> zone_query_;
absl::optional<std::string> zone_;
OrphanablePtr<IPv6Query> ipv6_query_;
OrphanablePtr<MetadataQuery> ipv6_query_;
absl::optional<bool> supports_ipv6_;
};
//
// GoogleCloud2ProdResolver::MetadataQuery
//
GoogleCloud2ProdResolver::MetadataQuery::MetadataQuery(
RefCountedPtr<GoogleCloud2ProdResolver> 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<char*>("Metadata-Flavor"),
const_cast<char*>("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<char*>(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_channel_credentials>(
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<MetadataQuery*>(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<GoogleCloud2ProdResolver> 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<std::string> 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<GoogleCloud2ProdResolver> 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<ZoneQuery>(Ref(), &pollent_);
ipv6_query_ = MakeOrphanable<IPv6Query>(Ref(), &pollent_);
zone_query_ = MakeOrphanable<MetadataQuery>(
std::string(MetadataQuery::kZoneAttribute), &pollent_,
[resolver = static_cast<RefCountedPtr<GoogleCloud2ProdResolver>>(Ref())](
std::string /* attribute */,
absl::StatusOr<std::string> 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<MetadataQuery>(
std::string(MetadataQuery::kIPv6Attribute), &pollent_,
[resolver = static_cast<RefCountedPtr<GoogleCloud2ProdResolver>>(Ref())](
std::string /* attribute */,
absl::StatusOr<std::string> result) mutable {
resolver->work_serializer_->Run(
[resolver, result = std::move(result)]() {
resolver->IPv6QueryDone(result.ok());
},
DEBUG_LOCATION);
},
Duration::Seconds(10));
}
void GoogleCloud2ProdResolver::RequestReresolutionLocked() {

@ -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 <grpc/support/port_platform.h>
#include "src/core/ext/gcp/metadata_query.h"
#include <string.h>
#include <initializer_list>
#include <memory>
#include <utility>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include <grpc/grpc.h>
#include <grpc/grpc_security.h>
#include <grpc/support/log.h>
#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<void(std::string /* attribute */,
absl::StatusOr<std::string> /* result */)>
callback,
Duration timeout)
: InternallyRefCounted<MetadataQuery>(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<char*>("Metadata-Flavor"),
const_cast<char*>("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_channel_credentials>(
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<MetadataQuery*>(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<std::string> 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

@ -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 <grpc/support/port_platform.h>
#include <string>
#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<MetadataQuery> {
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<void(std::string /* attribute */,
absl::StatusOr<std::string> /* 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<void(std::string /* attribute */,
absl::StatusOr<std::string> /* result */)>
callback_;
OrphanablePtr<HttpRequest> http_request_;
grpc_http_response response_;
};
} // namespace grpc_core
#endif // GRPC_SRC_CORE_EXT_GCP_METADATA_QUERY_H

@ -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",
],
)

@ -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 <grpc/support/port_platform.h>
#include "src/cpp/ext/gcp/environment_autodetect.h"
#include <algorithm>
#include <memory>
#include <utility>
#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 <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/sync.h>
#include <grpcpp/impl/grpc_library.h>
#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<const char*>((*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<EnvironmentAutoDetectHelper>,
private internal::GrpcLibrary {
public:
EnvironmentAutoDetectHelper(
std::string project_id,
absl::AnyInvocable<void(EnvironmentAutoDetect::ResourceType)> on_done,
std::shared_ptr<grpc_event_engine::experimental::EventEngine>
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<grpc_pollset*>(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<grpc_pollset*>(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<grpc_core::MetadataQuery>(
element.first, &pollent_,
[this](std::string attribute, absl::StatusOr<std::string> 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<EnvironmentAutoDetect::ResourceType> 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<void(EnvironmentAutoDetect::ResourceType)> on_done_;
std::shared_ptr<grpc_event_engine::experimental::EventEngine> event_engine_;
grpc_core::Mutex mu_;
bool notify_poller_ = false;
absl::flat_hash_map<std::string /* metadata_server_attribute */,
std::string /* resource_attribute */>
attributes_to_fetch_ ABSL_GUARDED_BY(mu_);
std::vector<grpc_core::OrphanablePtr<grpc_core::MetadataQuery>> 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<absl::AnyInvocable<void()>> callbacks;
{
grpc_core::MutexLock lock(&mu_);
resource_ = std::make_unique<EnvironmentAutoDetect::ResourceType>(
std::move(resource));
callbacks = std::move(callbacks_);
}
for (auto& callback : callbacks) {
callback();
}
},
event_engine_);
}
void EnvironmentAutoDetect::NotifyOnDone(absl::AnyInvocable<void()> 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

@ -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 <grpc/support/port_platform.h>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "absl/base/thread_annotations.h"
#include "absl/functional/any_invocable.h"
#include "absl/status/status.h"
#include <grpc/event_engine/event_engine.h>
#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<std::string, std::string> 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<void()> 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<grpc_event_engine::experimental::EventEngine> event_engine_;
grpc_core::Mutex mu_;
std::unique_ptr<ResourceType> resource_ ABSL_GUARDED_BY(mu_);
std::vector<absl::AnyInvocable<void()>> callbacks_ ABSL_GUARDED_BY(mu_);
};
} // namespace internal
} // namespace grpc
#endif // GRPC_SRC_CPP_EXT_GCP_ENVIRONMENT_AUTODETECT_H

@ -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',

@ -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",
],
)

@ -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 <string>
#include <thread> // NOLINT
#include <vector>
#include "absl/strings/string_view.h"
#include "absl/synchronization/notification.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <grpc/grpc.h>
#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, &notify);
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, &notify);
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, &notify);
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, &notify);
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, &notify);
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, &notify[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;
}

@ -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,

@ -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 \

@ -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 \

Loading…
Cancel
Save