@@ -67,93 +67,88 @@ 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
7676def 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+ try :
103+ abs_cert_path = os .path .abspath (cert_config_path )
104+ abs_well_known_dir = os .path .abspath (well_known_dir )
105+ should_poll = (
106+ os .path .commonpath ([abs_well_known_dir , abs_cert_path ])
107+ == abs_well_known_dir
108+ )
109+ except ValueError :
110+ should_poll = False
98111
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
112+ return _get_cert_path_with_optional_polling (cert_config_path , should_poll )
102113
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
110114
115+ def _get_cert_path_with_optional_polling (cert_config_path , should_poll ):
116+ """Gets the certificate path, optionally polling until it is ready.
117+
118+ Args:
119+ cert_config_path (str): The path to the certificate configuration file.
120+ should_poll (bool): If True, the function will poll for the file and
121+ certificate to be ready. If False, it will check only once and
122+ return early if they are not immediately available.
123+
124+ Returns:
125+ str: The path to the certificate file, or None if unavailable.
126+
127+ Raises:
128+ google.auth.exceptions.RefreshError: If the certificate config file
129+ or the certificate file cannot be found after retries.
130+ """
111131 has_logged_config_warning = False
112132 has_logged_cert_warning = False
113133
114134 for interval in _POLLING_INTERVALS :
115135 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
137-
138- cert_path = workload_config ["cert_path" ]
139- if _is_certificate_file_ready (cert_path ):
140- return cert_path
136+ cert_path = _parse_cert_path_from_config (cert_config_path )
141137
142- # The config was parsed, but the cert file is not ready yet
143- target_path = cert_path
138+ if cert_path is None :
139+ return None
144140
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
141+ if _is_certificate_file_ready (cert_path ):
142+ return cert_path
149143
150- # The well-known cert file is not ready yet
151- target_path = _WELL_KNOWN_CERT_PATH
144+ # The config was parsed, but the cert file is not ready yet
145+ if not should_poll :
146+ # If polling is disabled, return early.
147+ return None
152148
153- # Log a warning on the first failed attempt to load the certificate file
154149 if not has_logged_cert_warning :
155150 warnings .warn (
156- f"Certificate file not ready at { target_path } . Retrying until startup timeout (up to { _TOTAL_TIMEOUT } seconds total)..."
151+ f"Certificate file not ready at { cert_path } . Retrying until startup timeout (up to { _TOTAL_TIMEOUT } seconds total)..."
157152 )
158153 has_logged_cert_warning = True
159154
@@ -164,25 +159,24 @@ def get_agent_identity_certificate_path():
164159 )
165160 return None
166161 except (IOError , ValueError , KeyError ) as e :
167- if cert_config_path and os .path .exists (cert_config_path ):
162+ if os .path .exists (cert_config_path ):
168163 # 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.
164+ # we assume it is in its final format and return early (returning None).
165+ return None
166+
167+ if not should_poll :
168+ # If polling is disabled, return early if the file doesn't exist.
170169 return None
171170
172- if not has_logged_config_warning and cert_config_path :
171+ if not has_logged_config_warning :
173172 warnings .warn (
174173 f"Certificate config file not found or incomplete: { e } (from "
175174 f"{ environment_vars .GOOGLE_API_CERTIFICATE_CONFIG } environment variable). "
176175 f"Retrying until startup timeout (up to { _TOTAL_TIMEOUT } seconds total)..."
177176 )
178177 has_logged_config_warning = True
179- pass
180178
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.
179+ # Sleep before the next polling attempt.
186180 time .sleep (interval )
187181
188182 raise exceptions .RefreshError (
@@ -193,6 +187,40 @@ def get_agent_identity_certificate_path():
193187 )
194188
195189
190+ def _parse_cert_path_from_config (cert_config_path ):
191+ """Reads the cert config file and returns the cert_path.
192+
193+ Args:
194+ cert_config_path (str): The path to the certificate configuration file.
195+
196+ Returns:
197+ Optional[str]: The path to the certificate file, or None if not found
198+ in the config.
199+
200+ Raises:
201+ IOError: If the certificate config file cannot be read.
202+ ValueError: If the certificate config file contains invalid JSON.
203+ KeyError: If the certificate config file does not contain the
204+ expected structure.
205+ """
206+ import json
207+
208+ with open (cert_config_path , "r" , encoding = "utf-8" ) as f :
209+ cert_config = json .load (f )
210+
211+ cert_configs = (
212+ cert_config .get ("cert_configs" ) if isinstance (cert_config , dict ) else None
213+ )
214+ workload_config = (
215+ cert_configs .get ("workload" ) if isinstance (cert_configs , dict ) else None
216+ )
217+
218+ if not isinstance (workload_config , dict ) or "cert_path" not in workload_config :
219+ return None
220+
221+ return workload_config ["cert_path" ]
222+
223+
196224def get_and_parse_agent_identity_certificate ():
197225 """Gets and parses the agent identity certificate if not opted out.
198226
0 commit comments