Skip to content

Commit cff90fc

Browse files
authored
fix runtime guard stale log filtering (#188)
1 parent 3be214f commit cff90fc

2 files changed

Lines changed: 99 additions & 7 deletions

File tree

scripts/cloud_run_runtime_guard.py

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ def _load_services() -> list[str]:
5656
for target in targets:
5757
if not isinstance(target, dict):
5858
continue
59+
if not _target_enabled(target):
60+
continue
5961
runtime_target = target.get("runtime_target") or target.get(
6062
"runtime_target_json"
6163
)
@@ -85,19 +87,43 @@ def _load_services() -> list[str]:
8587
return unique
8688

8789

90+
def _service_job_aliases(service: str) -> list[str]:
91+
service_name = str(service or "").strip()
92+
if not service_name:
93+
return []
94+
aliases = [service_name]
95+
if service_name.endswith("-service"):
96+
aliases.append(service_name.removesuffix("-service"))
97+
return list(dict.fromkeys(aliases))
98+
99+
88100
def _scheduler_job_pattern_for_services(services: list[str]) -> str:
89101
candidates: list[str] = []
90102
for service in services:
91-
service_name = str(service or "").strip()
92-
if not service_name:
93-
continue
94-
candidates.append(service_name)
95-
if service_name.endswith("-service"):
96-
candidates.append(service_name.removesuffix("-service"))
103+
candidates.extend(_service_job_aliases(service))
97104
unique = list(dict.fromkeys(candidates))
98105
return "|".join(re.escape(candidate) for candidate in unique)
99106

100107

108+
def _entry_job_name(entry: dict[str, Any]) -> str:
109+
labels = _labels(entry)
110+
return str(labels.get("job_id") or labels.get("job_name") or "")
111+
112+
113+
def _scheduler_entry_since(
114+
entry: dict[str, Any],
115+
service_since_by_name: dict[str, dt.datetime],
116+
fallback: dt.datetime,
117+
) -> dt.datetime:
118+
job_name = _entry_job_name(entry)
119+
matches = [
120+
service_since
121+
for service, service_since in service_since_by_name.items()
122+
if any(alias and alias in job_name for alias in _service_job_aliases(service))
123+
]
124+
return max(matches) if matches else fallback
125+
126+
101127
def _run_gcloud(args: list[str]) -> subprocess.CompletedProcess[str]:
102128
return subprocess.run(args, text=True, capture_output=True, check=False)
103129

@@ -184,6 +210,27 @@ def _runtime_target(target: dict[str, Any]) -> dict[str, Any]:
184210
return runtime_target if isinstance(runtime_target, dict) else {}
185211

186212

213+
def _coerce_bool(value: Any, default: bool) -> bool:
214+
if value is None:
215+
return default
216+
if isinstance(value, bool):
217+
return value
218+
text = str(value).strip().lower()
219+
if not text:
220+
return default
221+
return text in {"1", "true", "yes", "y", "on"}
222+
223+
224+
def _target_enabled(target: dict[str, Any]) -> bool:
225+
runtime_target = _runtime_target(target)
226+
for key in ("runtime_target_enabled", "RUNTIME_TARGET_ENABLED"):
227+
if key in target:
228+
return _coerce_bool(target.get(key), True)
229+
if key in runtime_target:
230+
return _coerce_bool(runtime_target.get(key), True)
231+
return True
232+
233+
187234
def _target_service_names(target: dict[str, Any]) -> list[str]:
188235
runtime_target = _runtime_target(target)
189236
for key in ("service", "service_name", "cloud_run_service"):
@@ -428,6 +475,7 @@ def main() -> int:
428475
issues: list[str] = []
429476
details: list[str] = []
430477
success_count = 0
478+
service_since_by_name: dict[str, dt.datetime] = {}
431479

432480
try:
433481
services = _load_services()
@@ -441,6 +489,7 @@ def main() -> int:
441489

442490
for service in services:
443491
service_since = _cloud_run_log_since(project, service, since) if ignore_pre_ready_logs else since
492+
service_since_by_name[service] = service_since
444493
service_since_text = _format_timestamp(service_since)
445494
log_filter = (
446495
'resource.type="cloud_run_revision" '
@@ -474,7 +523,15 @@ def main() -> int:
474523
for entry in entries
475524
if regex.search(str(_labels(entry).get("job_id") or _labels(entry).get("job_name") or ""))
476525
]
477-
failures = [entry for entry in entries if _is_failure(entry)]
526+
failures = []
527+
for entry in entries:
528+
if not _is_failure(entry):
529+
continue
530+
entry_timestamp = _parse_timestamp(entry.get("timestamp"))
531+
entry_since = _scheduler_entry_since(entry, service_since_by_name, since)
532+
if entry_timestamp and entry_timestamp < entry_since:
533+
continue
534+
failures.append(entry)
478535
if failures:
479536
issues.append(f"{len(failures)} Cloud Scheduler failure log(s)")
480537
details.extend(_summarize(entry) for entry in failures[:5])

tests/test_cloud_run_runtime_guard.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,38 @@ def test_region_for_service_prefers_target_region(monkeypatch):
9090
)
9191

9292
assert guard._region_for_service("firstrade-service") == "asia-east1"
93+
94+
95+
def test_load_services_ignores_disabled_runtime_targets(monkeypatch):
96+
monkeypatch.delenv("RUNTIME_GUARD_CLOUD_RUN_SERVICES", raising=False)
97+
monkeypatch.delenv("CLOUD_RUN_SERVICES", raising=False)
98+
monkeypatch.delenv("CLOUD_RUN_SERVICE", raising=False)
99+
monkeypatch.setenv(
100+
"CLOUD_RUN_SERVICE_TARGETS_JSON",
101+
json.dumps(
102+
{
103+
"targets": [
104+
{"service": "enabled-service", "RUNTIME_TARGET_ENABLED": "true"},
105+
{"service": "disabled-service", "RUNTIME_TARGET_ENABLED": "false"},
106+
{"service": "disabled-lower-service", "runtime_target_enabled": "false"},
107+
]
108+
}
109+
),
110+
)
111+
112+
assert guard._load_services() == ["enabled-service"]
113+
114+
115+
def test_scheduler_entry_since_uses_matching_service_revision_window():
116+
fallback = dt.datetime(2026, 7, 1, 1, 0, tzinfo=dt.timezone.utc)
117+
service_since = dt.datetime(2026, 7, 1, 2, 0, tzinfo=dt.timezone.utc)
118+
entry = {"resource": {"labels": {"job_id": "enabled-service-scheduler"}}}
119+
120+
assert (
121+
guard._scheduler_entry_since(entry, {"enabled-service": service_since}, fallback)
122+
== service_since
123+
)
124+
assert (
125+
guard._scheduler_entry_since(entry, {"other-service": service_since}, fallback)
126+
== fallback
127+
)

0 commit comments

Comments
 (0)