feat: CHV 审核修复 P2(范围恢复为全做)
P2-1:preflight reuse/CPU/内存校验块小提取为 _preflight_config_checks(纯移动,深耦合处如实停)。 P2-2:build_vm_command 降级为私有 _build_vm_command; Docker goal 内联解析顶部加正典分叉说明 + 同步检查清单。 P2-3:gaps grep 文本断言改成真实 Worker._run_attempt 行为断言。P2-4:README/build.sh 口径对齐(章节顺序、 BUILDINFO 名字列表、Node.js 不装写实)。P2-5:stop_vm 删除失败记审计。P2-6:CPU/内存校验统一进 preflight。 测试:原 84 + 新增 33 = 117 项,mock,无真 VM。
This commit is contained in:
1 parent
9771ed9e8c
commit
445376743d
9 files changed
+169
-44
No files matched your search
+52
-18
@@ -96,7 +96,7 @@ class CloudHypervisorExecutor(Executor):
|
||||
wake_vm() 先 preflight,再经 launcher 以 --api-socket
|
||||
只启动 VMM 进程(CLI 上不带内核/磁盘配置),等
|
||||
vmm.ping 通,再 PUT vm.create(VmConfig)+ PUT vm.boot。
|
||||
build_vm_command() 保留的直接启动命令只是文档里的手动
|
||||
_build_vm_command() 保留的直接启动命令只是文档里的手动
|
||||
等价写法(运维手工起 VM 时用),执行器代码里不走它。
|
||||
- 就绪判定只选 SSH,不做 vsock 第二套:boot 后轮询 SSH
|
||||
“true”命令,直到 CHV_READY_TIMEOUT(默认 120 秒,
|
||||
@@ -207,7 +207,7 @@ class CloudHypervisorExecutor(Executor):
|
||||
且可读、SSH key 存在且可读。任一项缺失都 raise
|
||||
RuntimeError,绝不静默回落 Docker/Local。_validate()
|
||||
即 preflight(旧名保留,供旧调用点用)。
|
||||
命令构造(build_vm_command/build_ssh_command/
|
||||
命令构造(_build_vm_command/build_ssh_command/
|
||||
build_vm_config)是纯函数,不做文件存在校验,单测可
|
||||
直接断言命令与 VmConfig 形状。
|
||||
|
||||
@@ -314,6 +314,35 @@ class CloudHypervisorExecutor(Executor):
|
||||
return os.path.isfile(path) and os.access(path, os.X_OK)
|
||||
return shutil.which(path) is not None
|
||||
|
||||
def _preflight_config_checks(self, missing):
|
||||
"""preflight 的配置校验块(feat-chv-fix,P2-1 小提取:
|
||||
仅把 reuse 拦截与 CPU/内存校验从 preflight() 里抽出,
|
||||
语义逐行不变;其余 preflight 检查项与 config.py 的
|
||||
字段解析深耦合,本轨如实停、不硬拆)。"""
|
||||
# P0-3:reuse 生产拦截。一行配置再也不能击穿租户隔离:
|
||||
# 只有显式实验二次确认 CHV_REUSE_SINGLE_TENANT_
|
||||
# EXPERIMENT=1 时才放行,且放行本身在 wake_vm 里审计;
|
||||
# 否则 preflight 直接拒绝(kind=disk_mode_reuse_blocked)。
|
||||
if getattr(self, "disk_mode", "copy") == "reuse" \
|
||||
and not getattr(self, "reuse_experiment", False):
|
||||
missing.append(("disk_mode_reuse_blocked",
|
||||
"CHV_DISK_MODE=reuse 未经显式实验确认:"
|
||||
"须同时设 CHV_REUSE_SINGLE_TENANT_"
|
||||
"EXPERIMENT=1(仅单租户实验)"))
|
||||
# P2-6:配置校验时机统一到 preflight。CPU 非整数、内存
|
||||
# 字符串非法过去只在 build_vm_config 才炸,现在 preflight
|
||||
# 一次报出(kind=config_invalid)。
|
||||
try:
|
||||
int(self.cpus)
|
||||
except (TypeError, ValueError):
|
||||
missing.append(("config_invalid",
|
||||
"CHV_CPU 必须是整数:%r" % (self.cpus,)))
|
||||
try:
|
||||
_parse_memory_bytes(self.memory)
|
||||
except ValueError:
|
||||
missing.append(("config_invalid",
|
||||
"CHV_MEM 格式非法:%r" % (self.memory,)))
|
||||
|
||||
def preflight(self):
|
||||
"""预检(缺一不可则 fail-fast):收集全部缺失项一次报出
|
||||
具体缺哪一项,绝不笼统报错;失败为 ChvFailure(带 kind,
|
||||
@@ -411,17 +440,7 @@ class CloudHypervisorExecutor(Executor):
|
||||
if not isinstance(_net, (list, dict)):
|
||||
missing.append(("net_config_invalid",
|
||||
"CHV_NET_JSON 须为 JSON list 或 dict"))
|
||||
# feat-chv-fix(P0-3):reuse 生产拦截。一行配置再也
|
||||
# 不能击穿租户隔离:只有显式实验二次确认
|
||||
# CHV_REUSE_SINGLE_TENANT_EXPERIMENT=1 时才放行,
|
||||
# 且放行本身在 wake_vm 里审计;否则 preflight 直接
|
||||
# 拒绝(kind=disk_mode_reuse_blocked)。
|
||||
if getattr(self, "disk_mode", "copy") == "reuse" \
|
||||
and not getattr(self, "reuse_experiment", False):
|
||||
missing.append(("disk_mode_reuse_blocked",
|
||||
"CHV_DISK_MODE=reuse 未经显式实验确认:"
|
||||
"须同时设 CHV_REUSE_SINGLE_TENANT_"
|
||||
"EXPERIMENT=1(仅单租户实验)"))
|
||||
self._preflight_config_checks(missing)
|
||||
if missing:
|
||||
kind = missing[0][0]
|
||||
detail = "; ".join(text for _, text in missing)
|
||||
@@ -451,8 +470,14 @@ class CloudHypervisorExecutor(Executor):
|
||||
pass
|
||||
|
||||
# -- 命令构造(纯函数,不做文件校验) -------------------------------
|
||||
def build_vm_command(self, task):
|
||||
"""构造 cloud-hypervisor 启动命令(干跑可测)。
|
||||
def _build_vm_command(self, task):
|
||||
"""构造 cloud-hypervisor 启动命令(干跑可测,仅私有)。
|
||||
|
||||
feat-chv-fix(P2-2):原公开 build_vm_command() 降级为
|
||||
私有——执行器唯一启动路径是 API 模式(wake_vm),公开
|
||||
的第二启动路径容易被后来人误当成可走的启动入口;手动
|
||||
等价写法保留在本方法与类 docstring 里,运维手工起 VM
|
||||
时仍可按此形状拼命令,不再经公开方法调用。
|
||||
|
||||
顺序固定:<二进制> --api-socket <socket> --cpus boot=N
|
||||
--memory size=M --kernel <内核> --cmdline <命令行>
|
||||
@@ -924,7 +949,7 @@ class CloudHypervisorExecutor(Executor):
|
||||
|
||||
def start_vm(self, task):
|
||||
"""旧名保留:即 wake_vm(API 模式)。直接启动命令
|
||||
build_vm_command() 只作手动等价写法,不再是执行器
|
||||
_build_vm_command() 只作手动等价写法(私有),不再是执行器
|
||||
的启动路径——两条启动路径并存是上一版的半吊子,
|
||||
本版只认 API 模式。"""
|
||||
return self.wake_vm(task)
|
||||
@@ -1102,8 +1127,17 @@ class CloudHypervisorExecutor(Executor):
|
||||
_p = os.path.join(self.state_dir, name, _suffix)
|
||||
if os.path.exists(_p):
|
||||
os.remove(_p)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as _cleanup_exc:
|
||||
# feat-chv-fix(P2-5):seed/私钥删除失败不再静默
|
||||
# 吞掉——记 vm_key_cleanup_failed 审计;且下次同名
|
||||
# VM 预置时若私钥残留,generate_host_keypair 的
|
||||
# “复用已有对”语义不变,但审计痕迹让“一性”破
|
||||
# 坏可被发现(彻底根治须另做“清理失败即拒起”,
|
||||
# 见 PR 描述的遗留项,不在本轨假装已根治)。
|
||||
task = task_or_name if isinstance(task_or_name, dict) \
|
||||
else {"id": task_or_name}
|
||||
self._emit("vm_key_cleanup_failed", task,
|
||||
"vm=%s error=%s" % (name, _cleanup_exc))
|
||||
task = task_or_name if isinstance(task_or_name, dict) \
|
||||
else {"id": task_or_name}
|
||||
if had:
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
seed 目录 state_dir/<vm>/seed/ 含 meta-data(instance-id
|
||||
即 VM 名)、user-data(write_files 写 /etc/ssh/ 的 host
|
||||
key 对并重启 sshd)。客机镜像硬性要求(见
|
||||
chv-guest/README.md §6):必须装 cloud-init 且支持
|
||||
chv-guest/README.md §5):必须装 cloud-init 且支持
|
||||
NoCloud seed、首启时先应用 seed 再启动 sshd、且不得在
|
||||
seed 应用前/后无条件重新生成 host key(否则预置即失效)。
|
||||
seed 磁盘/目录是 per-VM 敏感物,VM 关闭/回收时必须删。
|
||||
|
||||
Reference in new issue
Block a user