178 lines
4.9 KiB
Python
178 lines
4.9 KiB
Python
"""
|
|
Core Constants
|
|
==============
|
|
ADR-027: Incident-Approval 同步架構
|
|
|
|
統一定義系統常量,避免散落各處的 magic numbers。
|
|
|
|
版本: v1.0
|
|
建立: 2026-03-26 (台北時區)
|
|
"""
|
|
|
|
# =============================================================================
|
|
# TTL Settings (秒)
|
|
# =============================================================================
|
|
|
|
# Working Memory TTL: 7 天
|
|
INCIDENT_TTL_SECONDS = 7 * 24 * 3600 # 604800
|
|
APPROVAL_TTL_SECONDS = 7 * 24 * 3600 # 604800
|
|
|
|
# Decision Token TTL: 24 小時
|
|
DECISION_TTL_SECONDS = 24 * 3600 # 86400
|
|
|
|
# =============================================================================
|
|
# Redis Key Prefixes
|
|
# =============================================================================
|
|
|
|
REDIS_KEY_INCIDENT = "incident:"
|
|
REDIS_KEY_APPROVAL = "approval:"
|
|
REDIS_KEY_PENDING = "pending_approvals"
|
|
REDIS_KEY_DECISION = "decision:"
|
|
|
|
# =============================================================================
|
|
# Status Mappings (ADR-027)
|
|
# =============================================================================
|
|
|
|
# Approval 狀態 → Incident 狀態
|
|
APPROVAL_TO_INCIDENT_STATUS = {
|
|
"pending": "investigating",
|
|
"approved": "resolved",
|
|
"rejected": "escalated",
|
|
"expired": "escalated",
|
|
}
|
|
|
|
# Incident 狀態 → 是否活躍
|
|
INCIDENT_ACTIVE_STATUSES = frozenset({"investigating", "mitigating"})
|
|
|
|
# =============================================================================
|
|
# Terminal Service Settings (Phase 19.4 P1)
|
|
# 2026-03-30 Claude Code: 首席架構師審查 - 常數提取
|
|
# =============================================================================
|
|
|
|
# SSE 事件間隔 (秒) - 避免前端過載
|
|
SSE_DELAY_SECONDS = 0.3
|
|
|
|
# Approval 清單顯示上限
|
|
MAX_APPROVAL_DISPLAY = 5
|
|
|
|
# 錯誤訊息截斷長度 (安全性)
|
|
ERROR_MESSAGE_MAX_LENGTH = 100
|
|
ERROR_MESSAGE_DISPLAY_LENGTH = 50
|
|
|
|
# =============================================================================
|
|
# CI/CD Alert Detection (2026-03-30 P1 配置化)
|
|
# =============================================================================
|
|
|
|
# CI/CD 告警 alertname 前綴
|
|
CICD_ALERT_PREFIXES = (
|
|
"CD_",
|
|
"CI_",
|
|
"E2E_",
|
|
"SmokeTest",
|
|
"Build_",
|
|
"Test_",
|
|
"Deploy_",
|
|
)
|
|
|
|
# CI/CD 告警 alertname 後綴
|
|
CICD_ALERT_SUFFIXES = (
|
|
"_Build",
|
|
"_Test",
|
|
"_Deploy",
|
|
"_E2E",
|
|
)
|
|
|
|
# CI/CD 告警關鍵字 (不區分大小寫)
|
|
CICD_ALERT_KEYWORDS = ("CI/CD", "cicd")
|
|
|
|
# =============================================================================
|
|
# Heartbeat/Watchdog Alert Detection (2026-04-10 Claude Sonnet 4.6 Asia/Taipei)
|
|
# 心跳/看門狗告警不觸發自動修復飛輪 — 這類告警代表監控系統狀態,不是服務故障
|
|
# =============================================================================
|
|
HEARTBEAT_ALERT_NAMES = frozenset({
|
|
"Watchdog",
|
|
"DeadMansSwitch",
|
|
"NoAlertsReceived",
|
|
"NoAlertsReceived2Hours",
|
|
"AlertmanagerDown",
|
|
"PrometheusNotConnectedToAlertmanager",
|
|
})
|
|
|
|
HEARTBEAT_ALERT_KEYWORDS = ("NoAlertsReceived", "Watchdog", "DeadMansSwitch", "Heartbeat")
|
|
|
|
|
|
def is_heartbeat_alertname(alertname: str) -> bool:
|
|
"""
|
|
判斷 alertname 是否為心跳/看門狗告警
|
|
|
|
心跳告警代表監控系統自身健康狀態,不是服務故障,
|
|
不應進入自動修復飛輪(不存在對應的 Playbook 修復動作)。
|
|
"""
|
|
return (
|
|
alertname in HEARTBEAT_ALERT_NAMES
|
|
or any(kw in alertname for kw in HEARTBEAT_ALERT_KEYWORDS)
|
|
)
|
|
|
|
|
|
def is_cicd_alertname(alertname: str) -> bool:
|
|
"""
|
|
判斷 alertname 是否為 CI/CD 告警
|
|
|
|
Args:
|
|
alertname: Alertmanager alertname 標籤值
|
|
|
|
Returns:
|
|
True 如果是 CI/CD 告警
|
|
"""
|
|
return (
|
|
alertname.startswith(CICD_ALERT_PREFIXES)
|
|
or alertname.endswith(CICD_ALERT_SUFFIXES)
|
|
or any(kw in alertname for kw in CICD_ALERT_KEYWORDS)
|
|
or "cicd" in alertname.lower()
|
|
)
|
|
|
|
|
|
def sanitize_error_message(error: str, max_length: int = ERROR_MESSAGE_MAX_LENGTH) -> str:
|
|
"""
|
|
安全化錯誤訊息 - 截斷並移除敏感資訊
|
|
|
|
2026-03-30 Claude Code: 首席架構師審查 - 錯誤訊息安全化
|
|
|
|
Args:
|
|
error: 原始錯誤訊息
|
|
max_length: 最大長度 (預設 100)
|
|
|
|
Returns:
|
|
安全化後的錯誤訊息
|
|
"""
|
|
if not error:
|
|
return "Unknown error"
|
|
|
|
# 移除可能的敏感資訊模式
|
|
sensitive_patterns = [
|
|
"password",
|
|
"token",
|
|
"secret",
|
|
"api_key",
|
|
"apikey",
|
|
"credential",
|
|
"bearer",
|
|
]
|
|
|
|
sanitized = error
|
|
for pattern in sensitive_patterns:
|
|
# 不區分大小寫替換
|
|
import re
|
|
sanitized = re.sub(
|
|
rf"({pattern})\s*[:=]\s*\S+",
|
|
f"{pattern}=[REDACTED]",
|
|
sanitized,
|
|
flags=re.IGNORECASE,
|
|
)
|
|
|
|
# 截斷
|
|
if len(sanitized) > max_length:
|
|
return sanitized[:max_length - 3] + "..."
|
|
|
|
return sanitized
|