round-12: merge round-11 head 717efc4 (V-9 real-machine fixes)
Round-11 moved on Gitea after round-12 branched (29d3a5a->717efc4): V-9 guest-env fixes must be in any image built from this branch, or the round-12 image would ship the known defects V-9 found (guest-env never created when owner=chv:chv; wake reporting ready ~30s before the runcmd chown lands). Contents, all from717efc4: - build.sh: runcmd chowns /etc/chv/guest-env to chv:chv after 'start chv-authd', before 'chv-sync boot' - check/generate_lockfile.py: DECLARED_SOURCES gains P2 trio (chv-cell/broker/browser -> repo_commit) - executor._wait_guest_env_usable: with secrets delivered, wait for guest-env readable by the SSH user before reporting ready; timeout stops the VM and raises guest_env_not_ready; called from _boot_vm - hostkeys.py: V-9 root-cause comment (owner always root:root) - test_chv_gitea_sync.py: 5 new tests Conflict: build.sh runcmd line only. Resolved as union: 717efc4's runcmd line (with chown) kept; HEAD's P4 trio + P7 chv-memory copy-in lines (absent on the717efc4side) kept. executor.py merged cleanly.
This commit is contained in:
commit
5d5041bb7c
6 files changed
+165
-6
No files matched your search
@@ -17,6 +17,9 @@ VM、不联网、不含任何真实 token(测试值全是假值)。
|
||||
GITEA_TOKEN 且 argv 里绝无 token 值;
|
||||
- worker:_gitea_sync_env 取值;_run_attempt 只对 CHV 执行器
|
||||
把同步变量并进 gateway_env。
|
||||
- V-9 移植:guest-env owner 恒 root:root(write_files 先于建用户)
|
||||
+ 客内 runcmd chown(build.sh 位置断言)+ wake 等 guest-env
|
||||
可读再报 ready(超时关 VM 抛 guest_env_not_ready)。
|
||||
"""
|
||||
|
||||
import os
|
||||
@@ -279,6 +282,47 @@ class WriteSeedGuestEnvTest(unittest.TestCase):
|
||||
self.assertNotIn("runcmd", parsed)
|
||||
self.assertEqual(len(parsed["write_files"]), 3)
|
||||
|
||||
def test_guest_env_owner_always_root(self):
|
||||
# V-9 移植:write_files 在 users-groups 之前跑,owner
|
||||
# 写 chv:chv 时 getpwnam 失败、整个模块失败,文件根本
|
||||
# 不产生;故 owner 恒为 root:root,改归 SSH 用户由客内
|
||||
# final 阶段 99-chv-sync.cfg runcmd chown(本分支的
|
||||
# write_seed 本来就没有 ssh_user 参数,owner 写死
|
||||
# root:root——此测试是防回退断言)。
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
seed_dir = os.path.join(tmp, "seed")
|
||||
write_seed(seed_dir, "vm1", "PRIV", "ssh-ed25519 AAAAx",
|
||||
secret_env={"SUB2API_API_KEY": "s3cr3t"})
|
||||
with open(os.path.join(seed_dir, "user-data")) as fh:
|
||||
parsed = yaml.safe_load(fh.read())
|
||||
guest = [e for e in parsed["write_files"]
|
||||
if e["path"] == "/etc/chv/guest-env"]
|
||||
self.assertEqual(len(guest), 1)
|
||||
self.assertEqual(guest[0]["owner"], "root:root")
|
||||
self.assertEqual(guest[0]["permissions"], "0600")
|
||||
|
||||
|
||||
# -- (3b) build.sh:客内 runcmd chown --------------------------------------
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
class BuildShGuestEnvChownTest(unittest.TestCase):
|
||||
def test_build_sh_runcmd_chowns_guest_env_before_sync_boot(self):
|
||||
# V-9 移植:guest-env 由 write_files 以 root:root 写
|
||||
# (建用户之前),须在 final 阶段 chv-sync boot 之前
|
||||
# 改归 chv,否则 SSH 派发 source 它会 Permission
|
||||
# denied;无文件时 test -f 护栏使该行空操作,runcmd
|
||||
# 失败不影响启动。
|
||||
text = (ROOT / "chv-guest" / "build.sh").read_text()
|
||||
chown = ("test -f /etc/chv/guest-env && "
|
||||
"chown chv:chv /etc/chv/guest-env || true")
|
||||
self.assertIn(chown, text)
|
||||
# "chv-sync boot" 在文件前部注释里先出现过一次,故从
|
||||
# chown 位置往后找同一 runcmd 行里的 boot。
|
||||
chown_at = text.index(chown)
|
||||
boot_at = text.index("/usr/local/bin/chv-sync boot", chown_at)
|
||||
self.assertLess(chown_at, boot_at)
|
||||
|
||||
|
||||
# -- (4) 执行器 wake_vm -------------------------------------------------
|
||||
class WakeGuestEnvCase(unittest.TestCase):
|
||||
@@ -324,7 +368,7 @@ class WakeGuestEnvCase(unittest.TestCase):
|
||||
self.launches.append(list(cmd))
|
||||
return FakeProc(alive=True)
|
||||
|
||||
def make_exec(self, env_extra=None):
|
||||
def make_exec(self, env_extra=None, runner=None, ssh_user=None):
|
||||
env = {"CHV_EGRESS_PROXY": "http://10.0.0.1:8888",
|
||||
"CHV_HOST_KEY_MODE": "provisioned",
|
||||
"CHV_SSH_FORWARD": "1",
|
||||
@@ -338,9 +382,11 @@ class WakeGuestEnvCase(unittest.TestCase):
|
||||
kernel=self.kernel, rootfs=self.rootfs,
|
||||
ssh_key=self.ssh_key, kvm_path=self.kvm,
|
||||
cpuinfo_path=self.cpuinfo, state_dir=self.state_dir,
|
||||
runner=self._runner, launcher=self._launcher,
|
||||
runner=runner if runner is not None else self._runner,
|
||||
launcher=self._launcher,
|
||||
api_client=lambda s, m, p, payload=None: (200, {}),
|
||||
sleeper=lambda s: None, env=env,
|
||||
ssh_user=ssh_user,
|
||||
sentinel_resolver=lambda host, port: ["93.184.216.34"])
|
||||
ex.audit_callback = lambda e, t, d, k=None: self.audit.append(
|
||||
(e, t.get("id") if isinstance(t, dict) else t, d, k))
|
||||
@@ -456,6 +502,60 @@ class WakeGuestEnvCase(unittest.TestCase):
|
||||
ex.wake_vm({"id": "sync6", "gitea_sync": dict(FAKE_SYNC)})
|
||||
self.assertEqual(self.launches, [])
|
||||
|
||||
def test_wake_with_chv_ssh_user_guest_env_owner_still_root(self):
|
||||
# V-9 移植:owner 恒为 root:root(write_files 先于建用户),
|
||||
# 改归 chv 由客内 99-chv-sync.cfg runcmd chown——即使
|
||||
# SSH 用户就是 chv,seed 里的 owner 也不许写 chv:chv。
|
||||
ex = self.make_exec(ssh_user="chv")
|
||||
out = ex.wake_vm({"id": "envown1"},
|
||||
extra_env={"SUB2API_API_KEY": "s3cr3t"})
|
||||
self.assertEqual(out["state"], "running")
|
||||
parsed = yaml.safe_load(
|
||||
_seed_user_text(self.state_dir, "chv-envown1"))
|
||||
guest = [e for e in parsed["write_files"]
|
||||
if e["path"] == "/etc/chv/guest-env"]
|
||||
self.assertEqual(len(guest), 1)
|
||||
self.assertEqual(guest[0]["owner"], "root:root")
|
||||
|
||||
def _readable_runner(self, answers):
|
||||
"""Fake runner:`test -r /etc/chv/guest-env` 按 answers
|
||||
依次回可读与否(用完后重复最后一个),其余命令走默认
|
||||
runner。answers 是 bool 列表。移植自 V-9(9fb0f4b)。"""
|
||||
state = {"n": 0}
|
||||
|
||||
def run(cmd, timeout=None):
|
||||
if ("test" in cmd
|
||||
and cmd[-1] == "/etc/chv/guest-env"):
|
||||
idx = min(state["n"], len(answers) - 1)
|
||||
state["n"] += 1
|
||||
return FakeResult(0 if answers[idx] else 1, "", "")
|
||||
return self._runner(cmd, timeout)
|
||||
run.readable_calls = lambda: state["n"]
|
||||
return run
|
||||
|
||||
def test_wake_waits_for_guest_env_readable(self):
|
||||
# V-9 移植:runcmd chown 比 SSH 就绪晚约 30 秒,wake 须等
|
||||
# guest-env 对 SSH 用户可读再报 ready,否则首个派发
|
||||
# source 它即 Permission denied。
|
||||
runner = self._readable_runner([False, False, True])
|
||||
ex = self.make_exec(runner=runner, ssh_user="chv")
|
||||
out = ex.wake_vm({"id": "envwait1"},
|
||||
extra_env={"SUB2API_API_KEY": "s3cr3t"})
|
||||
self.assertEqual(out["state"], "running")
|
||||
self.assertEqual(runner.readable_calls(), 3)
|
||||
|
||||
def test_wake_fails_when_guest_env_never_readable(self):
|
||||
# V-9 移植:guest-env 一直对 SSH 用户不可读时 wake 超时即
|
||||
# 关 VM 再抛 guest_env_not_ready,绝不带着读不到的密钥
|
||||
# 跑任务。
|
||||
runner = self._readable_runner([False])
|
||||
ex = self.make_exec(runner=runner, ssh_user="chv")
|
||||
with self.assertRaises(ChvFailure) as ctx:
|
||||
ex.wake_vm({"id": "envwait2"},
|
||||
extra_env={"SUB2API_API_KEY": "s3cr3t"})
|
||||
self.assertEqual(ctx.exception.kind, "guest_env_not_ready")
|
||||
self.assertGreater(runner.readable_calls(), 1)
|
||||
|
||||
|
||||
# -- (4b) seed 附加项(users 块 + network-config)------------------------
|
||||
class SeedExtrasTest(unittest.TestCase):
|
||||
|
||||
Reference in new issue
Block a user