fix(chv): V-3/F1/F2 真机缺陷修复 + V-1/V-4 附带修复
V-3 快照/restore: - snapshot 改传裸 URL(source_url= 是 restore 的键,服务端 Could not extract path from URL);快照产物是目录,校验 改为目录非空,登记计量/剪枝同步支持目录 - restore 起 VMM 前补 _restore_prepare:杀旧 VMM(登记的 + 未登记跨实例扫描)并清旧 socket,与 _wake_prepare 对等 - hibernate 评估结论:快照 config.json 引用原盘,旧 stop 删 盘致 restore 必失败;hibernate 的 stop 改 keep_disk=True, GC 不删有登记快照的 VM 的盘,restore 前预检快照引用的盘 (snapshot_disk_missing fail-fast) F1:re-wake 删旧盘——H5 删私钥致 re-wake 必然生成新 key 对, 旧盘复用时 cloud-init 不重应用新 seed,sshd 仍呈旧 key 而 known_hosts 钉新 key;wake 本就是冷启动,旧盘不是要保留的状态 F2:stop_vm 跨实例按 socket 进程扫描杀未登记 VMM (/proc cmdline 精确 socket + VMM 二进制名 + 跳过自身/pid 1, 绝不误杀 ch-remote 与他人 VMM) V-4:remote_command source 失败改 || exit 126 fail-fast; guest-env 条目 owner 改归 SSH 用户(0600 不变),root 仍 root:root V-1 附带:build.sh 预建 /app/work(1777 粘滞位 + stat 断言); host.py default_codex_bin() 改 shutil.which,裸名不再让 health 报 ok:false 测试:新 test_chv_v3_fixes.py 23 项;旧快照测试按目录产物改写; test_chv* 458 全过,全量 cloud 套件 1930 全过(skipped=8)
This commit is contained in:
1 parent
ef206cf554
commit
65504ae8cb
10 files changed
+962
-91
No files matched your search
+38
-19
@@ -180,15 +180,19 @@ def build_seed_user_data(private_key_text: str, public_key_text: str) -> str:
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
def _guest_env_entry(merged_env: dict) -> list:
|
||||
"""guest-env 的 write_files 条目行(0600 root,值经
|
||||
shlex.quote 引用):只能并进已有的 write_files 列表,
|
||||
绝不能再写一个 write_files: 键(YAML 重复键会覆盖掉
|
||||
host key 的两条写文件条目,预置即失效)。"""
|
||||
def _guest_env_entry(merged_env: dict, owner: str = "root:root") -> list:
|
||||
"""guest-env 的 write_files 条目行(0600,值经 shlex.quote
|
||||
引用):只能并进已有的 write_files 列表,绝不能再写一个
|
||||
write_files: 键(YAML 重复键会覆盖掉 host key 的两条写
|
||||
文件条目,预置即失效)。owner 缺省 root:root;SSH 用户
|
||||
非 root 时由调用方传 "<用户>:<用户>"——远程 shell 以该
|
||||
用户 source 此文件,0600 root 时它读不到且旧拼装不中断、
|
||||
密钥静默缺失(V-4),故文件须归实际读它的用户所有;
|
||||
权限仍 0600(只给属主读),不放宽给组/其它。"""
|
||||
lines = [
|
||||
" - path: %s" % GUEST_ENV_PATH,
|
||||
" permissions: '0600'",
|
||||
" owner: root:root",
|
||||
" owner: %s" % owner,
|
||||
" content: |",
|
||||
]
|
||||
for key in sorted(merged_env):
|
||||
@@ -227,22 +231,46 @@ def _apply_seed_extras(seed_dir: str, user_data: str,
|
||||
return user_data
|
||||
|
||||
|
||||
def _insert_guest_env_entry(user_data: str, merged_env: dict,
|
||||
ssh_user: str | None) -> str:
|
||||
"""把 guest-env 条目并进 user-data 已有的 write_files
|
||||
列表(绝不再写一个 write_files: 键,见 _guest_env_entry)。
|
||||
owner:SSH 用户非 root 时写 "<用户>:<用户>"(0600 不变),
|
||||
否则 root:root。条目插在 runcmd 段之前、仍属同一
|
||||
write_files 列表(seed 自 2026-11-07 起不再带 runcmd,
|
||||
见 build_seed_user_data 的根因禁令;marker 分支只作
|
||||
防御,将来模板若恢复 runcmd 仍插在其前)。"""
|
||||
owner = "root:root"
|
||||
if ssh_user and ssh_user != "root":
|
||||
owner = "%s:%s" % (ssh_user, ssh_user)
|
||||
lines = _guest_env_entry(merged_env, owner=owner)
|
||||
marker = "runcmd:\n"
|
||||
if marker in user_data:
|
||||
head, tail = user_data.split(marker, 1)
|
||||
return head + "\n".join(lines) + "\n" + marker + tail
|
||||
return user_data + "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
def write_seed(seed_dir: str, vm_name: str, private_key_text: str,
|
||||
public_key_text: str,
|
||||
secret_env: dict | None = None,
|
||||
guest_env: dict | None = None,
|
||||
user_data_extra: str | None = None,
|
||||
network_config: str | None = None) -> str:
|
||||
network_config: str | None = None,
|
||||
ssh_user: str | None = None) -> str:
|
||||
"""写 seed 目录:meta-data + user-data(均为 0600),
|
||||
返回 seed 目录路径。调用方负责随 VM 销毁删掉它。
|
||||
|
||||
secret_env 给定时写进客内 GUEST_ENV_PATH(0600 root,
|
||||
secret_env 给定时写进客内 GUEST_ENV_PATH(0600,
|
||||
远程命令 source 它得密钥环境,值不进宿主 argv/环境/
|
||||
审计明文)。guest_env 给定时与 secret_env 合并写同一
|
||||
个条目(secret 优先于同名键,防两行同名);guest_env
|
||||
装 boot 时要用的非密钥同步变量(GITEA_URL 等,见
|
||||
gitea_sync):只走 SSH 前缀到不了 cloud-init 时运行
|
||||
的 chv-sync boot。两者皆空时不写该条目。
|
||||
ssh_user:远程 shell 的用户;非 root 时 guest-env 条目
|
||||
owner 写 "<用户>:<用户>"(0600 不变),否则该用户读
|
||||
不到文件(V-4);root/未给时仍 root:root。
|
||||
user_data_extra / network_config 见 _apply_seed_extras
|
||||
(客机用户与静态网络的供给,皆空即不写)。"""
|
||||
os.makedirs(seed_dir, exist_ok=True)
|
||||
@@ -260,17 +288,8 @@ def write_seed(seed_dir: str, vm_name: str, private_key_text: str,
|
||||
merged_env = dict(guest_env or {})
|
||||
merged_env.update(secret_env or {})
|
||||
if merged_env:
|
||||
lines = _guest_env_entry(merged_env)
|
||||
# 条目插在 runcmd 段之前、仍属同一 write_files 列表
|
||||
#(seed 自 2026-11-07 起不再带 runcmd,见
|
||||
# build_seed_user_data 的根因禁令;marker 分支只作
|
||||
# 防御,将来模板若恢复 runcmd 仍插在其前)。
|
||||
marker = "runcmd:\n"
|
||||
if marker in user_data:
|
||||
head, tail = user_data.split(marker, 1)
|
||||
user_data = head + "\n".join(lines) + "\n" + marker + tail
|
||||
else:
|
||||
user_data = user_data + "\n".join(lines) + "\n"
|
||||
user_data = _insert_guest_env_entry(
|
||||
user_data, merged_env, ssh_user)
|
||||
user_data = _apply_seed_extras(seed_dir, user_data,
|
||||
user_data_extra, network_config)
|
||||
atomic_write_text(user_path, user_data)
|
||||
|
||||
Reference in new issue
Block a user