diff --git a/BUILD b/BUILD index ba6e32c491e3a..0630650ea647e 100644 --- a/BUILD +++ b/BUILD @@ -4358,7 +4358,6 @@ grpc_cc_library( "grpc_core_credentials_header", "grpc_credentials_util", "grpc_security_base", - "//src/core:default_event_engine", "grpc_trace", "httpcli", "iomgr", @@ -4368,6 +4367,7 @@ grpc_cc_library( "uri", "//src/core:arena_promise", "//src/core:closure", + "//src/core:default_event_engine", "//src/core:error", "//src/core:gpr_manual_constructor", "//src/core:grpc_check", diff --git a/src/core/BUILD b/src/core/BUILD index 5ddae4080567d..ce63d98043880 100644 --- a/src/core/BUILD +++ b/src/core/BUILD @@ -5077,9 +5077,9 @@ grpc_cc_library( "json", "json_reader", "load_file", + "map", "metadata_batch", "ref_counted", - "map", "regional_access_boundary_fetcher", "slice", "slice_refcount", @@ -5096,12 +5096,12 @@ grpc_cc_library( "//:grpc_trace", "//:httpcli", "//:iomgr", - "//src/core:default_event_engine", "//:orphanable", "//:promise", "//:ref_counted_ptr", "//:transport_auth_context", "//:uri", + "//src/core:default_event_engine", ], ) @@ -11373,7 +11373,6 @@ grpc_cc_library( deps = [ "closure", "dual_ref_counted", - "//src/core:default_event_engine", "env", "error", "grpc_check", @@ -11394,6 +11393,7 @@ grpc_cc_library( "//:ref_counted_ptr", "//:transport_auth_context", "//:uri", + "//src/core:default_event_engine", ], ) diff --git a/src/core/credentials/call/external/external_account_credentials.cc b/src/core/credentials/call/external/external_account_credentials.cc index 30eae586febe3..c6e838c6d5b97 100644 --- a/src/core/credentials/call/external/external_account_credentials.cc +++ b/src/core/credentials/call/external/external_account_credentials.cc @@ -35,6 +35,7 @@ #include "src/core/credentials/call/json_util.h" #include "src/core/credentials/call/regional_access_boundary_fetcher.h" #include "src/core/credentials/transport/transport_credentials.h" +#include "src/core/lib/promise/map.h" #include "src/core/lib/transport/status_conversion.h" #include "src/core/util/grpc_check.h" #include "src/core/util/http_client/httpcli_ssl_credentials.h" @@ -46,7 +47,6 @@ #include "absl/log/log.h" #include "absl/status/status.h" #include "absl/status/statusor.h" -#include "src/core/lib/promise/map.h" #include "absl/strings/escaping.h" #include "absl/strings/match.h" #include "absl/strings/numbers.h" @@ -79,10 +79,9 @@ namespace { class TokenWithRegionalAccessBoundary final : public TokenFetcherCredentials::Token { public: - TokenWithRegionalAccessBoundary( - grpc_core::Slice token, grpc_core::Timestamp expiration, - grpc_core::RefCountedPtr - regional_access_boundary_fetcher) + TokenWithRegionalAccessBoundary(Slice token, Timestamp expiration, + RefCountedPtr + regional_access_boundary_fetcher) : Token(std::move(token), expiration), regional_access_boundary_fetcher_( std::move(regional_access_boundary_fetcher)) { @@ -96,7 +95,7 @@ class TokenWithRegionalAccessBoundary final } private: - grpc_core::RefCountedPtr + RefCountedPtr regional_access_boundary_fetcher_; }; @@ -449,7 +448,7 @@ void ExternalAccountCredentials::ExternalFetchRequest::FinishTokenFetch( result = MakeRefCounted( std::move(*token_value), Timestamp::Now() + token_lifetime); } - } + } } creds_->event_engine().Run([on_done = std::exchange(on_done_, nullptr), result = std::move(result)]() mutable { @@ -486,9 +485,11 @@ struct WorkloadIdentityPoolFields { // Expression to match: // //iam.googleapis.com/projects//locations/global/workloadIdentityPools//providers/.+ // -// Returns the project and pool ID within the WorkloadIdentityPoolFields struct if the audience matches -// the Workload Identity Pool format, otherwise returns std::nullopt. -std::optional MatchWorkloadIdentityPoolAudience(absl::string_view audience) { +// Returns the project and pool ID within the WorkloadIdentityPoolFields struct +// if the audience matches the Workload Identity Pool format, otherwise returns +// std::nullopt. +std::optional MatchWorkloadIdentityPoolAudience( + absl::string_view audience) { // Match "//iam.googleapis.com/projects/" if (!absl::ConsumePrefix(&audience, "//iam.googleapis.com/projects/")) { return std::nullopt; @@ -498,8 +499,8 @@ std::optional MatchWorkloadIdentityPoolAudience(absl if (location_pos == absl::string_view::npos) return std::nullopt; auto project = audience.substr(0, location_pos); if (project.empty()) return std::nullopt; - audience.remove_prefix(location_pos + - sizeof("/locations/global/workloadIdentityPools/") - 1); + audience.remove_prefix( + location_pos + sizeof("/locations/global/workloadIdentityPools/") - 1); // Match "/providers/" auto provider_pos = audience.find("/providers/"); if (provider_pos == absl::string_view::npos) return std::nullopt; @@ -602,8 +603,7 @@ ExternalAccountCredentials::Create( } it = json.object().find("workforce_pool_user_project"); if (it != json.object().end()) { - if (auto workforce_pool_id = - MatchWorkforcePoolAudience(options.audience); + if (auto workforce_pool_id = MatchWorkforcePoolAudience(options.audience); !workforce_pool_id.empty()) { options.workforce_pool_id = std::string(workforce_pool_id); options.workforce_pool_user_project = it->second.string(); @@ -709,9 +709,8 @@ ExternalAccountCredentials::ExternalAccountCredentials( std::shared_ptr event_engine) : TokenFetcherCredentials(event_engine), options_(std::move(options)), - regional_access_boundary_fetcher_( - RegionalAccessBoundaryFetcher::Create( - BuildRegionalAccessBoundaryUrl(options_), std::move(event_engine))) { + regional_access_boundary_fetcher_(RegionalAccessBoundaryFetcher::Create( + BuildRegionalAccessBoundaryUrl(options_), std::move(event_engine))) { if (scopes.empty()) { scopes.push_back(GOOGLE_CLOUD_PLATFORM_DEFAULT_SCOPE); } diff --git a/src/core/credentials/call/external/external_account_credentials.h b/src/core/credentials/call/external/external_account_credentials.h index d169f5dc2a297..8f676cf48a75c 100644 --- a/src/core/credentials/call/external/external_account_credentials.h +++ b/src/core/credentials/call/external/external_account_credentials.h @@ -203,7 +203,7 @@ class ExternalAccountCredentials : public TokenFetcherCredentials { Options options_; std::vector scopes_; - grpc_core::RefCountedPtr + RefCountedPtr regional_access_boundary_fetcher_; }; diff --git a/src/core/credentials/call/jwt/jwt_credentials.cc b/src/core/credentials/call/jwt/jwt_credentials.cc index 52f7f90be1d74..5516ff0b11f2a 100644 --- a/src/core/credentials/call/jwt/jwt_credentials.cc +++ b/src/core/credentials/call/jwt/jwt_credentials.cc @@ -16,9 +16,9 @@ // #include "src/core/credentials/call/jwt/jwt_credentials.h" -#include #include +#include #include #include #include @@ -103,8 +103,8 @@ grpc_service_account_jwt_access_credentials::GetRequestMetadata( } if (regional_access_boundary_fetcher_ != nullptr) { - regional_access_boundary_fetcher_->Fetch( - jwt_value->as_string_view(), *initial_metadata); + regional_access_boundary_fetcher_->Fetch(jwt_value->as_string_view(), + *initial_metadata); } initial_metadata->Append( GRPC_AUTHORIZATION_METADATA_KEY, std::move(*jwt_value), @@ -115,9 +115,12 @@ grpc_service_account_jwt_access_credentials::GetRequestMetadata( grpc_service_account_jwt_access_credentials:: grpc_service_account_jwt_access_credentials(grpc_auth_json_key key, gpr_timespec token_lifetime) - : key_(key), regional_access_boundary_fetcher_(grpc_core::RegionalAccessBoundaryFetcher::Create( - absl::StrFormat("https://iamcredentials.googleapis.com/v1/projects/-/" - "serviceAccounts/%s/allowedLocations", key_.client_email))) { + : key_(key), + regional_access_boundary_fetcher_( + grpc_core::RegionalAccessBoundaryFetcher::Create(absl::StrFormat( + "https://iamcredentials.googleapis.com/v1/projects/-/" + "serviceAccounts/%s/allowedLocations", + key_.client_email))) { gpr_timespec max_token_lifetime = grpc_max_auth_token_lifetime(); if (gpr_time_cmp(token_lifetime, max_token_lifetime) > 0) { VLOG(2) << "Cropping token lifetime to maximum allowed value (" diff --git a/src/core/credentials/call/jwt/jwt_credentials.h b/src/core/credentials/call/jwt/jwt_credentials.h index dd03ed5a53900..2bf070ce1b2af 100644 --- a/src/core/credentials/call/jwt/jwt_credentials.h +++ b/src/core/credentials/call/jwt/jwt_credentials.h @@ -89,7 +89,8 @@ class grpc_service_account_jwt_access_credentials grpc_auth_json_key key_; gpr_timespec jwt_lifetime_; - grpc_core::RefCountedPtr regional_access_boundary_fetcher_; + grpc_core::RefCountedPtr + regional_access_boundary_fetcher_; }; // Private constructor for jwt credentials from an already parsed json key. diff --git a/src/core/credentials/call/jwt_token_file/jwt_token_file_call_credentials.cc b/src/core/credentials/call/jwt_token_file/jwt_token_file_call_credentials.cc index e4bf4b0a4b892..74bc48a574c29 100644 --- a/src/core/credentials/call/jwt_token_file/jwt_token_file_call_credentials.cc +++ b/src/core/credentials/call/jwt_token_file/jwt_token_file_call_credentials.cc @@ -30,7 +30,7 @@ class JwtTokenFileCallCredentials::FileReader final absl::AnyInvocable>)> on_done) - : creds_(creds), on_done_(std::move(on_done)) { + : creds_(creds), on_done_(std::move(on_done)) { creds->event_engine().Run([self = RefAsSubclass()]() { ExecCtx exec_ctx; self->ReadFile(); diff --git a/src/core/credentials/call/oauth2/oauth2_credentials.cc b/src/core/credentials/call/oauth2/oauth2_credentials.cc index 46f9db9ee86a2..6b97e92ef0a5f 100644 --- a/src/core/credentials/call/oauth2/oauth2_credentials.cc +++ b/src/core/credentials/call/oauth2/oauth2_credentials.cc @@ -19,6 +19,7 @@ #include "src/core/credentials/call/oauth2/oauth2_credentials.h" #include +#include #include #include #include @@ -53,7 +54,6 @@ #include "src/core/util/ref_counted_ptr.h" #include "src/core/util/status_helper.h" #include "src/core/util/uri.h" -#include #include "absl/log/log.h" #include "absl/status/status.h" #include "absl/strings/numbers.h" @@ -259,12 +259,14 @@ namespace { class TokenWithEmail final : public grpc_core::TokenFetcherCredentials::Token { public: - TokenWithEmail(grpc_core::Slice token, grpc_core::Timestamp expiration, - grpc_core::RefCountedPtr email_fetcher) + TokenWithEmail( + grpc_core::Slice token, grpc_core::Timestamp expiration, + grpc_core::RefCountedPtr email_fetcher) : Token(std::move(token), expiration), email_fetcher_(std::move(email_fetcher)) {} - void AddTokenToClientInitialMetadata(grpc_core::ClientMetadata& metadata) override { + void AddTokenToClientInitialMetadata( + grpc_core::ClientMetadata& metadata) override { Token::AddTokenToClientInitialMetadata(metadata); email_fetcher_->Fetch(token().as_string_view(), metadata); } @@ -293,8 +295,8 @@ class grpc_compute_engine_token_fetcher_credentials } grpc_core::OrphanablePtr StartHttpRequest( - grpc_polling_entity* pollent, grpc_core::Timestamp deadline, - grpc_http_response* response, grpc_closure* on_complete) override { + grpc_polling_entity* pollent, grpc_core::Timestamp deadline, + grpc_http_response* response, grpc_closure* on_complete) override { memset(response, 0, sizeof(*response)); grpc_http_header header = {const_cast("Metadata-Flavor"), const_cast("Google")}; @@ -311,8 +313,8 @@ class grpc_compute_engine_token_fetcher_credentials query_params_, "" /* fragment */); GRPC_CHECK(uri.ok()); // params are hardcoded auto http_request = grpc_core::HttpRequest::Get( - std::move(*uri), /*args=*/nullptr, pollent, &request, - deadline, on_complete, response, + std::move(*uri), /*args=*/nullptr, pollent, &request, deadline, + on_complete, response, grpc_core::RefCountedPtr( grpc_insecure_credentials_create())); http_request->Start(); @@ -321,8 +323,8 @@ class grpc_compute_engine_token_fetcher_credentials grpc_core::OrphanablePtr FetchToken( grpc_core::Timestamp deadline, - absl::AnyInvocable< - void(absl::StatusOr>)> + absl::AnyInvocable>)> on_done) override { email_fetcher_->StartEmailFetch(); return grpc_core::MakeOrphanable( diff --git a/src/core/credentials/call/regional_access_boundary_fetcher.cc b/src/core/credentials/call/regional_access_boundary_fetcher.cc index 926c31168a0be..a9771796cd679 100644 --- a/src/core/credentials/call/regional_access_boundary_fetcher.cc +++ b/src/core/credentials/call/regional_access_boundary_fetcher.cc @@ -19,34 +19,35 @@ #include #include -#include "src/core/util/host_port.h" -#include "src/core/util/env.h" #include "src/core/credentials/call/call_credentials.h" -#include "src/core/util/http_client/httpcli_ssl_credentials.h" #include "src/core/credentials/transport/transport_credentials.h" +#include "src/core/lib/iomgr/polling_entity.h" +#include "src/core/lib/iomgr/pollset_set.h" +#include "src/core/util/env.h" +#include "src/core/util/host_port.h" +#include "src/core/util/http_client/httpcli_ssl_credentials.h" #include "src/core/util/json/json.h" #include "src/core/util/json/json_reader.h" #include "absl/log/log.h" #include "absl/strings/str_format.h" -#include "src/core/lib/iomgr/polling_entity.h" -#include "src/core/lib/iomgr/pollset_set.h" namespace grpc_core { namespace { - constexpr absl::string_view kAllowedLocationsKey = "x-allowed-locations"; - constexpr Duration kRegioanlAccessBoundarySoftCacheGraceDuration = Duration::Hours(1); - constexpr Duration kRegionalAccessBoundaryCacheDuration = Duration::Hours(6); - constexpr absl::string_view kRegionalEndpoint = "rep.googleapis.com"; - constexpr char kComputeEngineDefaultSaEmailPath[] = +constexpr absl::string_view kAllowedLocationsKey = "x-allowed-locations"; +constexpr Duration kRegioanlAccessBoundarySoftCacheGraceDuration = + Duration::Hours(1); +constexpr Duration kRegionalAccessBoundaryCacheDuration = Duration::Hours(6); +constexpr absl::string_view kRegionalEndpoint = "rep.googleapis.com"; +constexpr char kComputeEngineDefaultSaEmailPath[] = "/computeMetadata/v1/instance/service-accounts/default/email"; -} +} // namespace RefCountedPtr RegionalAccessBoundaryFetcher::Create( absl::string_view lookup_url, std::shared_ptr event_engine, - std::optional backoff_options) { + std::optional backoff_options) { auto uri = URI::Parse(lookup_url); if (!uri.ok()) { LOG(WARNING) << "Invalid RegionalAccessBoundary lookup URI \"" << lookup_url @@ -58,44 +59,42 @@ RegionalAccessBoundaryFetcher::Create( } RegionalAccessBoundaryFetcher::RegionalAccessBoundaryFetcher( - grpc_core::URI lookup_uri, + URI lookup_uri, std::shared_ptr event_engine, - std::optional backoff_options) - : event_engine_(event_engine == nullptr - ? grpc_event_engine::experimental::GetDefaultEventEngine() - : std::move(event_engine)), + std::optional backoff_options) + : event_engine_( + event_engine == nullptr + ? grpc_event_engine::experimental::GetDefaultEventEngine() + : std::move(event_engine)), lookup_uri_(std::move(lookup_uri)), - backoff_( - backoff_options.has_value() - ? *backoff_options - : BackOff::Options() - .set_initial_backoff(Duration::Seconds(1)) - .set_multiplier(2.0) - .set_jitter(0.2) - .set_max_backoff(Duration::Seconds(60))) { + backoff_(backoff_options.has_value() + ? *backoff_options + : BackOff::Options() + .set_initial_backoff(Duration::Seconds(1)) + .set_multiplier(2.0) + .set_jitter(0.2) + .set_max_backoff(Duration::Seconds(60))) { CHECK(event_engine_ != nullptr); } void RegionalAccessBoundaryFetcher::OnFetchSuccess(Slice encoded_locations) { - grpc_core::MutexLock lock(&cache_mu_); + MutexLock lock(&cache_mu_); if (shutdown_) return; cache_ = {std::move(encoded_locations), - grpc_core::Timestamp::Now() + - kRegionalAccessBoundaryCacheDuration}; + Timestamp::Now() + kRegionalAccessBoundaryCacheDuration}; backoff_.Reset(); pending_request_.reset(); } void RegionalAccessBoundaryFetcher::OnFetchFailure( - grpc_core::RefCountedPtr req, grpc_error_handle error, - int http_status, absl::string_view response_body) { - grpc_core::MutexLock lock(&cache_mu_); + RefCountedPtr req, grpc_error_handle error, int http_status, + absl::string_view response_body) { + MutexLock lock(&cache_mu_); if (shutdown_) return; LOG(WARNING) << "Regional access boundary request will be retried after " "failing with error: " - << grpc_core::StatusToString(error) - << ", HTTP Status: " << http_status << ", Body: " - << response_body; + << StatusToString(error) << ", HTTP Status: " << http_status + << ", Body: " << response_body; next_fetch_time_ = Timestamp::Now() + backoff_.NextAttemptDelay(); pending_request_.reset(); } @@ -118,8 +117,9 @@ void RegionalAccessBoundaryFetcher::Fetch(absl::string_view access_token, // Regional access boundary is only applicable for non-regional googleapis // endpoints. All other endpoints would not benefit from the regional access // boundary metadata. - bool is_regional = authority == kRegionalEndpoint || - absl::EndsWith(authority, absl::StrCat(".", kRegionalEndpoint)); + bool is_regional = + authority == kRegionalEndpoint || + absl::EndsWith(authority, absl::StrCat(".", kRegionalEndpoint)); if (is_regional) { return; } @@ -133,20 +133,18 @@ void RegionalAccessBoundaryFetcher::Fetch(absl::string_view access_token, // - There is no pending fetch currently in flight. // - We are not currently in backoff after a failed fetch attempt. if ((!cache_.has_value() || - (cache_->expiration - now) <= - kRegioanlAccessBoundarySoftCacheGraceDuration) && - pending_request_ == nullptr && - next_fetch_time_ <= now) { + (cache_->expiration - now) <= + kRegioanlAccessBoundarySoftCacheGraceDuration) && + pending_request_ == nullptr && next_fetch_time_ <= now) { pending_request_ = MakeOrphanable(WeakRef(), access_token); pending_request_->Start(); } // If we have cached non-expired Regional Access Boundary data, use it. if (cache_.has_value() && cache_->expiration > now) { initial_metadata.Append( - kAllowedLocationsKey, - cache_->encoded_locations.Ref(), - [](absl::string_view, const Slice&) { - LOG_EVERY_N_SEC(WARNING, 60) + kAllowedLocationsKey, cache_->encoded_locations.Ref(), + [](absl::string_view, const Slice&) { + LOG_EVERY_N_SEC(WARNING, 60) << "Regional access boundary header could not be appended"; }); } @@ -154,18 +152,18 @@ void RegionalAccessBoundaryFetcher::Fetch(absl::string_view access_token, } void RegionalAccessBoundaryFetcher::Orphaned() { - grpc_core::MutexLock lock(&cache_mu_); + MutexLock lock(&cache_mu_); shutdown_ = true; pending_request_.reset(); } -RegionalAccessBoundaryFetcher::Request:: - Request(grpc_core::WeakRefCountedPtr fetcher, - absl::string_view access_token) +RegionalAccessBoundaryFetcher::Request::Request( + WeakRefCountedPtr fetcher, + absl::string_view access_token) : access_token_(access_token), fetcher_(std::move(fetcher)) { memset(&response_, 0, sizeof(response_)); - pollent_ = grpc_polling_entity_create_from_pollset_set( - grpc_pollset_set_create()); + pollent_ = + grpc_polling_entity_create_from_pollset_set(grpc_pollset_set_create()); } RegionalAccessBoundaryFetcher::Request::~Request() { @@ -177,49 +175,47 @@ void RegionalAccessBoundaryFetcher::Request::Start() { grpc_http_request request; memset(&request, 0, sizeof(request)); grpc_http_header header = {const_cast("Authorization"), - const_cast(access_token_.data())}; + const_cast(access_token_.data())}; request.hdr_count = 1; request.hdrs = &header; // We pass this as arg to OnResponseWrapper. We must // manually take a ref because C-callback doesn't. The Ref is consumed in // OnResponseWrapper. Ref().release(); - GRPC_CLOSURE_INIT(&closure_, OnResponseWrapper, - this, grpc_schedule_on_exec_ctx); - http_request_ = grpc_core::HttpRequest::Get( - fetcher_->lookup_uri_, - nullptr, // channel_args - &pollent_, &request, - grpc_core::Timestamp::Now() + grpc_core::Duration::Seconds(60), - &closure_, - &response_, - grpc_core::RefCountedPtr( - grpc_core::CreateHttpRequestSSLCredentials())); + GRPC_CLOSURE_INIT(&closure_, OnResponseWrapper, this, + grpc_schedule_on_exec_ctx); + http_request_ = HttpRequest::Get(fetcher_->lookup_uri_, + nullptr, // channel_args + &pollent_, &request, + Timestamp::Now() + Duration::Seconds(60), + &closure_, &response_, + RefCountedPtr( + CreateHttpRequestSSLCredentials())); http_request_->Start(); } void RegionalAccessBoundaryFetcher::Request::Orphan() { - http_request_.reset(); - Unref(); + http_request_.reset(); + Unref(); } void RegionalAccessBoundaryFetcher::Request::OnResponseWrapper( void* arg, grpc_error_handle error) { - grpc_core::RefCountedPtr req( - static_cast(arg)); + RefCountedPtr req(static_cast(arg)); req->OnResponse(error); } -void RegionalAccessBoundaryFetcher::Request::OnResponse(grpc_error_handle error) { +void RegionalAccessBoundaryFetcher::Request::OnResponse( + grpc_error_handle error) { bool success = false; std::string encoded_locations; if (error.ok() && response_.status == 200) { - absl::StatusOr json = grpc_core::JsonParse( - absl::string_view(response_.body, response_.body_length)); - if (json.ok() && json->type() == grpc_core::Json::Type::kObject) { + absl::StatusOr json = + JsonParse(absl::string_view(response_.body, response_.body_length)); + if (json.ok() && json->type() == Json::Type::kObject) { auto it_encoded = json->object().find("encodedLocations"); if (it_encoded != json->object().end() && - it_encoded->second.type() == grpc_core::Json::Type::kString) { + it_encoded->second.type() == Json::Type::kString) { encoded_locations = it_encoded->second.string(); } if (!encoded_locations.empty()) { @@ -236,13 +232,14 @@ void RegionalAccessBoundaryFetcher::Request::OnResponse(grpc_error_handle error) } } -class EmailFetcher::EmailRequest final : public InternallyRefCounted { +class EmailFetcher::EmailRequest final + : public InternallyRefCounted { public: explicit EmailRequest(WeakRefCountedPtr fetcher) : fetcher_(std::move(fetcher)) { memset(&response_, 0, sizeof(response_)); - pollent_ = grpc_polling_entity_create_from_pollset_set( - grpc_pollset_set_create()); + pollent_ = + grpc_polling_entity_create_from_pollset_set(grpc_pollset_set_create()); } ~EmailRequest() override { @@ -257,10 +254,10 @@ class EmailFetcher::EmailRequest final : public InternallyRefCountedOnEmailFetchError(error); } else if (response_.status != 200) { - fetcher_->OnEmailFetchError(GRPC_ERROR_CREATE( - absl::StrCat("Failed to fetch service account email: HTTP ", - response_.status))); + fetcher_->OnEmailFetchError(GRPC_ERROR_CREATE(absl::StrCat( + "Failed to fetch service account email: HTTP ", response_.status))); } else { fetcher_->OnEmailFetchComplete( absl::string_view(response_.body, response_.body_length)); @@ -307,9 +303,10 @@ class EmailFetcher::EmailRequest final : public InternallyRefCounted event_engine) - : event_engine_(event_engine == nullptr - ? grpc_event_engine::experimental::GetDefaultEventEngine() - : std::move(event_engine)) {} + : event_engine_( + event_engine == nullptr + ? grpc_event_engine::experimental::GetDefaultEventEngine() + : std::move(event_engine)) {} EmailFetcher::~EmailFetcher() = default; @@ -320,10 +317,11 @@ void EmailFetcher::StartEmailFetch() { } // Check if we are in the initial/retryable state (null EmailRequest) auto* pending = std::get_if>(&state_); - if (pending == nullptr) return; // Already have RAB fetcher. + if (pending == nullptr) return; // Already have RAB fetcher. if (*pending != nullptr) return; // Email fetch already in progress. auto request = MakeOrphanable(WeakRef()); - // We keep a temporary ref to start it, but transfer ownership to state_ first. + // We keep a temporary ref to start it, but transfer ownership to state_ + // first. auto* request_ptr = request.get(); state_ = std::move(request); request_ptr->Start(); @@ -338,8 +336,6 @@ void EmailFetcher::Fetch(absl::string_view token, } } - - void EmailFetcher::Orphaned() { MutexLock lock(&mu_); state_ = RefCountedPtr(nullptr); @@ -348,28 +344,29 @@ void EmailFetcher::Orphaned() { void EmailFetcher::OnEmailFetchComplete(absl::string_view email) { MutexLock lock(&mu_); if (std::holds_alternative>(state_)) { - std::string rab_url = absl::StrFormat( - "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/" - "%s/allowedLocations", - email); - auto rab_fetcher = RegionalAccessBoundaryFetcher::Create( - rab_url, event_engine_); - if (rab_fetcher != nullptr) { - state_ = std::move(rab_fetcher); - } else { - state_ = OrphanablePtr(nullptr); - } + std::string rab_url = absl::StrFormat( + "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/" + "%s/allowedLocations", + email); + auto rab_fetcher = + RegionalAccessBoundaryFetcher::Create(rab_url, event_engine_); + if (rab_fetcher != nullptr) { + state_ = std::move(rab_fetcher); + } else { + state_ = OrphanablePtr(nullptr); + } } } void EmailFetcher::OnEmailFetchError(grpc_error_handle error) { MutexLock lock(&mu_); if (std::holds_alternative>(state_)) { - LOG_EVERY_N_SEC(ERROR, 60) - << "Regional Access Boundary fetch skipped due to service account email " - "fetch failure: " - << StatusToString(error); - state_ = OrphanablePtr(nullptr); // Reset to allow retry on next invocation. + LOG_EVERY_N_SEC(ERROR, 60) << "Regional Access Boundary fetch skipped due " + "to service account email " + "fetch failure: " + << StatusToString(error); + state_ = OrphanablePtr( + nullptr); // Reset to allow retry on next invocation. next_fetch_earliest_time_ = Timestamp::Now() + backoff_.NextAttemptDelay(); } } diff --git a/src/core/credentials/call/regional_access_boundary_fetcher.h b/src/core/credentials/call/regional_access_boundary_fetcher.h index 2269c5aefd6fe..9b2082519e461 100644 --- a/src/core/credentials/call/regional_access_boundary_fetcher.h +++ b/src/core/credentials/call/regional_access_boundary_fetcher.h @@ -17,47 +17,49 @@ #ifndef GRPC_SRC_CORE_CREDENTIALS_CALL_REGIONAL_ACCESS_BOUNDARY_FETCHER_H #define GRPC_SRC_CORE_CREDENTIALS_CALL_REGIONAL_ACCESS_BOUNDARY_FETCHER_H +#include #include #include #include +#include #include #include -#include -#include -#include "absl/status/statusor.h" #include "src/core/call/metadata.h" -#include "src/core/util/dual_ref_counted.h" -#include "src/core/util/ref_counted_ptr.h" -#include "src/core/util/backoff.h" #include "src/core/lib/iomgr/error.h" +#include "src/core/util/backoff.h" +#include "src/core/util/dual_ref_counted.h" #include "src/core/util/http_client/httpcli.h" +#include "src/core/util/ref_counted_ptr.h" +#include "absl/status/statusor.h" namespace grpc_core { -class RegionalAccessBoundaryFetcher final : public DualRefCounted { +class RegionalAccessBoundaryFetcher final + : public DualRefCounted { public: - static grpc_core::RefCountedPtr Create( + static RefCountedPtr Create( absl::string_view lookup_url, - std::shared_ptr event_engine = nullptr, - std::optional backoff_options = std::nullopt); + std::shared_ptr + event_engine = nullptr, + std::optional backoff_options = std::nullopt); explicit RegionalAccessBoundaryFetcher( - grpc_core::URI lookup_uri, - std::shared_ptr event_engine = nullptr, - std::optional backoff_options = std::nullopt); - - // Attaches regional access boundary header (x-allowed-locations) to the initial metadata - // if available, otherwise initiates non-blocking, asynchronous fetch of regional access - // boundary if not already cached or in flight. - void Fetch( - absl::string_view access_token, - ClientMetadata& initial_metadata); - - // Cancels any pending fetch of regional access boundary which must be called during - // destruction of any CallCredential which supports regional access boundary to - // avoid memory leaks from pending http requests. + URI lookup_uri, + std::shared_ptr + event_engine = nullptr, + std::optional backoff_options = std::nullopt); + + // Attaches regional access boundary header (x-allowed-locations) to the + // initial metadata if available, otherwise initiates non-blocking, + // asynchronous fetch of regional access boundary if not already cached or in + // flight. + void Fetch(absl::string_view access_token, ClientMetadata& initial_metadata); + + // Cancels any pending fetch of regional access boundary which must be called + // during destruction of any CallCredential which supports regional access + // boundary to avoid memory leaks from pending http requests. void Orphaned() override; private: @@ -65,28 +67,29 @@ class RegionalAccessBoundaryFetcher final : public DualRefCounted req, grpc_error_handle error, int http_status, absl::string_view response_body); + void OnFetchFailure(RefCountedPtr req, grpc_error_handle error, + int http_status, absl::string_view response_body); std::shared_ptr event_engine_; - const grpc_core::URI lookup_uri_; - grpc_core::Mutex cache_mu_; - std::optional cache_ ABSL_GUARDED_BY(&cache_mu_) ; + const URI lookup_uri_; + Mutex cache_mu_; + std::optional cache_ ABSL_GUARDED_BY(&cache_mu_); Timestamp next_fetch_time_ ABSL_GUARDED_BY(&cache_mu_) = Timestamp::InfPast(); - grpc_core::BackOff backoff_ ABSL_GUARDED_BY(&cache_mu_); - grpc_core::OrphanablePtr pending_request_ ABSL_GUARDED_BY(&cache_mu_); + BackOff backoff_ ABSL_GUARDED_BY(&cache_mu_); + OrphanablePtr pending_request_ ABSL_GUARDED_BY(&cache_mu_); bool shutdown_ ABSL_GUARDED_BY(&cache_mu_) = false; }; class RegionalAccessBoundaryFetcher::Request final -: public grpc_core::InternallyRefCounted { + : public InternallyRefCounted { public: - Request(grpc_core::WeakRefCountedPtr fetcher, + Request(WeakRefCountedPtr fetcher, absl::string_view access_token); ~Request() override; @@ -98,23 +101,23 @@ class RegionalAccessBoundaryFetcher::Request final void Orphan() override; private: - static void OnResponseWrapper(void* arg, grpc_error_handle error); void OnResponse(grpc_error_handle error); grpc_http_response response_; - grpc_core::OrphanablePtr http_request_; + OrphanablePtr http_request_; std::string access_token_; grpc_polling_entity pollent_; - grpc_core::WeakRefCountedPtr fetcher_; + WeakRefCountedPtr fetcher_; grpc_closure closure_; }; class EmailFetcher final : public DualRefCounted { public: explicit EmailFetcher( - std::shared_ptr event_engine = nullptr); - + std::shared_ptr + event_engine = nullptr); + ~EmailFetcher() override; void StartEmailFetch(); @@ -131,18 +134,20 @@ class EmailFetcher final : public DualRefCounted { void OnEmailFetchError(grpc_error_handle error); std::shared_ptr event_engine_; - grpc_core::Mutex mu_; + Mutex mu_; // Either a pending email request or an RAB fetcher. - std::variant, RefCountedPtr> state_ - ABSL_GUARDED_BY(&mu_); - BackOff backoff_ ABSL_GUARDED_BY(&mu_) = BackOff( - BackOff::Options() - .set_initial_backoff(Duration::Seconds(15)) - .set_multiplier(1.75) - .set_jitter(0.1) - .set_max_backoff(Duration::Minutes(2))); - Timestamp next_fetch_earliest_time_ ABSL_GUARDED_BY(&mu_) = Timestamp::InfPast(); + std::variant, + RefCountedPtr> + state_ ABSL_GUARDED_BY(&mu_); + BackOff backoff_ ABSL_GUARDED_BY(&mu_) = + BackOff(BackOff::Options() + .set_initial_backoff(Duration::Seconds(15)) + .set_multiplier(1.75) + .set_jitter(0.1) + .set_max_backoff(Duration::Minutes(2))); + Timestamp next_fetch_earliest_time_ ABSL_GUARDED_BY(&mu_) = + Timestamp::InfPast(); }; } // namespace grpc_core diff --git a/src/core/credentials/call/token_fetcher/token_fetcher_credentials.h b/src/core/credentials/call/token_fetcher/token_fetcher_credentials.h index e091940528b67..73aaa5f278efe 100644 --- a/src/core/credentials/call/token_fetcher/token_fetcher_credentials.h +++ b/src/core/credentials/call/token_fetcher/token_fetcher_credentials.h @@ -60,6 +60,7 @@ class TokenFetcherCredentials : public grpc_call_credentials { protected: // Returns the token's value. const Slice& token() const { return token_; } + private: Slice token_; Timestamp expiration_; @@ -71,7 +72,7 @@ class TokenFetcherCredentials : public grpc_call_credentials { ArenaPromise> GetRequestMetadata( ClientMetadataHandle initial_metadata, - const GetRequestMetadataArgs* args) override final; + const GetRequestMetadataArgs* args) final; protected: // Base class for fetch requests. diff --git a/test/core/credentials/call/BUILD b/test/core/credentials/call/BUILD index 6ea01803fd83f..fd6a3b718f00c 100644 --- a/test/core/credentials/call/BUILD +++ b/test/core/credentials/call/BUILD @@ -126,15 +126,15 @@ grpc_cc_test( ], deps = [ "//:gpr", + "//:grpc_security_base", + "//:httpcli", "//src/core:arena", "//src/core:arena_promise", - "//test/core/event_engine/fuzzing_event_engine:fuzzing_event_engine", - "//test/core/event_engine/fuzzing_event_engine:fuzzing_event_engine_cc_proto", - "//:grpc_security_base", "//src/core:metadata_batch", "//src/core:regional_access_boundary_fetcher", "//src/core:resource_quota", - "//:httpcli", + "//test/core/event_engine/fuzzing_event_engine", + "//test/core/event_engine/fuzzing_event_engine:fuzzing_event_engine_cc_proto", "//test/core/test_util:grpc_test_util", ], ) diff --git a/test/core/credentials/call/call_credentials_test.cc b/test/core/credentials/call/call_credentials_test.cc index b019635fac0bc..35d3bff118db7 100644 --- a/test/core/credentials/call/call_credentials_test.cc +++ b/test/core/credentials/call/call_credentials_test.cc @@ -541,7 +541,8 @@ class RequestMetadataState : public RefCounted { md_.Remove(HttpPathMetadata()); LOG(INFO) << "expected metadata: " << expected_; LOG(INFO) << "actual metadata: " << md_.DebugString(); - // We cannot use DebugString() for validation because it redacts sensitive headers + // We cannot use DebugString() for validation because it redacts sensitive + // headers std::multimap actual_metadata; md_.Log([&actual_metadata](absl::string_view key, absl::string_view value) { if (key == "authorization") { @@ -553,14 +554,17 @@ class RequestMetadataState : public RefCounted { if (!expected_.empty()) { std::vector parts = absl::StrSplit(expected_, ", "); for (const auto& part : parts) { - std::pair kv = absl::StrSplit(part, absl::MaxSplits(": ", 1)); + std::pair kv = + absl::StrSplit(part, absl::MaxSplits(": ", 1)); if (kv.first == "authorization") { continue; } - expected_metadata.emplace(std::string(kv.first), std::string(kv.second)); + expected_metadata.emplace(std::string(kv.first), + std::string(kv.second)); } } - EXPECT_THAT(actual_metadata, ::testing::UnorderedElementsAreArray(expected_metadata)); + EXPECT_THAT(actual_metadata, + ::testing::UnorderedElementsAreArray(expected_metadata)); } grpc_error_handle expected_error_; @@ -742,22 +746,25 @@ TEST_F(CredentialsTest, TestChannelOauth2GoogleIamCompositeCreds) { void validate_compute_engine_http_request(const grpc_http_request* request, const URI& uri) { - if (uri.path() == "/v1/projects/-/serviceAccounts/foo@bar.com/allowedLocations") { + if (uri.path() == + "/v1/projects/-/serviceAccounts/foo@bar.com/allowedLocations") { EXPECT_EQ(uri.authority(), "iamcredentials.googleapis.com"); ASSERT_EQ(request->hdr_count, 1); EXPECT_EQ(absl::string_view(request->hdrs[0].key), "Authorization"); - EXPECT_THAT(absl::string_view(request->hdrs[0].value), ::testing::StartsWith("Bearer ")); + EXPECT_THAT(absl::string_view(request->hdrs[0].value), + ::testing::StartsWith("Bearer ")); } else { EXPECT_EQ(uri.authority(), "metadata.google.internal."); ASSERT_EQ(request->hdr_count, 1); EXPECT_EQ(absl::string_view(request->hdrs[0].key), "Metadata-Flavor"); EXPECT_EQ(absl::string_view(request->hdrs[0].value), "Google"); } - EXPECT_THAT(uri.path(), - ::testing::AnyOf( - "/computeMetadata/v1/instance/service-accounts/default/token", - "/computeMetadata/v1/instance/service-accounts/default/email", - "/v1/projects/-/serviceAccounts/foo@bar.com/allowedLocations")); + EXPECT_THAT( + uri.path(), + ::testing::AnyOf( + "/computeMetadata/v1/instance/service-accounts/default/token", + "/computeMetadata/v1/instance/service-accounts/default/email", + "/v1/projects/-/serviceAccounts/foo@bar.com/allowedLocations")); } void assert_query_parameters(const URI& uri, absl::string_view expected_key, @@ -777,7 +784,8 @@ int compute_engine_httpcli_get_success_override( if (uri.path() == "/computeMetadata/v1/instance/service-accounts/default/email") { *response = http_response(200, "foo@bar.com"); - } else if (uri.path() == "/v1/projects/-/serviceAccounts/foo@bar.com/allowedLocations") { + } else if (uri.path() == + "/v1/projects/-/serviceAccounts/foo@bar.com/allowedLocations") { *response = http_response(200, "{\"locations\": [\"us-west1\"]}"); } else { *response = http_response(200, valid_oauth2_json_response); @@ -789,8 +797,10 @@ int compute_engine_httpcli_get_success_override( int compute_engine_httpcli_get_success_alts_override( const grpc_http_request* request, const URI& uri, Timestamp deadline, grpc_closure* on_done, grpc_http_response* response) { - if (uri.path() != "/computeMetadata/v1/instance/service-accounts/default/email" && - uri.path() != "/v1/projects/-/serviceAccounts/foo@bar.com/allowedLocations") { + if (uri.path() != + "/computeMetadata/v1/instance/service-accounts/default/email" && + uri.path() != + "/v1/projects/-/serviceAccounts/foo@bar.com/allowedLocations") { assert_query_parameters(uri, "transport", "alts"); } return compute_engine_httpcli_get_success_override(request, uri, deadline, @@ -809,8 +819,10 @@ int compute_engine_httpcli_get_failure_override( int compute_engine_httpcli_get_failure_alts_override( const grpc_http_request* request, const URI& uri, Timestamp deadline, grpc_closure* on_done, grpc_http_response* response) { - if (uri.path() != "/computeMetadata/v1/instance/service-accounts/default/email" && - uri.path() != "/v1/projects/-/serviceAccounts/foo@bar.com/allowedLocations") { + if (uri.path() != + "/computeMetadata/v1/instance/service-accounts/default/email" && + uri.path() != + "/v1/projects/-/serviceAccounts/foo@bar.com/allowedLocations") { assert_query_parameters(uri, "transport", "alts"); } return compute_engine_httpcli_get_failure_override(request, uri, deadline, @@ -1412,7 +1424,8 @@ int httpcli_get_valid_json_regional_access_boundary( Timestamp /*deadline*/, grpc_closure* on_done, grpc_http_response* response) { *response = http_response( - 200, "{\"encodedLocations\": \"us-west1\", \"locations\": [\"us-west1\"]}"); + 200, + "{\"encodedLocations\": \"us-west1\", \"locations\": [\"us-west1\"]}"); ExecCtx::Run(DEBUG_LOCATION, on_done, absl::OkStatus()); return 1; } @@ -1423,22 +1436,25 @@ TEST_F(CredentialsTest, TestJwtCredsWithRegionalAccessBoundary) { char* json_key_string = test_json_key_str(); ExecCtx exec_ctx; std::string expected_md_value = absl::StrCat("Bearer ", test_signed_jwt); - std::string emd_without_rab = absl::StrCat("authorization: ", expected_md_value); - std::string emd_with_rab = absl::StrCat("authorization: ", expected_md_value, ", ", - "x-allowed-locations: us-west1"); + std::string emd_without_rab = + absl::StrCat("authorization: ", expected_md_value); + std::string emd_with_rab = + absl::StrCat("authorization: ", expected_md_value, ", ", + "x-allowed-locations: us-west1"); grpc_call_credentials* creds = grpc_service_account_jwt_access_credentials_create( json_key_string, grpc_max_auth_token_lifetime(), nullptr); HttpRequest::SetOverride(httpcli_get_valid_json_regional_access_boundary, nullptr, nullptr); // First request: jwt_encode_and_sign should be called. - auto state = RequestMetadataState::NewInstance(absl::OkStatus(), emd_without_rab); + auto state = + RequestMetadataState::NewInstance(absl::OkStatus(), emd_without_rab); grpc_jwt_encode_and_sign_set_override(encode_and_sign_jwt_success); state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority, kTestPath); ExecCtx::Get()->Flush(); EXPECT_THAT(creds->debug_string(), - ::testing::StartsWith(expected_creds_debug_string_prefix)); + ::testing::StartsWith(expected_creds_debug_string_prefix)); state = RequestMetadataState::NewInstance(absl::OkStatus(), emd_with_rab); state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority, kTestPath); @@ -1455,16 +1471,19 @@ TEST_F(CredentialsTest, TestJwtCredsFetchRegionalAccessBoundaryRespectsCache) { char* json_key_string = test_json_key_str(); ExecCtx exec_ctx; std::string expected_md_value = absl::StrCat("Bearer ", test_signed_jwt); - std::string emd_without_rab = absl::StrCat("authorization: ", expected_md_value); - std::string emd_with_rab = absl::StrCat("authorization: ", expected_md_value, ", ", - "x-allowed-locations: us-west1"); + std::string emd_without_rab = + absl::StrCat("authorization: ", expected_md_value); + std::string emd_with_rab = + absl::StrCat("authorization: ", expected_md_value, ", ", + "x-allowed-locations: us-west1"); grpc_call_credentials* creds = grpc_service_account_jwt_access_credentials_create( json_key_string, grpc_max_auth_token_lifetime(), nullptr); HttpRequest::SetOverride(httpcli_get_valid_json_regional_access_boundary, nullptr, nullptr); // First request: jwt_encode_and_sign should be called. - auto state = RequestMetadataState::NewInstance(absl::OkStatus(), emd_without_rab); + auto state = + RequestMetadataState::NewInstance(absl::OkStatus(), emd_without_rab); grpc_jwt_encode_and_sign_set_override(encode_and_sign_jwt_success); state->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority, kTestPath); @@ -1477,7 +1496,7 @@ TEST_F(CredentialsTest, TestJwtCredsFetchRegionalAccessBoundaryRespectsCache) { kTestPath); ExecCtx::Get()->Flush(); EXPECT_THAT(creds->debug_string(), - ::testing::StartsWith(expected_creds_debug_string_prefix)); + ::testing::StartsWith(expected_creds_debug_string_prefix)); creds->Unref(); gpr_free(json_key_string); grpc_jwt_encode_and_sign_set_override(nullptr); @@ -1520,7 +1539,8 @@ TEST_F(CredentialsTest, TestJwtCredsWithInvalidRabUri) { grpc_service_account_jwt_access_credentials_create( json_key_str.c_str(), grpc_max_auth_token_lifetime(), nullptr); GRPC_CHECK_NE(creds, nullptr); - // Expectation: JWT token is generated, but RAB fetcher is null (or doesn't fetch). + // Expectation: JWT token is generated, but RAB fetcher is null (or doesn't + // fetch). std::string expected_md_value = absl::StrCat("Bearer ", test_signed_jwt); // Only authorization header expected. std::string emd = absl::StrCat("authorization: ", expected_md_value); @@ -1564,7 +1584,7 @@ TEST_F(CredentialsTest, TestJwtCredsSuccess) { kTestOtherPath); ExecCtx::Get()->Flush(); EXPECT_THAT(creds->debug_string(), - ::testing::StartsWith(expected_creds_debug_string_prefix)); + ::testing::StartsWith(expected_creds_debug_string_prefix)); creds->Unref(); gpr_free(json_key_string); grpc_jwt_encode_and_sign_set_override(nullptr); @@ -1585,7 +1605,7 @@ TEST_F(CredentialsTest, TestJwtCredsSigningFailure) { kTestPath); gpr_free(json_key_string); EXPECT_THAT(creds->debug_string(), - ::testing::StartsWith(expected_creds_debug_string_prefix)); + ::testing::StartsWith(expected_creds_debug_string_prefix)); creds->Unref(); grpc_jwt_encode_and_sign_set_override(nullptr); } @@ -4496,21 +4516,24 @@ TEST_F(ExternalAccountCredentialsTest, SuccessWithWorkforcePoolRab) { auto creds = MakeRefCounted( options, std::vector(), event_engine_); // Check ref count of event_engine. It should be three to cover: - // (one in test, one in TokenFetcherCredentials, and one in RegionalAccessBoundaryFetcher + // (one in test, one in TokenFetcherCredentials, and one in + // RegionalAccessBoundaryFetcher EXPECT_EQ(event_engine_.use_count(), 3); auto state_initial = RequestMetadataState::NewInstance( absl::OkStatus(), "authorization: Bearer token_exchange_access_token"); auto state_cached = RequestMetadataState::NewInstance( - absl::OkStatus(), "authorization: Bearer token_exchange_access_token, x-allowed-locations: 0x08"); + absl::OkStatus(), + "authorization: Bearer token_exchange_access_token, x-allowed-locations: " + "0x08"); HttpRequest::SetOverride(external_account_creds_httpcli_get_rab_success, external_account_creds_httpcli_post_success, httpcli_put_should_not_be_called); - state_initial->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority, - kTestPath); + state_initial->RunRequestMetadataTest(creds.get(), kTestUrlScheme, + kTestAuthority, kTestPath); event_engine_->TickUntilIdle(); ExecCtx::Get()->Flush(); - state_cached->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority, - kTestPath); + state_cached->RunRequestMetadataTest(creds.get(), kTestUrlScheme, + kTestAuthority, kTestPath); event_engine_->TickUntilIdle(); ExecCtx::Get()->Flush(); HttpRequest::SetOverride(nullptr, nullptr, nullptr); @@ -4544,16 +4567,18 @@ TEST_F(ExternalAccountCredentialsTest, SuccessWithWorkloadIdentityPoolRab) { auto state_initial = RequestMetadataState::NewInstance( absl::OkStatus(), "authorization: Bearer token_exchange_access_token"); auto state_cached = RequestMetadataState::NewInstance( - absl::OkStatus(), "authorization: Bearer token_exchange_access_token, x-allowed-locations: 0x08"); + absl::OkStatus(), + "authorization: Bearer token_exchange_access_token, x-allowed-locations: " + "0x08"); HttpRequest::SetOverride(external_account_creds_httpcli_get_rab_success, external_account_creds_httpcli_post_success, httpcli_put_should_not_be_called); - state_initial->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kGoogleTestAuthority, - kTestPath); + state_initial->RunRequestMetadataTest(creds.get(), kTestUrlScheme, + kGoogleTestAuthority, kTestPath); event_engine_->TickUntilIdle(); ExecCtx::Get()->Flush(); - state_cached->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kGoogleTestAuthority, - kTestPath); + state_cached->RunRequestMetadataTest(creds.get(), kTestUrlScheme, + kGoogleTestAuthority, kTestPath); event_engine_->TickUntilIdle(); ExecCtx::Get()->Flush(); HttpRequest::SetOverride(nullptr, nullptr, nullptr); @@ -4563,7 +4588,9 @@ int external_account_creds_httpcli_get_rab_impersonated( const grpc_http_request* /*request*/, const URI& uri, Timestamp /*deadline*/, grpc_closure* on_done, grpc_http_response* response) { - if (uri.path() == "/v1/projects/-/serviceAccounts/test_service_account@test.com/allowedLocations") { + if (uri.path() == + "/v1/projects/-/serviceAccounts/test_service_account@test.com/" + "allowedLocations") { *response = http_response(200, "{\"encodedLocations\": \"0x08\", " "\"locations\": [\"europe-west1\"]}"); @@ -4582,7 +4609,9 @@ int external_account_creds_httpcli_post_success_impersonated( if (uri.path() == "/token") { *response = http_response( 200, valid_external_account_creds_token_exchange_response); - } else if (uri.path() == "/v1/projects/-/serviceAccounts/test_service_account@test.com:generateAccessToken") { + } else if (uri.path() == + "/v1/projects/-/serviceAccounts/" + "test_service_account@test.com:generateAccessToken") { *response = http_response( 200, valid_external_account_creds_service_account_impersonation_response); @@ -4594,17 +4623,19 @@ int external_account_creds_httpcli_post_success_impersonated( return 1; } -TEST_F(ExternalAccountCredentialsTest, SuccessWithServiceAccountImpersonationRab) { +TEST_F(ExternalAccountCredentialsTest, + SuccessWithServiceAccountImpersonationRab) { ExecCtx exec_ctx; Json credential_source = Json::FromString(""); TestExternalAccountCredentials::ServiceAccountImpersonation service_account_impersonation; service_account_impersonation.token_lifetime_seconds = 3600; TestExternalAccountCredentials::Options options = { - "external_account", // type; - "audience", // audience; - "subject_token_type", // subject_token_type; - "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/test_service_account@test.com:generateAccessToken", // service_account_impersonation_url; + "external_account", // type; + "audience", // audience; + "subject_token_type", // subject_token_type; + "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/" + "test_service_account@test.com:generateAccessToken", // service_account_impersonation_url; service_account_impersonation, // service_account_impersonation; "https://foo.com:5555/token", // token_url; "https://foo.com:5555/token_info", // token_info_url; @@ -4620,18 +4651,22 @@ TEST_F(ExternalAccountCredentialsTest, SuccessWithServiceAccountImpersonationRab auto creds = MakeRefCounted( options, std::vector(), event_engine_); auto state_initial = RequestMetadataState::NewInstance( - absl::OkStatus(), "authorization: Bearer service_account_impersonation_access_token"); + absl::OkStatus(), + "authorization: Bearer service_account_impersonation_access_token"); auto state_cached = RequestMetadataState::NewInstance( - absl::OkStatus(), "authorization: Bearer service_account_impersonation_access_token, x-allowed-locations: 0x08"); - HttpRequest::SetOverride(external_account_creds_httpcli_get_rab_impersonated, - external_account_creds_httpcli_post_success_impersonated, - httpcli_put_should_not_be_called); - state_initial->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kGoogleTestAuthority, - kTestPath); + absl::OkStatus(), + "authorization: Bearer service_account_impersonation_access_token, " + "x-allowed-locations: 0x08"); + HttpRequest::SetOverride( + external_account_creds_httpcli_get_rab_impersonated, + external_account_creds_httpcli_post_success_impersonated, + httpcli_put_should_not_be_called); + state_initial->RunRequestMetadataTest(creds.get(), kTestUrlScheme, + kGoogleTestAuthority, kTestPath); event_engine_->TickUntilIdle(); ExecCtx::Get()->Flush(); - state_cached->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kGoogleTestAuthority, - kTestPath); + state_cached->RunRequestMetadataTest(creds.get(), kTestUrlScheme, + kGoogleTestAuthority, kTestPath); event_engine_->TickUntilIdle(); ExecCtx::Get()->Flush(); HttpRequest::SetOverride(nullptr, nullptr, nullptr); @@ -4668,8 +4703,8 @@ TEST_F(ExternalAccountCredentialsTest, SuccessWithRab401) { HttpRequest::SetOverride(external_account_creds_httpcli_get_rab_401, external_account_creds_httpcli_post_success, httpcli_put_should_not_be_called); - state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kGoogleTestAuthority, - kTestPath); + state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, + kGoogleTestAuthority, kTestPath); event_engine_->TickUntilIdle(); HttpRequest::SetOverride(nullptr, nullptr, nullptr); } @@ -4700,12 +4735,12 @@ TEST_F(ExternalAccountCredentialsTest, SuccessWithRab500NoHeader) { auto creds = MakeRefCounted( options, std::vector(), event_engine_); auto state = RequestMetadataState::NewInstance( - absl::OkStatus(), "authorization: Bearer token_exchange_access_token"); + absl::OkStatus(), "authorization: Bearer token_exchange_access_token"); HttpRequest::SetOverride(external_account_creds_httpcli_get_rab_500, external_account_creds_httpcli_post_success, httpcli_put_should_not_be_called); - state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kGoogleTestAuthority, - kTestPath); + state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, + kGoogleTestAuthority, kTestPath); event_engine_->TickUntilIdle(); HttpRequest::SetOverride(nullptr, nullptr, nullptr); } @@ -4956,7 +4991,8 @@ TEST_F(JwtTokenFileCallCredentialsTest, Basic) { auto creds = MakeRefCounted(path, event_engine_); GRPC_CHECK_EQ(creds->min_security_level(), GRPC_PRIVACY_AND_INTEGRITY); ExecCtx exec_ctx; - auto state = RequestMetadataState::NewInstance(absl::OkStatus(), "authorization: Bearer " + token); + auto state = RequestMetadataState::NewInstance( + absl::OkStatus(), "authorization: Bearer " + token); state->RunRequestMetadataTest(creds.get(), kTestUrlScheme, kTestAuthority, kTestPath); event_engine_->TickUntilIdle(); @@ -5003,15 +5039,15 @@ void OnEmailDoneWrapper(void* /*arg*/, grpc_error_handle error) { // Wait... if it is LIFO, the LAST scheduled runs FIRST. // We want Email callback to run FIRST. // So Email callback must be LAST scheduled. - + if (g_token_on_done != nullptr) { - grpc_core::Closure::Run(DEBUG_LOCATION, g_token_on_done, absl::OkStatus()); + Closure::Run(DEBUG_LOCATION, g_token_on_done, absl::OkStatus()); g_token_on_done = nullptr; } - + // Run the original email callback last (so it runs first) if (g_original_email_on_done != nullptr) { - grpc_core::Closure::Run(DEBUG_LOCATION, g_original_email_on_done, error); + Closure::Run(DEBUG_LOCATION, g_original_email_on_done, error); } } @@ -5068,22 +5104,27 @@ TEST_F(CredentialsTest, TestComputeEngineCredsWithRabSuccess) { ExecCtx exec_ctx; g_token_on_done = nullptr; g_original_email_on_done = nullptr; - std::string emd_cached = "authorization: Bearer ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_, x-allowed-locations: 0x08"; - std::string emd_initial = "authorization: Bearer ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_"; + std::string emd_cached = + "authorization: Bearer ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_, " + "x-allowed-locations: 0x08"; + std::string emd_initial = + "authorization: Bearer ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_"; grpc_call_credentials* creds = grpc_google_compute_engine_credentials_create(nullptr); - auto state_initial = RequestMetadataState::NewInstance(absl::OkStatus(), emd_initial); - auto state_cached = RequestMetadataState::NewInstance(absl::OkStatus(), emd_cached); + auto state_initial = + RequestMetadataState::NewInstance(absl::OkStatus(), emd_initial); + auto state_cached = + RequestMetadataState::NewInstance(absl::OkStatus(), emd_cached); HttpRequest::SetOverride(compute_engine_with_rab_httpcli_get_success_override, httpcli_post_should_not_be_called, httpcli_put_should_not_be_called); // 1. Initial request (fetches email, might miss RAB) state_initial->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority, - kTestPath); + kTestPath); ExecCtx::Get()->Flush(); // 2. Secondary request (uses cached RAB) state_cached->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority, - kTestPath); + kTestPath); ExecCtx::Get()->Flush(); creds->Unref(); HttpRequest::SetOverride(nullptr, nullptr, nullptr); @@ -5107,13 +5148,15 @@ TEST_F(CredentialsTest, TestComputeEngineCredsWithRab401) { } std::atomic g_email_fetch_count{0}; -int compute_engine_concurrent_fetch_override( - const grpc_http_request* request, const URI& uri, Timestamp deadline, - grpc_closure* on_done, grpc_http_response* response) { +int compute_engine_concurrent_fetch_override(const grpc_http_request* request, + const URI& uri, Timestamp deadline, + grpc_closure* on_done, + grpc_http_response* response) { if (absl::StrContains(uri.path(), "email")) { g_email_fetch_count++; } - return compute_engine_with_rab_httpcli_get_success_override(request, uri, deadline, on_done, response); + return compute_engine_with_rab_httpcli_get_success_override( + request, uri, deadline, on_done, response); } TEST_F(CredentialsTest, TestComputeEngineCredsConcurrentFetch) { @@ -5128,9 +5171,9 @@ TEST_F(CredentialsTest, TestComputeEngineCredsConcurrentFetch) { httpcli_post_should_not_be_called, httpcli_put_should_not_be_called); state1->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority, - kTestPath); + kTestPath); state2->RunRequestMetadataTest(creds, kTestUrlScheme, kTestAuthority, - kTestPath); + kTestPath); EXPECT_EQ(g_email_fetch_count, 1); ExecCtx::Get()->Flush(); creds->Unref(); @@ -5145,7 +5188,7 @@ int compute_engine_httpcli_get_stalled_email_override( if (uri.path() == "/computeMetadata/v1/instance/service-accounts/default/email") { g_stalled_email_fetch_on_done = on_done; - return 1; // Stall the request + return 1; // Stall the request } else { *response = http_response(200, valid_oauth2_json_response); ExecCtx::Run(DEBUG_LOCATION, on_done, absl::OkStatus()); @@ -5172,15 +5215,12 @@ TEST_F(CredentialsTest, TestComputeEngineCredsEmailFetchCancellation) { auto arena = SimpleArenaAllocator()->MakeArena(); auto activity = MakeActivity( [&]() { - return Map(creds->GetRequestMetadata( - arena->MakePooled(), &args), - [](absl::StatusOr r) { - return r.status(); - }); + return Map( + creds->GetRequestMetadata(arena->MakePooled(), + &args), + [](absl::StatusOr r) { return r.status(); }); }, - ExecCtxWakeupScheduler(), - [](absl::Status res) {}, - arena.get(), &pollent); + ExecCtxWakeupScheduler(), [](absl::Status res) {}, arena.get(), &pollent); ExecCtx::Get()->Flush(); EXPECT_NE(g_stalled_email_fetch_on_done, nullptr); activity.reset(); diff --git a/test/core/credentials/call/regional_access_boundary_fetcher_test.cc b/test/core/credentials/call/regional_access_boundary_fetcher_test.cc index 6479054a59e16..fec55df78001a 100644 --- a/test/core/credentials/call/regional_access_boundary_fetcher_test.cc +++ b/test/core/credentials/call/regional_access_boundary_fetcher_test.cc @@ -16,50 +16,64 @@ #include "src/core/credentials/call/regional_access_boundary_fetcher.h" -#include - -#include #include +#include +#include #include #include #include -#include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "absl/strings/string_view.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" #include "src/core/call/metadata_batch.h" #include "src/core/credentials/call/call_credentials.h" #include "src/core/lib/promise/arena_promise.h" #include "src/core/lib/promise/poll.h" #include "src/core/lib/resource_quota/arena.h" +#include "src/core/util/http_client/httpcli.h" #include "src/core/util/ref_counted_ptr.h" #include "src/core/util/sync.h" -#include "src/core/util/http_client/httpcli.h" +#include "src/core/util/wait_for_single_owner.h" #include "test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.h" #include "test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.pb.h" #include "test/core/test_util/test_config.h" -#include "src/core/util/wait_for_single_owner.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/string_view.h" namespace grpc_core { - class RegionalAccessBoundaryFetcherTest : public ::testing::Test { protected: - using RegionalAccessBoundary = RegionalAccessBoundaryFetcher::RegionalAccessBoundary; - static constexpr grpc_core::Duration kRegioanlAccessBoundarySoftCacheGraceDuration = grpc_core::Duration::Hours(1); - bool has_cache() { grpc_core::MutexLock lock(&fetcher_->cache_mu_); return fetcher_->cache_.has_value(); } - std::string cached_encoded_locations() { grpc_core::MutexLock lock(&fetcher_->cache_mu_); return std::string(fetcher_->cache_->encoded_locations.as_string_view()); } - void set_cache(RegionalAccessBoundary cache) { grpc_core::MutexLock lock(&fetcher_->cache_mu_); fetcher_->cache_ = std::move(cache); } - bool fetch_in_flight() { grpc_core::MutexLock lock(&fetcher_->cache_mu_); return fetcher_->pending_request_ != nullptr; } - grpc_core::Timestamp next_fetch_time() { grpc_core::MutexLock lock(&fetcher_->cache_mu_); return fetcher_->next_fetch_time_; } - - - bool fetch_in_flight(RegionalAccessBoundaryFetcher* fetcher) { - grpc_core::MutexLock lock(&fetcher->cache_mu_); - return fetcher->pending_request_ != nullptr; + using RegionalAccessBoundary = + RegionalAccessBoundaryFetcher::RegionalAccessBoundary; + static constexpr Duration kRegioanlAccessBoundarySoftCacheGraceDuration = + Duration::Hours(1); + bool has_cache() { + MutexLock lock(&fetcher_->cache_mu_); + return fetcher_->cache_.has_value(); + } + std::string cached_encoded_locations() { + MutexLock lock(&fetcher_->cache_mu_); + return std::string(fetcher_->cache_->encoded_locations.as_string_view()); + } + void set_cache(RegionalAccessBoundary cache) { + MutexLock lock(&fetcher_->cache_mu_); + fetcher_->cache_ = std::move(cache); + } + bool fetch_in_flight() { + MutexLock lock(&fetcher_->cache_mu_); + return fetcher_->pending_request_ != nullptr; + } + Timestamp next_fetch_time() { + MutexLock lock(&fetcher_->cache_mu_); + return fetcher_->next_fetch_time_; + } + + bool fetch_in_flight(RegionalAccessBoundaryFetcher* fetcher) { + MutexLock lock(&fetcher->cache_mu_); + return fetcher->pending_request_ != nullptr; } WeakRefCountedPtr WeakFetcher() { @@ -67,28 +81,28 @@ class RegionalAccessBoundaryFetcherTest : public ::testing::Test { } bool IsShutdown(RegionalAccessBoundaryFetcher* fetcher) { - grpc_core::MutexLock lock(&fetcher->cache_mu_); + MutexLock lock(&fetcher->cache_mu_); return fetcher->shutdown_; } bool CheckPendingRequestIsNull(RegionalAccessBoundaryFetcher* fetcher) { - grpc_core::MutexLock lock(&fetcher->cache_mu_); + MutexLock lock(&fetcher->cache_mu_); return fetcher->pending_request_ == nullptr; } bool HasCache(RegionalAccessBoundaryFetcher* fetcher) { - grpc_core::MutexLock lock(&fetcher->cache_mu_); + MutexLock lock(&fetcher->cache_mu_); return fetcher->cache_.has_value(); } void SetUp() override { grpc_init(); - fuzzing_event_engine_ = std::make_shared( - grpc_event_engine::experimental::FuzzingEventEngine::Options(), - fuzzing_event_engine::Actions()); + fuzzing_event_engine_ = + std::make_shared( + grpc_event_engine::experimental::FuzzingEventEngine::Options(), + fuzzing_event_engine::Actions()); fetcher_ = RegionalAccessBoundaryFetcher::Create( - "https://googleapis.com", - fuzzing_event_engine_, + "https://googleapis.com", fuzzing_event_engine_, BackOff::Options() .set_initial_backoff(Duration::Seconds(1)) .set_multiplier(1.1) @@ -104,7 +118,7 @@ class RegionalAccessBoundaryFetcherTest : public ::testing::Test { void TearDown() override { HttpRequest::SetOverride(nullptr, nullptr, nullptr); { - grpc_core::ExecCtx exec_ctx; + ExecCtx exec_ctx; fetcher_.reset(); } fuzzing_event_engine_->FuzzingDone(); @@ -117,35 +131,39 @@ class RegionalAccessBoundaryFetcherTest : public ::testing::Test { RefCountedPtr fetcher_; RefCountedPtr arena_; ClientMetadataHandle metadata_; - std::shared_ptr fuzzing_event_engine_; + std::shared_ptr + fuzzing_event_engine_; }; namespace { - TEST_F(RegionalAccessBoundaryFetcherTest, CacheMissTriggersFetch) { - metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), [](absl::string_view, const Slice&) { abort(); }); + metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), + [](absl::string_view, const Slice&) { abort(); }); fetcher_->Fetch("", *metadata_); EXPECT_TRUE(fetch_in_flight()); } TEST_F(RegionalAccessBoundaryFetcherTest, CacheHitDoesNotTriggerFetch) { - set_cache(RegionalAccessBoundary{ - Slice::FromStaticString("us-west1"), grpc_core::Timestamp::Now() + grpc_core::Duration::Seconds(7200)}); - metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), [](absl::string_view, const Slice&) { abort(); }); + set_cache(RegionalAccessBoundary{Slice::FromStaticString("us-west1"), + Timestamp::Now() + Duration::Seconds(7200)}); + metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), + [](absl::string_view, const Slice&) { abort(); }); fetcher_->Fetch("", *metadata_); EXPECT_FALSE(fetch_in_flight()); std::string buffer; - std::optional value = metadata_->GetStringValue("x-allowed-locations", &buffer); + std::optional value = + metadata_->GetStringValue("x-allowed-locations", &buffer); EXPECT_THAT(value, ::testing::Optional(absl::string_view("us-west1"))); } TEST_F(RegionalAccessBoundaryFetcherTest, ExpiredCacheTriggersFetch) { ExecCtx exec_ctx; - set_cache(RegionalAccessBoundary{ - Slice::FromStaticString("us-west1"), grpc_core::Timestamp::Now() + grpc_core::Duration::Seconds(100)}); - fuzzing_event_engine_->TickForDuration(grpc_core::Duration::Seconds(101)); - metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), [](absl::string_view, const Slice&) { abort(); }); + set_cache(RegionalAccessBoundary{Slice::FromStaticString("us-west1"), + Timestamp::Now() + Duration::Seconds(100)}); + fuzzing_event_engine_->TickForDuration(Duration::Seconds(101)); + metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), + [](absl::string_view, const Slice&) { abort(); }); fetcher_->Fetch("", *metadata_); EXPECT_TRUE(fetch_in_flight()); } @@ -162,11 +180,14 @@ TEST_F(RegionalAccessBoundaryFetcherTest, InvalidUriParsing) { } TEST_F(RegionalAccessBoundaryFetcherTest, RegionalEndpointIgnored) { - metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), [](absl::string_view, const Slice&) { abort(); }); - metadata_->Set(HttpAuthorityMetadata(), Slice::FromStaticString("rep.googleapis.com")); + metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), + [](absl::string_view, const Slice&) { abort(); }); + metadata_->Set(HttpAuthorityMetadata(), + Slice::FromStaticString("rep.googleapis.com")); fetcher_->Fetch("", *metadata_); EXPECT_FALSE(fetch_in_flight()); - metadata_->Set(HttpAuthorityMetadata(), Slice::FromStaticString("foo.rep.googleapis.com")); + metadata_->Set(HttpAuthorityMetadata(), + Slice::FromStaticString("foo.rep.googleapis.com")); fetcher_->Fetch("", *metadata_); EXPECT_FALSE(fetch_in_flight()); } @@ -184,7 +205,9 @@ int httpcli_get_valid_json(const grpc_http_request* /*request*/, const URI& /*uri*/, Timestamp /*deadline*/, grpc_closure* on_done, grpc_http_response* response) { - *response = http_response(200, "{\"encodedLocations\": \"us-west1\", \"locations\": [\"us-west1\"]}"); + *response = http_response( + 200, + "{\"encodedLocations\": \"us-west1\", \"locations\": [\"us-west1\"]}"); ExecCtx::Run(DEBUG_LOCATION, on_done, absl::OkStatus()); return 1; } @@ -192,8 +215,10 @@ int httpcli_get_valid_json(const grpc_http_request* /*request*/, TEST_F(RegionalAccessBoundaryFetcherTest, GoogleApisEndpointAllowed) { ExecCtx exec_ctx; HttpRequest::SetOverride(httpcli_get_valid_json, nullptr, nullptr); - metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), [](absl::string_view, const Slice&) { abort(); }); - metadata_->Set(HttpAuthorityMetadata(), Slice::FromStaticString("googleapis.com")); + metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), + [](absl::string_view, const Slice&) { abort(); }); + metadata_->Set(HttpAuthorityMetadata(), + Slice::FromStaticString("googleapis.com")); fetcher_->Fetch("", *metadata_); EXPECT_TRUE(fetch_in_flight()); } @@ -201,8 +226,10 @@ TEST_F(RegionalAccessBoundaryFetcherTest, GoogleApisEndpointAllowed) { TEST_F(RegionalAccessBoundaryFetcherTest, GoogleApisEndpointWithPortAllowed) { ExecCtx exec_ctx; HttpRequest::SetOverride(httpcli_get_valid_json, nullptr, nullptr); - metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), [](absl::string_view, const Slice&) { abort(); }); - metadata_->Set(HttpAuthorityMetadata(), Slice::FromStaticString("googleapis.com:443")); + metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), + [](absl::string_view, const Slice&) { abort(); }); + metadata_->Set(HttpAuthorityMetadata(), + Slice::FromStaticString("googleapis.com:443")); fetcher_->Fetch("", *metadata_); EXPECT_TRUE(fetch_in_flight()); } @@ -210,35 +237,40 @@ TEST_F(RegionalAccessBoundaryFetcherTest, GoogleApisEndpointWithPortAllowed) { TEST_F(RegionalAccessBoundaryFetcherTest, SubdomainGoogleApisEndpointAllowed) { ExecCtx exec_ctx; HttpRequest::SetOverride(httpcli_get_valid_json, nullptr, nullptr); - metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), [](absl::string_view, const Slice&) { abort(); }); - metadata_->Set(HttpAuthorityMetadata(), Slice::FromStaticString("pubsub.googleapis.com")); + metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), + [](absl::string_view, const Slice&) { abort(); }); + metadata_->Set(HttpAuthorityMetadata(), + Slice::FromStaticString("pubsub.googleapis.com")); fetcher_->Fetch("", *metadata_); EXPECT_TRUE(fetch_in_flight()); } int httpcli_get_malformed_json(const grpc_http_request* /*request*/, - const URI& /*uri*/, Timestamp /*deadline*/, - grpc_closure* on_done, - grpc_http_response* response) { + const URI& /*uri*/, Timestamp /*deadline*/, + grpc_closure* on_done, + grpc_http_response* response) { *response = http_response(200, "{\"encodedLocations\""); ExecCtx::Run(DEBUG_LOCATION, on_done, absl::OkStatus()); return 1; } int httpcli_get_missing_fields_json(const grpc_http_request* /*request*/, - const URI& /*uri*/, Timestamp /*deadline*/, - grpc_closure* on_done, - grpc_http_response* response) { + const URI& /*uri*/, Timestamp /*deadline*/, + grpc_closure* on_done, + grpc_http_response* response) { *response = http_response(200, "{\"locations\": [\"us-west1\"]}"); ExecCtx::Run(DEBUG_LOCATION, on_done, absl::OkStatus()); return 1; } -int httpcli_get_valid_json_with_non_string_locations(const grpc_http_request* /*request*/, - const URI& /*uri*/, Timestamp /*deadline*/, - grpc_closure* on_done, - grpc_http_response* response) { - *response = http_response(200, "{\"encodedLocations\": \"us-west1\", \"locations\": [\"us-west1\", 123, \"us-east1\"]}"); +int httpcli_get_valid_json_with_non_string_locations( + const grpc_http_request* /*request*/, const URI& /*uri*/, + Timestamp /*deadline*/, grpc_closure* on_done, + grpc_http_response* response) { + *response = + http_response(200, + "{\"encodedLocations\": \"us-west1\", \"locations\": " + "[\"us-west1\", 123, \"us-east1\"]}"); ExecCtx::Run(DEBUG_LOCATION, on_done, absl::OkStatus()); return 1; } @@ -246,44 +278,48 @@ int httpcli_get_valid_json_with_non_string_locations(const grpc_http_request* /* TEST_F(RegionalAccessBoundaryFetcherTest, ValidJsonResponse) { ExecCtx exec_ctx; HttpRequest::SetOverride(httpcli_get_valid_json, nullptr, nullptr); - metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), [](absl::string_view, const Slice&) { abort(); }); + metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), + [](absl::string_view, const Slice&) { abort(); }); fetcher_->Fetch("", *metadata_); EXPECT_TRUE(fetch_in_flight()); ExecCtx::Get()->Flush(); EXPECT_FALSE(fetch_in_flight()); EXPECT_TRUE(has_cache()); EXPECT_EQ(cached_encoded_locations(), "us-west1"); - } TEST_F(RegionalAccessBoundaryFetcherTest, MalformedJsonResponse) { ExecCtx exec_ctx; HttpRequest::SetOverride(httpcli_get_malformed_json, nullptr, nullptr); - metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), [](absl::string_view, const Slice&) { abort(); }); + metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), + [](absl::string_view, const Slice&) { abort(); }); fetcher_->Fetch("", *metadata_); EXPECT_TRUE(fetch_in_flight()); ExecCtx::Get()->Flush(); EXPECT_FALSE(fetch_in_flight()); EXPECT_FALSE(has_cache()); - EXPECT_GT(next_fetch_time(), grpc_core::Timestamp::Now()); + EXPECT_GT(next_fetch_time(), Timestamp::Now()); } TEST_F(RegionalAccessBoundaryFetcherTest, ValidJsonMissingFields) { ExecCtx exec_ctx; HttpRequest::SetOverride(httpcli_get_missing_fields_json, nullptr, nullptr); - metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), [](absl::string_view, const Slice&) { abort(); }); + metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), + [](absl::string_view, const Slice&) { abort(); }); fetcher_->Fetch("", *metadata_); EXPECT_TRUE(fetch_in_flight()); ExecCtx::Get()->Flush(); EXPECT_FALSE(fetch_in_flight()); EXPECT_FALSE(has_cache()); - EXPECT_GT(next_fetch_time(), grpc_core::Timestamp::Now()); + EXPECT_GT(next_fetch_time(), Timestamp::Now()); } TEST_F(RegionalAccessBoundaryFetcherTest, ValidJsonWithNonStringLocations) { ExecCtx exec_ctx; - HttpRequest::SetOverride(httpcli_get_valid_json_with_non_string_locations, nullptr, nullptr); - metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), [](absl::string_view, const Slice&) { abort(); }); + HttpRequest::SetOverride(httpcli_get_valid_json_with_non_string_locations, + nullptr, nullptr); + metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), + [](absl::string_view, const Slice&) { abort(); }); fetcher_->Fetch("", *metadata_); EXPECT_TRUE(fetch_in_flight()); ExecCtx::Get()->Flush(); @@ -294,20 +330,18 @@ TEST_F(RegionalAccessBoundaryFetcherTest, ValidJsonWithNonStringLocations) { int g_mock_get_count = 0; -int httpcli_get_500(const grpc_http_request* /*request*/, - const URI& /*uri*/, Timestamp /*deadline*/, - grpc_closure* on_done, - grpc_http_response* response) { +int httpcli_get_500(const grpc_http_request* /*request*/, const URI& /*uri*/, + Timestamp /*deadline*/, grpc_closure* on_done, + grpc_http_response* response) { g_mock_get_count++; *response = http_response(500, ""); ExecCtx::Run(DEBUG_LOCATION, on_done, absl::OkStatus()); return 1; } -int httpcli_get_404(const grpc_http_request* /*request*/, - const URI& /*uri*/, Timestamp /*deadline*/, - grpc_closure* on_done, - grpc_http_response* response) { +int httpcli_get_404(const grpc_http_request* /*request*/, const URI& /*uri*/, + Timestamp /*deadline*/, grpc_closure* on_done, + grpc_http_response* response) { g_mock_get_count++; *response = http_response(404, ""); ExecCtx::Run(DEBUG_LOCATION, on_done, absl::OkStatus()); @@ -318,15 +352,19 @@ TEST_F(RegionalAccessBoundaryFetcherTest, RetryableHttpErrors) { ExecCtx exec_ctx; g_mock_get_count = 0; HttpRequest::SetOverride(httpcli_get_500, nullptr, nullptr); - metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), [](absl::string_view, const Slice&) { abort(); }); + metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), + [](absl::string_view, const Slice&) { abort(); }); fetcher_->Fetch("", *metadata_); EXPECT_TRUE(fetch_in_flight()); ExecCtx::Get()->Flush(); - // After failure, fetch should no longer be in flight (request is reset during backoff). + // After failure, fetch should no longer be in flight (request is reset during + // backoff). EXPECT_FALSE(fetch_in_flight()); EXPECT_EQ(g_mock_get_count, 1); // We can advance time and retry. - fuzzing_event_engine_->TickForDuration(grpc_core::Duration::Seconds(1) * 1.5 + grpc_core::Duration::Milliseconds(100)); // Initial backoff (1s) + jitter + buffer + fuzzing_event_engine_->TickForDuration( + Duration::Seconds(1) * 1.5 + + Duration::Milliseconds(100)); // Initial backoff (1s) + jitter + buffer fetcher_->Fetch("", *metadata_); EXPECT_TRUE(fetch_in_flight()); ExecCtx::Get()->Flush(); @@ -338,14 +376,15 @@ TEST_F(RegionalAccessBoundaryFetcherTest, RetryClearsPendingRequest) { ExecCtx exec_ctx; g_mock_get_count = 0; HttpRequest::SetOverride(httpcli_get_500, nullptr, nullptr); - metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), [](absl::string_view, const Slice&) { abort(); }); + metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), + [](absl::string_view, const Slice&) { abort(); }); fetcher_->Fetch("", *metadata_); EXPECT_TRUE(fetch_in_flight()); ExecCtx::Get()->Flush(); // Request should be cleared (reset) during backoff. EXPECT_FALSE(fetch_in_flight()); // Advance time past backoff to allow retry. - fuzzing_event_engine_->TickForDuration(grpc_core::Duration::Seconds(10)); + fuzzing_event_engine_->TickForDuration(Duration::Seconds(10)); // Trigger retry. fetcher_->Fetch("", *metadata_); EXPECT_TRUE(fetch_in_flight()); @@ -353,13 +392,11 @@ TEST_F(RegionalAccessBoundaryFetcherTest, RetryClearsPendingRequest) { EXPECT_EQ(g_mock_get_count, 2); } - - - TEST_F(RegionalAccessBoundaryFetcherTest, CancelPendingFetch) { ExecCtx exec_ctx; HttpRequest::SetOverride(httpcli_get_500, nullptr, nullptr); - metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), [](absl::string_view, const Slice&) { abort(); }); + metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), + [](absl::string_view, const Slice&) { abort(); }); fetcher_->Fetch("", *metadata_); EXPECT_TRUE(fetch_in_flight()); ExecCtx::Get()->Flush(); @@ -373,9 +410,8 @@ grpc_closure* g_stalled_on_done = nullptr; grpc_http_response* g_stalled_response = nullptr; int httpcli_get_stalled(const grpc_http_request* /*request*/, - const URI& /*uri*/, Timestamp /*deadline*/, - grpc_closure* on_done, - grpc_http_response* response) { + const URI& /*uri*/, Timestamp /*deadline*/, + grpc_closure* on_done, grpc_http_response* response) { g_mock_get_count++; g_stalled_on_done = on_done; g_stalled_response = response; @@ -383,22 +419,25 @@ int httpcli_get_stalled(const grpc_http_request* /*request*/, return 1; } -TEST_F(RegionalAccessBoundaryFetcherTest, CancelPendingFetchWithInFlightRequest) { +TEST_F(RegionalAccessBoundaryFetcherTest, + CancelPendingFetchWithInFlightRequest) { ExecCtx exec_ctx; g_mock_get_count = 0; g_stalled_on_done = nullptr; g_stalled_response = nullptr; HttpRequest::SetOverride(httpcli_get_stalled, nullptr, nullptr); - metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), [](absl::string_view, const Slice&) { abort(); }); + metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), + [](absl::string_view, const Slice&) { abort(); }); fetcher_->Fetch("", *metadata_); EXPECT_TRUE(fetch_in_flight()); // Cancel immediately while the HTTP request is still in flight (but stalled). auto fetcher = WeakFetcher(); fetcher_.reset(); - // The fetcher should have cleared the pending request internally. We can verify - // by executing the stashed on_done closure explicitly. + // The fetcher should have cleared the pending request internally. We can + // verify by executing the stashed on_done closure explicitly. if (g_stalled_on_done != nullptr) { - ExecCtx::Run(DEBUG_LOCATION, g_stalled_on_done, absl::CancelledError("orphaned")); + ExecCtx::Run(DEBUG_LOCATION, g_stalled_on_done, + absl::CancelledError("orphaned")); } ExecCtx::Get()->Flush(); // The fetch shouldn't be in flight anymore since it was cleanly aborted. @@ -412,7 +451,8 @@ TEST_F(RegionalAccessBoundaryFetcherTest, ResponseAfterShutdownIgnored) { g_stalled_on_done = nullptr; g_stalled_response = nullptr; HttpRequest::SetOverride(httpcli_get_stalled, nullptr, nullptr); - metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), [](absl::string_view, const Slice&) { abort(); }); + metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), + [](absl::string_view, const Slice&) { abort(); }); fetcher_->Fetch("", *metadata_); EXPECT_TRUE(fetch_in_flight()); auto fetcher = WeakFetcher(); @@ -424,7 +464,9 @@ TEST_F(RegionalAccessBoundaryFetcherTest, ResponseAfterShutdownIgnored) { EXPECT_TRUE(CheckPendingRequestIsNull(fetcher_raw)); // Simulate a successful response arriving AFTER shutdown if (g_stalled_response != nullptr) { - *g_stalled_response = http_response(200, "{\"encodedLocations\": \"us-west1\", \"locations\": [\"us-west1\"]}"); + *g_stalled_response = http_response( + 200, + "{\"encodedLocations\": \"us-west1\", \"locations\": [\"us-west1\"]}"); } if (g_stalled_on_done != nullptr) { ExecCtx::Run(DEBUG_LOCATION, g_stalled_on_done, absl::OkStatus()); @@ -439,33 +481,41 @@ TEST_F(RegionalAccessBoundaryFetcherTest, BackoffResetsOnSuccess) { g_mock_get_count = 0; // 1. Force a 500 error to trigger backoff. HttpRequest::SetOverride(httpcli_get_500, nullptr, nullptr); - metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), [](absl::string_view, const Slice&) { abort(); }); + metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), + [](absl::string_view, const Slice&) { abort(); }); fetcher_->Fetch("", *metadata_); EXPECT_TRUE(fetch_in_flight()); ExecCtx::Get()->Flush(); EXPECT_FALSE(fetch_in_flight()); // Verify backoff is active (next fetch time is in future) - grpc_core::Timestamp after_first_fail = grpc_core::Timestamp::Now(); + Timestamp after_first_fail = Timestamp::Now(); EXPECT_GT(next_fetch_time(), after_first_fail); // 2. Advance time past backoff. - fuzzing_event_engine_->TickForDuration(grpc_core::Duration::Seconds(2)); + fuzzing_event_engine_->TickForDuration(Duration::Seconds(2)); // 3. Force a success. HttpRequest::SetOverride(httpcli_get_valid_json, nullptr, nullptr); auto metadata_success = arena_->MakePooled(); - metadata_success->Set(HttpAuthorityMetadata(), Slice::FromStaticString("googleapis.com")); - metadata_success->Append("authorization", Slice::FromStaticString("Bearer token"), [](absl::string_view, const Slice&) { abort(); }); + metadata_success->Set(HttpAuthorityMetadata(), + Slice::FromStaticString("googleapis.com")); + metadata_success->Append("authorization", + Slice::FromStaticString("Bearer token"), + [](absl::string_view, const Slice&) { abort(); }); fetcher_->Fetch("", *metadata_success); EXPECT_TRUE(fetch_in_flight()); ExecCtx::Get()->Flush(); EXPECT_FALSE(fetch_in_flight()); EXPECT_TRUE(has_cache()); // 4. Invalidate cache to force new fetch. - set_cache(RegionalAccessBoundary{Slice::FromStaticString(""), grpc_core::Timestamp::InfPast()}); + set_cache(RegionalAccessBoundary{Slice::FromStaticString(""), + Timestamp::InfPast()}); // 5. Force another 500 error. HttpRequest::SetOverride(httpcli_get_500, nullptr, nullptr); auto metadata_fail = arena_->MakePooled(); - metadata_fail->Set(HttpAuthorityMetadata(), Slice::FromStaticString("googleapis.com")); - metadata_fail->Append("authorization", Slice::FromStaticString("Bearer token"), [](absl::string_view, const Slice&) { abort(); }); + metadata_fail->Set(HttpAuthorityMetadata(), + Slice::FromStaticString("googleapis.com")); + metadata_fail->Append("authorization", + Slice::FromStaticString("Bearer token"), + [](absl::string_view, const Slice&) { abort(); }); fetcher_->Fetch("", *metadata_fail); EXPECT_TRUE(fetch_in_flight()); ExecCtx::Get()->Flush(); @@ -475,27 +525,32 @@ TEST_F(RegionalAccessBoundaryFetcherTest, BackoffResetsOnSuccess) { // If it wasn't reset, it would be much larger (exponentially increased). // Initial backoff is 1ms. W/ jitter & multiplier it's small. // We can just verify it is > now and <= now + max_expected_initial_backoff. - grpc_core::Timestamp now = grpc_core::Timestamp::Now(); - grpc_core::Timestamp next = next_fetch_time(); + Timestamp now = Timestamp::Now(); + Timestamp next = next_fetch_time(); EXPECT_GT(next, now); // Initial backoff is 1s (set in SetUp). W/ jitter (0.1) it's [0.9s, 1.1s]. - EXPECT_LT(next - now, grpc_core::Duration::Milliseconds(1200)); - EXPECT_GT(next - now, grpc_core::Duration::Milliseconds(800)); + EXPECT_LT(next - now, Duration::Milliseconds(1200)); + EXPECT_GT(next - now, Duration::Milliseconds(800)); } TEST_F(RegionalAccessBoundaryFetcherTest, CacheSoftExpirationTriggersRefresh) { ExecCtx exec_ctx; g_mock_get_count = 0; HttpRequest::SetOverride(httpcli_get_valid_json, nullptr, nullptr); - grpc_core::Timestamp now = grpc_core::Timestamp::Now(); - grpc_core::Timestamp soft_expired_timestamp = now + (kRegioanlAccessBoundarySoftCacheGraceDuration / 2); - set_cache(RegionalAccessBoundary{Slice::FromStaticString("us-east1"), soft_expired_timestamp}); + Timestamp now = Timestamp::Now(); + Timestamp soft_expired_timestamp = + now + (kRegioanlAccessBoundarySoftCacheGraceDuration / 2); + set_cache(RegionalAccessBoundary{Slice::FromStaticString("us-east1"), + soft_expired_timestamp}); // Verify our mock cache setup is correct EXPECT_TRUE(has_cache()); EXPECT_FALSE(fetch_in_flight()); auto metadata_soft_expired = arena_->MakePooled(); - metadata_soft_expired->Append("authorization", Slice::FromStaticString("Bearer token"), [](absl::string_view, const Slice&) { abort(); }); - metadata_soft_expired->Set(HttpAuthorityMetadata(), Slice::FromStaticString("googleapis.com")); + metadata_soft_expired->Append( + "authorization", Slice::FromStaticString("Bearer token"), + [](absl::string_view, const Slice&) { abort(); }); + metadata_soft_expired->Set(HttpAuthorityMetadata(), + Slice::FromStaticString("googleapis.com")); fetcher_->Fetch("https://googleapis.com", *metadata_soft_expired); // We should still get the cached location EXPECT_EQ(cached_encoded_locations(), "us-east1"); @@ -520,7 +575,7 @@ class EmailFetcherTest : public ::testing::Test { void TearDown() override { HttpRequest::SetOverride(nullptr, nullptr, nullptr); - email_fetcher_.reset(); // Ensure cleanup before grpc_shutdown + email_fetcher_.reset(); // Ensure cleanup before grpc_shutdown grpc_shutdown(); } @@ -533,11 +588,14 @@ int httpcli_get_email_success(const grpc_http_request* /*request*/, const URI& uri, Timestamp /*deadline*/, grpc_closure* on_done, grpc_http_response* response) { - if (uri.path() == "/computeMetadata/v1/instance/service-accounts/default/email") { + if (uri.path() == + "/computeMetadata/v1/instance/service-accounts/default/email") { *response = http_response(200, "foo@bar.com"); } else { // RAB fetch - *response = http_response(200, "{\"encodedLocations\": \"us-west1\", \"locations\": [\"us-west1\"]}"); + *response = http_response( + 200, + "{\"encodedLocations\": \"us-west1\", \"locations\": [\"us-west1\"]}"); } ExecCtx::Run(DEBUG_LOCATION, on_done, absl::OkStatus()); return 1; @@ -553,22 +611,28 @@ TEST_F(EmailFetcherTest, FetchesEmailAndThenRab) { // Since we use ExecCtx::Run in mock, flushing ExecCtx should handle it. ExecCtx::Get()->Flush(); // Now call Fetch. It should allow RAB fetch to proceed. - metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), [](absl::string_view, const Slice&) { abort(); }); - metadata_->Append(":authority", Slice::FromStaticString("foo.googleapis.com"), [](absl::string_view, const Slice&) { abort(); }); + metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), + [](absl::string_view, const Slice&) { abort(); }); + metadata_->Append(":authority", Slice::FromStaticString("foo.googleapis.com"), + [](absl::string_view, const Slice&) { abort(); }); email_fetcher_->Fetch("token", *metadata_); // RAB fetch should happen (async). ExecCtx::Get()->Flush(); // First fetch won't have metadata (cache miss). std::string buffer; - std::optional value = metadata_->GetStringValue("x-allowed-locations", &buffer); + std::optional value = + metadata_->GetStringValue("x-allowed-locations", &buffer); EXPECT_FALSE(value.has_value()); // Verify cache is populated by fetching again. metadata_ = arena_->MakePooled(); - metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), [](absl::string_view, const Slice&) { abort(); }); - metadata_->Append(":authority", Slice::FromStaticString("foo.googleapis.com"), [](absl::string_view, const Slice&) { abort(); }); + metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), + [](absl::string_view, const Slice&) { abort(); }); + metadata_->Append(":authority", Slice::FromStaticString("foo.googleapis.com"), + [](absl::string_view, const Slice&) { abort(); }); email_fetcher_->Fetch("token", *metadata_); std::string buffer2; - std::optional value2 = metadata_->GetStringValue("x-allowed-locations", &buffer2); + std::optional value2 = + metadata_->GetStringValue("x-allowed-locations", &buffer2); EXPECT_THAT(value2, ::testing::Optional(absl::string_view("us-west1"))); } @@ -586,19 +650,21 @@ TEST_F(EmailFetcherTest, EmailFetchFailureSkipsRab) { HttpRequest::SetOverride(httpcli_get_email_failure, nullptr, nullptr); email_fetcher_->StartEmailFetch(); ExecCtx::Get()->Flush(); - metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), [](absl::string_view, const Slice&) { abort(); }); + metadata_->Append("authorization", Slice::FromStaticString("Bearer token"), + [](absl::string_view, const Slice&) { abort(); }); email_fetcher_->Fetch("token", *metadata_); ExecCtx::Get()->Flush(); std::string buffer; - std::optional value = metadata_->GetStringValue("x-allowed-locations", &buffer); + std::optional value = + metadata_->GetStringValue("x-allowed-locations", &buffer); EXPECT_FALSE(value.has_value()); } - int httpcli_get_email_failure_counted(const grpc_http_request* /*request*/, - const URI& /*uri*/, Timestamp /*deadline*/, - grpc_closure* on_done, - grpc_http_response* response) { + const URI& /*uri*/, + Timestamp /*deadline*/, + grpc_closure* on_done, + grpc_http_response* response) { g_mock_get_count++; *response = http_response(404, "Not Found"); ExecCtx::Run(DEBUG_LOCATION, on_done, absl::OkStatus()); @@ -618,7 +684,7 @@ TEST_F(EmailFetcherTest, EmailFetchBackoffRespected) { ExecCtx::Get()->Flush(); EXPECT_EQ(g_mock_get_count, 1); // 3. Advance time past initial backoff (1s with jitters, say 2s to be safe). - exec_ctx.TestOnlySetNow(grpc_core::Timestamp::Now() + grpc_core::Duration::Seconds(20)); + exec_ctx.TestOnlySetNow(Timestamp::Now() + Duration::Seconds(20)); // 4. Retry should proceed. email_fetcher_->StartEmailFetch(); ExecCtx::Get()->Flush(); @@ -636,7 +702,6 @@ TEST_F(EmailFetcherTest, EarlyDestructionDoesNotCrash) { } // namespace - } // namespace grpc_core int main(int argc, char** argv) {