架构:WarmPool 从 cloud/chv/pool.py 移到 experimental_pool.py, 生产残留清理彻底分文件到 cloud/chv/orphans.py(facade 直接读 registry 文件清理,不构造池、不碰 prewarm/acquire/release); cloud/chv 与 cloud/sandbox 不再导出 WarmPool,旧路径一律 ImportError(CHV 未合并、无生产用户,兼容保留已无保留理由); worker 改走 orphans facade,不再持有 _warm_pool;#215 对齐从 接口形状推进到 REPLACEMENT_POINTS 替换清单(R1/R2 可机械替换、 R3 永不替换,#215 模块当前不存在由测试钉住)。 可维护性:build_chv_config 拆成 base/ssh/network/quota/disk/ timeout 六片段(组装只做 dict 合并,键冲突即报错;38 字段集 与取值不变,单项非法报错文案逐字一致);ADR-12 模块职责图 与禁 import 清单(AST 钉住);cloud/chv/ 公开 API 全量类型 标注、无 Any 逃逸,并修正三处旧标注错误;死代码扫一遍 (删实验池已无调用的 _quarantine 委托与未用 _LOGGER)。 前端(只读侧,不做控制按钮):chv.ts 新增纯逻辑谓词与文案 (执行器精确匹配、附件守卫、VM 状态文案、列表徽标);列表 行新增 VM 已报告死亡徽标;详情对全部 CHV 任务显示 VM 行 (已报告死亡/未报告死亡,不猜 live);WorkersSection 与成本 卡沿用已有数据,不新开池管理/成本页;服务端未新增第二个 生命周期字段(持久化事实只有三项,chv_vm_dead 已是完整派生), 只把派生抽成 _chv_vm_dead_from_row 并补 goal 回落形态测试。
115 lines
5.2 KiB
Python
115 lines
5.2 KiB
Python
"""#215(feat-arch-converge)接口对齐层,CHV 本地实现。
|
||
|
||
背景:#215 未合并,本基线里没有 cloud/timeouts.py、
|
||
cloud/gates.py、cloud/quota.py、cloud/sandbox_policy.py。
|
||
本模块绝不复制 #215 的文件,只把 CHV 真正能映射的
|
||
接口写成与 #215 相同的签名,#215 合并后替换为 import
|
||
即是机械的(见 docs/chv-architecture.md ADR-8)。
|
||
|
||
映射表(逐个核对过 #215 远程分支的真实函数,不是凭空编的):
|
||
- gates.resolve_inside(base, rel):目录门,CHV 快照/磁盘
|
||
路径白名单正好需要它——本模块实现同语义并已在
|
||
hardening.validate_snapshot_url 里使用,#215 合并后
|
||
改为 from cloud.gates import resolve_inside 即可。
|
||
- sandbox_policy.tmpfs_isolated(executor_kind, path):CHV
|
||
客机 /tmp 落在客机磁盘上,不是宿主 tmpfs,故对
|
||
cloud-hypervisor 一律返回 (False, ...)——fail-closed,
|
||
绝不宣称隔离。未知执行器同样 fail-closed。
|
||
- timeouts.resolve_module_timeout / quota.decide:无映射。
|
||
前者是审批/队列超时(None=不过期、0=不过期),CHV
|
||
的 api/ready 超时是秒数且 0 无效,语义不同,硬套会
|
||
改行为;后者是按月美元配额,CHV 的配额是 VM 数/磁盘
|
||
GB/快照数,单位不同。两者在本模块里 deliberately
|
||
不提供,测试钉住“CHV 不 import 它们”。
|
||
|
||
本模块只依赖标准库,是叶子模块,不 import cloud.sandbox。
|
||
|
||
合并后替换清单(REPLACEMENT_POINTS,本轨从“接口形状”
|
||
推进到可机械执行的清单;#215 未合并前一律不 import
|
||
cloud.gates / cloud.sandbox_policy / cloud.timeouts /
|
||
cloud.quota,测试钉住这些模块在本基线里不存在):
|
||
- R1 gates.resolve_inside:唯一生产调用方是
|
||
hardening._is_within(快照 URL 白名单)。合并后两步:
|
||
① hardening 改为 `from cloud.gates import resolve_inside`;
|
||
② 删本模块 resolve_inside。语义必须逐字一致,改完
|
||
重跑快照白名单测试(逃逸/符号链接/绝对路径三例)。
|
||
- R2 sandbox_policy.tmpfs_isolated:形状占位,当前无
|
||
生产调用方(只有测试)。合并后一步:删本模块
|
||
tmpfs_isolated;将来有调用方时直接 import #215 版,
|
||
不得再本地复刻。CHV 语义是“一律不隔离(fail-closed)”,
|
||
若 #215 版对 cloud-hypervisor 返回 True,替换即改行为,
|
||
必须先停下来核对,不得机械替换。
|
||
- R3 timeouts / quota:永不替换。#215 的 timeouts 是
|
||
审批/队列超时(None/0 = 不过期),quota 是按月美元
|
||
配额;CHV 的 api/ready 超时是秒数(0 无效)、配额是
|
||
VM 数/磁盘 GB/快照数,单位与语义都不同。清单里明写
|
||
“不替换”,防止合并时有人按名字硬套。
|
||
"""
|
||
|
||
import os
|
||
|
||
|
||
# 合并后替换清单:每项的 local/replacement/test 字段由测试
|
||
# 逐项核对(local 函数真实存在、#215 模块当前不存在、
|
||
# test 命名的测试类真实存在),清单本身不得漂移。
|
||
REPLACEMENT_POINTS = (
|
||
{
|
||
"id": "R1",
|
||
"upstream": "cloud.gates.resolve_inside",
|
||
"local": "cloud.chv.compat215.resolve_inside",
|
||
"caller": "cloud.chv.hardening._is_within",
|
||
"replacement": "hardening 改为 from cloud.gates import "
|
||
"resolve_inside,并删除本地 resolve_inside",
|
||
"test": "Compat215Test + 快照白名单测试",
|
||
},
|
||
{
|
||
"id": "R2",
|
||
"upstream": "cloud.sandbox_policy.tmpfs_isolated",
|
||
"local": "cloud.chv.compat215.tmpfs_isolated",
|
||
"caller": None,
|
||
"replacement": "删除本地 tmpfs_isolated;有调用方时"
|
||
"直接 import #215 版(先核对 CHV 一律 False 语义)",
|
||
"test": "Compat215Test.test_tmpfs_isolated_chv_and_unknown_"
|
||
"fail_closed",
|
||
},
|
||
{
|
||
"id": "R3",
|
||
"upstream": "cloud.timeouts / cloud.quota",
|
||
"local": None,
|
||
"caller": None,
|
||
"replacement": None,
|
||
"test": "Compat215Test.test_no_timeout_or_quota_map",
|
||
},
|
||
)
|
||
|
||
|
||
def resolve_inside(base: str, rel: str) -> str | None:
|
||
"""目录门:base 内的相对路径安全解析,越界回 None。
|
||
|
||
空串回 base 本身;绝对路径、.. 逃逸、符号链接逃逸、
|
||
commonpath 跨盘符 ValueError 一律 None。签名与 #215
|
||
的 gates.resolve_inside 一致,合并后可直接替换为 import。"""
|
||
if not rel:
|
||
return base
|
||
if os.path.isabs(rel):
|
||
return None
|
||
real_base = os.path.realpath(base)
|
||
target = os.path.realpath(os.path.join(real_base, rel))
|
||
try:
|
||
if os.path.commonpath([real_base, target]) != real_base:
|
||
return None
|
||
except ValueError:
|
||
return None
|
||
return target
|
||
|
||
|
||
def tmpfs_isolated(executor_kind: str, path: str) -> tuple[bool, str]:
|
||
"""某执行器下某路径是否走 tmpfs 隔离:(isolated, reason)。
|
||
|
||
cloud-hypervisor:客机 /tmp 落在客机磁盘上,不走宿主
|
||
tmpfs,一律 (False, ...);未知执行器 fail-closed 同样
|
||
False。签名与 #215 的 sandbox_policy.tmpfs_isolated 一致。"""
|
||
if executor_kind == "cloud-hypervisor":
|
||
return False, "CHV 客机 /tmp 落在客机磁盘上,不走宿主 tmpfs"
|
||
return False, "未知执行器,按不隔离处理(fail-closed 口径)"
|