Skip to content

Commit ef6e47e

Browse files
committed
refactor: exit early when agent cert config is outside well-known directory
To prevent unnecessary startup latency, this updates the agent certificate retrieval to only poll when the config points to the well-known workload directory (where Cloud Run dynamically mounts files). For all other paths, it exits early. - Extracts config parsing into a dedicated helper function - Removes the hardcoded fallback to the well-known path when no config is provided - Adds test coverage for new logic
1 parent fd44326 commit ef6e47e

2 files changed

Lines changed: 182 additions & 170 deletions

File tree

packages/google-auth/google/auth/_agent_identity_utils.py

Lines changed: 82 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -67,93 +67,80 @@ def _is_certificate_file_ready(path):
6767
st = os.stat(path)
6868
return stat.S_ISREG(st.st_mode) and st.st_size > 0
6969
except PermissionError:
70-
# Propagate PermissionError to let caller handle it (fail-fast or fallback)
70+
# Propagate PermissionError to let caller handle it (e.g., return early or fallback)
7171
raise
7272
except OSError:
7373
return False
7474

7575

7676
def get_agent_identity_certificate_path():
77-
"""Gets the certificate path from the certificate config file.
77+
"""Gets the agent certificate path from the certificate config file.
7878
7979
The path to the certificate config file is read from the
8080
GOOGLE_API_CERTIFICATE_CONFIG environment variable. This function
81-
implements a retry mechanism to handle cases where the environment
81+
can optionally trigger polling to handle cases where the environment
8282
variable is set before the files are available on the filesystem.
8383
8484
Returns:
85-
str: The path to the leaf certificate file.
85+
Optional[str]: The path to the agent's certificate file, or None if unavailable.
8686
8787
Raises:
8888
google.auth.exceptions.RefreshError: If the certificate config file
8989
or the certificate file cannot be found after retries.
9090
"""
91-
import json
92-
9391
cert_config_path = os.environ.get(environment_vars.GOOGLE_API_CERTIFICATE_CONFIG)
9492

95-
# Check if the well-known workload directory is mounted.
93+
if not cert_config_path:
94+
return None
95+
96+
# We trigger polling only if the config path points to the well-known directory.
97+
# Cloud Run dynamically generates these files in this directory, and both the
98+
# config file and the certificate file may experience a brief startup latency.
99+
# For all other paths, we return early to avoid introducing unnecessary startup
100+
# delays.
96101
well_known_dir = os.path.dirname(_WELL_KNOWN_CERT_PATH)
97-
has_well_known_dir = os.path.exists(well_known_dir)
102+
should_poll = cert_config_path.startswith(well_known_dir)
98103

99-
# If we have neither a config path nor a well-known mount directory, exit immediately.
100-
if not cert_config_path and not has_well_known_dir:
101-
return None
104+
return _get_cert_path_with_optional_polling(cert_config_path, should_poll)
102105

103-
# If ECP config path is specified but does not exist, and we are on a workstation, fail-fast immediately.
104-
if (
105-
cert_config_path
106-
and not has_well_known_dir
107-
and not os.path.exists(cert_config_path)
108-
):
109-
return None
110106

107+
def _get_cert_path_with_optional_polling(cert_config_path, should_poll):
108+
"""Gets the certificate path, optionally polling until it is ready.
109+
110+
Args:
111+
cert_config_path (str): The path to the certificate configuration file.
112+
should_poll (bool): If True, the function will poll for the file and
113+
certificate to be ready. If False, it will check only once and
114+
return early if they are not immediately available.
115+
116+
Returns:
117+
str: The path to the certificate file, or None if unavailable.
118+
119+
Raises:
120+
google.auth.exceptions.RefreshError: If the certificate config file
121+
or the certificate file cannot be found after retries.
122+
"""
111123
has_logged_config_warning = False
112124
has_logged_cert_warning = False
113125

114126
for interval in _POLLING_INTERVALS:
115127
try:
116-
# Path A: Config file is explicitly set
117-
if cert_config_path:
118-
with open(cert_config_path, "r") as f:
119-
cert_config = json.load(f)
120-
121-
cert_configs = (
122-
cert_config.get("cert_configs")
123-
if isinstance(cert_config, dict)
124-
else None
125-
)
126-
workload_config = (
127-
cert_configs.get("workload")
128-
if isinstance(cert_configs, dict)
129-
else None
130-
)
131-
132-
if (
133-
not isinstance(workload_config, dict)
134-
or "cert_path" not in workload_config
135-
):
136-
return None
128+
cert_path = _parse_cert_path_from_config(cert_config_path)
137129

138-
cert_path = workload_config["cert_path"]
139-
if _is_certificate_file_ready(cert_path):
140-
return cert_path
141-
142-
# The config was parsed, but the cert file is not ready yet
143-
target_path = cert_path
130+
if cert_path is None:
131+
return None
144132

145-
# Path B: Config is NOT set, fallback to the well-known path
146-
else:
147-
if _is_certificate_file_ready(_WELL_KNOWN_CERT_PATH):
148-
return _WELL_KNOWN_CERT_PATH
133+
if _is_certificate_file_ready(cert_path):
134+
return cert_path
149135

150-
# The well-known cert file is not ready yet
151-
target_path = _WELL_KNOWN_CERT_PATH
136+
# The config was parsed, but the cert file is not ready yet
137+
if not should_poll:
138+
# If polling is disabled, return early.
139+
return None
152140

153-
# Log a warning on the first failed attempt to load the certificate file
154141
if not has_logged_cert_warning:
155142
warnings.warn(
156-
f"Certificate file not ready at {target_path}. Retrying until startup timeout (up to {_TOTAL_TIMEOUT} seconds total)..."
143+
f"Certificate file not ready at {cert_path}. Retrying until startup timeout (up to {_TOTAL_TIMEOUT} seconds total)..."
157144
)
158145
has_logged_cert_warning = True
159146

@@ -164,25 +151,24 @@ def get_agent_identity_certificate_path():
164151
)
165152
return None
166153
except (IOError, ValueError, KeyError) as e:
167-
if cert_config_path and os.path.exists(cert_config_path):
154+
if os.path.exists(cert_config_path):
168155
# If the file exists but has invalid JSON or is unreadable,
169-
# we assume it is in its final format and fail-fast by returning None.
156+
# we assume it is in its final format and return early (returning None).
157+
return None
158+
159+
if not should_poll:
160+
# If polling is disabled, return early if the file doesn't exist.
170161
return None
171162

172-
if not has_logged_config_warning and cert_config_path:
163+
if not has_logged_config_warning:
173164
warnings.warn(
174165
f"Certificate config file not found or incomplete: {e} (from "
175166
f"{environment_vars.GOOGLE_API_CERTIFICATE_CONFIG} environment variable). "
176167
f"Retrying until startup timeout (up to {_TOTAL_TIMEOUT} seconds total)..."
177168
)
178169
has_logged_config_warning = True
179-
pass
180170

181-
# A sleep is required in two cases:
182-
# 1. The config file is not found (the except block).
183-
# 2. The config file/well-known path is found, but the certificate is not yet available.
184-
# In both cases, we need to poll, so we sleep on every iteration
185-
# that doesn't return a certificate.
171+
# Sleep before the next polling attempt.
186172
time.sleep(interval)
187173

188174
raise exceptions.RefreshError(
@@ -193,6 +179,40 @@ def get_agent_identity_certificate_path():
193179
)
194180

195181

182+
def _parse_cert_path_from_config(cert_config_path):
183+
"""Reads the cert config file and returns the cert_path.
184+
185+
Args:
186+
cert_config_path (str): The path to the certificate configuration file.
187+
188+
Returns:
189+
Optional[str]: The path to the certificate file, or None if not found
190+
in the config.
191+
192+
Raises:
193+
IOError: If the certificate config file cannot be read.
194+
ValueError: If the certificate config file contains invalid JSON.
195+
KeyError: If the certificate config file does not contain the
196+
expected structure.
197+
"""
198+
import json
199+
200+
with open(cert_config_path, "r") as f:
201+
cert_config = json.load(f)
202+
203+
cert_configs = (
204+
cert_config.get("cert_configs") if isinstance(cert_config, dict) else None
205+
)
206+
workload_config = (
207+
cert_configs.get("workload") if isinstance(cert_configs, dict) else None
208+
)
209+
210+
if not isinstance(workload_config, dict) or "cert_path" not in workload_config:
211+
return None
212+
213+
return workload_config["cert_path"]
214+
215+
196216
def get_and_parse_agent_identity_certificate():
197217
"""Gets and parses the agent identity certificate if not opted out.
198218

0 commit comments

Comments
 (0)