feat(drift): B4 drift_reports DB 持久化 + CronJob 修復
All checks were successful
CD Pipeline / build-and-deploy (push) Successful in 12m17s

- drift_repository.py: DriftReportRepository (save/get/list/update)
- drift.py router: 移除 in-memory dict,改用 DB repository
- drift-cronjob.yaml: 修正 SA/NetworkPolicy/NodePort 問題
- allow-intra-namespace NetworkPolicy (已套用至 prod)
- migrate-phase8/9: symptoms_hash + drift_reports migration Job YAML

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OG T
2026-04-09 20:28:55 +08:00
parent b1e207ffae
commit c92cdeea0f
5 changed files with 356 additions and 22 deletions

View File

@@ -40,11 +40,13 @@ spec:
component: drift-scanner
spec:
restartPolicy: Never
serviceAccountName: awoooi-api # 使用 API 的 ServiceAccount有 kubectl 權限)
# 2026-04-09 Claude Sonnet 4.6: awoooi-api SA 不存在,改用 default只需呼叫內部 API不需 K8s 權限)
serviceAccountName: default
containers:
- name: drift-scanner
# 使用 awoooi-api 鏡像(含 kubectl + Python 環境)
image: harbor.wooo.work/awoooi/api:latest
# 2026-04-09 Claude Sonnet 4.6: 改用內網 registry + 固定 SHA tag (禁止 latest)
image: 192.168.0.110:5000/awoooi/api:21567a7a6dbee7db2c0f59c265f80713ff5e6fe4
imagePullPolicy: Always
command:
- python
@@ -58,8 +60,10 @@ spec:
print(f"status={r.status_code} body={r.text[:200]}")
asyncio.run(run())
env:
# 2026-04-09 Claude Sonnet 4.6: ClusterIP 和 DNS 在 Job Pod 均不可達
# 改用 NodePort 直連 K3s worker node同 K8s_API_SERVER_URL 解法)
- name: INTERNAL_API_URL
value: "http://awoooi-api.awoooi-prod.svc.cluster.local:8000"
value: "http://192.168.0.121:32334"
- name: DRIFT_SCAN_NAMESPACES
value: "awoooi-prod"
resources:

View File

@@ -0,0 +1,84 @@
apiVersion: batch/v1
kind: Job
metadata:
name: migrate-phase8-symptoms-hash
namespace: awoooi-prod
labels:
app: awoooi-migration
phase: phase25
spec:
ttlSecondsAfterFinished: 300
backoffLimit: 1
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: postgres:15-alpine
command:
- /bin/sh
- -c
- |
echo "=========================================="
echo "Phase 25 P1: knowledge_entries symptoms_hash Migration"
echo "=========================================="
DB_HOST=$(echo $DATABASE_URL | sed 's/.*@\([^:]*\):.*/\1/')
DB_PORT=$(echo $DATABASE_URL | sed 's/.*:\([0-9]*\)\/.*/\1/')
DB_NAME=$(echo $DATABASE_URL | sed 's/.*\/\([^?]*\).*/\1/')
DB_USER=$(echo $DATABASE_URL | sed 's/.*\/\/\([^:]*\):.*/\1/')
DB_PASS=$(echo $DATABASE_URL | sed 's/.*:\/\/[^:]*:\([^@]*\)@.*/\1/')
echo "Connecting to: $DB_HOST:$DB_PORT/$DB_NAME"
export PGPASSWORD="$DB_PASS"
psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" <<'EOSQL'
-- Phase 25 P1: Anti-Pattern symptoms_hash 欄位
-- 2026-04-09 Claude Sonnet 4.6
ALTER TABLE knowledge_entries
ADD COLUMN IF NOT EXISTS symptoms_hash VARCHAR(16);
CREATE INDEX IF NOT EXISTS idx_knowledge_anti_pattern_hash
ON knowledge_entries (entry_type, symptoms_hash, created_at)
WHERE entry_type = 'anti_pattern' AND symptoms_hash IS NOT NULL;
-- 自動處理 CHECK constraint若有
DO $$
DECLARE
v_conname text;
BEGIN
SELECT conname INTO v_conname
FROM pg_constraint
WHERE conrelid = 'knowledge_entries'::regclass AND contype = 'c' AND conname LIKE '%status%';
IF v_conname IS NOT NULL THEN
EXECUTE format('ALTER TABLE knowledge_entries DROP CONSTRAINT %I', v_conname);
ALTER TABLE knowledge_entries ADD CONSTRAINT knowledge_entries_status_check
CHECK (status IN ('draft', 'review', 'approved', 'archived', 'published'));
RAISE NOTICE 'Updated status CHECK constraint: % → added published', v_conname;
ELSE
RAISE NOTICE 'No status CHECK constraint found, skipping';
END IF;
END $$;
-- 驗證
SELECT column_name, data_type
FROM information_schema.columns
WHERE table_name = 'knowledge_entries'
ORDER BY ordinal_position;
EOSQL
echo "=========================================="
echo "Migration completed!"
echo "=========================================="
envFrom:
- secretRef:
name: awoooi-secrets
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "128Mi"
cpu: "200m"

