2020import logging
2121import os
2222import threading
23- from typing import NamedTuple , Optional
23+ from typing import NamedTuple , Optional , TYPE_CHECKING
2424
2525from google .auth import _helpers
2626from google .auth import environment_vars
2727
28+ if TYPE_CHECKING :
29+ import google .auth .credentials
30+ import google .auth .transport
31+
2832_LOGGER = logging .getLogger (__name__ )
2933
3034
@@ -97,6 +101,7 @@ def __init__(self):
97101 )
98102 self .refresh_manager = _RegionalAccessBoundaryRefreshManager ()
99103 self ._update_lock = threading .Lock ()
104+ self ._use_blocking_regional_access_boundary_lookup = False
100105
101106 def __getstate__ (self ):
102107 """Pickle helper that serializes the _update_lock attribute."""
@@ -109,6 +114,43 @@ def __setstate__(self, state):
109114 self .__dict__ .update (state )
110115 self ._update_lock = threading .Lock ()
111116
117+ def __eq__ (self , other ):
118+ """Checks if two managers are equal."""
119+ if not isinstance (other , _RegionalAccessBoundaryManager ):
120+ return NotImplemented
121+ return (
122+ self ._data == other ._data
123+ and self ._use_blocking_regional_access_boundary_lookup
124+ == other ._use_blocking_regional_access_boundary_lookup
125+ )
126+
127+ def enable_blocking_lookup (self ):
128+ """Enables blocking Regional Access Boundary lookup.
129+
130+ When enabled, the Regional Access Boundary lookup will be performed
131+ synchronously in the calling thread instead of asynchronously in a
132+ background thread.
133+ """
134+ self ._use_blocking_regional_access_boundary_lookup = True
135+
136+ def set_initial_regional_access_boundary (self , encoded_locations = None , expiry = None ):
137+ """Manually sets the regional access boundary to the client provided seed.
138+
139+ Args:
140+ encoded_locations (Optional[str]): The encoded locations string.
141+ expiry (Optional[datetime.datetime]): The expiry time for the boundary.
142+ If encoded_locations is not provided, expiry is ignored.
143+ """
144+ if not encoded_locations :
145+ expiry = None
146+
147+ self ._data = _RegionalAccessBoundaryData (
148+ encoded_locations = encoded_locations ,
149+ expiry = expiry ,
150+ cooldown_expiry = None ,
151+ cooldown_duration = DEFAULT_REGIONAL_ACCESS_BOUNDARY_COOLDOWN ,
152+ )
153+
112154 def apply_headers (self , headers ):
113155 """Applies the Regional Access Boundary header to the provided dictionary.
114156
@@ -151,48 +193,50 @@ def maybe_start_refresh(self, credentials, request):
151193 return
152194
153195 # If all checks pass, start the background refresh.
154- self .refresh_manager .start_refresh (credentials , request , self )
155-
156-
157- class _RegionalAccessBoundaryRefreshThread (threading .Thread ):
158- """Thread for background refreshing of the Regional Access Boundary."""
196+ if self ._use_blocking_regional_access_boundary_lookup :
197+ self .start_blocking_refresh (credentials , request )
198+ else :
199+ self .refresh_manager .start_refresh (credentials , request , self )
159200
160- def __init__ (self , credentials , request , rab_manager ):
161- super ().__init__ ()
162- self .daemon = True
163- self ._credentials = credentials
164- self ._request = request
165- self ._rab_manager = rab_manager
201+ def start_blocking_refresh (self , credentials , request ):
202+ """Initiates a blocking lookup of the Regional Access Boundary.
166203
167- def run ( self ):
168- """
169- Performs the Regional Access Boundary lookup and updates the state .
204+ If the lookup raises an exception, it is caught and logged as a warning,
205+ and the lookup is treated as a failure (entering cooldown). Exceptions
206+ are not propagated to the caller .
170207
171- This method is run in a separate thread. It delegates the actual lookup
172- to the credentials object's `_lookup_regional_access_boundary` method.
173- Based on the lookup's outcome (success or complete failure after retries),
174- it updates the cached Regional Access Boundary information,
175- its expiry, its cooldown expiry, and its exponential cooldown duration.
208+ Args:
209+ credentials (google.auth.credentials.Credentials): The credentials to refresh.
210+ request (google.auth.transport.Request): The object used to make HTTP requests.
176211 """
177- # Catch exceptions (e.g., from the underlying transport) to prevent the
178- # background thread from crashing. This ensures we can gracefully enter
179- # an exponential cooldown state on failure.
180212 try :
213+ # The fail_fast parameter is set to True to ensure we don't block the calling
214+ # thread for too long. This will do two things: 1) set a timeout to 3s
215+ # instead of the default 120s and 2) ensure we do not retry at all
181216 regional_access_boundary_info = (
182- self . _credentials . _lookup_regional_access_boundary (self . _request )
217+ credentials . _lookup_regional_access_boundary (request , fail_fast = True )
183218 )
184219 except Exception as e :
185220 if _helpers .is_logging_enabled (_LOGGER ):
186221 _LOGGER .warning (
187- "Asynchronous Regional Access Boundary lookup raised an exception: %s" ,
222+ "Blocking Regional Access Boundary lookup raised an exception: %s" ,
188223 e ,
189224 exc_info = True ,
190225 )
191226 regional_access_boundary_info = None
192227
193- with self ._rab_manager ._update_lock :
228+ self .process_regional_access_boundary_info (regional_access_boundary_info )
229+
230+ def process_regional_access_boundary_info (self , regional_access_boundary_info ):
231+ """Processes the regional access boundary info and updates the state.
232+
233+ Args:
234+ regional_access_boundary_info (Optional[Mapping[str, str]]): The regional access
235+ boundary info to process.
236+ """
237+ with self ._update_lock :
194238 # Capture the current state before calculating updates.
195- current_data = self ._rab_manager . _data
239+ current_data = self ._data
196240
197241 if regional_access_boundary_info :
198242 # On success, update the boundary and its expiry, and clear any cooldown.
@@ -206,14 +250,12 @@ def run(self):
206250 cooldown_duration = DEFAULT_REGIONAL_ACCESS_BOUNDARY_COOLDOWN ,
207251 )
208252 if _helpers .is_logging_enabled (_LOGGER ):
209- _LOGGER .debug (
210- "Asynchronous Regional Access Boundary lookup successful."
211- )
253+ _LOGGER .debug ("Regional Access Boundary lookup successful." )
212254 else :
213255 # On failure, calculate cooldown and update state.
214256 if _helpers .is_logging_enabled (_LOGGER ):
215257 _LOGGER .warning (
216- "Asynchronous Regional Access Boundary lookup failed. Entering cooldown."
258+ "Regional Access Boundary lookup failed. Entering cooldown."
217259 )
218260
219261 next_cooldown_expiry = (
@@ -241,7 +283,53 @@ def run(self):
241283 )
242284
243285 # Perform the atomic swap of the state object.
244- self ._rab_manager ._data = updated_data
286+ self ._data = updated_data
287+
288+
289+ class _RegionalAccessBoundaryRefreshThread (threading .Thread ):
290+ """Thread for background refreshing of the Regional Access Boundary."""
291+
292+ def __init__ (
293+ self ,
294+ credentials : "google.auth.credentials.CredentialsWithRegionalAccessBoundary" , # noqa: F821
295+ request : "google.auth.transport.Request" , # noqa: F821
296+ rab_manager : "_RegionalAccessBoundaryManager" ,
297+ ):
298+ super ().__init__ ()
299+ self .daemon = True
300+ self ._credentials = credentials
301+ self ._request = request
302+ self ._rab_manager = rab_manager
303+
304+ def run (self ):
305+ """
306+ Performs the Regional Access Boundary lookup and updates the state.
307+
308+ This method is run in a separate thread. It delegates the actual lookup
309+ to the credentials object's `_lookup_regional_access_boundary` method.
310+ Based on the lookup's outcome (success or complete failure after retries),
311+ it updates the cached Regional Access Boundary information,
312+ its expiry, its cooldown expiry, and its exponential cooldown duration.
313+ """
314+ # Catch exceptions (e.g., from the underlying transport) to prevent the
315+ # background thread from crashing. This ensures we can gracefully enter
316+ # an exponential cooldown state on failure.
317+ try :
318+ regional_access_boundary_info = (
319+ self ._credentials ._lookup_regional_access_boundary (self ._request )
320+ )
321+ except Exception as e :
322+ if _helpers .is_logging_enabled (_LOGGER ):
323+ _LOGGER .warning (
324+ "Asynchronous Regional Access Boundary lookup raised an exception: %s" ,
325+ e ,
326+ exc_info = True ,
327+ )
328+ regional_access_boundary_info = None
329+
330+ self ._rab_manager .process_regional_access_boundary_info (
331+ regional_access_boundary_info
332+ )
245333
246334
247335class _RegionalAccessBoundaryRefreshManager (object ):
0 commit comments