55f7621c90
gates / gates (push) Successful in 30s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
134 lines
5.4 KiB
Python
134 lines
5.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Tests for felhom-restore-beside.sh (R-834) — a fake `pct` on PATH holds the guest config in a file.
|
|
|
|
The restored config is the PRODUCTION one (onboot 1, mp8 on the household's drives, mp9 on the
|
|
original's bootstrap, NICs up). The script must end with onboot 0, no host-path bind, every NIC
|
|
link_down, and never start the guest. Asserted on the consequence: the fake's final config file and
|
|
its call log. Red-proof: drop the `pct set --delete` line from the script and test_strips fails.
|
|
|
|
Run: python3 scripts/test_felhom_restore_beside.py
|
|
"""
|
|
import os
|
|
import pathlib
|
|
import stat
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
|
|
SCRIPT = pathlib.Path(__file__).with_name("felhom-restore-beside.sh")
|
|
|
|
PROD_CONF = """arch: amd64
|
|
hostname: demo-hp
|
|
onboot: 1
|
|
rootfs: nvme-scratch:9299/vm-9299-disk-0.raw,size=16G
|
|
mp0: nvme-scratch:9299/vm-9299-disk-1.raw,mp=/var/lib/felhom,backup=1,size=70G
|
|
mp8: /mnt/felhom-drives,mp=/mnt/felhom-drives
|
|
mp9: /var/lib/felhom-agent/guests/9201/bootstrap,mp=/etc/felhom-bootstrap,ro=1
|
|
net0: name=eth0,bridge=vmbr0,hwaddr=BC:24:11:0F:7E:C5,ip=dhcp,type=veth
|
|
net1: name=eth1,bridge=vmbr9,hwaddr=BC:24:11:28:B4:F5,ip=169.254.253.2/30,type=veth
|
|
"""
|
|
|
|
# A minimal pct: status (exists iff the conf file exists), restore (writes PROD_CONF, records whether
|
|
# --onboot 0 was passed), config, set (--delete / --netN / --onboot), start (recorded — must never run).
|
|
FAKE_PCT = r'''#!/usr/bin/env python3
|
|
import os, sys
|
|
d = os.environ["FAKE_PCT_DIR"]
|
|
conf = os.path.join(d, "conf")
|
|
with open(os.path.join(d, "calls"), "a") as f:
|
|
f.write(" ".join(sys.argv[1:]) + "\n")
|
|
cmd = sys.argv[1]
|
|
def read():
|
|
return [l for l in open(conf).read().splitlines() if l]
|
|
def write(lines):
|
|
open(conf, "w").write("\n".join(lines) + "\n")
|
|
if cmd == "status":
|
|
sys.exit(0 if os.path.exists(conf) else 2)
|
|
if cmd == "restore":
|
|
lines = open(os.path.join(d, "prod")).read().splitlines()
|
|
args = sys.argv[3:]
|
|
if "--onboot" in args:
|
|
v = args[args.index("--onboot") + 1]
|
|
lines = [("onboot: " + v) if l.startswith("onboot:") else l for l in lines]
|
|
write([l for l in lines if l])
|
|
sys.exit(0)
|
|
if cmd == "config":
|
|
print("\n".join(read()))
|
|
sys.exit(0)
|
|
if cmd == "set":
|
|
lines = read()
|
|
a = sys.argv[3:]
|
|
while a:
|
|
k, v = a[0], a[1]
|
|
a = a[2:]
|
|
if k == "--delete":
|
|
drop = set(v.split(","))
|
|
lines = [l for l in lines if l.split(":")[0] not in drop]
|
|
else:
|
|
key = k.lstrip("-")
|
|
lines = [l for l in lines if l.split(":")[0] != key] + [key + ": " + v]
|
|
write(lines)
|
|
sys.exit(0)
|
|
if cmd == "start":
|
|
sys.exit(0)
|
|
sys.exit(9)
|
|
'''
|
|
|
|
|
|
def run(tmp, *args, script=SCRIPT, exists=False):
|
|
bindir = pathlib.Path(tmp, "bin")
|
|
bindir.mkdir(exist_ok=True)
|
|
pct = bindir / "pct"
|
|
pct.write_text(FAKE_PCT)
|
|
pct.chmod(pct.stat().st_mode | stat.S_IEXEC)
|
|
pathlib.Path(tmp, "prod").write_text(PROD_CONF)
|
|
if exists:
|
|
pathlib.Path(tmp, "conf").write_text(PROD_CONF)
|
|
env = dict(os.environ, PATH=f"{bindir}:{os.environ['PATH']}", FAKE_PCT_DIR=tmp)
|
|
return subprocess.run(["bash", str(script), *args], env=env, capture_output=True, text=True)
|
|
|
|
|
|
class RestoreBeside(unittest.TestCase):
|
|
def test_strips(self):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
r = run(tmp, "9299", "tmp:backup/ct/9201/x", "nvme-scratch")
|
|
self.assertEqual(r.returncode, 0, r.stderr + r.stdout)
|
|
conf = pathlib.Path(tmp, "conf").read_text()
|
|
calls = pathlib.Path(tmp, "calls").read_text()
|
|
self.assertIn("onboot: 0", conf)
|
|
self.assertNotIn("onboot: 1", conf)
|
|
for line in conf.splitlines():
|
|
self.assertFalse(line.startswith("mp") and ": /" in line, f"host bind left: {line}")
|
|
if line.startswith("net"):
|
|
self.assertIn("link_down=1", line)
|
|
self.assertIn("mp0:", conf, "a storage volume must stay")
|
|
self.assertNotIn("\nstart", "\n" + calls, "the script started the guest")
|
|
self.assertIn("--onboot 0", calls.splitlines()[1], "onboot 0 must be set AT restore time")
|
|
|
|
def test_refuses_an_existing_vmid(self):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
r = run(tmp, "9201", "tmp:backup/ct/9201/x", "nvme-scratch", exists=True)
|
|
self.assertNotEqual(r.returncode, 0)
|
|
self.assertIn("already exists", r.stderr)
|
|
self.assertNotIn("restore", pathlib.Path(tmp, "calls").read_text())
|
|
|
|
def test_refuses_the_agent_bands(self):
|
|
for v in ("990003", "9999", "abc"):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
r = run(tmp, v, "tmp:backup/ct/9201/x", "s")
|
|
self.assertNotEqual(r.returncode, 0, v)
|
|
|
|
def test_readback_catches_a_bind_left_behind(self):
|
|
# Red-proof built in: the same script with the --delete line removed must FAIL its read-back.
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
broken = pathlib.Path(tmp, "broken.sh")
|
|
broken.write_text(SCRIPT.read_text().replace('\tpct set "$vmid" --delete "$binds"\n', "\t:\n"))
|
|
self.assertNotEqual(broken.read_text(), SCRIPT.read_text(), "mutation did not apply")
|
|
r = run(tmp, "9299", "tmp:backup/ct/9201/x", "s", script=broken)
|
|
self.assertEqual(r.returncode, 2, r.stdout + r.stderr)
|
|
self.assertIn("host-bind-left", r.stderr)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(unittest.main())
|