@@ -41,6 +41,8 @@ def _env_bool(name: str, default: bool = False) -> bool:
4141
4242def _load_services () -> list [str ]:
4343 services = []
44+ enabled_target_services = []
45+ disabled_target_services = []
4446 for name in (
4547 "RUNTIME_GUARD_CLOUD_RUN_SERVICES" ,
4648 "CLOUD_RUN_SERVICES" ,
@@ -52,37 +54,27 @@ def _load_services() -> list[str]:
5254 if raw_targets :
5355 try :
5456 payload = json .loads (raw_targets )
57+ defaults = payload .get ("defaults" ) if isinstance (payload , dict ) else {}
58+ defaults = defaults if isinstance (defaults , dict ) else {}
5559 targets = payload .get ("targets" ) if isinstance (payload , dict ) else payload
5660 if isinstance (targets , list ):
5761 for target in targets :
5862 if not isinstance (target , dict ):
5963 continue
60- if not _target_enabled (target ):
61- continue
62- runtime_target = target .get ("runtime_target" ) or target .get (
63- "runtime_target_json"
64- )
65- if isinstance (runtime_target , str ):
66- try :
67- runtime_target = json .loads (runtime_target )
68- except json .JSONDecodeError :
69- runtime_target = {}
70- for key in ("service" , "service_name" , "cloud_run_service" ):
71- value = target .get (key ) or (
72- runtime_target .get (key )
73- if isinstance (runtime_target , dict )
74- else None
75- )
76- if value :
77- services .extend (_split_values (str (value )))
78- break
64+ target_services = _target_service_names (target , defaults )
65+ if _target_enabled (target , defaults ):
66+ enabled_target_services .extend (target_services )
67+ else :
68+ disabled_target_services .extend (target_services )
7969 except json .JSONDecodeError as exc :
8070 raise RuntimeError (f"CLOUD_RUN_SERVICE_TARGETS_JSON is invalid: { exc } " ) from exc
8171
72+ services .extend (enabled_target_services )
73+ disabled = set (disabled_target_services ) - set (enabled_target_services )
8274 seen = set ()
8375 unique = []
8476 for service in services :
85- if service not in seen :
77+ if service not in seen and service not in disabled :
8678 seen .add (service )
8779 unique .append (service )
8880 return unique
@@ -112,9 +104,29 @@ def _service_job_aliases(service: str) -> list[str]:
112104def _scheduler_job_pattern_for_services (services : list [str ]) -> str :
113105 candidates : list [str ] = []
114106 for service in services :
115- candidates .extend (_service_job_aliases (service ))
107+ candidates .extend (_scheduler_job_names (service ))
116108 unique = list (dict .fromkeys (candidates ))
117- return "|" .join (re .escape (candidate ) for candidate in unique )
109+ if not unique :
110+ return ""
111+ return r"^(?:" + "|" .join (re .escape (candidate ) for candidate in unique ) + r")\Z"
112+
113+
114+ def _scheduler_job_names (service : str ) -> list [str ]:
115+ names = []
116+ for alias in _service_job_aliases (service ):
117+ names .extend (
118+ (
119+ f"{ alias } -scheduler" ,
120+ f"{ alias } -probe-scheduler" ,
121+ f"{ alias } -precheck-scheduler" ,
122+ )
123+ )
124+ return list (dict .fromkeys (names ))
125+
126+
127+ def _job_matches_service (job_name : str , service : str ) -> bool :
128+ normalized = str (job_name or "" ).strip ().rsplit ("/" , 1 )[- 1 ]
129+ return normalized in _scheduler_job_names (service )
118130
119131
120132def _entry_job_name (entry : dict [str , Any ]) -> str :
@@ -131,7 +143,7 @@ def _scheduler_entry_since(
131143 matches = [
132144 service_since
133145 for service , service_since in service_since_by_name .items ()
134- if any ( alias and alias in job_name for alias in _service_job_aliases ( service ) )
146+ if _job_matches_service ( job_name , service )
135147 ]
136148 return max (matches ) if matches else fallback
137149
@@ -147,7 +159,7 @@ def _is_duplicate_scheduler_failure(
147159
148160 tolerance = dt .timedelta (seconds = SCHEDULER_CLOUD_RUN_DEDUP_SECONDS )
149161 for service , failures in cloud_run_failures_by_service .items ():
150- if not any ( alias and alias in job_name for alias in _service_job_aliases ( service ) ):
162+ if not _job_matches_service ( job_name , service ):
151163 continue
152164 for failure in failures :
153165 cloud_run_timestamp = _parse_timestamp (failure .get ("timestamp" ))
@@ -233,22 +245,51 @@ def _format_timestamp(value: dt.datetime) -> str:
233245 return value .astimezone (dt .timezone .utc ).isoformat ().replace ("+00:00" , "Z" )
234246
235247
236- def _target_payloads () -> list [dict [str , Any ]]:
248+ def _target_configuration () -> tuple [ list [dict [ str , Any ]], dict [str , Any ]]:
237249 raw_targets = (os .environ .get ("CLOUD_RUN_SERVICE_TARGETS_JSON" ) or "" ).strip ()
238250 if not raw_targets :
239- return []
251+ return [], {}
240252 try :
241253 payload = json .loads (raw_targets )
242254 except json .JSONDecodeError :
243- return []
255+ return [], {}
256+ defaults = payload .get ("defaults" ) if isinstance (payload , dict ) else {}
257+ defaults = defaults if isinstance (defaults , dict ) else {}
244258 targets = payload .get ("targets" ) if isinstance (payload , dict ) else payload
245259 if not isinstance (targets , list ):
246- return []
247- return [target for target in targets if isinstance (target , dict )]
260+ return [], defaults
261+ return [target for target in targets if isinstance (target , dict )], defaults
248262
249263
250- def _runtime_target (target : dict [str , Any ]) -> dict [str , Any ]:
251- runtime_target = target .get ("runtime_target" ) or target .get ("runtime_target_json" )
264+ def _target_payloads () -> list [dict [str , Any ]]:
265+ targets , _defaults = _target_configuration ()
266+ return targets
267+
268+
269+ def _target_field (
270+ target : dict [str , Any ],
271+ defaults : dict [str , Any ],
272+ * names : str ,
273+ ) -> Any :
274+ target_env = target .get ("env" ) if isinstance (target .get ("env" ), dict ) else {}
275+ defaults_env = defaults .get ("env" ) if isinstance (defaults .get ("env" ), dict ) else {}
276+ for source in (target , target_env , defaults , defaults_env ):
277+ for name in names :
278+ if name in source :
279+ return source [name ]
280+ return None
281+
282+
283+ def _runtime_target (
284+ target : dict [str , Any ],
285+ defaults : dict [str , Any ] | None = None ,
286+ ) -> dict [str , Any ]:
287+ runtime_target = _target_field (
288+ target ,
289+ defaults or {},
290+ "runtime_target" ,
291+ "runtime_target_json" ,
292+ )
252293 if isinstance (runtime_target , str ):
253294 try :
254295 runtime_target = json .loads (runtime_target )
@@ -268,32 +309,57 @@ def _coerce_bool(value: Any, default: bool) -> bool:
268309 return text in {"1" , "true" , "yes" , "y" , "on" }
269310
270311
271- def _target_enabled (target : dict [str , Any ]) -> bool :
272- runtime_target = _runtime_target (target )
312+ def _target_enabled (
313+ target : dict [str , Any ],
314+ defaults : dict [str , Any ] | None = None ,
315+ ) -> bool :
316+ defaults = defaults or {}
317+ runtime_target = _runtime_target (target , defaults )
318+ value = _target_field (
319+ target ,
320+ defaults ,
321+ "runtime_target_enabled" ,
322+ "RUNTIME_TARGET_ENABLED" ,
323+ )
324+ if value is not None :
325+ return _coerce_bool (value , True )
273326 for key in ("runtime_target_enabled" , "RUNTIME_TARGET_ENABLED" ):
274- if key in target :
275- return _coerce_bool (target .get (key ), True )
276327 if key in runtime_target :
277328 return _coerce_bool (runtime_target .get (key ), True )
278329 return True
279330
280331
281- def _target_service_names (target : dict [str , Any ]) -> list [str ]:
282- runtime_target = _runtime_target (target )
283- for key in ("service" , "service_name" , "cloud_run_service" ):
284- value = target .get (key ) or runtime_target .get (key )
285- if value :
286- return _split_values (str (value ))
332+ def _target_service_names (
333+ target : dict [str , Any ],
334+ defaults : dict [str , Any ] | None = None ,
335+ ) -> list [str ]:
336+ defaults = defaults or {}
337+ runtime_target = _runtime_target (target , defaults )
338+ value = _target_field (
339+ target ,
340+ defaults ,
341+ "service" ,
342+ "service_name" ,
343+ "cloud_run_service" ,
344+ )
345+ if value is None :
346+ for key in ("service" , "service_name" , "cloud_run_service" ):
347+ if runtime_target .get (key ):
348+ value = runtime_target [key ]
349+ break
350+ if value :
351+ return _split_values (str (value ))
287352 return []
288353
289354
290355def _region_for_service (service : str ) -> str :
291- for target in _target_payloads ():
292- if service not in _target_service_names (target ):
356+ targets , defaults = _target_configuration ()
357+ for target in targets :
358+ if service not in _target_service_names (target , defaults ):
293359 continue
294- runtime_target = _runtime_target (target )
360+ runtime_target = _runtime_target (target , defaults )
295361 for key in ("region" , "cloud_run_region" , "location" ):
296- value = target . get ( key ) or runtime_target .get (key )
362+ value = _target_field ( target , defaults , key ) or runtime_target .get (key )
297363 if value :
298364 return str (value ).strip ()
299365 return (
@@ -596,7 +662,7 @@ def main() -> int:
596662 entries = [
597663 entry
598664 for entry in entries
599- if regex .search (str ( _labels ( entry ).get ( "job_id" ) or _labels ( entry ). get ( "job_name" ) or "" ) )
665+ if regex .search (_entry_job_name ( entry ).rsplit ( "/" , 1 )[ - 1 ] )
600666 ]
601667 failures = []
602668 for entry in entries :
0 commit comments