# ADR-016: K8s 資源名稱規範與動態驗證

> **狀態**: Accepted
> **日期**: 2026-03-26
> **決策者**: 統帥 + 首席架構師

## 背景

在 E2E Tool Call 驗證中發現，AI (OpenClaw) 產生的 kubectl 指令會執行失敗，原因是資源名稱不正確：

```bash
# AI 產生的指令 (錯誤)
kubectl rollout restart deployment/https://api.awoooi.wooo.work -n default

# 正確應該是
kubectl rollout restart deployment/awoooi-api -n awoooi-prod
```

**根因**：Alert 來源的 `target_resource` 欄位傳入 URL 而非 K8s Deployment 名稱，AI 直接使用導致無效指令。

## 決策

實施**三層防禦架構**，確保 kubectl 指令有效：

```
┌─────────────────────────────────────────────────────────────┐
│  Alert 來源 (Alertmanager/Sentry/UptimeKuma)                │
│  target_resource = "https://api.awoooi.wooo.work"          │
└─────────────────────┬───────────────────────────────────────┘
                      ↓
┌─────────────────────────────────────────────────────────────┐
│  第一層: 入口正規化 (k8s_naming.py)                          │
│  - 移除 URL scheme                                          │
│  - 查詢靜態映射表                                            │
│  - 轉換為 RFC 1123 合規名稱                                  │
└─────────────────────┬───────────────────────────────────────┘
                      ↓
┌─────────────────────────────────────────────────────────────┐
│  第二層: 動態驗證 (resource_resolver.py)                     │
│  - 調用 kubectl_get 驗證資源存在性                           │
│  - 模糊匹配 namespace 內的 Deployments                       │
│  - 回傳匹配結果或候選列表                                    │
└─────────────────────┬───────────────────────────────────────┘
                      ↓
┌─────────────────────────────────────────────────────────────┐
│  第三層: 執行前攔截 (Multi-Sig)                              │
│  - 若 requires_confirmation=true，標記需人工確認            │
│  - 顯示候選列表讓人類選擇                                    │
└─────────────────────────────────────────────────────────────┘
```

## K8s 命名規則 (RFC 1123)

| 規則 | 說明 | 範例 |
|------|------|------|
| 最大長度 | 63 字元 | `my-very-long-service-name-...` |
| 允許字元 | 小寫字母、數字、連字號 | `awoooi-api-v2` |
| 開頭結尾 | 必須是字母或數字 | ✅ `api-v1` ❌ `-api-v1-` |
| 禁止 | 大寫、底線、點、特殊字元 | ❌ `API_Service.v1` |

## 靜態映射表

已知的 URL → Deployment 映射 (在 `k8s_naming.py` 中維護)：

| 輸入 | Deployment | Namespace |
|------|------------|-----------|
| `api.awoooi.wooo.work` | `awoooi-api` | `awoooi-prod` |
| `awoooi.wooo.work` | `awoooi-web` | `awoooi-prod` |
| `wooo.work` | `awoooi-web` | `awoooi-prod` |

### 非 K8s 資源標記

以下主機不在 K8s 中，應跳過 kubectl 操作：

| 主機 | 類型 | 處理方式 |
|------|------|---------|
| `prod-docker-188` | Docker Container | SKIP_K8S |
| `192.168.0.188` | VM Host | SKIP_K8S |
| `192.168.0.110` | VM Host | SKIP_K8S |

## 實作檔案

| 檔案 | 功能 |
|------|------|
| `src/utils/k8s_naming.py` | 正規化函數、靜態映射表 |
| `src/services/resource_resolver.py` | 動態驗證器、模糊匹配 |
| `webhooks.py` | 入口呼叫正規化 |
| `openclaw.py` | 執行前驗證 |

## API 契約

### 輸入格式

`target_resource` 欄位應盡可能使用 K8s 資源名稱：

```json
{
  "target_resource": "awoooi-api",  // ✅ 優先
  "target_resource": "api.awoooi.wooo.work",  // ⚠️ 會被轉換
  "namespace": "awoooi-prod"
}
```

### 輸出格式 (ResolveResult)

```python
@dataclass
class ResolveResult:
    success: bool              # 是否成功解析
    resource_name: str | None  # 解析後的名稱
    namespace: str | None      # 命名空間
    resource_type: ResourceType  # deployment/statefulset/pod
    confidence: float          # 0.0 - 1.0
    is_k8s_resource: bool      # 是否為 K8s 資源
    requires_confirmation: bool  # 是否需人工確認
    candidates: list[str]      # 候選列表 (多重匹配時)
    note: str | None           # 備註
```

## 後果

### 優點

- **消除無效指令**：所有 kubectl 指令在執行前都經過驗證
- **智能容錯**：URL/域名自動轉換為正確的 K8s 名稱
- **可觀測性**：日誌記錄所有正規化和匹配過程
- **可擴展**：映射表可透過 Memory 或 DB 動態更新

### 缺點

- **額外延遲**：動態驗證需調用 K8s API (~50ms)
- **維護成本**：映射表需定期更新

### 風險

| 風險 | 緩解措施 |
|------|---------|
| K8s API 不可用 | Fallback 到靜態映射表 |
| 模糊匹配錯誤 | 低信心度時標記需人工確認 |
| 映射表過時 | 定期審查 + 動態驗證為主 |

## 相關文件

- `feedback_api_path_naming.md` - API 路徑命名規範
- `reference_four_hosts.md` - 五主機架構
- Skill 03 - OpenClaw 認知專家 (更新提醒)
