@@ -623,6 +623,97 @@ def _hydrate_runtime_target_schedules(
623623 return hydrated
624624
625625
626+ def _cloud_run_region () -> str :
627+ return (
628+ os .environ .get ("RUNTIME_HEARTBEAT_CLOUD_RUN_REGION" )
629+ or os .environ .get ("CLOUD_RUN_REGION" )
630+ or "us-central1"
631+ )
632+
633+
634+ def _describe_cloud_run_service (
635+ service : str ,
636+ * ,
637+ project : str | None ,
638+ ) -> dict [str , Any ]:
639+ command = [
640+ "gcloud" ,
641+ "run" ,
642+ "services" ,
643+ "describe" ,
644+ service ,
645+ "--region" ,
646+ _cloud_run_region (),
647+ "--format=json" ,
648+ ]
649+ if project :
650+ command .extend (["--project" , project ])
651+ result = _run_gcloud (command )
652+ if result .returncode != 0 :
653+ detail = (result .stderr or result .stdout or "" ).strip ()
654+ raise RuntimeError (detail or f"gcloud run services describe failed for { service } " )
655+ try :
656+ payload = json .loads (result .stdout )
657+ except json .JSONDecodeError as exc :
658+ raise RuntimeError (
659+ f"gcloud run services describe returned invalid JSON for { service } : { exc } "
660+ ) from exc
661+ if not isinstance (payload , dict ):
662+ raise RuntimeError (f"gcloud run services describe returned no data for { service } " )
663+ return payload
664+
665+
666+ def _deployed_runtime_target (payload : dict [str , Any ]) -> dict [str , Any ]:
667+ template = payload .get ("spec" , {}).get ("template" , {})
668+ containers = template .get ("spec" , {}).get ("containers" )
669+ if not isinstance (containers , list ):
670+ containers = template .get ("containers" )
671+ for container in containers if isinstance (containers , list ) else []:
672+ if not isinstance (container , dict ):
673+ continue
674+ for entry in container .get ("env" ) or []:
675+ if not isinstance (entry , dict ) or entry .get ("name" ) not in {
676+ "RUNTIME_TARGET_JSON" ,
677+ "QSL_RUNTIME_TARGET_JSON" ,
678+ }:
679+ continue
680+ try :
681+ runtime_target = json .loads (str (entry .get ("value" ) or "" ))
682+ except json .JSONDecodeError as exc :
683+ raise RuntimeError ("deployed runtime target JSON is invalid" ) from exc
684+ if isinstance (runtime_target , dict ):
685+ return runtime_target
686+ return {}
687+
688+
689+ def _hydrate_runtime_target_profiles (
690+ targets : list [dict [str , Any ]],
691+ * ,
692+ project : str | None ,
693+ ) -> list [dict [str , Any ]]:
694+ hydrated = []
695+ deployed_by_service : dict [str , dict [str , Any ]] = {}
696+ for target in targets :
697+ strategy_profile = str (target .get ("strategy_profile" ) or "" ).strip ()
698+ service = str (target .get ("service" ) or "" ).strip ()
699+ if not strategy_profile or not service :
700+ hydrated .append (target )
701+ continue
702+ if service not in deployed_by_service :
703+ deployed_by_service [service ] = _deployed_runtime_target (
704+ _describe_cloud_run_service (service , project = project )
705+ )
706+ canonical_profile = str (
707+ deployed_by_service [service ].get ("strategy_profile" ) or ""
708+ ).strip ()
709+ if not canonical_profile :
710+ raise RuntimeError (
711+ f"deployed runtime target has no strategy profile for { service } "
712+ )
713+ hydrated .append ({** target , "strategy_profile" : canonical_profile })
714+ return hydrated
715+
716+
626717def _scheduler_job_targets_strategy_run (job : dict [str , Any ], service : str ) -> bool :
627718 if str (job .get ("state" ) or "" ).strip ().upper () not in {"" , "ENABLED" }:
628719 return False
@@ -1045,19 +1136,18 @@ def main(now: dt.datetime | None = None) -> int:
10451136 now = now .replace (tzinfo = dt .timezone .utc )
10461137 now = now .astimezone (dt .timezone .utc )
10471138 since = now - dt .timedelta (hours = lookback_hours )
1048- from runtime_config_support import resolve_strategy_profile
1049-
1050- runtime_targets = load_runtime_targets (
1051- os .environ ,
1052- profile_resolver = resolve_strategy_profile ,
1053- )
1139+ runtime_targets = load_runtime_targets (os .environ )
10541140 try :
1141+ runtime_targets = _hydrate_runtime_target_profiles (
1142+ runtime_targets ,
1143+ project = project ,
1144+ )
10551145 runtime_targets = _hydrate_runtime_target_schedules (
10561146 runtime_targets ,
10571147 project = project ,
10581148 )
10591149 except RuntimeError as exc :
1060- message = f"[Execution Report Heartbeat] { name } \n Scheduler policy error: { exc } "
1150+ message = f"[Execution Report Heartbeat] { name } \n Runtime target policy error: { exc } "
10611151 print (message )
10621152 _send_telegram (message )
10631153 return 1 if fail_workflow else 0
0 commit comments