ci / test (push) Failing after 47m51s
第 2 轮复测(0dc8328)p2 仍 FAIL 的两处根因: 1. 挂盘时序竞态:add-disk 返回 06:59:10,root hotplug 路径 (cloud-init-hotplugd → cloud-final runcmd → chv-data-mount) 06:59:14–16 才挂上;旧 attach 以 chv 调 chv-data-mount 并判 rc==0,但 /dev/vd? 属 root:disk、chv 的 blkid/mkfs 必败, 脚本又静默 exit 0,rc 检查是空洞的成功假象。 2. 卸载权限:挂载是 root 做的,chv 的 plain umount 恒 rc=32 must be superuser,release 按现写法永远失败、VM 留置。 修法选 (a) 轮询,不选 (b) root 特权挂载:挂盘本身已有 root 路径在跑,host 侧不另开新的 root 挂载通道(新特权面只扩 攻击面);轮询以 chv 只读判定挂载态,零新增特权。 - attach:删 chv 调 chv-data-mount,改为轮询 mountpoint -q /home/chv/workspace && test -w(默认 60 秒/2 秒,可经 CHV_DATA_MOUNT_TIMEOUT_SECONDS / CHV_DATA_MOUNT_INTERVAL_SECONDS 注入短值);超时记 data_disk_mount_timeout 审计并抛同名 kind,不写归属标记。attach 返回成功 = 挂盘真实发生。 resume_seconds 因此真实包含挂盘等待(§2.1 口径),≤2 秒 目标不改口径、不改目标。 - detach:改走 sudo -n /usr/bin/umount /home/chv/workspace; 失败即 False,release 留置 VM 不回收(fail-closed)。 - chv-data-mount.sh:盘已找到后 mkfs/mount 失败改 exit 1 (原全部失败路径 exit 0);无盘仍 exit 0(启动期 no-op, 不挡 boot)。本脚本只由 root 跑(service/runcmd)。 - build.sh:apt 加 sudo;写 /etc/sudoers.d/chv-data-umount 窄规则(chv 仅能以 root 跑 umount /home/chv/workspace 这一条精确 argv,NOPASSWD,无 shell/其它命令/挂载权), 0440 + visudo -cf 作构建门;buildinfo/lockfile 登记 sudo 与 chv-data-umount。 特权边界:本轮唯一新增提权面就是上述一条 sudoers 规则; attach 零提权(只读轮询),挂载权仍只在 root 的 hotplug/service 路径手里。旧镜像(无 sudo/无规则)上 detach 返回 False、release 留置,须重建镜像才恢复回收。 测试:新增 10 项(attach 等三次轮询才成功、超时 loud 失败、 永不以 chv 调 chv-data-mount、detach 精确 sudo argv、 sudo 失败 False、卸载失败 release 留置、客机脚本 exit 码、 build.sh sudo/窄规则/buildinfo 登记);另修 2 处旧断言 (attach 不再调挂载脚本)与 stage4 lockfile 夹具(补 sudo dpkg 条目),并把 attach 热插段抽为 _hotplug_tenant_disk (50 行函数上限)。全量 Ran 4688,OK (skipped=15,均为 环境 skip:无 PG DSN/驱动、缺 SearXNG_URL、fallback state 文件在位)。
82 lines
4.2 KiB
Bash
Executable File
82 lines
4.2 KiB
Bash
Executable File
#!/bin/sh
|
||
# chv-data-mount:把数据盘(LABEL=CHVDATA)挂到 /home/chv/workspace。
|
||
#
|
||
# 由镜像 99-chv-sync.cfg runcmd 在 chv-sync boot 之前调(final 阶段,
|
||
# 用户 chv 已存在,可 chown)。幂等:已挂载即返回;已有 CHVDATA
|
||
# 标签的盘即直接挂;无数据盘(copy 模式旧 VM 没有第三块盘)即
|
||
# 静默退出 0,不报错、不挡启动。
|
||
#
|
||
# 找盘不依赖 /dev/vd? 固定名:先按 LABEL 找;找不到则在所有整盘里
|
||
# 找一块无文件系统、不是根盘的盘——seed ISO 有 iso9660,不会被选
|
||
# 中;根盘本身无 fs,但按持有 / 分区的盘排除;分区(/dev/vda1 等)
|
||
# 不参与(glob 只取整盘名)。
|
||
#
|
||
# 阶段 1(feat-chv-opt-landing):工作区与浏览器 profile 的载体从
|
||
# 系统盘 overlay 里剥离出来,独立持久;快照只登记系统盘 overlay,
|
||
# 数据盘不进快照(见 cloud/chv/disk.py)。
|
||
set -u
|
||
MOUNT=/home/chv/workspace
|
||
LABEL=CHVDATA
|
||
|
||
log() { echo "chv-data-mount: $*"; }
|
||
|
||
mountpoint -q "$MOUNT" 2>/dev/null && { log "already mounted"; exit 0; }
|
||
|
||
DEV=$(blkid -L "$LABEL" 2>/dev/null || true)
|
||
if [ -z "$DEV" ]; then
|
||
ROOT_SRC=$(findmnt -n -o SOURCE / 2>/dev/null || echo "")
|
||
ROOT_DISK=$(printf '%s' "$ROOT_SRC" | sed -e 's/p[0-9]*$//' -e 's/[0-9]*$//')
|
||
for d in /dev/vd[a-z] /dev/sd[a-z] /dev/xvd[a-z]; do
|
||
[ -b "$d" ] || continue
|
||
[ "$d" = "$ROOT_DISK" ] && continue
|
||
TYPE=$(blkid -o value -s TYPE "$d" 2>/dev/null || echo none)
|
||
[ "$TYPE" = "none" ] || continue
|
||
DEV=$d
|
||
break
|
||
done
|
||
fi
|
||
|
||
# p2 第三轮退出码契约(2026-11-09 第 2 轮复测):无数据盘
|
||
# 即静默退出 0(启动期 no-op:干净池 VM 与 copy 模式旧 VM
|
||
# 本来就没有第三块盘,boot 服务绝不能因此失败);但盘已
|
||
# 找到后 mkfs/mount 任一步失败即 exit 1 并记日志——旧版
|
||
# 全部失败路径 exit 0 是“成功假象”的另一半:host 侧曾以
|
||
# chv 调本脚本并判 rc==0,chv 无权读 /dev/vd?、mkfs 必败
|
||
# 却被报成成功。host 侧现已改走轮询等 root hotplug 路径
|
||
# 落地(见 executor.py attach),不再以 chv 调本脚本;
|
||
# 本脚本只由 root 跑(chv-data-mount.service 与
|
||
# cloud-final runcmd),失败 loud 不挡启动(runcmd 行带
|
||
# || true,service 失败可见于 systemctl status)。
|
||
[ -n "$DEV" ] || { log "no data disk found, skip"; exit 0; }
|
||
if [ "$(blkid -o value -s LABEL "$DEV" 2>/dev/null || echo "")" != "$LABEL" ]; then
|
||
log "formatting $DEV as $LABEL"
|
||
mkfs.ext4 -L "$LABEL" "$DEV" || { log "mkfs failed on $DEV"; exit 1; }
|
||
fi
|
||
mkdir -p "$MOUNT"
|
||
mount "LABEL=$LABEL" "$MOUNT" || { log "mount failed"; exit 1; }
|
||
chown chv:chv "$MOUNT" || true
|
||
# feat-chv-opt-landing(阶段 3):浏览器持久 profile 落数据盘。
|
||
# chv-browser 以 chv-browser 身份跑,工作区根属 chv、它写不进;
|
||
# 此处以 root 预建 profile 目录并定好属主与 0700,服务重启后
|
||
# 同一 profile 即登录态跨恢复保留。数据盘不在时浏览器服务
|
||
# 自己回落 /var/lib/chv-browser/profile(见 chv-browser.py)。
|
||
mkdir -p "$MOUNT/.chv-browser-profile" || true
|
||
chown chv-browser:chv-browser "$MOUNT/.chv-browser-profile" 2>/dev/null || true
|
||
chmod 0700 "$MOUNT/.chv-browser-profile" 2>/dev/null || true
|
||
# feat-chv-opt-landing(阶段 5):具名会话的 storage-state 落
|
||
# 数据盘 .chv-browser-sessions/<会话>/storage-state.json。工作区
|
||
# 根属 chv,chv-browser 自己建不进子目录,此处以 root 预建并
|
||
# 定好属主与 0700;浏览器服务自己建目录失败时只记
|
||
# storage_saved=false,不谎报 open/close 成功。
|
||
mkdir -p "$MOUNT/.chv-browser-sessions" || true
|
||
chown chv-browser:chv-browser "$MOUNT/.chv-browser-sessions" 2>/dev/null || true
|
||
chmod 0700 "$MOUNT/.chv-browser-sessions" 2>/dev/null || true
|
||
# 阶段 3 live 实证(2026-11-08,构建 #8 驱动):/home/chv 默认
|
||
# 0750 chv:chv,chv-browser 穿不过家目录去够数据盘 profile——
|
||
# ismount 判假、profile 静默回落本机盘(服务本身不报错)。
|
||
# 放到 0751:只放行穿过(+x),不放行列表(无 r),
|
||
# chv-browser 能进已知子目录,看不见家目录内容。
|
||
chmod 0751 /home/chv 2>/dev/null || true
|
||
log "mounted $DEV -> $MOUNT"
|
||
exit 0
|