feat(chv): 架构与代码质量提升——WarmPool 定案、长函数提取、严格测试替身、Docker 同步守卫、配置统一校验、类型注解与 ADR

堆叠于 #246(feat-chv-secfix),只到 PR OPEN,未合并、未部署。
安全语义不变:egress、key seed 投递、磁盘删除、默认 provisioned、
tap 校验一律保留;前端零改动。
- WarmPool 定案为实验性(ADR-5):dispatch 永远冷启动,
  wired_to_dispatch 恒 False,acquire/resume 只保留实验接口,
  worker 仍调 reconcile_orphans 清理残留。
- 长函数提取:preflight 126→31、wake_vm 135→36、__init__ 69→31、
  build_chv_config 159→58;错误文案逐字不变。
- 严格测试替身:fake API 校验 VmConfig(任务盘+seed 盘、net
  无重复),fake SSH 校验 known_hosts 钉死;新增 5 项测试。
- Docker 同步守卫:8 个真实标记用例下 Docker 内联版与正典
  parse_goal_result 状态全等,改分叉即 fail。
- 配置统一校验:preflight 以 config_invalid 统一兜底端口/配额/
  超时/快照/host-key 模式(构造时已校验正常值,preflight 兜底
  事后改写);CHV_NET 与 CHV_NET_JSON 双设仍警告不硬拒。
- 类型与 lint:chv 叶子模块公开 API 补类型注解;stdlib 扫描
  揪出 executor 内 1 个真死 import(host_key_paths)并删除。
- ADR:docs/chv-architecture.md(ADR-1 至 ADR-7)。
测试:CHV 163 项全过;cloud 全量 1593 项三跑结果为 8 errors
(首跑详细日志因 tail 截断丢失)、全过、1 失败(浏览器代理
时序测试,与 CHV 无关),全量本身存在时序偶发,CHV 套件
确定性全过。
This commit is contained in:
host committed 2026-10-31 05:50:56 +00:00
1 parent baf26beb78
commit c908a0147a
12 files changed
+725 -239

No files matched your search

