feat(awooop): add provider upstream canary
All checks were successful
Code Review / ai-code-review (push) Successful in 11s
CD Pipeline / tests (push) Successful in 5m50s
CD Pipeline / build-and-deploy (push) Successful in 3m58s
CD Pipeline / post-deploy-checks (push) Successful in 1m48s

This commit is contained in:
Your Name
2026-05-20 20:48:36 +08:00
parent e6cc008b87
commit f3fbd39898
8 changed files with 719 additions and 12 deletions

View File

@@ -456,6 +456,20 @@ def check_webhook_health(api_url: str) -> list[CheckResult]:
return results
def _clean_source_providers(providers: list[str]) -> list[str]:
return [
provider.strip().lower()
for provider in providers
if provider.strip().lower() in {"sentry", "signoz"}
]
def _safe_run_ref(run_ref: str | None) -> str:
raw = (run_ref or f"manual-{int(time.time())}").strip()
cleaned = re.sub(r"[^A-Za-z0-9_.-]+", "-", raw).strip("-")
return cleaned[:64] or f"manual-{int(time.time())}"
def send_source_provider_heartbeat(
api_url: str,
*,
@@ -465,11 +479,7 @@ def send_source_provider_heartbeat(
run_ref: str | None = None,
) -> CheckResult:
"""Record low-noise provider freshness evidence without creating incidents."""
cleaned_providers = [
provider.strip().lower()
for provider in providers
if provider.strip().lower() in {"sentry", "signoz"}
]
cleaned_providers = _clean_source_providers(providers)
if not cleaned_providers:
return CheckResult(
"Source Provider Heartbeat",
@@ -532,6 +542,157 @@ def send_source_provider_heartbeat(
)
def _build_sentry_upstream_canary_payload(safe_ref: str) -> dict[str, Any]:
issue_id = f"awoooi-canary-{safe_ref}"
return {
"action": "triggered",
"data": {
"issue": {
"id": issue_id,
"shortId": "AWOOOI-CANARY",
"title": "AwoooPSourceProviderCanary",
"culprit": "source-provider-ingestion",
"level": "info",
"project": {"slug": "awoooi"},
"permalink": "https://awoooi.wooo.work/zh-TW/awooop/work-items",
},
"event": {
"message": "AwoooP upstream source provider canary",
"platform": "python",
"tags": [
["awoooi_canary", "true"],
["run_ref", safe_ref],
],
},
},
"actor": {"type": "application", "name": "AwoooP E2E"},
}
def _build_signoz_upstream_canary_payload(safe_ref: str) -> dict[str, Any]:
fingerprint = f"source-provider-canary:signoz:{safe_ref}"
return {
"alerts": [
{
"status": "firing",
"alertname": "AwoooPSourceProviderCanary",
"labels": {
"alertname": "AwoooPSourceProviderCanary",
"severity": "info",
"namespace": "awoooi-prod",
"service": "source-provider-ingestion",
"service_name": "source-provider-ingestion",
"awoooi_canary": "true",
"run_ref": safe_ref,
"fingerprint": fingerprint,
},
"annotations": {
"summary": "AwoooP upstream source provider canary",
"description": (
"Synthetic provider-shaped SignOz alert used only to "
"verify source dossier ingestion."
),
},
"generatorURL": "https://awoooi.wooo.work/zh-TW/awooop/work-items",
}
]
}
def _validate_upstream_canary_response(provider: str, data: dict[str, Any]) -> str | None:
if provider == "sentry":
if data.get("status") == "canary_recorded" and data.get("provider") == "sentry":
return None
return f"unexpected Sentry response: {data}"
results = data.get("results", [])
recorded = [
item
for item in results
if isinstance(item, dict)
and item.get("status") == "canary_recorded"
and item.get("provider") == "signoz"
]
if recorded:
return None
return f"unexpected SignOz response: {data}"
def send_source_provider_upstream_canary(
api_url: str,
*,
providers: list[str],
operator_key: str | None,
operator_id: str,
run_ref: str | None = None,
) -> CheckResult:
"""Send provider-shaped canaries through webhook-native ingestion paths."""
cleaned_providers = _clean_source_providers(providers)
if not cleaned_providers:
return CheckResult(
"Source Provider Upstream Canary",
False,
"沒有有效 provider允許 sentry/signoz",
)
if not operator_key:
return CheckResult(
"Source Provider Upstream Canary",
False,
"AWOOOP_OPERATOR_API_KEY 未設定;無法打入受保護 upstream canary",
)
safe_ref = _safe_run_ref(run_ref)
endpoints = {
"sentry": (
f"{api_url}/api/v1/webhooks/sentry/error",
_build_sentry_upstream_canary_payload(safe_ref),
),
"signoz": (
f"{api_url}/api/v1/webhooks/signoz/alert",
_build_signoz_upstream_canary_payload(safe_ref),
),
}
recorded: list[str] = []
for provider in cleaned_providers:
url, payload = endpoints[provider]
try:
resp = http_post_json(
url,
payload,
headers={
"X-AwoooP-Operator-Id": operator_id,
"X-AwoooP-Operator-Key": operator_key,
},
timeout=TIMEOUT,
)
data = resp.json()
except (URLError, TimeoutError, OSError, json.JSONDecodeError) as e:
return CheckResult(
"Source Provider Upstream Canary",
False,
f"{provider} upstream canary failed: {_http_error_message(e)}",
)
if resp.status_code >= 400:
return CheckResult(
"Source Provider Upstream Canary",
False,
f"{provider} HTTP {resp.status_code}: {data.get('detail', resp.text) if isinstance(data, dict) else resp.text}",
)
validation_error = _validate_upstream_canary_response(provider, data)
if validation_error:
return CheckResult(
"Source Provider Upstream Canary",
False,
validation_error,
)
recorded.append(provider)
return CheckResult(
"Source Provider Upstream Canary",
True,
f"recorded {', '.join(sorted(recorded))} webhook-native canary event(s)",
)
def check_signoz_reachable(signoz_url: str) -> CheckResult:
"""Check 4: SigNoz UI 可達"""
try:
@@ -627,6 +788,7 @@ def run_smoke_test(
*,
metrics_api_url: str | None = None,
source_provider_heartbeat: bool = False,
source_provider_upstream_canary: bool = False,
source_providers: list[str] | None = None,
operator_key: str | None = None,
operator_id: str = "gitea-e2e-health",
@@ -679,6 +841,29 @@ def run_smoke_test(
)
)
if source_provider_upstream_canary:
provider_list = source_providers or ["sentry", "signoz"]
canary_result = send_source_provider_upstream_canary(
api_url,
providers=provider_list,
operator_key=operator_key,
operator_id=operator_id,
run_ref=run_ref,
)
report.add(canary_result)
if fail_fast and not canary_result.passed and canary_result.critical:
return report
if canary_result.passed:
for source in provider_list:
report.add(
check_alert_chain_metric(
PROMETHEUS_URL,
metrics_url,
source=source,
)
)
# Check 4: SigNoz
report.add(check_signoz_reachable(SIGNOZ_URL))
@@ -715,11 +900,16 @@ def main() -> int:
action="store_true",
help="寫入 Sentry/SignOz 低噪音 freshness heartbeat 並驗證 provider 指標",
)
parser.add_argument(
"--source-provider-upstream-canary",
action="store_true",
help="透過 Sentry/SignOz 原生 webhook 寫入 provider-shaped canary 來源證據",
)
parser.add_argument(
"--source-provider",
action="append",
choices=["sentry", "signoz"],
help="指定要寫入 heartbeat 的 provider可重複指定預設 sentry+signoz",
help="指定要驗證的 provider可重複指定預設 sentry+signoz",
)
parser.add_argument(
"--operator-id",
@@ -743,6 +933,7 @@ def main() -> int:
args.fail_fast,
metrics_api_url=args.metrics_api_url,
source_provider_heartbeat=args.source_provider_heartbeat,
source_provider_upstream_canary=args.source_provider_upstream_canary,
source_providers=args.source_provider,
operator_key=os.environ.get(args.operator_key_env),
operator_id=args.operator_id,