From b2243a85f758e1c27e2d8e7c39aea0bce7ea8e61 Mon Sep 17 00:00:00 2001 From: xavierk Date: Thu, 3 Sep 2026 10:18:04 +0530 Subject: [PATCH] fix(packaging): add RPM ownership assertions, ghost group fix, and conffile check for #46 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix nfpm.yaml ghost directory to include `group: fenris` so RPM metadata matches the tmpfiles.d-created ownership (root:fenris 2750) - Add RPM-native ownership assertions: store dir reported as package-owned via `rpm -qf`, store contents verified as never owned by the package - Add RPM conffile assertion: `rpm -qc` verifies fenris.conf is listed - Unify store dir stat assertion across both formats (deb and rpm both assert mode 2750 root:fenris) - Remove unused `distro` parameter from `_find_package()` All 16 packaging tests pass across the full matrix (3 deb + 1 rpm × 4 scenarios). All 289 non-packaging tests pass. Co-authored-by: CommandCodeBot --- packaging/nfpm.yaml | 1 + tests/test_packaging.py | 48 ++++++++++++++++++++++++++++++----------- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/packaging/nfpm.yaml b/packaging/nfpm.yaml index c6f5e02..756e371 100644 --- a/packaging/nfpm.yaml +++ b/packaging/nfpm.yaml @@ -39,6 +39,7 @@ contents: type: ghost file_info: mode: 2750 + group: fenris scripts: preinstall: packaging/preinst.sh diff --git a/tests/test_packaging.py b/tests/test_packaging.py index ad690ca..65b50b8 100644 --- a/tests/test_packaging.py +++ b/tests/test_packaging.py @@ -55,7 +55,7 @@ def _container_exec(container: str, cmd: str) -> tuple[int, str]: return r.returncode, r.stdout + r.stderr -def _find_package(fmt: str, distro: str | None = None) -> Path: +def _find_package(fmt: str) -> Path: """Locate the built package artifact.""" version = _get_version() if fmt == "deb": @@ -184,22 +184,46 @@ def _assert_dormant_layout(container: str, fmt: str, version: str) -> None: assert "device" in out.lower() or "devices" in out.lower() or "#" in out, \ "Config does not appear to be placeholder-commented" + if fmt == "rpm": + # rpm-native: config marked noreplace (not replaced on upgrade) + rc, out = _container_exec( + container, + "rpm -qc fenris 2>/dev/null | grep fenris.conf || true", + ) + assert "fenris.conf" in out, "fenris.conf not listed as conffile by RPM" + # fenris group exists rc, out = _container_exec(container, "getent group fenris") assert rc == 0, "fenris group not created" # Observation store directory - if fmt == "deb": - rc, out = _container_exec(container, "stat -c '%a %U %G' /var/lib/fenris") - assert rc == 0, "Observation store directory not created" - parts = out.strip().split() - assert parts[0] == "2750", f"Store dir mode: expected 2750, got {parts[0]}" - assert parts[1] == "root", f"Store dir owner: expected root, got {parts[1]}" - assert parts[2] == "fenris", f"Store dir group: expected fenris, got {parts[2]}" - else: - # rpm: directory owned by package (ghost) - rc, _ = _container_exec(container, "test -d /var/lib/fenris") - assert rc == 0, "Observation store directory not found" + rc, out = _container_exec(container, "stat -c '%a %U %G' /var/lib/fenris") + assert rc == 0, "Observation store directory not created" + parts = out.strip().split() + assert parts[0] == "2750", f"Store dir mode: expected 2750, got {parts[0]}" + assert parts[1] == "root", f"Store dir owner: expected root, got {parts[1]}" + assert parts[2] == "fenris", f"Store dir group: expected fenris, got {parts[2]}" + + if fmt == "rpm": + # rpm-native: store dir reported as package-owned (ghost), + # but no contents are owned by the package + rc, out = _container_exec(container, "rpm -qf /var/lib/fenris 2>/dev/null") + assert rc == 0, "Store dir not reported as package-owned by RPM" + assert "fenris" in out.lower(), f"Store dir not owned by fenris package: {out}" + + # No files inside the store should be package-owned + rc, out = _container_exec( + container, + "find /var/lib/fenris -mindepth 1 -type f -exec rpm -qf {} \\; 2>&1", + ) + # rpm -qf exits 1 for unowned files; we expect all to be unowned + owned = [ + line for line in out.strip().splitlines() + if not line.startswith("not owned by any package") + and "is not owned by any package" not in line + and rc == 0 + ] + assert not owned, f"Store contents owned by RPM (should not be): {owned}" # Timer is disabled and inactive (dormant) rc, out = _container_exec(