View File

@@ -0,0 +1,83 @@
apiVersion: batch/v1
kind: Job
metadata:
name: migrate-phase9-drift-reports
namespace: awoooi-prod
labels:
app: awoooi-migration
phase: phase25
spec:
ttlSecondsAfterFinished: 300
backoffLimit: 1
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: postgres:15-alpine
command:
- /bin/sh
- -c
- |
echo "=========================================="
echo "Phase 25 P2: drift_reports 資料表 Migration"
echo "=========================================="
DB_HOST=$(echo $DATABASE_URL | sed 's/.*@\([^:]*\):.*/\1/')
DB_PORT=$(echo $DATABASE_URL | sed 's/.*:\([0-9]*\)\/.*/\1/')
DB_NAME=$(echo $DATABASE_URL | sed 's/.*\/\([^?]*\).*/\1/')
DB_USER=$(echo $DATABASE_URL | sed 's/.*\/\/\([^:]*\):.*/\1/')
DB_PASS=$(echo $DATABASE_URL | sed 's/.*:\/\/[^:]*:\([^@]*\)@.*/\1/')
echo "Connecting to: $DB_HOST:$DB_PORT/$DB_NAME"
export PGPASSWORD="$DB_PASS"
psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" <<'EOSQL'
-- Phase 25 P2: Config Drift Detection — drift_reports 資料表
-- 2026-04-09 Claude Sonnet 4.6
CREATE TABLE IF NOT EXISTS drift_reports (
report_id VARCHAR(32) PRIMARY KEY,
namespace VARCHAR(128) NOT NULL,
triggered_by VARCHAR(64) NOT NULL DEFAULT 'cron',
scanned_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
high_count INT NOT NULL DEFAULT 0,
medium_count INT NOT NULL DEFAULT 0,
info_count INT NOT NULL DEFAULT 0,
items JSONB NOT NULL DEFAULT '[]',
interpretation JSONB,
status VARCHAR(32) NOT NULL DEFAULT 'pending',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
resolved_at TIMESTAMPTZ
);
CREATE INDEX IF NOT EXISTS idx_drift_reports_namespace
ON drift_reports(namespace);
CREATE INDEX IF NOT EXISTS idx_drift_reports_status
ON drift_reports(status);
CREATE INDEX IF NOT EXISTS idx_drift_reports_created_at
ON drift_reports(created_at DESC);
CREATE INDEX IF NOT EXISTS idx_drift_reports_high_count
ON drift_reports(high_count)
WHERE high_count > 0;
-- 驗證
SELECT column_name, data_type
FROM information_schema.columns
WHERE table_name = 'drift_reports'
ORDER BY ordinal_position;
EOSQL
echo "=========================================="
echo "Migration completed!"
echo "=========================================="
envFrom:
- secretRef:
name: awoooi-secrets
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "128Mi"
cpu: "200m"