后端: - routes.go: 为 statefulsets/daemonsets 注册专门 YAML 子路由(Gin radix tree 冲突) - kubernetes.go: GetResourceYAMLNS/UpdateResourceYAMLNS 添加 URL path fallback 前端: - WorkloadViewPage.tsx 重写: 修复 kind 提取 bug(useLocation) + 更新策略/选择器/PVC模板/InitContainers - NamespaceResourcePage.tsx: handleDetail 添加 STS/DS 点击导航 验证: DS YAML API 200 ✅ | DS Detail 200 ✅ | Dep YAML 200 ✅ | SPA路由 200 ✅ | TS零错误 ✅
390 lines
14 KiB
Markdown
390 lines
14 KiB
Markdown
# TDD 验证计划 — K8s 管理功能
|
||
|
||
> 每个模块实现前先写验证用例,实现后必须跑通,不能只编译通过。
|
||
|
||
## 通用验证流程
|
||
|
||
```bash
|
||
# 1. 重建后端镜像
|
||
cd /srv/aiops && docker-compose up -d --build backend
|
||
|
||
# 2. 等待 healthy
|
||
sleep 10 && docker ps --format '{{.Names}} {{.Status}}' | grep aiops-backend
|
||
|
||
# 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')
|
||
|
||
# 4. 验证 API
|
||
curl -s http://127.0.0.1:8080/api/v1/kubernetes-clusters \
|
||
-H "Authorization: Bearer $TOKEN" | jq '.'
|
||
```
|
||
|
||
---
|
||
|
||
## M1: SyncCluster metrics 采集
|
||
|
||
### 验证用例
|
||
|
||
```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"
|
||
|
||
# U2: 触发同步
|
||
curl -X POST http://127.0.0.1:8080/api/v1/kubernetes-clusters/1/sync \
|
||
-H "Authorization: Bearer $TOKEN"
|
||
|
||
# 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
|
||
|
||
# U5: 前端构建无报错
|
||
cd /srv/aiops/frontend && npm run build 2>&1 | tail -5
|
||
# 期望: vite build 成功
|
||
```
|
||
|
||
### 通过标准
|
||
- [x] U1: 同步前 cpu_usage=0
|
||
- [x] U2: 同步 API 200
|
||
- [x] U3: 同步后 cpu_usage>0
|
||
- [x] U4: 集群计数正确
|
||
- [x] U5: 前端构建成功
|
||
|
||
---
|
||
|
||
## M2: Pod Metrics
|
||
|
||
### 验证用例
|
||
```bash
|
||
# 同步后 Pod 列表含 CPU/内存
|
||
curl -s "http://127.0.0.1:8080/api/v1/kubernetes-clusters/1/pods" \
|
||
-H "Authorization: Bearer ***" | jq '.data[0]'
|
||
# 期望: 含 cpu_usage_m / mem_usage_mi 字段
|
||
```
|
||
|
||
### 通过标准
|
||
- [x] Pod 列表 API 返回 cpu_usage_m / mem_usage_mi 字段
|
||
- [x] 前端 Pod 列表表格显示 CPU/内存列(KubernetesWorkloads + NamespaceResourcePage)
|
||
- [x] 17 个 Pod 全部有真实 metrics 数据
|
||
|
||
---
|
||
|
||
## 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 ***" \
|
||
-F "file=@/tmp/test-upload.txt"
|
||
# 期望: 200 + 文件出现在列表中
|
||
```
|
||
|
||
### 通过标准
|
||
- [x] 上传 API 返回 200 + verify 字段匹配文件大小
|
||
- [x] 文件列表 (ls) 能看到上传的文件
|
||
- [x] ExecInPod 使用 WebSocket channel.k8s.io 协议(不再用 SPDY raw HTTP)
|
||
- [x] BusyBox ls 解析兼容(Mon DD HH:MM 格式)
|
||
|
||
---
|
||
|
||
## M5: Deployment 暂停更新 + CI/CD 集成
|
||
|
||
### 验证用例
|
||
```bash
|
||
# U1: 暂停前 — 确认 paused=false
|
||
curl -s "http://127.0.0.1:8080/api/v1/kubernetes/prod-cluster/namespace/devops/deployments" \
|
||
-H "Authorization: Bearer $TOKEN" | jq '.data[] | select(.name=="redis") | .paused'
|
||
# 期望: false
|
||
|
||
# U2: 暂停 Deployment
|
||
curl -X POST "http://127.0.0.1:8080/api/v1/kubernetes/prod-cluster/namespace/devops/deployments/redis/pause" \
|
||
-H "Authorization: Bearer $TOKEN"
|
||
# 期望: code=200, msg="已暂停滚动更新"
|
||
|
||
# U3: 暂停后 — 确认 paused=true
|
||
curl -s "http://127.0.0.1:8080/api/v1/kubernetes/prod-cluster/namespace/devops/deployments" \
|
||
-H "Authorization: Bearer $TOKEN" | jq '.data[] | select(.name=="redis") | .paused'
|
||
# 期望: true
|
||
|
||
# U4: 恢复 Deployment
|
||
curl -X POST "http://127.0.0.1:8080/api/v1/kubernetes/prod-cluster/namespace/devops/deployments/redis/resume" \
|
||
-H "Authorization: Bearer $TOKEN"
|
||
# 期望: code=200, msg="已恢复滚动更新"
|
||
|
||
# U5: 恢复后 — 确认 paused=false
|
||
curl -s "http://127.0.0.1:8080/api/v1/kubernetes/prod-cluster/namespace/devops/deployments" \
|
||
-H "Authorization: Bearer $TOKEN" | jq '.data[] | select(.name=="redis") | .paused'
|
||
# 期望: false
|
||
```
|
||
|
||
### 通过标准
|
||
- [x] U1: 暂停前 paused=false
|
||
- [x] U2: pause API 返回 200
|
||
- [x] U3: 暂停后 paused=true
|
||
- [x] U4: resume API 返回 200
|
||
- [x] U5: 恢复后 paused=false
|
||
- [x] ID 路由 + Kuboard code 路由均可用
|
||
- [x] 前端操作菜单显示「暂停更新/恢复更新」动态切换
|
||
- [x] CI/CD 发布按钮可跳转到流水线页面
|
||
|
||
---
|
||
|
||
## M6: 编辑器基础设施 + 基本信息 Tab + YAML编辑器
|
||
|
||
### 验证用例
|
||
|
||
```bash
|
||
# U1: 三模式路由 SPA fallback 200(非 301/404)
|
||
curl -s -o /dev/null -w "%{http_code}" \
|
||
"http://127.0.0.1/kubernetes/prod-cluster/namespace/devops/workload/yaml/Deployment/redis"
|
||
# 期望: 200
|
||
|
||
# U2: YAML GET API — CodeMirror 编辑器数据源
|
||
curl -s "http://127.0.0.1:8080/api/v1/kubernetes/prod-cluster/namespace/devops/deployments/redis/yaml" \
|
||
-H "Authorization: Bearer *** | head -5
|
||
# 期望: apiVersion: apps/v1 / kind: Deployment / metadata:
|
||
|
||
# U3: 前端构建含 CodeMirror 依赖
|
||
curl -s http://127.0.0.1/ | grep -oP 'index-[A-Za-z0-9]+\.js'
|
||
# 期望: 新 hash(每次构建变化)
|
||
|
||
# U4: TypeScript 编译无错误
|
||
cd frontend && npx tsc --noEmit
|
||
# 期望: 退出码 0
|
||
|
||
# U5: Vite 生产构建成功
|
||
cd frontend && npm run build 2>&1 | tail -3
|
||
# 期望: built in Ns
|
||
```
|
||
|
||
### 通过标准
|
||
- [x] U1: yaml 路由 SPA fallback 返回 200
|
||
- [x] U2: YAML API 返回有效 K8s manifest(apiVersion/kind/metadata 齐全)
|
||
- [x] U3: 前端 bundle 含 CodeMirror(codemirror 关键字在 JS chunk 中)
|
||
- [x] U4: TypeScript 零编译错误
|
||
- [x] U5: Vite 生产构建成功
|
||
- [x] YamlViewer 支持弹窗模式 + 内联模式(inline prop)
|
||
- [x] DeploymentEditBasic 可编辑 replicas/labels/annotations/selector
|
||
- [x] 编辑模式 Tab 导航系统就绪(基本信息可用 + M7-M9 预留)
|
||
|
||
---
|
||
|
||
## M7: 容器信息 Tab(工作容器 + 初始化容器)
|
||
|
||
### 验证用例
|
||
|
||
```bash
|
||
# U1: 登录获取 Token
|
||
TOKEN=$(curl -s http://127.0.0.1:8080/api/v1/auth/login \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"username":"admin","password":"admin123"}' | jq -r '.data.token')
|
||
# 期望: code=200
|
||
|
||
# U2: YAML GET — 容器编辑器数据源
|
||
curl -s "http://127.0.0.1:8080/api/v1/kubernetes/test-k8s/namespace/devops/deployments/aiops/yaml" \
|
||
-H "Authorization: Bearer $TOKEN" | head -5
|
||
# 期望: apiVersion: apps/v1 / kind: Deployment / containers:
|
||
|
||
# U3: PUT YAML 保存(容器编辑器保存路径)
|
||
curl -s -X PUT "http://127.0.0.1:8080/api/v1/kubernetes/test-k8s/namespace/devops/deployments/aiops/yaml" \
|
||
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/yaml" \
|
||
-d "$(curl -s .../aiops/yaml -H "Authorization: Bearer $TOKEN")"
|
||
# 期望: code=200
|
||
|
||
# U4: SPA fallback — edit 路由 200(非 301)
|
||
curl -s -o /dev/null -w "%{http_code}" \
|
||
"http://127.0.0.1/kubernetes/test-k8s/namespace/devops/workload/edit/Deployment/aiops"
|
||
# 期望: 200
|
||
|
||
# U5: TypeScript 编译 + Vite 构建无错误
|
||
cd frontend && npm run build 2>&1 | tail -3
|
||
# 期望: built in Ns
|
||
|
||
# U6: 前端 bundle 含 DeploymentEditContainers 组件(新 hash)
|
||
curl -s http://127.0.0.1/ | grep -oP 'index-[A-Za-z0-9]+\.js'
|
||
# 期望: 新 hash(每次构建变化)
|
||
```
|
||
|
||
### 通过标准
|
||
- [x] U1: 登录成功
|
||
- [x] U2: YAML API 返回含 containers 的有效 K8s manifest
|
||
- [x] U3: PUT 保存返回 code=200
|
||
- [x] U4: 编辑模式 SPA 路由返回 200(非 301)
|
||
- [x] U5: TypeScript 零编译错误 + Vite 构建成功
|
||
- [x] U6: 前端 bundle 已更新(含新组件)
|
||
- [x] DeploymentEditContainers 支持工作容器 + 初始化容器增删
|
||
- [x] 每个容器可编辑:镜像/拉取策略/命令/参数/环境变量/端口/资源/健康检查/生命周期/安全上下文/卷挂载
|
||
- [x] 镜像拉取凭证(imagePullSecrets) Pod 级别编辑
|
||
- [x] 三种探针类型(httpGet/tcpSocket/exec)可切换
|
||
|
||
---
|
||
|
||
## M8: 存储挂载 + 高级设置 Tab
|
||
|
||
### 验证用例
|
||
|
||
```bash
|
||
# U1: 登录获取 Token
|
||
TOKEN=$(curl -s http://127.0.0.1:8080/api/v1/auth/login \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"username":"admin","password":"admin123"}' | jq -r '.data.token')
|
||
|
||
# U2: SPA fallback — edit 路由返回 200(非 301)
|
||
curl -s -o /dev/null -w "%{http_code}" \
|
||
"http://127.0.0.1/kubernetes/prod-cluster/namespace/devops/workload/edit/Deployment/redis"
|
||
# 期望: 200
|
||
|
||
# U3: YAML GET API — Storage/Advanced Tab 数据源
|
||
curl -s "http://127.0.0.1:8080/api/v1/kubernetes/prod-cluster/namespace/devops/deployments/redis/yaml" \
|
||
-H "Authorization: Bearer $TOKEN" | head -5
|
||
# 期望: apiVersion: apps/v1 / kind: Deployment / metadata:
|
||
|
||
# U4: TypeScript 编译 + Vite 构建
|
||
cd frontend && npx tsc --noEmit && npm run build 2>&1 | tail -3
|
||
# 期望: 零错误 + built in Ns
|
||
|
||
# U5: 前端 bundle 含 DeploymentEditStorage + DeploymentEditAdvanced 组件
|
||
curl -s http://127.0.0.1/ | grep -oP 'index-[A-Za-z0-9]+\.js'
|
||
# 期望: 新 hash(每次构建变化)
|
||
```
|
||
|
||
### 通过标准
|
||
- [x] U1: 登录成功
|
||
- [x] U2: edit 路由 SPA fallback 返回 200
|
||
- [x] U3: YAML API 返回含 spec.strategy/spec.template.spec 的有效 manifest
|
||
- [x] U4: TypeScript 零编译错误 + Vite 构建成功
|
||
- [x] U5: 前端 bundle 已更新
|
||
- [x] DeploymentEditStorage 支持 6 种 volume 类型(EmptyDir/HostPath/ConfigMap/Secret/PVC/NFS)
|
||
- [x] DeploymentEditAdvanced 支持更新策略/调度/nodeSelector/tolerations/DNS/安全上下文/主机设置/hostAliases/dnsConfig
|
||
- [x] 各 Tab 独立保存(PUT application/yaml 直通 K8s API)
|
||
|
||
---
|
||
|
||
## M10: Node 详情页(PID/容器运行时/分配率)
|
||
|
||
### 验证用例
|
||
|
||
```bash
|
||
# U1: 登录获取 Token
|
||
TOKEN=$(curl -s http://127.0.0.1:8080/api/v1/auth/login \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"username":"admin","password":"admin123"}' | jq -r '.data.token')
|
||
|
||
# U2: Node detail API — Kuboard code route
|
||
curl -s "http://127.0.0.1:8080/api/v1/kubernetes/test-k8s/nodes/k8s-master-01" \
|
||
-H "Authorization: Bearer *** | jq '.data.container_runtime'
|
||
# 期望: "containerd://2.1.4"
|
||
|
||
# U3: 资源分配率字段
|
||
curl -s "http://127.0.0.1:8080/api/v1/kubernetes/test-k8s/nodes/k8s-master-01" \
|
||
-H "Authorization: Bearer *** | jq '.data | {cpu_alloc_pct, mem_alloc_pct, pod_alloc_pct, pod_count}'
|
||
# 期望: cpu_alloc_pct/mem_alloc_pct/pod_alloc_pct 均为数字 > 0
|
||
|
||
# U4: conditions + taints + pods 列表
|
||
curl -s "http://127.0.0.1:8080/api/v1/kubernetes/test-k8s/nodes/k8s-master-01" \
|
||
-H "Authorization: Bearer *** | jq '.data | {conditions: (.conditions|length), taints: (.taints|length), pods: (.pods|length)}'
|
||
# 期望: conditions>=4, pods>=1
|
||
|
||
# U5: SPA fallback — node detail 路由 200
|
||
curl -s -o /dev/null -w "%{http_code}" \
|
||
"http://127.0.0.1/kubernetes/test-k8s/nodes/k8s-master-01"
|
||
# 期望: 200
|
||
|
||
# U6: TypeScript + Vite 构建
|
||
cd frontend && npx tsc --noEmit && npm run build 2>&1 | tail -3
|
||
# 期望: 零错误 + built in Ns
|
||
```
|
||
|
||
### 通过标准
|
||
- [x] U1: 登录成功
|
||
- [x] U2: Node detail API 返回 container_runtime/kubelet/os/kernel/architecture
|
||
- [x] U3: 资源分配率正确(cpu_alloc_pct/mem_alloc_pct/pod_alloc_pct 数字)
|
||
- [x] U4: conditions/taints/pods 列表非空
|
||
- [x] U5: SPA 路由 200(非 301)
|
||
- [x] U6: TypeScript 零错误 + Vite 构建成功
|
||
- [x] 前端节点名称可点击跳转详情页
|
||
|
||
---
|
||
|
||
## M9: 服务/应用路由 Tab + 整体联调
|
||
|
||
### 验证用例
|
||
|
||
```bash
|
||
# U1: 5 Tab 全部 available(检查 EditModeContainer)
|
||
# 验证方式: 浏览器打开 edit 模式,5 个 Tab 均可点击切换
|
||
|
||
# U2: TypeScript 编译
|
||
cd frontend && npx tsc --noEmit
|
||
# 期望: 退出码 0
|
||
|
||
# U3: Vite 构建成功
|
||
cd frontend && npm run build 2>&1 | tail -3
|
||
# 期望: built in Ns
|
||
```
|
||
|
||
### 通过标准
|
||
- [x] DeploymentEditService.tsx 组件存在且编译通过
|
||
- [x] DeploymentWorkload.tsx EditModeContainer 5 Tab 全部 available
|
||
- [x] Service Tab selector 双路径兼容(spec.selector.matchLabels || selector)
|
||
- [x] TypeScript 零编译错误 + Vite 构建成功
|
||
- [x] 前端 bundle 已更新
|
||
|
||
---
|
||
|
||
## M11: StatefulSet/DaemonSet 详情页
|
||
|
||
### 验证用例
|
||
|
||
```bash
|
||
# U1: 登录获取 Token
|
||
TOKEN=$(curl -s http://127.0.0.1:8080/api/v1/auth/login \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"username":"admin","password":"admin123"}' | jq -r '.data.token')
|
||
|
||
# U2: DaemonSet YAML API(路由冲突修复验证)
|
||
curl -s "http://127.0.0.1:8080/api/v1/kubernetes/prod-cluster/namespace/kube-system/daemonsets/calico-node/yaml" \
|
||
-H "Authorization: Bearer *** | head -3
|
||
# 期望: apiVersion: apps/v1 / kind: DaemonSet / metadata:
|
||
|
||
# U3: DaemonSet Detail API
|
||
curl -s "http://127.0.0.1:8080/api/v1/kubernetes/prod-cluster/namespace/kube-system/daemonsets/calico-node" \
|
||
-H "Authorization: Bearer *** | jq '.data | {name: .metadata.name, desired: .status.desiredNumberScheduled, ready: .status.numberReady}'
|
||
# 期望: name="calico-node", desired=2, ready=2
|
||
|
||
# U4: Deployment YAML 仍正常(generic 路由未受影响)
|
||
curl -s "http://127.0.0.1:8080/api/v1/kubernetes/prod-cluster/namespace/devops/deployments/redis/yaml" \
|
||
-H "Authorization: Bearer *** | head -1
|
||
# 期望: apiVersion: apps/v1
|
||
|
||
# U5: SPA fallback — workload 路由 200
|
||
curl -s -o /dev/null -w "%{http_code}" \
|
||
"http://127.0.0.1/kubernetes/prod-cluster/namespace/kube-system/workload/view/DaemonSet/calico-node"
|
||
# 期望: 200
|
||
|
||
# U6: TypeScript + Vite 构建
|
||
cd frontend && npx tsc --noEmit && npm run build 2>&1 | tail -3
|
||
# 期望: 零错误 + built in Ns
|
||
```
|
||
|
||
### 通过标准
|
||
- [x] U2: DS YAML API 返回 200 + apiVersion + kind=DaemonSet(Gin radix tree 路由冲突已修复)
|
||
- [x] U3: DS Detail API 返回 name/desired/current/ready/containers
|
||
- [x] U4: Deployment YAML API 仍正常(generic `/:resource/:name/yaml` 路由未受影响)
|
||
- [x] U5: SPA 路由 view/yaml 两种模式均返回 200
|
||
- [x] U6: TypeScript 零编译错误 + Vite 构建成功
|
||
- [x] NamespaceResourcePage 点击 STS/DS 行可跳转详情页
|
||
- [x] WorkloadViewPage 支持 view/yaml 双模式切换
|
||
- [x] WorkloadViewPage kind 从 useLocation 正确提取(DaemonSet 不被误判为 StatefulSet)
|
||
- [x] 更新策略/选择器/PVC模板/InitContainers 全部展示
|