fix(agent): auto-authorize noncritical alert control
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 1s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m38s
CD Pipeline / build-and-deploy (push) Successful in 6m58s
CD Pipeline / post-deploy-checks (push) Successful in 1m42s
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 1s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m38s
CD Pipeline / build-and-deploy (push) Successful in 6m58s
CD Pipeline / post-deploy-checks (push) Successful in 1m42s
This commit is contained in:
@@ -159,7 +159,7 @@ def approval_request_to_record_data(
|
||||
record_data = {
|
||||
"action": request.action,
|
||||
"description": request.description,
|
||||
"status": ApprovalStatus.APPROVED if risk_level == RiskLevel.LOW else ApprovalStatus.PENDING,
|
||||
"status": ApprovalStatus.APPROVED if required_sigs == 0 else ApprovalStatus.PENDING,
|
||||
"risk_level": risk_level,
|
||||
"required_signatures": required_sigs,
|
||||
"current_signatures": 0,
|
||||
@@ -169,7 +169,7 @@ def approval_request_to_record_data(
|
||||
"requested_by": request.requested_by,
|
||||
"expires_at": request.expires_at,
|
||||
"extra_metadata": metadata or None,
|
||||
"resolved_at": now if risk_level == RiskLevel.LOW else None,
|
||||
"resolved_at": now if required_sigs == 0 else None,
|
||||
# 戰略 B: 告警風暴收斂
|
||||
"fingerprint": fingerprint,
|
||||
"hit_count": 1,
|
||||
@@ -279,7 +279,7 @@ def _approval_decision_mode(record: ApprovalRecord) -> str:
|
||||
risk_level = _record_value(record.risk_level)
|
||||
if _approval_needs_human(record) or current > 0:
|
||||
return "manual"
|
||||
if risk_level == "low" and required == 0:
|
||||
if risk_level in {"low", "medium", "high"} and required == 0:
|
||||
return "auto"
|
||||
return "manual"
|
||||
|
||||
@@ -442,6 +442,66 @@ class ApprovalDBService:
|
||||
|
||||
return approval_record_to_request(record)
|
||||
|
||||
async def apply_current_owner_policy(
|
||||
self,
|
||||
approval_id: UUID,
|
||||
) -> tuple[ApprovalRequest | None, bool]:
|
||||
"""Promote a legacy pending non-critical gate to controlled automation.
|
||||
|
||||
Existing alert fingerprints can otherwise keep converging onto an old
|
||||
PENDING row for 24 hours. This bounded reconciliation updates only one
|
||||
row, re-runs the current critical classifier, and leaves critical rows
|
||||
untouched for break-glass handling.
|
||||
"""
|
||||
|
||||
async with get_db_context() as db:
|
||||
result = await db.execute(
|
||||
select(ApprovalRecord)
|
||||
.where(ApprovalRecord.id == str(approval_id))
|
||||
.with_for_update()
|
||||
)
|
||||
record = result.scalar_one_or_none()
|
||||
if record is None:
|
||||
return None, False
|
||||
|
||||
current = approval_record_to_request(record)
|
||||
status = _record_value(record.status)
|
||||
effective_risk = classify_risk(
|
||||
action=str(record.action or ""),
|
||||
blast_radius=current.blast_radius,
|
||||
explicit_level=current.risk_level,
|
||||
)
|
||||
if status != "pending" or effective_risk == RiskLevel.CRITICAL:
|
||||
return current, False
|
||||
|
||||
metadata = dict(record.extra_metadata or {})
|
||||
metadata.update(
|
||||
{
|
||||
"owner_policy": "global_product_governance_v2",
|
||||
"owner_review_gate": "auto_waived_for_low_medium_high",
|
||||
"controlled_apply_required": True,
|
||||
"legacy_pending_promoted": True,
|
||||
}
|
||||
)
|
||||
record.status = ApprovalStatus.APPROVED
|
||||
record.risk_level = effective_risk
|
||||
record.required_signatures = 0
|
||||
record.current_signatures = 0
|
||||
record.signatures = []
|
||||
record.resolved_at = datetime.now(UTC)
|
||||
record.extra_metadata = metadata
|
||||
add_approval_created_timeline_event(db, record)
|
||||
await db.flush()
|
||||
await db.refresh(record)
|
||||
|
||||
logger.info(
|
||||
"approval_promoted_by_current_owner_policy",
|
||||
id=record.id,
|
||||
risk_level=effective_risk.value,
|
||||
incident_id=record.incident_id,
|
||||
)
|
||||
return approval_record_to_request(record), True
|
||||
|
||||
async def find_by_fingerprint(
|
||||
self,
|
||||
fingerprint: str,
|
||||
|
||||
Reference in New Issue
Block a user