Files
felhom-agent/configs/test_felhom_internal_place.py
T
admin 98f3cdd76a
gates / gates (push) Successful in 25s
Security review of 912f7c1: the mkfs wrapper trusts no sibling claim; the internal-place helper fails closed
- felhom-mkfs-guarded: the 0.158.0 exemption for a sibling part mounted at /mnt/<name> + under /mnt/felhom-drives is
  removed — the agent can make that corroborating mount itself. Every mount of every node outside /mnt/felhom-drives
  refuses (kernel mount table); an unlistable disk refuses. CandidatePartition.Formattable is false for every part of a
  disk with a mounted node, so it is never offered for formatting (attach still is).
- felhom-internal-place: a failed lvs listing, a listing without the pool, or a blkid exit other than 0/2 refuses
  (they read as "nothing promised" / "no signature" before).
Every new test was seen failing against the 0.158.0 versions.

Claude-Session: https://claude.ai/code/session_012qRErfCoiTkvDK9N5XHbzb
2026-10-16 04:02:00 +02:00

256 lines
11 KiB
Python

#!/usr/bin/env python3
"""Tests for configs/felhom-internal-place (felhom.eu architecture/16 §5, §7 — sizes ruled 2026-10-16).
The helper is run for real under bash; `lvs`, `lvcreate`, `blkid`, `mkfs.ext4` and `stat` are PATH stubs that read and
write one JSON state file, so nothing on this machine is touched. Every refusal has a positive control: the same setup
without the one condition goes through — so a test cannot pass because the helper refuses everything.
Run: python3 configs/test_felhom_internal_place.py (also run by the Go suite, internal/internalplace)
"""
import json, os, pathlib, stat, subprocess, tempfile, unittest
HELPER = pathlib.Path(__file__).resolve().parent / "felhom-internal-place"
GIB = 1 << 30
STUB_LVS = r'''#!/usr/bin/env python3
import json, os, sys
st = json.load(open(os.environ["IP_STATE"]))
if st.get("lvs_fail_listing") and "lv_name,pool_lv,lv_size" in sys.argv: sys.exit(5)
args = [a for a in sys.argv[1:] if not a.startswith("-") and a not in ("b",)]
fields = sys.argv[sys.argv.index("-o") + 1].split(",")
target = args[-1]
listing = target == st["vg"]
for lv in st["lvs"]:
full = st["vg"] + "/" + lv["name"]
if listing and st.get("hide_pool_in_listing") and lv.get("pool", "") == "":
continue # a listing that silently lost the pool row (the pool's own size stays readable)
if listing or target == full:
row = {"lv_name": lv["name"], "pool_lv": lv.get("pool", ""), "lv_size": str(lv["size"])}
print(" " + " ".join(row[f] for f in fields))
'''
STUB_LVCREATE = r'''#!/usr/bin/env python3
import json, os, sys
p = os.environ["IP_STATE"]; st = json.load(open(p))
a = sys.argv[1:]
size = int(a[a.index("-V") + 1].rstrip("G")) * (1 << 30)
vg, pool = a[a.index("-T") + 1].split("/")
st["lvs"].append({"name": a[a.index("-n") + 1], "pool": pool, "size": size, "active": True})
st.setdefault("calls", []).append("lvcreate " + " ".join(a))
if st.get("dirty_new"): st["fs"]["felhom-belso"] = {"TYPE": "", "RAW": "junk"}
json.dump(st, open(p, "w"))
'''
STUB_BLKID = r'''#!/usr/bin/env python3
# like blkid -p: exit 0 when something is found, 2 when nothing is; st["blkid_rc"] forces an error exit
import json, os, sys
st = json.load(open(os.environ["IP_STATE"]))
if st.get("blkid_rc"): sys.exit(st["blkid_rc"])
fs = st["fs"].get(os.path.basename(sys.argv[-1]), {})
if "-s" in sys.argv:
v = fs.get(sys.argv[sys.argv.index("-s") + 1], "")
if v: print(v)
sys.exit(0 if v else 2)
vals = [fs.get(k, "") for k in ("TYPE", "LABEL", "UUID", "RAW") if fs.get(k)]
if vals: print("\n".join(vals))
sys.exit(0 if vals else 2)
'''
STUB_MKFS = r'''#!/usr/bin/env python3
import json, os, sys
p = os.environ["IP_STATE"]; st = json.load(open(p))
dev = os.path.basename(sys.argv[-1])
st["fs"][dev] = {"TYPE": "ext4", "LABEL": sys.argv[sys.argv.index("-L") + 1], "UUID": "0b6e4c5a-1d2e-4f3a-9b8c-7d6e5f4a3b2c"}
st.setdefault("calls", []).append("mkfs.ext4 " + " ".join(sys.argv[1:]))
json.dump(st, open(p, "w"))
'''
STUB_STAT = r'''#!/usr/bin/env python3
import json, os, sys
st = json.load(open(os.environ["IP_STATE"]))
name = os.path.basename(sys.argv[-1])
if any(lv["name"] == name and lv.get("active", True) for lv in st["lvs"]): print("block special file")
else: sys.exit(1)
'''
CFG = "dir: local\n\tpath /var/lib/vz\n\tcontent iso,backup\n\nlvmthin: local-lvm\n\tthinpool data\n\tvgname pve\n\tcontent rootdir,images\n"
class Box:
def __init__(self, pool_gib, lvs=(), fs=None, cfg=CFG, **extra):
self.t = tempfile.TemporaryDirectory()
r = pathlib.Path(self.t.name)
self.bin = r / "bin"; self.bin.mkdir()
for name, body in (("lvs", STUB_LVS), ("lvcreate", STUB_LVCREATE), ("blkid", STUB_BLKID),
("mkfs.ext4", STUB_MKFS), ("stat", STUB_STAT)):
f = self.bin / name; f.write_text(body); f.chmod(0o755)
self.cfg = r / "storage.cfg"; self.cfg.write_text(cfg)
self.state = r / "state.json"
st = {"vg": "pve", "lvs": [{"name": "data", "pool": "", "size": pool_gib * GIB}] + [dict(l) for l in lvs],
"fs": fs or {}}
st.update(extra)
self.state.write_text(json.dumps(st))
def run(self, *args):
env = {"PATH": f"{self.bin}:/usr/bin:/bin", "IP_STATE": str(self.state), "FELHOM_STORAGE_CFG": str(self.cfg)}
p = subprocess.run(["bash", str(HELPER), *args], env=env, capture_output=True, text=True)
return p.returncode, p.stdout.strip(), p.stderr.strip()
def st(self):
return json.loads(self.state.read_text())
def calls(self):
return self.st().get("calls", [])
def close(self):
self.t.cleanup()
def thin(name, gib, pool="data"):
return {"name": name, "pool": pool, "size": gib * GIB}
class Sizes(unittest.TestCase):
"""The operator's table (SPACE-CHECK §4, ruled 2026-10-16), as the helper computes it on a fresh box."""
def plan(self, pool, planned, lvs=()):
b = Box(pool, lvs)
try:
rc, out, err = b.run("plan", str(planned))
self.assertEqual(rc, 0, err)
self.assertEqual(b.calls(), [], "plan must change nothing")
return dict(kv.split("=") for kv in out.split()[1:])
finally:
b.close()
def test_the_ruled_table(self):
# disk → pool (GiB), data volume (planned = 32 + mp0), expected internal
for pool, mp0, reserve, internal in ((140, 50, 16, 42), (349, 100, 34, 183), (800, 130, 48, 590), (1720, 130, 48, 1510)):
got = self.plan(pool, 32 + mp0)
self.assertEqual((int(got["reserve"]), int(got["internal"])), (reserve, internal), (pool, got))
def test_promised_volumes_count_but_vzdump_snapshots_and_other_pools_do_not(self):
got = self.plan(349, 0, [thin("vm-9201-disk-0", 32), thin("vm-9201-disk-1", 250),
thin("snap_vm-9201-disk-1_vzdump", 250), thin("vm-500-disk-0", 40, pool="other")])
self.assertEqual((int(got["promised"]), int(got["internal"])), (282, 33), got)
class Create(unittest.TestCase):
def test_a_fresh_box_gets_the_place(self):
b = Box(349)
try:
rc, out, err = b.run("create", "132")
self.assertEqual(rc, 0, err)
self.assertRegex(out, r"^created uuid=[0-9a-f-]{36} gib=183$")
self.assertEqual(len([c for c in b.calls() if c.startswith("lvcreate")]), 1)
self.assertIn("-V 183G -T pve/data -n felhom-belso", b.calls()[0])
self.assertIn("-L felhom-belso", b.calls()[1])
finally:
b.close()
def test_too_little_space_makes_nothing(self):
b = Box(53) # demo-hp's 128 GB disk
try:
rc, out, err = b.run("create", str(32 + 50))
self.assertEqual(rc, 0, err)
self.assertTrue(out.startswith("skipped "), out)
self.assertEqual(b.calls(), [])
finally:
b.close()
def test_an_existing_place_is_found_never_formatted(self):
fs = {"felhom-belso": {"TYPE": "ext4", "LABEL": "felhom-belso", "UUID": "11111111-2222-3333-4444-555555555555"}}
b = Box(349, [thin("felhom-belso", 183)], fs)
try:
rc, out, err = b.run("create", "0")
self.assertEqual(rc, 0, err)
self.assertEqual(out, "existing uuid=11111111-2222-3333-4444-555555555555 gib=183")
self.assertEqual(b.calls(), [], "an existing place must never be created or formatted again")
finally:
b.close()
def test_a_foreign_filesystem_on_the_name_is_refused(self):
# positive control: test_an_existing_place_is_found_never_formatted (same LV, Felhom's label) goes through
for fs in ({"TYPE": "xfs", "LABEL": "felhom-belso", "UUID": "x"}, {"TYPE": "ext4", "LABEL": "films", "UUID": "y"}):
b = Box(349, [thin("felhom-belso", 183)], {"felhom-belso": fs})
try:
rc, out, err = b.run("create", "0")
self.assertEqual(rc, 1, (fs, out))
self.assertIn("REFUSED", err)
self.assertEqual(b.calls(), [], fs)
finally:
b.close()
def test_a_crash_between_lvcreate_and_mkfs_is_finished(self):
b = Box(349, [thin("felhom-belso", 183)], {})
try:
rc, out, err = b.run("create", "0")
self.assertEqual(rc, 0, err)
self.assertRegex(out, r"^formatted uuid=")
self.assertEqual([c.split()[0] for c in b.calls()], ["mkfs.ext4"])
finally:
b.close()
def test_an_unnamed_signature_is_refused(self):
b = Box(349, [thin("felhom-belso", 183)], {"felhom-belso": {"RAW": "something"}})
try:
rc, out, err = b.run("create", "0")
self.assertEqual(rc, 1, out)
self.assertEqual(b.calls(), [])
finally:
b.close()
def test_a_new_volume_that_is_not_empty_is_not_formatted(self):
b = Box(349, dirty_new=True)
try:
rc, out, err = b.run("create", "132")
self.assertEqual(rc, 1, out)
self.assertNotIn("mkfs", " ".join(b.calls()))
finally:
b.close()
def test_bad_arguments_and_no_local_lvm_are_refused(self):
b = Box(349, cfg="dir: local\n\tpath /var/lib/vz\n")
try:
self.assertEqual(b.run("create", "0")[0], 1)
self.assertEqual(b.calls(), [])
finally:
b.close()
b = Box(349)
try:
for args in (("create", "-1"), ("create", "1x"), ("wipe", "0"), ("create",)):
self.assertEqual(b.run(*args)[0], 1, args)
self.assertEqual(b.calls(), [])
finally:
b.close()
class FailsClosed(unittest.TestCase):
"""A read that fails must never read as „empty" or „nothing promised". Positive control: Create (same setups)."""
def test_a_failed_volume_listing_creates_nothing(self):
b = Box(349, lvs_fail_listing=True)
try:
rc, out, err = b.run("create", "132")
self.assertEqual(rc, 1, out)
self.assertEqual(b.calls(), [])
finally:
b.close()
def test_a_listing_without_the_pool_creates_nothing(self):
b = Box(349, [thin("vm-9201-disk-1", 250)], hide_pool_in_listing=True)
try:
self.assertEqual(b.run("create", "132")[0], 1)
self.assertEqual(b.calls(), [])
finally:
b.close()
def test_a_blkid_error_never_reads_as_empty(self):
for setup in ({"lvs": [thin("felhom-belso", 183)], "fs": {}}, {}):
b = Box(349, **({"lvs": setup["lvs"], "fs": setup["fs"]} if setup else {}), blkid_rc=4)
try:
rc, out, err = b.run("create", "0" if setup else "132")
self.assertEqual(rc, 1, (setup, out))
self.assertNotIn("mkfs", " ".join(b.calls()), setup)
finally:
b.close()
if __name__ == "__main__":
unittest.main()