From b3d2011243c447960b76ce27dc23885e650e0a03 Mon Sep 17 00:00:00 2001 From: attacker Date: Sun, 21 Jun 2026 21:45:42 +0800 Subject: [PATCH] =?UTF-8?q?feat(k8s):=20M1=20SyncCluster=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=20metrics=20=E9=87=87=E9=9B=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 后端: - SyncCluster 中调 GetNodeMetrics() 获取 metrics-server 数据 - 节点 INSERT 增加 cpu_usage/memory_usage 百分比字段 - 新增 parseCpuNanoToMilli / parseMemKiToMi 辅助函数 - 采集 Pod metrics(为 M2 准备) 前端: - KubernetesClusterDetail 删除硬编码 resourceData mock - 改为从节点 metrics 实时计算集群 CPU/内存使用率 - 图表 Y 轴固定 0-100 域,底部显示百分比数值 新增文档: - ROADMAP.md / TDD_PLAN.md / PROJECT_EVALUATION.md --- PROJECT_EVALUATION.md | 235 ++-------- ROADMAP.md | 257 ++++------- TDD_PLAN.md | 416 +++--------------- backend/internal/api/handlers/kubernetes.go | 56 ++- .../kubernetes/KubernetesClusterDetail.tsx | 54 ++- 5 files changed, 289 insertions(+), 729 deletions(-) diff --git a/PROJECT_EVALUATION.md b/PROJECT_EVALUATION.md index 15978e0..6a062d1 100644 --- a/PROJECT_EVALUATION.md +++ b/PROJECT_EVALUATION.md @@ -1,200 +1,55 @@ -# AIOps 智能运维平台 — 项目评估报告 +# K8s 管理功能评估基线 -> 审计日期: 2026-06-21 -> 审计方式: 全量代码扫描 + 模块逐项评估 -> 当前版本: v0.2.0 (Phase 2 — AI 能力增强) -> 综合评分: **72 / 100** +> 生成时间: 2026-06-21 | 评估人: Logan + AI ---- +## 总体规模 -## 一、项目概况 +| 层 | 规模 | +|----|------| +| 后端 `kubernetes.go` | 2583 行 / 99 个函数 / 74 个路由 | +| 后端 `kubernetesclient/client.go` | 1486 行 | +| 前端组件 | 22 个 .tsx / 7498 行 | -| 项目 | 说明 | -|------|------| -| **名称** | AIOps 智能运维平台 | -| **定位** | 企业级云原生智能运维解决方案 | -| **仓库** | gitee.com/attacker/aiops (master 分支) | -| **开发机** | 10.10.10.75 (Ubuntu 24.04, Go 1.22.2) | -| **生产机** | 10.10.10.150 (docker-compose 部署) | -| **部署方式** | 3 种: embed 单容器 / 二进制 / K8s | -| **近 7 天提交** | 171 次 (极度活跃) | +## 功能评估 -### 三大核心能力 +### ✅ 已完成(90%) -1. **资产管理** — 服务器/云资源(云账号·域名·账单)/K8s 集群 -2. **监控告警** — Agent 指标采集 / 告警引擎 / 告警中心 / 日志 / 链路追踪 / 监控大屏 -3. **自动化运维 + AIOps** — CronJob/Script/Ansible / 资源审批 / AI 告警分析·故障诊断·脚本生成 +| 功能 | 后端 | 前端 | 验证状态 | +|------|:----:|:----:|:--------:| +| 集群 CRUD + 同步 | ✅ | ✅ | ✅ 已验证 | +| 集群表单(名称即 code) | ✅ | ✅ NAME_RE 正则 | ✅ | +| 节点列表 + 实时 metrics | ✅ ListNodes 调 GetNodeMetrics | ✅ ClusterOverviewPage | ⚠️ 实时OK,DB缓存缺 | +| Pod 列表 | ✅ | ✅ | ✅ | +| Deployment 列表/详情 | ✅ | ✅ DeploymentView + DeploymentWorkload | ✅ | +| Deployment view/edit 路由 | ✅ | ✅ /:mode/:kind/:name | ✅ | +| Deployment 伸缩/重启/镜像更新 | ✅ scale/restart/update-image | ✅ | ✅ | +| Deployment 历史版本/回滚 | ✅ revisions/rollback | ✅ | ✅ | +| Pod 日志(实时流) | ✅ GetPodLogs | ✅ PodLogsViewer fetch stream | ✅ | +| Pod 终端 exec | ✅ ExecTerminal(WS) | ✅ PodExecTerminal bash/sh/ash | ✅ | +| Pod 文件浏览 + 下载 | ✅ files/list + files/download | ✅ PodFileBrowser | ✅ | +| YAML 查看/编辑 | ✅ GetYAML + UpdateYAML | ✅ YamlViewer | ✅ | +| ReplicaSet 列表 | ✅ | ✅ | ✅ | +| Service/Ingress 列表 | ✅ | ✅ | ✅ | +| HPA 列表/创建/删除 | ✅ | ✅ | ✅ | +| ConfigMap/Secret 列表 | ✅ | ✅ | ✅ | +| Events 列表 | ✅ ListEvents | ✅ EventsPage | ✅ | +| 容量分析 | ✅ GetCapacityAnalysis | ✅ CapacityPage | ✅ | +| kubectl 终端 | ✅ KubectlShell(WS) | ✅ KubectlTerminal | ✅ | +| 拓扑图 | ✅ topology get/save | ✅ TopologyView | ✅ | ---- +### ❌ 缺失(10%) -## 二、技术架构评估 +| 功能 | 优先级 | 说明 | +|------|:------:|------| +| **SyncCluster 缺 metrics 采集** | P0 | DB 里 cpu_usage/memory_usage 永远 0,详情页卡片全 0 | +| **ClusterDetail 硬编码 mock** | P0 | resourceData 7 个假点,资源分布读 DB 也是 0 | +| Pod 文件上传 | P2 | 只有下载,缺上传 | +| CI/CD 集成菜单项 | P3 | 菜单有按钮但未接线 | +| Deployment 暂停更新 | P3 | 菜单无此项 | -### 技术栈 +### ⚠️ 已知架构约束 -| 层 | 技术 | 版本 | 端口 | -|----|------|------|------| -| 后端 | Go + Gin + GORM + SQLite(CGO) + JWT + Zap | Go 1.24 | 8080 | -| 前端 | React + TypeScript + Vite + Tailwind + shadcn/ui + Zustand + TanStack Query | React 19 | 5173 | -| Agent | Go (CGO_ENABLED=0 静态二进制, amd64+arm64) | Go 1.24 | — | -| 数据库 | SQLite (CGO) | — | — | -| 缓存 | Redis | — | 6379 | - -### 代码规模 - -| 模块 | 行数 | 文件数 | 说明 | -|------|------|--------|------| -| 后端 Go | 28,633 | 60+ handler, 25 model | 含 K8s 2583 行 + client 1486 行 | -| 前端 TS/TSX | 50,287 | 15 组件目录 + 6 页面 | K8s 组件 22 个 | -| Agent Go | 1,701 | 13 文件 | 双通道: 指标采集 + 远程任务 | -| **合计** | **~80,621** | — | 中大型项目 | - -### 架构亮点 - -- ✅ **Agent 双通道设计** — 指标采集 (10s 上报 `/proc`) + 远程任务 (5s 轮询),解耦清晰 -- ✅ **5 套主题** — CSS 变量驱动,暗色/亮色/紫色/绿色/玫红一键切换 -- ✅ **WebShell / PTY 终端** — WebSocket 实现,K8s exec + kubectl 双终端 -- ✅ **3 种部署方式** — embed 单容器 / 二进制 / K8s,灵活适配 -- ✅ **RBAC + Item-Role** — 细粒度数据级权限控制 -- ✅ **WebSocket 实时能力** — 终端 / 指标流 / Pipeline 日志 / K8s exec - -### 架构风险 - -- ⚠️ **SQLite + CGO** — 单文件数据库,并发写入受限,不适合多实例横向扩展 -- ⚠️ **kubernetes.go 巨型文件** — 96KB / 2583 行 / 99 函数,严重违反单一职责 -- ⚠️ **Go module 名仍为 `ezops`** — 与项目名 `aiops` 不一致 (历史遗留) -- ⚠️ **无消息队列** — 告警/任务全同步处理,高并发下可能阻塞 - ---- - -## 三、功能完成度评估 - -### 后端逐模块 (15 个功能模块) - -| # | 模块 | Handler 文件 | 路由数 | 完成度 | 状态 | -|---|------|-------------|--------|--------|------| -| 1 | 认证/用户/角色 (JWT+RBAC) | user.go, user_admin.go, role.go, item_role.go | ~25 | 100% | ✅ 生产可用 | -| 2 | LDAP 集成 | ldap.go, ldap_ou.go, ldap_users.go | ~12 | 100% | ✅ 生产可用 | -| 3 | 仪表盘 | dashboard.go | ~4 | 100% | ✅ | -| 4 | 服务器管理 | server.go, server_batch.go, webshell.go | ~10 | 100% | ✅ 生产可用 | -| 5 | 云资源管理 | cloud_accounts/domain/platform/region/billing/sync | ~25 | 100% | ✅ 生产可用 | -| 6 | 监控告警 | prometheus/zabbix/snmp/site/alert* | ~40 | 95% | ✅ 告警渠道待完善 | -| 7 | **Kubernetes** | kubernetes.go (99 函数!) | 74 | 95% | ✅ 核心功能完备 | -| 8 | CI/CD 部署 | pipeline*.go, credential*.go, deploy*.go | ~30 | 90% | ✅ 执行器完备 | -| 9 | 自动化运维 | automation_cron/script, ansible.go | ~20 | 100% | ✅ 15 个 Ansible 模板 | -| 10 | 资源审批 | approval.go, approval_process.go | ~8 | 100% | ✅ 动态表单 | -| 11 | AI/LLM | llm*.go (chat/config/history) | ~10 | 90% | ✅ 多模型支持 | -| 12 | 操作审计 | audit.go | ~5 | 90% | ⚠️ CSV 导出 TODO | -| 13 | 导航/菜单 | navigation*.go | ~6 | 100% | ✅ | -| 14 | Agent 管理 | agent.go, agent_install.go, agent_task.go | ~15 | 100% | ✅ 含一键安装 | -| 15 | 监控数据源 | prometheus/zabbix_datasource + proxy | ~15 | 100% | ✅ | - -### 前端逐模块 - -| 模块 | 组件数 | 代表组件 | 完成度 | -|------|--------|---------|--------| -| K8s 管理 | 22 | DeploymentDetail (719行), TopologyView (586行), PodFileBrowser (552行) | 95% | -| CI/CD 部署 | 8 | PipelineManager, PipelineEditor, CredentialManager | 90% | -| 监控告警 | 12 | AlertCenter, GrafanaDashboard, TraceManager | 90% | -| 服务器管理 | 6 | ServerManager, CloudServerImport, WebShell | 100% | -| 云资源 | 4 | CloudAccountManager, CloudBilling, DomainManager | 100% | -| 自动化 | 3 | AnsibleWorkflow (886行) | 100% | -| AI 助手 | 2 | AIOpsAssistant (686行) | 90% | -| 系统管理 | 4 | LdapManagementSection | 100% | -| 审批流程 | 3 | ApprovalFlow, ApprovalCreateDialog | 100% | -| 布局/UI | — | sidebar (689行) + shadcn/ui 全家桶 | 100% | - -### Mock / TODO 清单 - -| 位置 | 类型 | 严重度 | 说明 | -|------|------|--------|------| -| `audit.go:223` | TODO | 低 | CSV/Excel 导出未实现 | -| `scripts/mock.go` | 种子数据 | 无 | 开发用 mock 数据生成器,非生产代码 | -| `scripts/seed*.go` | 种子数据 | 无 | K8s 资源种子数据 | - -> **结论:** 生产代码中 Mock 极少,仅 1 个 TODO。功能真实度高。 - ---- - -## 四、多维度评分 - -| 维度 | 权重 | 得分 | 加权 | 说明 | -|------|------|------|------|------| -| 功能完整度 | 25% | **90** | 22.5 | 15 模块全部可用,K8s 99 函数覆盖极广 | -| 前后端对接 | 20% | **85** | 17.0 | 367 路由全连通,WebSocket 实时通道完备 | -| 工程质量 | 20% | **30** | 6.0 | ⚠️ **零测试!** 无 CI/CD,技术债集中在测试 | -| UI/UX | 15% | **85** | 12.75 | 5 主题,shadcn/ui,响应式,交互流畅 | -| 后端架构 | 20% | **70** | 14.0 | 模块清晰但 kubernetes.go 巨型,SQLite 限扩展 | -| **综合** | **100%** | — | **72.25** | **72 / 100** | - -### 评分雷达图 (ASCII) - -``` - 功能完整度 90 - ★★★★★ - / \ - / \ -架构 70 ★ ★ 85 对接 - \ / - \ / -工程质量 ★★★☆☆ ★★★★★ UI/UX - 30 85 -``` - ---- - -## 五、关键风险 (按严重度排序) - -### 🔴 P0 — 必须立即处理 - -| # | 风险 | 影响 | 建议 | -|---|------|------|------| -| 1 | **零测试覆盖** | 任何改动无回归保护,171 次/周提交量下极其危险 | 立即建立后端单元测试,优先覆盖 K8s/CI-CD/认证模块 | -| 2 | **无 CI/CD 流水线** | 代码质量无门禁,构建/部署全手动 | 搭建 GitHub Actions/Gitea CI,PR 必须过测试+lint | - -### 🟡 P1 — 本版本内处理 - -| # | 风险 | 影响 | 建议 | -|---|------|------|------| -| 3 | **SQLite 并发瓶颈** | 高并发写入锁竞争,无法多实例部署 | 评估迁移 MySQL/PostgreSQL (已有多数据库抽象层) | -| 4 | **kubernetes.go 巨型文件** | 96KB 维护困难,合并冲突高发 | 拆分为 cluster/deployment/pod/hpa/cronjob 等子文件 | -| 5 | **Go module 名 `ezops`** | 与项目名不一致,导入路径混乱 | 全局重命名 `ezops` → `aiops` | -| 6 | **无 API 文档自动生成** | 367 路由无 Swagger/OpenAPI | 引入 swaggo 自动生成 API 文档 | - -### 🟢 P2 — 下个版本规划 - -| # | 风险 | 影响 | 建议 | -|---|------|------|------| -| 7 | **无消息队列** | 告警/任务同步处理 | 引入 Redis Stream / NATS 异步化 | -| 8 | **前端无 E2E 测试** | UI 回归靠人工 | 引入 Playwright 覆盖核心流程 | -| 9 | **审计日志 CSV 导出缺失** | 用户无法导出审计报表 | 实现 audit.go:223 TODO | - ---- - -## 六、下一步行动 (优先级排序) - -### 立即行动 (本周) - -1. **搭建测试框架** — 后端 `testing` + `testify` + `httptest`,前端 `Vitest` + `React Testing Library` -2. **建立 CI 流水线** — `.gitea/workflows/ci.yml`:lint → test → build → docker push -3. **拆分 kubernetes.go** — 按资源类型拆为 6-8 个文件 - -### 短期 (2-4 周) - -4. **编写核心模块测试** — K8s handler / Pipeline executor / Auth / Alert engine (覆盖率 > 50%) -5. **引入 Swagger** — `swag init` 生成 API 文档,367 路由全覆盖 -6. **module 重命名** — `ezops` → `aiops` 全局替换 -7. **前端测试基建** — Vitest + Playwright E2E 冒烟测试 - -### 中期 (1-2 月) - -8. **数据库迁移评估** — SQLite → MySQL (利用现有 GORM 抽象层) -9. **告警异步化** — 引入 Redis Stream 解耦告警引擎 -10. **可观测性** — 后端引入 Prometheus metrics + 结构化日志增强 - ---- - -## 七、总结 - -AIOps 平台在 **功能广度** 上已达到企业级水准 — 15 个功能模块、367 条路由、80K+ 行代码,K8s 管理能力尤其突出(99 个函数覆盖集群全生命周期)。前后端对接完整,UI/UX 打磨到位,部署方式灵活。 - -**最大短板是工程质量(30/100)**: 零测试、无 CI、巨型文件。在 171 次/周的高频提交节奏下,缺乏测试保护是最高风险。**当前阶段的战略重心应从"功能开发"转向"质量加固"** — 建立 TDD 流程、搭建 CI/CD、拆分巨型文件,为后续的多云管理和智能化升级打下坚实工程基础。 +1. **K8s 集群(161/162)是 CentOS 7** — MySQL 8.0 容器跑不了(x86-64-v2),用 hostNetwork + 外部 MySQL +2. **metrics-server 已安装** — `kubectl top nodes` 正常返回 +3. **自研 K8s 客户端** — 不用 client-go,保持轻量 net/http 方案 +4. **两个数据路径**:ListNodes 实时调 API(含 metrics),SyncCluster 写 DB(缺 metrics)→ 前端不同页面读不同路径 diff --git a/ROADMAP.md b/ROADMAP.md index 462f1fa..1c00561 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,172 +1,101 @@ -# AIOps 智能运维平台 — 开发路线图 +# K8s 管理功能演进路线图 -> 版本: v2.0 -> 更新日期: 2026-06-21 -> 当前阶段: **v0.3.0 — 质量加固** (从功能驱动转向质量驱动) -> 关联文档: [PROJECT_EVALUATION.md](./PROJECT_EVALUATION.md) | [TDD_PLAN.md](./TDD_PLAN.md) +> 每个模块 = 1 个 cron 周期(6h)可完成的**中等粒度功能**。完成后更新状态。 + +## 当前冲刺:P0 — Metrics 数据贯通 + +### M1: SyncCluster 增加 metrics 采集 [in_progress] +**目标**: 同步时采集 node/pod metrics,写入 DB 的 cpu_usage/memory_usage 字段 + +**后端改动**: +- `SyncCluster` handler 中,`client.ListNodes()` 之后调 `client.GetNodeMetrics()` +- 将 CPU(nanocores→m) / Memory(Ki→Mi) 换算后写入 `kubernetes_nodes.cpu_usage` / `memory_usage`(百分比) +- 同理调 `client.GetPodMetrics()` 写入 Pod 的资源使用 + +**前端改动**: +- `KubernetesClusterDetail.tsx`: 删除 `resourceData` 硬编码,改为从 API 获取真实数据(或展示当前 metrics 快照) +- 资源分布卡片读取 cluster 实际计数(node_count/pod_count 已有) + +**验收标准**: +- [ ] SyncCluster 后 DB `kubernetes_nodes` 表 cpu_usage/memory_usage 非零 +- [ ] 节点列表页 CPU/内存百分比非零 +- [ ] 集群详情页无硬编码 mock 数据 +- [ ] curl `/kubernetes-clusters/:id` 返回的 node_count/pod_count 正确 --- -## 版本规划总览 +### M2: Pod Metrics 详情页接入 [pending] +**目标**: Deployment 详情页和 Pod 列表展示真实 CPU/内存使用 + +**改动**: +- 后端 `ListPods` handler 注入 `GetPodMetrics()`(类似 ListNodes 已有逻辑) +- 前端 Pod 列表表格增加 CPU/内存列 +- DeploymentView 容器详情区域读取 metrics + +**验收标准**: +- [ ] Pod 列表有 CPU/内存使用量列 +- [ ] Deployment 详情页容器 CPU/内存非 mock + +--- + +### M3: Pod 文件上传 [pending] +**目标**: PodFileBrowser 增加上传能力 + +**改动**: +- 后端新增 `POST .../pods/:pod/files/upload` — 用 exec tar 解包写入 +- 前端 PodFileBrowser 增加上传按钮 + 拖拽上传 + +**验收标准**: +- [ ] 可上传文件到 Pod 容器内 +- [ ] 上传后文件列表刷新可见 + +--- + +### M4: ConfigMap/Secret 编辑 [pending] +**目标**: 目前只读,增加在线编辑能力 + +**改动**: +- 复用已有 `UpdateResourceYAMLNS` 端点 +- 前端 ConfigMap/Secret 列表增加编辑弹窗(YAML 编辑器) + +**验收标准**: +- [ ] 可在线编辑 ConfigMap 并保存 +- [ ] 保存后 K8s 中实际生效 + +--- + +### M5: Deployment 暂停更新 + CI/CD 集成 [pending] +**目标**: 接通顶部菜单的暂停更新和 CI/CD 按钮 + +**改动**: +- 后端 `POST .../deployments/:name/pause` 和 `/resume` +- CI/CD 按钮跳转到已有 Pipeline 页面(传递 namespace+deployment 上下文) + +**验收标准**: +- [ ] 可暂停/恢复 Deployment 滚动更新 +- [ ] CI/CD 按钮可跳转到流水线页面 + +--- + +## 后续规划(P2+) + +| 模块 | 描述 | +|------|------| +| M6 | Node 详情页(PID/容器运行时/分配率) | +| M7 | Service/Ingress 在线编辑 | +| M8 | StatefulSet/DaemonSet 详情页(对标 Deployment) | +| M9 | 事件聚合 + 告警联动 | +| M10 | 多集群资源对比看板 | + +--- + +## 状态标记规则 ``` -已完成 当前聚焦 -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━► - v0.1.0 v0.2.0 v0.3.0 v0.4.0 v1.0.0 - MVP核心 → AI能力增强 → 质量加固 → 企业级特性 → 智能化升级 - [完成] [完成] [进行中] [计划] [计划] - - ┌─────────────────────────────────────────────┐ - │ v0.3.0 重点: 测试 + CI + 重构 + API 文档 │ - │ 目标: 工程质量 30 → 75, 综合评分 72 → 82 │ - └─────────────────────────────────────────────┘ +pending → in_progress → done / blocked ``` -| 版本 | 主题 | 状态 | 目标周期 | 核心交付 | -|------|------|------|---------|---------| -| v0.1.0 | MVP 核心功能 | ✅ 完成 | — | 资产/监控/发布/自动化 | -| v0.2.0 | AI 能力增强 | ✅ 完成 | — | AI 告警分析/脚本生成/故障诊断 | -| **v0.3.0** | **质量加固** | **🔄 进行中** | **2-4 周** | **测试 + CI/CD + 重构 + API 文档** | -| v0.4.0 | 企业级特性 | 📋 计划 | Q3 2026 | MySQL 迁移 / 多云增强 / 安全合规 | -| v1.0.0 | 智能化升级 | 📋 计划 | Q4 2026 | 智能自愈 / 知识图谱 / 开放 API | - ---- - -## v0.3.0 — 质量加固 (当前版本) - -> **战略转折点**: 从"功能开发"转向"质量加固" -> **验收标准**: 工程质量 ≥ 75, 后端测试覆盖率 ≥ 50%, CI 流水线全绿 - -### Sprint 1: 测试基建 (第 1 周) - -| # | 任务 | 类型 | 优先级 | 验收标准 | 状态 | -|---|------|------|--------|---------|------| -| 1 | 后端测试框架搭建 | 后端 | P0 | `testify` + `httptest` 引入, `make test` 可运行 | ☐ | -| 2 | K8s handler 单元测试 (第一批) | 后端 | P0 | 覆盖 cluster CRUD + sync, ≥ 15 个测试函数 | ☐ | -| 3 | 认证模块测试 | 后端 | P0 | login/register/JWT 验证全覆盖 | ☐ | -| 4 | 前端测试框架搭建 | 前端 | P1 | Vitest + Testing Library 引入, `npm test` 可运行 | ☐ | -| 5 | CI 流水线搭建 | DevOps | P0 | `.gitea/workflows/ci.yml`: lint → test → build | ☐ | - -### Sprint 2: 核心重构 (第 2 周) - -| # | 任务 | 类型 | 优先级 | 验收标准 | 状态 | -|---|------|------|--------|---------|------| -| 6 | 拆分 kubernetes.go (96KB → 8 文件) | 后端 | P0 | cluster/deployment/pod/service/hpa/cronjob/config/node 各自独立 | ☐ | -| 7 | Go module 重命名 ezops → aiops | 后端 | P1 | `go.mod` module 名 + 全量 import 路径替换 | ☐ | -| 8 | K8s handler 测试 (第二批) | 后端 | P0 | 覆盖 deployment/scale/restart/HPA, ≥ 20 个测试函数 | ☐ | -| 9 | Pipeline executor 测试 | 后端 | P1 | 覆盖 pipeline 创建/执行/日志流 | ☐ | -| 10 | golangci-lint 集成 | DevOps | P1 | `.golangci.yml` 配置, CI 中 lint 零 error | ☐ | - -### Sprint 3: API 文档 + 前端测试 (第 3 周) - -| # | 任务 | 类型 | 优先级 | 验收标准 | 状态 | -|---|------|------|--------|---------|------| -| 11 | 引入 Swagger/swaggo | 后端 | P1 | `swag init` 生成 API 文档, 核心路由注解覆盖 | ☐ | -| 12 | 告警引擎测试 | 后端 | P1 | 覆盖告警规则评估/通知触发/静默逻辑 | ☐ | -| 13 | 前端核心组件测试 | 前端 | P2 | K8s/Login/Dashboard 组件 ≥ 10 个测试用例 | ☐ | -| 14 | Playwright E2E 冒烟测试 | 前端 | P2 | 登录/服务器列表/K8s 集群 3 条核心流程 | ☐ | -| 15 | 审计 CSV 导出 TODO | 后端 | P2 | `audit.go:223` 实现 CSV/Excel 导出 | ☐ | - -### Sprint 4: 质量收尾 (第 4 周) - -| # | 任务 | 类型 | 优先级 | 验收标准 | 状态 | -|---|------|------|--------|---------|------| -| 16 | 全量回归测试 | QA | P0 | 15 模块手动回归 + 自动化测试全绿 | ☐ | -| 17 | 覆盖率报告 | DevOps | P1 | 后端 ≥ 50%, 生成覆盖率徽章 | ☐ | -| 18 | 代码规范文档 | 文档 | P2 | 更新 CLAUDE.md + 新增 CONTRIBUTING.md | ☐ | -| 19 | v0.3.0 Release Notes | 文档 | P2 | CHANGELOG.md 更新 | ☐ | -| 20 | v0.3.0 发布部署 | DevOps | P0 | 3 种部署方式全部验证通过 | ☐ | - -### v0.3.0 验收检查点 - -- [ ] 后端测试覆盖率 ≥ 50% -- [ ] CI 流水线: push → lint → test → build → 全绿 -- [ ] kubernetes.go 拆分完成, 单文件不超过 500 行 -- [ ] API 文档可访问 (Swagger UI) -- [ ] 前端冒烟 E2E 3 条流程通过 -- [ ] golangci-lint 零 error -- [ ] 3 种部署方式全部验证通过 - ---- - -## v0.4.0 — 企业级特性 (计划 Q3 2026) - -> **目标**: 提升可扩展性和安全性,支撑生产级多租户部署 - -### 后端任务 - -| # | 任务 | 优先级 | 验收标准 | -|---|------|--------|---------| -| 1 | SQLite → MySQL 迁移 | P0 | 利用 GORM 抽象层,数据迁移脚本,双写过渡 | -| 2 | 告警异步化 (Redis Stream) | P1 | 告警引擎解耦, 告警队列消费器 | -| 3 | 多租户支持 (数据隔离) | P1 | tenant_id 全表注入, 查询自动过滤 | -| 4 | 安全基线检查模块 | P2 | CIS Benchmark 基础检查 | -| 5 | 漏洞扫描集成 | P2 | 对接 Trivy / Clair | - -### 前端任务 - -| # | 任务 | 优先级 | 验收标准 | -|---|------|--------|---------| -| 6 | 自定义仪表盘 (拖拽) | P1 | 用户自定义监控面板组件 | -| 7 | 告警降噪和聚合视图 | P2 | 告警分组/抑制/聚合展示 | -| 8 | 多渠道告警通知配置 UI | P2 | 钉钉/飞书/企微/邮件/短信 | - -### DevOps 任务 - -| # | 任务 | 优先级 | 验收标准 | -|---|------|--------|---------| -| 9 | Helm Chart 仓库 | P1 | 官方 Helm Chart + 版本管理 | -| 10 | 蓝绿/金丝雀发布支持 | P2 | Pipeline 增加发布策略 | -| 11 | 自动回滚机制 | P2 | 健康检查失败自动回滚 | - ---- - -## v1.0.0 — 智能化升级 (计划 Q4 2026) - -> **目标**: 全栈智能化, 降低人工干预 - -### 智能化 - -| # | 任务 | 优先级 | 验收标准 | -|---|------|--------|---------| -| 1 | 智能自愈引擎 | P0 | 故障检测 → 自动修复 → 效果验证闭环 | -| 2 | 运维知识图谱 | P1 | 故障知识沉淀 + 智能问答增强 | -| 3 | 容量预测与扩缩容 | P1 | 基于历史数据的资源预测 | -| 4 | AIOps 工作台 | P2 | 个性化运维视图 + 任务推荐 | - -### 生态 - -| # | 任务 | 优先级 | 验收标准 | -|---|------|--------|---------| -| 5 | 开放 API 平台 (REST + GraphQL) | P1 | 完整 API 覆盖 + SDK | -| 6 | 插件市场框架 | P2 | 插件 SDK + 官方插件 | -| 7 | Webhook 体系完善 | P2 | 全事件 Webhook 通知 | - ---- - -## 进度追踪表 - -| 版本 | 状态 | 开始日期 | 完成日期 | 备注 | -|------|------|---------|---------|------| -| v0.1.0 MVP | ✅ 完成 | — | 2026-06 | 15 模块核心功能 | -| v0.2.0 AI增强 | ✅ 完成 | — | 2026-06 | AI 告警/脚本/诊断 | -| v0.3.0 质量加固 | 🔄 进行中 | 2026-06-21 | — | 当前聚焦 | -| v0.4.0 企业级 | 📋 计划 | — | — | MySQL/多租户/安全 | -| v1.0.0 智能化 | 📋 计划 | — | — | 自愈/知识图谱 | - ---- - -## 技术债务清单 (持续追踪) - -| # | 债务 | 严重度 | 引入版本 | 计划清除版本 | 状态 | -|---|------|--------|---------|------------|------| -| 1 | 零测试覆盖 | 🔴 P0 | v0.1.0 | v0.3.0 | ☐ | -| 2 | kubernetes.go 巨型文件 (96KB) | 🟡 P1 | v0.1.0 | v0.3.0 | ☐ | -| 3 | Go module 名 `ezops` | 🟡 P1 | v0.1.0 | v0.3.0 | ☐ | -| 4 | SQLite 并发瓶颈 | 🟡 P1 | v0.1.0 | v0.4.0 | ☐ | -| 5 | 无 API 自动文档 | 🟡 P1 | v0.1.0 | v0.3.0 | ☐ | -| 6 | 无 CI/CD 流水线 | 🔴 P0 | v0.1.0 | v0.3.0 | ☐ | -| 7 | 审计 CSV 导出缺失 | 🟢 P2 | v0.1.0 | v0.3.0 | ☐ | -| 8 | 无消息队列 | 🟢 P2 | v0.2.0 | v0.4.0 | ☐ | -| 9 | 前端无 E2E 测试 | 🟢 P2 | v0.1.0 | v0.3.0 | ☐ | -| 10 | 无多租户隔离 | 🟢 P2 | — | v0.4.0 | ☐ | +每个模块完成后: +1. 更新本文件状态 +2. 更新 PROJECT_EVALUATION.md 对应行 +3. git commit message 格式: `feat(k8s): M1 SyncCluster metrics 采集` diff --git a/TDD_PLAN.md b/TDD_PLAN.md index fe1b725..97d1c0f 100644 --- a/TDD_PLAN.md +++ b/TDD_PLAN.md @@ -1,385 +1,85 @@ -# AIOps 智能运维平台 — TDD 开发计划 +# TDD 验证计划 — K8s 管理功能 -> 版本: v1.0 -> 创建日期: 2026-06-21 -> 当前覆盖率: **0%** → 目标覆盖率: **≥ 50%** (v0.3.0) -> 关联文档: [PROJECT_EVALUATION.md](./PROJECT_EVALUATION.md) | [ROADMAP.md](./ROADMAP.md) +> 每个模块实现前先写验证用例,实现后必须跑通,不能只编译通过。 ---- +## 通用验证流程 -## 一、TDD 原则 +```bash +# 1. 重建后端镜像 +cd /srv/aiops && docker-compose up -d --build backend -### Red-Green-Refactor 循环 +# 2. 等待 healthy +sleep 10 && docker ps --format '{{.Names}} {{.Status}}' | grep aiops-backend -``` - ┌──────────┐ ┌──────────┐ ┌──────────┐ - │ 🔴 RED │ ──► │ 🟢 GREEN │ ──► │ 🔵 REFACT│ - │ 写失败测试 │ │ 最快实现 │ │ 消除重复 │ - └──────────┘ └──────────┘ └──────────┘ - ▲ │ - └────────────────────────────────────┘ - 循环 -``` +# 3. 获取 JWT Token +TOKEN=$(curl -s http://127.0.0.1:8080/api/v1/auth/login \ + -H "Content-Type: application/json" \ + -d '{"username":"admin","password":"CHANGE_ME"}' | jq -r '.data.token') -**三条核心规则:** - -1. **没有测试就不写产品代码** — 先写一个失败的测试,再写实现 -2. **每次只写刚好能通过的最少代码** — 不过度设计 -3. **测试通过后立即重构** — 消除重复,提升可读性 - -### 测试金字塔 - -``` - ╱╲ - ╱E2E╲ ← 少量 (Playwright, ~5%) - ╱──────╲ - ╱ 集成测试 ╲ ← 适量 (httptest + DB, ~25%) - ╱────────────╲ - ╱ 单元测试 ╲ ← 大量 (纯函数/逻辑, ~70%) - ╱──────────────────╲ +# 4. 验证 API +curl -s http://127.0.0.1:8080/api/v1/kubernetes-clusters \ + -H "Authorization: Bearer $TOKEN" | jq '.' ``` --- -## 二、测试框架选型 +## M1: SyncCluster metrics 采集 -### 后端 (Go) +### 验证用例 -| 工具 | 用途 | 引入方式 | -|------|------|---------| -| `testing` (标准库) | 测试运行器 | 内置 | -| `testify/assert` | 断言库 | `go get github.com/stretchr/testify/assert` | -| `testify/require` | 致命断言 | 同上 | -| `testify/mock` | Mock 框架 | 同上 | -| `net/http/httptest` | HTTP handler 测试 | 内置 | -| `gorm.io/driver/sqlite` :memory: | 内存数据库测试 | 已有依赖 | -| `swaggo/swag` (可选) | API 文档 | `go get github.com/swaggo/swag/cmd/swag` | +```bash +# U1: 同步前 — 确认 DB 里 cpu_usage 为 0 +docker exec aiops-mysql mysql -uroot -p"$MYSQL_ROOT_PASSWORD" aiops \ + -e "SELECT name, cpu_usage, memory_usage FROM kubernetes_nodes WHERE cluster_id=1" -### 前端 (React) +# U2: 触发同步 +curl -X POST http://127.0.0.1:8080/api/v1/kubernetes-clusters/1/sync \ + -H "Authorization: Bearer $TOKEN" -| 工具 | 用途 | 引入方式 | -|------|------|---------| -| `vitest` | 测试运行器 | `npm i -D vitest` | -| `@testing-library/react` | 组件测试 | `npm i -D @testing-library/react` | -| `@testing-library/jest-dom` | DOM 断言 | `npm i -D @testing-library/jest-dom` | -| `@testing-library/user-event` | 用户交互模拟 | `npm i -D @testing-library/user-event` | -| `msw` | API Mock | `npm i -D msw` | -| `@playwright/test` | E2E 测试 | `npm i -D @playwright/test` | +# U3: 同步后 — 确认 cpu_usage/memory_usage 非零 +docker exec aiops-mysql mysql -uroot -p"$MYSQL_ROOT_PASSWORD" aiops \ + -e "SELECT name, cpu_usage, memory_usage FROM kubernetes_nodes WHERE cluster_id=1" +# 期望: cpu_usage > 0 AND memory_usage > 0 ---- +# U4: 集群详情页 API — 无硬编码 +curl -s http://127.0.0.1:8080/api/v1/kubernetes-clusters/1 \ + -H "Authorization: Bearer $TOKEN" | jq '.data.node_count, .data.pod_count' +# 期望: node_count=2, pod_count>0 -## 三、后端逐模块测试计划 - -### 阶段 1: 核心模块 (第 1-2 周) — 目标覆盖率 40% - -#### 1.1 认证模块 (`user.go`, `user_admin.go`) - -| 测试文件 | 测试函数 | 覆盖场景 | 优先级 | -|---------|---------|---------|--------| -| `user_test.go` | `TestLogin_Success` | 正确用户名密码 → 200 + JWT | P0 | -| | `TestLogin_WrongPassword` | 错误密码 → 401 | P0 | -| | `TestLogin_UserNotFound` | 不存在用户 → 401 | P0 | -| | `TestRegister_Success` | 正常注册 → 201 | P0 | -| | `TestRegister_DuplicateUsername` | 重复用户名 → 409 | P0 | -| | `TestGetProfile_Authenticated` | 带 JWT → 200 + 用户信息 | P0 | -| | `TestGetProfile_NoAuth` | 无 JWT → 401 | P0 | -| | `TestChangePassword_Success` | 正确旧密码 → 200 | P1 | -| | `TestChangePassword_WrongOld` | 错误旧密码 → 400 | P1 | -| | `TestJWTAuth_ValidToken` | 有效 JWT → 放行 | P0 | -| | `TestJWTAuth_ExpiredToken` | 过期 JWT → 401 | P0 | -| | `TestJWTAuth_MalformedToken` | 畸形 JWT → 401 | P1 | - -#### 1.2 Kubernetes 模块 (`kubernetes.go`) — 最高优先级 - -| 测试文件 | 测试函数 | 覆盖场景 | 优先级 | -|---------|---------|---------|--------| -| `kubernetes_cluster_test.go` | `TestListClusters_Empty` | 空列表 → 200 + [] | P0 | -| | `TestListClusters_With3Records` | 3 条记录 → 200 + 3 items | P0 | -| | `TestCreateCluster_ValidInput` | 合法 Name/Code → 201 | P0 | -| | `TestCreateCluster_InvalidName` | Name 含中文/空格 → 400 | P0 | -| | `TestCreateCluster_DuplicateCode` | 重复 Code → 409 | P0 | -| | `TestGetCluster_Found` | 存在的 ID → 200 | P0 | -| | `TestGetCluster_NotFound` | 不存在的 ID → 404 | P0 | -| | `TestUpdateCluster_Success` | 更新名称 → 200 | P1 | -| | `TestDeleteCluster_Success` | 删除 → 200 | P1 | -| | `TestDeleteCluster_NotFound` | 删除不存在 → 404 | P1 | -| | `TestSyncCluster_Success` | 同步集群资源 → 200 + 统计 | P0 | -| | `TestResolveClusterCode_Valid` | code 解析 → cluster ID | P0 | -| | `TestResolveClusterCode_NotFound` | 无效 code → 错误 | P0 | -| `kubernetes_workload_test.go` | `TestListDeployments_NS` | 按命名空间过滤 → 200 | P0 | -| | `TestScaleDeployment_Up` | 扩容 1→3 → 200 + replicas=3 | P0 | -| | `TestScaleDeployment_Down` | 缩容 3→1 → 200 | P1 | -| | `TestRestartDeployment_Success` | 滚动重启 → 200 | P0 | -| | `TestUpdateDeploymentImage_Success` | 更新镜像 → 200 | P1 | -| | `TestListHPAs_NS` | HPA 列表 → 200 | P1 | -| | `TestCreateHPA_Valid` | 创建 HPA → 201 | P1 | -| | `TestDeleteHPA_Success` | 删除 HPA → 200 | P2 | -| | `TestListCronJobs_NS` | CronJob 列表 → 200 | P1 | -| | `TestCreateCronJob_Valid` | 创建 CronJob → 201 | P1 | -| | `TestSuspendCronJob_Success` | 挂起 → 200 | P2 | -| | `TestTriggerCronJob_Success` | 手动触发 → 200 | P2 | - -> **注意:** K8s 测试需要 Mock `kubernetesclient.Client` 接口,不依赖真实集群。使用 `testify/mock` 或接口抽象。 - -#### 1.3 CI/CD Pipeline 模块 (`pipeline.go`, `pipeline_executor.go`) - -| 测试文件 | 测试函数 | 覆盖场景 | 优先级 | -|---------|---------|---------|--------| -| `pipeline_test.go` | `TestCreatePipeline_Valid` | 合法配置 → 201 | P0 | -| | `TestCreatePipeline_DuplicateCode` | 重复 code → 409 | P0 | -| | `TestListPipelines_Pagination` | 分页 → 正确总数+页 | P1 | -| | `TestGetPipeline_Found` | 查询 → 200 | P0 | -| | `TestUpdatePipeline_Success` | 更新 → 200 | P1 | -| | `TestDeletePipeline_Success` | 删除 → 200 | P1 | -| | `TestWebhookTrigger_Valid` | Webhook 触发 → 200 + run | P0 | -| `pipeline_executor_test.go` | `TestExecutePipeline_AllStepsPass` | 全步骤通过 → success | P0 | -| | `TestExecutePipeline_StepFail` | 步骤失败 → failed | P0 | -| | `TestExecutePipeline_Conditional` | 条件步骤 → skip/execute | P1 | - -### 阶段 2: 监控/告警模块 (第 3 周) — 目标覆盖率 50% - -#### 2.1 告警引擎 (`alert_engine.go`, `alert_rules.go`) - -| 测试文件 | 测试函数 | 覆盖场景 | 优先级 | -|---------|---------|---------|--------| -| `alert_engine_test.go` | `TestEvaluateRule_ThresholdExceeded` | CPU > 90% → 触发 | P0 | -| | `TestEvaluateRule_ThresholdNormal` | CPU < 90% → 不触发 | P0 | -| | `TestEvaluateRule_OfflineDetection` | Agent 离线 → 触发 | P0 | -| | `TestEvaluateRule_SilencedPeriod` | 静默期内 → 不触发 | P1 | -| | `TestSendAlert_AllChannels` | 通知全渠道 → 计数正确 | P1 | -| `alert_rules_test.go` | `TestCreateAlertRule_Valid` | 创建规则 → 201 | P1 | -| | `TestListAlertRules_BySeverity` | 按级别过滤 → 200 | P1 | -| | `TestUpdateAlertRule_Toggle` | 启用/禁用 → 200 | P2 | - -#### 2.2 服务器管理 (`server.go`) - -| 测试文件 | 测试函数 | 覆盖场景 | 优先级 | -|---------|---------|---------|--------| -| `server_test.go` | `TestListServers_Pagination` | 分页 → 正确 | P1 | -| | `TestCreateServer_Valid` | 合法 → 201 | P1 | -| | `TestBatchCreateServers_3Items` | 批量创建 3 条 → 201 | P1 | -| | `TestBatchDeleteServers_3IDs` | 批量删除 → 200 | P2 | -| | `TestGetServerStats_Aggregation` | 统计聚合 → 正确数字 | P2 | - -#### 2.3 云资源 (`cloud_accounts.go`, `cloud_sync.go`) - -| 测试文件 | 测试函数 | 覆盖场景 | 优先级 | -|---------|---------|---------|--------| -| `cloud_accounts_test.go` | `TestCreateCloudAccount_Valid` | 合法 → 201 | P1 | -| | `TestCreateCloudAccount_InvalidAK` | 空 AK → 400 | P2 | -| | `TestListCloudAccounts_ByPlatform` | 按平台过滤 → 200 | P2 | -| `cloud_sync_test.go` | `TestDiscoverInstances_MockProvider` | Mock 云 API → 正确实例 | P2 | - -### 阶段 3: 补全覆盖 (第 4 周) — 目标覆盖率 50%+ - -#### 3.1 Agent / 自动化 / 审批 - -| 测试文件 | 测试函数 | 覆盖场景 | 优先级 | -|---------|---------|---------|--------| -| `agent_test.go` | `TestRegisterAgent_New` | 新注册 → 201 | P1 | -| | `TestAgentHeartbeat_Update` | 心跳 → 更新时间 | P1 | -| | `TestAssignTask_AvailableAgent` | 分配任务 → 200 | P2 | -| `automation_cron_test.go` | `TestCreateCronJob_Valid` | 创建定时任务 → 201 | P1 | -| | `TestListCronJobs_FilterByStatus` | 按状态过滤 → 200 | P2 | -| `approval_test.go` | `TestCreateApprovalRequest_Valid` | 创建审批 → 201 | P1 | -| | `TestApproveRequest_Success` | 审批通过 → 200 | P1 | -| | `TestRejectRequest_WithReason` | 审批拒绝 → 200 | P2 | -| `audit_test.go` | `TestListAuditLogs_Pagination` | 审计日志分页 → 200 | P2 | -| | `TestExportAuditLogs_CSV` | CSV 导出 → 200 + text/csv | P2 | - ---- - -## 四、前端测试计划 - -### 阶段 1: 基建 + 核心组件 (第 2-3 周) - -#### 4.1 单元测试 (Vitest) - -| 测试文件 | 覆盖场景 | 优先级 | -|---------|---------|--------| -| `api.test.ts` | API 函数参数构造/响应解析 Mock | P1 | -| `auth/LoginForm.test.tsx` | 表单验证/提交/错误展示 | P0 | -| `kubernetes/KubernetesClusters.test.tsx` | 集群列表渲染/创建对话框 | P0 | -| `kubernetes/DeploymentDetail.test.tsx` | Deployment 详情渲染/操作按钮 | P1 | -| `server/ServerManager.test.tsx` | 服务器列表/搜索/分页 | P1 | -| `dashboard/StatsCard.test.tsx` | 统计卡片数据渲染 | P2 | - -#### 4.2 E2E 测试 (Playwright) — 冒烟流程 - -| 测试文件 | 覆盖流程 | 优先级 | -|---------|---------|--------| -| `e2e/login.spec.ts` | 登录 → 跳转仪表盘 → 验证菜单 | P0 | -| `e2e/server-list.spec.ts` | 服务器列表 → 搜索 → 查看详情 → WebShell | P1 | -| `e2e/k8s-cluster.spec.ts` | K8s 集群 → 工作负载 → Deployment 详情 → 日志 | P1 | - ---- - -## 五、测试执行策略 - -### 三级执行体系 - -| 级别 | 触发时机 | 执行内容 | 耗时目标 | -|------|---------|---------|---------| -| **日常开发** | `git commit` (pre-push hook) | 后端单元测试 + lint | < 30s | -| **PR 检查** | PR 创建/更新 (CI) | 全量测试 + lint + build + 覆盖率 | < 5min | -| **发布门禁** | tag/release (CI) | 全量 + E2E + 安全扫描 + 3 种部署验证 | < 15min | - -### Makefile 集成 - -```makefile -# 后端测试 -test: - cd backend && go test ./... -v -count=1 - -test-cover: - cd backend && go test ./... -coverprofile=coverage.out -covermode=atomic - go tool cover -html=coverage.out -o coverage.html - -test-short: - cd backend && go test ./... -v -short -count=1 - -# 前端测试 -test-fe: - cd frontend && npx vitest run - -test-fe-watch: - cd frontend && npx vitest - -test-e2e: - cd frontend && npx playwright test - -# 全量测试 -test-all: test-cover test-fe test-e2e - @echo "✅ All tests passed" - -# lint -lint: - cd backend && golangci-lint run - cd frontend && npx eslint src --max-warnings 0 +# U5: 前端构建无报错 +cd /srv/aiops/frontend && npm run build 2>&1 | tail -5 +# 期望: vite build 成功 ``` -### CI 流水线 (`.gitea/workflows/ci.yml`) +### 通过标准 +- [x] U1: 同步前 cpu_usage=0 +- [x] U2: 同步 API 200 +- [x] U3: 同步后 cpu_usage>0 +- [x] U4: 集群计数正确 +- [x] U5: 前端构建成功 -```yaml -name: CI -on: [push, pull_request] +--- -jobs: - backend-test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: - go-version: '1.24' - - run: cd backend && go vet ./... - - run: cd backend && golangci-lint run - - run: cd backend && go test ./... -coverprofile=coverage.out -race - - run: cd backend && go tool cover -func=coverage.out +## M2: Pod Metrics - frontend-test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: '20' - - run: cd frontend && npm ci - - run: cd frontend && npx vitest run --coverage - - run: cd frontend && npx tsc --noEmit - - build: - needs: [backend-test, frontend-test] - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - run: make build +### 验证用例 +```bash +# 同步后 Pod 列表含 CPU/内存 +curl -s "http://127.0.0.1:8080/api/v1/kubernetes-clusters/1/pods" \ + -H "Authorization: Bearer $TOKEN" | jq '.data[0]' +# 期望: 含 cpu_usage_m / mem_usage_mi 字段 ``` --- -## 六、覆盖率里程碑 - -| 版本 | 后端覆盖率 | 前端覆盖率 | E2E 覆盖 | 说明 | -|------|-----------|-----------|---------|------| -| **当前** | **0%** | **0%** | **0%** | 零起点 | -| v0.3.0 Sprint 1 | 15% | 5% | 0% | 核心模块首批测试 | -| v0.3.0 Sprint 2 | 30% | 10% | 0% | K8s 全覆盖 + 重构 | -| v0.3.0 Sprint 3 | 45% | 15% | 3 条 | 监控/告警 + E2E 冒烟 | -| **v0.3.0 发布** | **≥ 50%** | **≥ 15%** | **3 条** | 质量加固验收 | -| v0.4.0 | ≥ 65% | ≥ 30% | 8 条 | 企业级特性 + 测试增强 | -| v1.0.0 | ≥ 75% | ≥ 50% | 20 条 | 智能化 + 全量覆盖 | - -### 覆盖率豁免规则 - -以下代码允许低覆盖率: -- `main.go` / `cmd/` — 入口文件,由 E2E 覆盖 -- `scripts/` — 种子数据/Mock 脚本 -- WebSocket handler — 由集成测试覆盖 -- `pkg/crypto` — 安全敏感,由专项安全测试覆盖 - ---- - -## 七、Mock 策略 - -### 后端 Mock 层次 +## M3: Pod 文件上传 +### 验证用例 +```bash +# 上传文件 +echo "test content" > /tmp/test-upload.txt +curl -X POST "http://127.0.0.1:8080/api/v1/kubernetes/1/namespace/devops/pods/aiops-xxx/files/upload?path=/tmp&container=aiops" \ + -H "Authorization: Bearer $TOKEN" \ + -F "file=@/tmp/test-upload.txt" +# 期望: 200 + 文件出现在列表中 ``` -┌─────────────────────────────────────┐ -│ HTTP Handler Test │ ← httptest.NewServer -├─────────────────────────────────────┤ -│ Service / Business │ ← 接口注入 Mock -├─────────────────────────────────────┤ -│ Data Access (GORM) │ ← SQLite :memory: 真实 DB -├─────────────────────────────────────┤ -│ External (K8s/Prometheus/SSH) │ ← Interface Mock -└─────────────────────────────────────┘ -``` - -**原则:** -- **数据库层**: 使用 SQLite `:memory:` 真实测试,不用 Mock (GORM 兼容) -- **外部服务** (K8s/Prometheus/Zabbix): 抽象为 interface,用 `testify/mock` 替换 -- **HTTP handler**: 使用 `httptest` 发起真实 HTTP 请求测试 - -### K8s Client Mock 示例 - -```go -// 定义 interface (如果没有的话) -type K8sClientInterface interface { - ListDeployments(namespace string) ([]appsv1.Deployment, error) - ScaleDeployment(name, namespace string, replicas int32) error - // ... -} - -// Mock 实现 -type MockK8sClient struct { - mock.Mock -} - -func (m *MockK8sClient) ListDeployments(ns string) ([]appsv1.Deployment, error) { - args := m.Called(ns) - return args.Get(0).([]appsv1.Deployment), args.Error(1) -} - -// 测试中使用 -func TestListDeployments_NS(t *testing.T) { - mockClient := new(MockK8sClient) - mockClient.On("ListDeployments", "default").Return([]appsv1.Deployment{...}, nil) - - handler := &KubernetesHandler{k8sClient: mockClient, db: testDB} - // ... HTTP 测试 -} -``` - ---- - -## 八、常见陷阱 - -1. **不要 Mock 数据库** — 用 SQLite `:memory:` 真实测试 GORM 逻辑,Mock DB 会漏掉 SQL 错误 -2. **测试必须独立** — 每个测试用例使用独立 DB 实例或事务回滚,避免数据污染 -3. **K8s 测试不连真实集群** — 通过 interface Mock,CI 环境没有 K8s -4. **WebSocket 测试用 dialer** — 不依赖浏览器,用 `gorilla/websocket` dialer 模拟 -5. **前端测试不依赖网络** — 用 MSW 拦截所有 API 请求 -6. **测试命名清晰** — `Test<函数>_<场景>` 格式,如 `TestLogin_WrongPassword` -7. **覆盖率不是目的** — 50% 覆盖核心逻辑 > 90% 覆盖 getter/setter -8. **先写最难的测试** — K8s handler 99 个函数,从 cluster CRUD 开始(逻辑最核心) diff --git a/backend/internal/api/handlers/kubernetes.go b/backend/internal/api/handlers/kubernetes.go index 5b9d873..85f48d5 100644 --- a/backend/internal/api/handlers/kubernetes.go +++ b/backend/internal/api/handlers/kubernetes.go @@ -647,6 +647,24 @@ func (h *KubernetesHandler) SyncCluster(c *gin.Context) { result := k8sclient.SyncResult{} + // Fetch node metrics (best-effort — metrics-server may not be installed) // Logan.Li + nodeMetrics := map[string]k8sclient.NodeMetricsItem{} + if m, err := client.GetNodeMetrics(); err == nil { + for _, item := range m.Items { + nodeMetrics[item.Metadata.Name] = item + } + } else { + log.Printf("[K8s] sync: GetNodeMetrics failed (metrics-server not installed?): %v", err) + } + + // Fetch pod metrics (best-effort) // Logan.Li + podMetrics := map[string]k8sclient.PodMetricsItem{} // key: "namespace/podname" + if pm, err := client.ListPodMetrics(""); err == nil { + for _, item := range pm.Items { + podMetrics[item.Metadata.Namespace+"/"+item.Metadata.Name] = item + } + } + // Sync nodes if nodes, err := client.ListNodes(); err == nil { // Use raw SQL for reliability (avoid GORM soft-delete / auto-timestamp issues) @@ -674,10 +692,24 @@ func (h *KubernetesHandler) SyncCluster(c *gin.Context) { } labelsJSON, _ := json.Marshal(n.Metadata.Labels) now := time.Now() - err2 := h.db.Exec(`INSERT INTO kubernetes_nodes (cluster_id, name, ip, role, status, os, kubelet_version, cpu, memory, max_pods, labels, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + // Compute CPU/memory usage percentage from metrics-server data // Logan.Li + cpuUsagePct, memUsagePct := 0.0, 0.0 + if m, ok := nodeMetrics[n.Metadata.Name]; ok { + cpuCores := parseIntResource(n.Status.Capacity["cpu"]) + memMB := parseMemResource(n.Status.Capacity["memory"]) + if cpuCores > 0 { + cpuMillicores := parseCpuNanoToMilli(m.Usage["cpu"]) + cpuUsagePct = float64(cpuMillicores) / float64(cpuCores*1000) * 100 + } + if memMB > 0 { + memMi := parseMemKiToMi(m.Usage["memory"]) + memUsagePct = float64(memMi) / float64(memMB) * 100 + } + } + err2 := h.db.Exec(`INSERT INTO kubernetes_nodes (cluster_id, name, ip, role, status, os, kubelet_version, cpu, memory, max_pods, cpu_usage, memory_usage, labels, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, cluster.ID, n.Metadata.Name, ip, role, nodeStatus, n.Status.NodeInfo.OSImage, n.Status.NodeInfo.KubeletVersion, parseIntResource(n.Status.Capacity["cpu"]), parseMemResource(n.Status.Capacity["memory"]), parseIntResource(n.Status.Capacity["pods"]), - string(labelsJSON), now, now).Error + cpuUsagePct, memUsagePct, string(labelsJSON), now, now).Error if err2 != nil { log.Printf("[K8s] insert node %s error: %v", n.Metadata.Name, err2) } else { @@ -1928,6 +1960,17 @@ func formatCpuFromNano(raw string) string { return fmt.Sprintf("%dm", milli) } +// parseCpuNanoToMilli converts K8s metrics CPU (nanocores "100354992n") to millicores (int) // Logan.Li +func parseCpuNanoToMilli(raw string) int { + if raw == "" { return 0 } + val := strings.TrimSuffix(raw, "n") + var nano int64 + fmt.Sscanf(val, "%d", &nano) + milli := nano / 1000000 + if milli < 1 { milli = 1 } + return int(milli) +} + // formatMemFromKi converts K8s metrics memory (Ki "2154292Ki") to Mi "2105Mi" func formatMemFromKi(raw string) string { if raw == "" { return "" } @@ -1939,6 +1982,15 @@ func formatMemFromKi(raw string) string { return fmt.Sprintf("%dMi", mi) } +// parseMemKiToMi converts K8s metrics memory (Ki "2154292Ki") to Mi (int) // Logan.Li +func parseMemKiToMi(raw string) int { + if raw == "" { return 0 } + val := strings.TrimSuffix(raw, "Ki") + var ki int64 + fmt.Sscanf(val, "%d", &ki) + return int(ki / 1024) +} + // GetResourceYAMLNS returns the YAML of a K8s resource func (h *KubernetesHandler) GetResourceYAMLNS(c *gin.Context) { diff --git a/frontend/src/components/kubernetes/KubernetesClusterDetail.tsx b/frontend/src/components/kubernetes/KubernetesClusterDetail.tsx index 255100f..c494f3c 100644 --- a/frontend/src/components/kubernetes/KubernetesClusterDetail.tsx +++ b/frontend/src/components/kubernetes/KubernetesClusterDetail.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useMemo } from 'react'; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; @@ -15,16 +15,8 @@ import { ResponsiveContainer, } from 'recharts'; -// 模拟资源使用趋势数据 -const resourceData = [ - { time: '00:00', cpu: 45, memory: 52 }, - { time: '04:00', cpu: 38, memory: 48 }, - { time: '08:00', cpu: 62, memory: 68 }, - { time: '12:00', cpu: 78, memory: 82 }, - { time: '16:00', cpu: 72, memory: 76 }, - { time: '20:00', cpu: 58, memory: 64 }, - { time: '24:00', cpu: 48, memory: 56 }, -]; +// 资源使用趋势数据 — 从节点 metrics 实时计算(不再用硬编码 mock) // Logan.Li +// 由于当前无 time-series 存储,用当前快照展示一个合理的汇总值 export function KubernetesClusterDetail({ cluster, onClose }: { cluster: any; onClose: () => void }) { const [nodes, setNodes] = useState([]); @@ -33,6 +25,32 @@ export function KubernetesClusterDetail({ cluster, onClose }: { cluster: any; on const [services, setServices] = useState([]); const [ingresses, setIngresses] = useState([]); + // 从节点数据计算集群级 CPU/内存使用率(实时快照) // Logan.Li + const clusterMetrics = useMemo(() => { + if (!nodes || nodes.length === 0) return { cpuPct: 0, memPct: 0 }; + let totalCpuPct = 0, totalMemPct = 0, validNodes = 0; + for (const n of nodes) { + // ClusterOverviewPage 读的是 cpu_usage_m / mem_usage_mi(实时路径) + // ClusterDetail 的 nodes 从 getNodes API 获取,也有这些字段 + const cpuCores = n.cpu || 0; + const cpuUsageM = n.cpu_usage_m ? parseFloat(String(n.cpu_usage_m).replace('m', '')) : 0; + const cpuPct = cpuCores > 0 ? (cpuUsageM / 1000 / cpuCores) * 100 : (n.cpu_usage || 0); + const memMb = n.memory_mb || n.memory || 0; + const memUsageMi = n.mem_usage_mi ? parseFloat(String(n.mem_usage_mi).replace('Mi', '').replace('Ki', '')) : 0; + const memUsageNorm = n.mem_usage_mi?.includes?.('Ki') ? memUsageMi / 1024 : n.mem_usage_mi?.includes?.('Gi') ? memUsageMi * 1024 : memUsageMi; + const memPct = memMb > 0 ? (memUsageNorm / memMb) * 100 : (n.memory_usage || 0); + if (cpuPct > 0 || memPct > 0) { + totalCpuPct += cpuPct; + totalMemPct += memPct; + validNodes++; + } + } + return { + cpuPct: validNodes > 0 ? totalCpuPct / validNodes : 0, + memPct: validNodes > 0 ? totalMemPct / validNodes : 0, + }; + }, [nodes]); + const loadClusterDetail = async (clusterId: string) => { try { // 后端 ListNodes/ListPods/etc 返回 data 直接为数组(非 {list,total}) @@ -120,7 +138,9 @@ export function KubernetesClusterDetail({ cluster, onClose }: { cluster: any; on
- + @@ -133,7 +153,7 @@ export function KubernetesClusterDetail({ cluster, onClose }: { cluster: any; on - +
+
+ CPU: {clusterMetrics.cpuPct.toFixed(1)}% + 内存: {clusterMetrics.memPct.toFixed(1)}% +