+80
View File
@@ -0,0 +1,80 @@
# Cloud Hypervisor 执行器架构决策记录(ADR)
范围:cloud/chv/ 执行器链(#216 → #221 → #230 → #232 → #234 → #239 → #246 → 本轨)。
本文件只记录已落代码的决策,不记录未验证为已验证;真机、压测、动态安全
仍见 docs/chv-production-readiness.md §11。
## ADR-1 SSH 目标:客机 IP 发现(已选) vs vsock / 纯 NAT(未选)
- 决策:显式 CHV_SSH_HOST > CHV_NET_JSON 首个带 ip 的 net 条目 >
显式 CHV_SSH_FORWARD=1 宣称下的回环;三者皆无报
ssh_target_undeclared,绝不无条件假设 127.0.0.1:2222 能通。
- 未选 vsock:需客内 agent + 宿主代理两套新代码,当前客机镜像
无此组件,写进代码仍是假保证。
- 未选纯 NAT:DNAT 是宿主防火墙外部状态,执行器代码既不能
创建也不能校验它,写进代码仍是假保证;宿主防火墙见就绪
清单 §11.4 部署清单,代码门 + 防火墙缺一不可。
## ADR-2 网关 key 走 seed 投递(已选) vs argv / 宿主环境(已弃)
- 决策:provisioned 模式下密钥子集随 seed 写进客内
/etc/chv/guest-env(0600),远程命令 source 它;传输层密钥
进 argv 直接 ValueError;keyscan 模式有密钥即 fail-closed。
- 放弃 argv:宿主 ps / /proc/<pid>/cmdline 可读;放弃宿主子进程
环境:Docker 线 -e 只传名、值仍在 CLI 子进程环境,CHV 比它
更严(值连宿主子进程环境都不进)。
- 残留:seed 随 stop 删,私钥在 seed 盘建好后即删,
cleanup_stale_secrets 扫崩溃残留;删失败 fail-fast 或脏标记拒起。
## ADR-3 磁盘 copy 默认,reuse 只作单租户实验(已选)
- 决策:CHV_DISK_MODE 默认 copy,每任务独立 disk.raw;
reuse 须同时满足 CHV_REUSE_SINGLE_TENANT_EXPERIMENT=1 与
CHV_SINGLE_TENANT_PROJECT 声明,且 wake 时审计放行。
- 边界:代码无法验证数据库多租户状态,声明 + 审计即机制,
不假装已校验;生产多租户下 reuse 一律不启用。
## ADR-4 restore 不重预置(纠正,见 #232 审计)
- 纠正:审计原说法“restore 须把 seed 接入 VmConfig”是错的——
restore 不走 vm.create,无 VmConfig 可挂盘;快照内已固化原
VM host key,重预置反而破坏钉死。
- 正确不变量:provisioned 下复用已钉死 known_hosts、缺失
fail-fast(host_key_not_pinned),绝不 keyscan 回落。
## ADR-5 WarmPool:实验性,生产 dispatch 永远冷启动(本轨定案)
- 决策(二选一的 B 项):把 WarmPool 从生产路径宣称里摘掉,
明确标为实验/停用,不再留“半接线”悬空状态。
- 原因:预热 VM 的磁盘是干净快照盘,不是任务盘;dispatch 前
查池若要真正接线,必须在派任务前给已暂停 VM 重新供给任务
盘(热换盘 / 二盘挂载 / host.py 磁盘语义三者都未实现),
把预热盘直接当任务盘用会击穿任务盘隔离不变量。
- 落地:WarmPool.wired_to_dispatch 恒为 False;_run_with_vm
绝不查池,dispatch 永远冷启动;acquire 内 resume 只保留为
实验接口;worker 启动/关闭仍调 reconcile_orphans()(上一世
残留清理是池子保留的唯一生产价值)。
- 重启条件:将来要重新接线,必须先实现任务盘重供给并补
“任务盘来源”不变量测试,否则不得把 wired 改成 True。
## ADR-6 Docker goal 解析分叉:共享正典 + 同步守卫(本轨定案)
- 决策:Local/CHV 共用 parse_goal_result 正典;Docker goal_task
内联版保留,但与正典的 8 个真实标记用例(met/unmet/
interrupted 标记/无结论异常退出/永久错误/无结论 rc0/
goal-resume 拒绝/已存在撞名)由 test_chv_arch95.py 的
DockerSyncGuardTest 行为测试钉住状态全等,任一实现改了
分叉测试即 fail,不再只靠文档清单。
- 注意:Docker 的超时证据路径仍与内联解析绑在一起,那是
有意保留的第二处差异,不在 8 用例的状态等价范围内。
## ADR-7 #215 未合并时的复用边界(不复制模块)
- 现状:#215(timeouts/gates/quota/sandbox_policy)未合并,
本链 base 里无其模块,本轨不复制其代码。
- 接口对齐点(#215 合并后机械替换,非重写):超时类
api/ready/task 解析 → timeouts;门禁类 preflight/容量/
磁盘配额/egress fail-closed → gates;配额类 max_vms/disk_gb
(任务成本仍走 worker quota)→ quota;隔离类单任务单 VM/
磁盘 copy/egress 代理/SSH 钉死/池一次性与孤儿清理 →
sandbox_policy。替换时须重跑本链 163 项测试,不得改语义。
+10 -9
View File
@@ -71,8 +71,10 @@
- ✅🧪 快照/恢复接口:snapshot_vm / restore_vm(restore 后显式
resume,见 §3),WarmPool 接口与状态机(cold → prewarming →
warm → assigned → running → recycled/destroyed)。
- ✅🧪 冷启动回落:池空时 acquire 返回 cold,调用方回落 wake_vm
冷启动,绝不降级 Docker/Local。
- ⚠️ 定案(feat-chv-arch95,ADR-5):WarmPool 是实验性、非生产
路径。dispatch 永远冷启动,_run_with_vm 绝不查池;池空回落
冷启动的说法不再作为生产特性宣称,acquire/resume 只保留为
实验接口。
- ✅🧪 预热条目一次性:acquire 消耗、release 一律销毁,绝不跨
任务复用脏 VM。
- ✅🧪 预热池 registry 与孤儿清理(优化轨):池状态落
@@ -245,14 +247,13 @@
未选,理由见 config.py)。
- ✅🧪 P1 已修或有结论:网关 extra_env 经 SSH env 前缀注入,
worker 守卫收窄到 Local/CHV;取消/清理/看门狗接 CHV 通用
路径;WarmPool acquire 内 resume(但 worker dispatch 前查
池仍未接线,预热收益仍不是已交付特性);容量门改持久化
路径;WarmPool acquire 内 resume(本轨定案为实验性,见 ADR-5,
不再是生产特性);容量门改持久化
计数(socket 联合计数);磁盘配额改总量+余量、已存在磁盘
校验重拷;API 客户端按 Content-Length 验完整性;快照
file:// 目标验存在且非空再 shutdown;execute 死端口归类
vm_not_running。
- ⚠️ 仍未接线/未验证(不得声称已交付):worker dispatch 前查
WarmPool;CHV + 网关真实任务;真机 wake/boot/SSH(含 seed
- ⚠️ 仍未验证(不得声称已交付):CHV + 网关真实任务;真机 wake/boot/SSH(含 seed
注入与 NAT/客机 IP 实测);快照恢复端到端;压测;安全审计;
CI 账单恢复。另:P0-2 审计原说法“restore 须把 seed 接入
VmConfig”已纠正——restore 不走 vm.create,seed 无从挂盘,
@@ -384,8 +385,7 @@
(tap/mac/ip/mask),未知字段直接拒绝。
- L5 WarmPool 预热盘与任务盘对应关系:定义已写进
pool.acquire 文档——预热盘是干净快照盘,不是任务盘;
将来接线时任务盘仍须按任务重新供给或校验快照来源与
任务的对应关系;接线本身仍未做,不假装已接。
本轨定案为实验性(ADR-5),不再作为接线待办。
### 11.4 宿主防火墙部署清单(代码做不到的部分,缺一不可)
@@ -424,4 +424,5 @@
- VM 控制按钮、预热池管理页、成本/配额新页面(前端零改动);
- 真机动态渗透、压测(无 KVM,做不了,只列清单);
- WarmPool dispatch 前查池接线(仍未接线,见 L5 定义)。
- WarmPool dispatch 前查池接线(已定案不做,见 ADR-5;重启须先
实现任务盘重供给并补不变量测试)。