Skip to content

Commit f858a15

Browse files
authoredAug 7, 2024··
fix: retry token request on retryable status code (#1563)
* fix: retry token request on retryable status code
1 parent 5030b82 commit f858a15

File tree

3 files changed

+80
-1
lines changed

3 files changed

+80
-1
lines changed
 

‎google/auth/compute_engine/_metadata.py

+12-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
from google.auth import environment_vars
2929
from google.auth import exceptions
3030
from google.auth import metrics
31+
from google.auth import transport
3132
from google.auth._exponential_backoff import ExponentialBackoff
3233

3334
_LOGGER = logging.getLogger(__name__)
@@ -204,7 +205,17 @@ def get(
204205
for attempt in backoff:
205206
try:
206207
response = request(url=url, method="GET", headers=headers_to_use)
207-
break
208+
if response.status in transport.DEFAULT_RETRYABLE_STATUS_CODES:
209+
_LOGGER.warning(
210+
"Compute Engine Metadata server unavailable on "
211+
"attempt %s of %s. Response status: %s",
212+
attempt,
213+
retry_count,
214+
response.status,
215+
)
216+
continue
217+
else:
218+
break
208219

209220
except exceptions.TransportError as e:
210221
_LOGGER.warning(

‎system_tests/secrets.tar.enc

0 Bytes
Binary file not shown.

‎tests/compute_engine/test__metadata.py

+68
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,74 @@ def test_get_universe_domain_not_found():
431431
assert universe_domain == "googleapis.com"
432432

433433

434+
def test_get_universe_domain_retryable_error_failure():
435+
# Test that if the universe domain endpoint returns a retryable error
436+
# we should retry.
437+
#
438+
# In this case, the error persists, and we still fail after retrying.
439+
request = make_request("too many requests", status=http_client.TOO_MANY_REQUESTS)
440+
441+
with pytest.raises(exceptions.TransportError) as excinfo:
442+
_metadata.get_universe_domain(request)
443+
444+
assert excinfo.match(r"Compute Engine Metadata server unavailable")
445+
446+
request.assert_called_with(
447+
method="GET",
448+
url=_metadata._METADATA_ROOT + "universe/universe_domain",
449+
headers=_metadata._METADATA_HEADERS,
450+
)
451+
assert request.call_count == 5
452+
453+
454+
def test_get_universe_domain_retryable_error_success():
455+
# Test that if the universe domain endpoint returns a retryable error
456+
# we should retry.
457+
#
458+
# In this case, the error is temporary, and we succeed after retrying.
459+
request_error = make_request(
460+
"too many requests", status=http_client.TOO_MANY_REQUESTS
461+
)
462+
request_ok = make_request(
463+
"fake_universe_domain", headers={"content-type": "text/plain"}
464+
)
465+
466+
class _RequestErrorOnce:
467+
"""This class forwards the request parameters to `request_error` once.
468+
469+
All subsequent calls are forwarded to `request_ok`.
470+
"""
471+
472+
def __init__(self, request_error, request_ok):
473+
self._request_error = request_error
474+
self._request_ok = request_ok
475+
self._call_index = 0
476+
477+
def request(self, *args, **kwargs):
478+
if self._call_index == 0:
479+
self._call_index += 1
480+
return self._request_error(*args, **kwargs)
481+
482+
return self._request_ok(*args, **kwargs)
483+
484+
request = _RequestErrorOnce(request_error, request_ok).request
485+
486+
universe_domain = _metadata.get_universe_domain(request)
487+
488+
request_error.assert_called_once_with(
489+
method="GET",
490+
url=_metadata._METADATA_ROOT + "universe/universe_domain",
491+
headers=_metadata._METADATA_HEADERS,
492+
)
493+
request_ok.assert_called_once_with(
494+
method="GET",
495+
url=_metadata._METADATA_ROOT + "universe/universe_domain",
496+
headers=_metadata._METADATA_HEADERS,
497+
)
498+
499+
assert universe_domain == "fake_universe_domain"
500+
501+
434502
def test_get_universe_domain_other_error():
435503
# Test that if the universe domain endpoint returns an error other than 404
436504
# we should throw the error

0 commit comments

Comments
 (0)
Please sign in to comment.