Compare commits
2
Commits
8fa86c3bf8
...
b2243a85f7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b2243a85f7 | ||
|
|
f1e1c0eebc |
@@ -39,6 +39,7 @@ contents:
|
|||||||
type: ghost
|
type: ghost
|
||||||
file_info:
|
file_info:
|
||||||
mode: 2750
|
mode: 2750
|
||||||
|
group: fenris
|
||||||
|
|
||||||
scripts:
|
scripts:
|
||||||
preinstall: packaging/preinst.sh
|
preinstall: packaging/preinst.sh
|
||||||
|
|||||||
+61
-27
@@ -55,7 +55,7 @@ def _container_exec(container: str, cmd: str) -> tuple[int, str]:
|
|||||||
return r.returncode, r.stdout + r.stderr
|
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."""
|
"""Locate the built package artifact."""
|
||||||
version = _get_version()
|
version = _get_version()
|
||||||
if fmt == "deb":
|
if fmt == "deb":
|
||||||
@@ -97,8 +97,8 @@ def _build_deb_dockerfile(image: str, pkg_name: str) -> str:
|
|||||||
RUN apt-get update && apt-get install -y --no-install-recommends \\
|
RUN apt-get update && apt-get install -y --no-install-recommends \\
|
||||||
python3 smartmontools systemd systemd-sysv dbus && \\
|
python3 smartmontools systemd systemd-sysv dbus && \\
|
||||||
rm -rf /var/lib/apt/lists/*
|
rm -rf /var/lib/apt/lists/*
|
||||||
COPY dist/{pkg_name} /tmp/{pkg_name}
|
COPY dist/{pkg_name} /pkg/{pkg_name}
|
||||||
RUN dpkg -i /tmp/{pkg_name} || apt-get install -f -y
|
RUN dpkg -i /pkg/{pkg_name} || apt-get install -f -y
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
|
||||||
@@ -109,8 +109,8 @@ def _build_rpm_dockerfile(image: str, pkg_name: str) -> str:
|
|||||||
RUN dnf install -y --setopt=install_weak_deps=False \
|
RUN dnf install -y --setopt=install_weak_deps=False \
|
||||||
python3 smartmontools systemd dbus && \
|
python3 smartmontools systemd dbus && \
|
||||||
dnf clean all
|
dnf clean all
|
||||||
COPY dist/{pkg_name} /tmp/{pkg_name}
|
COPY dist/{pkg_name} /pkg/{pkg_name}
|
||||||
RUN rpm -ivh /tmp/{pkg_name}
|
RUN rpm -ivh /pkg/{pkg_name}
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
|
||||||
@@ -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, \
|
assert "device" in out.lower() or "devices" in out.lower() or "#" in out, \
|
||||||
"Config does not appear to be placeholder-commented"
|
"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
|
# fenris group exists
|
||||||
rc, out = _container_exec(container, "getent group fenris")
|
rc, out = _container_exec(container, "getent group fenris")
|
||||||
assert rc == 0, "fenris group not created"
|
assert rc == 0, "fenris group not created"
|
||||||
|
|
||||||
# Observation store directory
|
# Observation store directory
|
||||||
if fmt == "deb":
|
rc, out = _container_exec(container, "stat -c '%a %U %G' /var/lib/fenris")
|
||||||
rc, out = _container_exec(container, "stat -c '%a %U %G' /var/lib/fenris")
|
assert rc == 0, "Observation store directory not created"
|
||||||
assert rc == 0, "Observation store directory not created"
|
parts = out.strip().split()
|
||||||
parts = out.strip().split()
|
assert parts[0] == "2750", f"Store dir mode: expected 2750, got {parts[0]}"
|
||||||
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[1] == "root", f"Store dir owner: expected root, got {parts[1]}"
|
assert parts[2] == "fenris", f"Store dir group: expected fenris, got {parts[2]}"
|
||||||
assert parts[2] == "fenris", f"Store dir group: expected fenris, got {parts[2]}"
|
|
||||||
else:
|
if fmt == "rpm":
|
||||||
# rpm: directory owned by package (ghost)
|
# rpm-native: store dir reported as package-owned (ghost),
|
||||||
rc, _ = _container_exec(container, "test -d /var/lib/fenris")
|
# but no contents are owned by the package
|
||||||
assert rc == 0, "Observation store directory not found"
|
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)
|
# Timer is disabled and inactive (dormant)
|
||||||
rc, out = _container_exec(
|
rc, out = _container_exec(
|
||||||
@@ -219,7 +243,7 @@ def _assert_dormant_layout(container: str, fmt: str, version: str) -> None:
|
|||||||
assert rc != 0, "Legacy manifest.txt should not exist in package install"
|
assert rc != 0, "Legacy manifest.txt should not exist in package install"
|
||||||
|
|
||||||
|
|
||||||
def _assert_migration_guard(container: str, fmt: str) -> None:
|
def _assert_migration_guard(container: str, fmt: str, pkg_name: str) -> None:
|
||||||
"""Assert that install aborts on make-install remnants (spec §7, §9)."""
|
"""Assert that install aborts on make-install remnants (spec §7, §9)."""
|
||||||
if fmt == "deb":
|
if fmt == "deb":
|
||||||
# Plant the legacy manifest marker
|
# Plant the legacy manifest marker
|
||||||
@@ -228,7 +252,7 @@ def _assert_migration_guard(container: str, fmt: str) -> None:
|
|||||||
# Attempt install — should fail with migration pointer
|
# Attempt install — should fail with migration pointer
|
||||||
rc, out = _container_exec(
|
rc, out = _container_exec(
|
||||||
container,
|
container,
|
||||||
"dpkg -i /tmp/fenris_0.3.0_amd64.deb 2>&1 || true",
|
f"dpkg -i /pkg/{pkg_name} 2>&1 || true",
|
||||||
)
|
)
|
||||||
assert "remnants" in out.lower() or "migrat" in out.lower() or rc != 0, \
|
assert "remnants" in out.lower() or "migrat" in out.lower() or rc != 0, \
|
||||||
f"Migration guard did not trigger: {out}"
|
f"Migration guard did not trigger: {out}"
|
||||||
@@ -243,7 +267,7 @@ def _assert_migration_guard(container: str, fmt: str) -> None:
|
|||||||
)
|
)
|
||||||
rc, out = _container_exec(
|
rc, out = _container_exec(
|
||||||
container,
|
container,
|
||||||
"rpm -ivh /tmp/fenris-0.3.0-1.x86_64.rpm 2>&1 || true",
|
f"rpm -ivh /pkg/{pkg_name} 2>&1 || true",
|
||||||
)
|
)
|
||||||
assert "remnants" in out.lower() or "migrat" in out.lower() or rc != 0, \
|
assert "remnants" in out.lower() or "migrat" in out.lower() or rc != 0, \
|
||||||
f"Migration guard did not trigger: {out}"
|
f"Migration guard did not trigger: {out}"
|
||||||
@@ -253,7 +277,7 @@ def _assert_migration_guard(container: str, fmt: str) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _assert_upgrade_semantics(container: str, fmt: str) -> None:
|
def _assert_upgrade_semantics(container: str, fmt: str, pkg_name: str, version: str) -> None:
|
||||||
"""Assert upgrade behavior (spec §7, ADR 0007 §6)."""
|
"""Assert upgrade behavior (spec §7, ADR 0007 §6)."""
|
||||||
# Create a fake observation store using system Python
|
# Create a fake observation store using system Python
|
||||||
# (venv Python may not execute if host shared libs differ)
|
# (venv Python may not execute if host shared libs differ)
|
||||||
@@ -268,16 +292,26 @@ def _assert_upgrade_semantics(container: str, fmt: str) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
if fmt == "deb":
|
if fmt == "deb":
|
||||||
|
# Fake older version so dpkg treats reinstall as upgrade
|
||||||
|
_container_exec(
|
||||||
|
container,
|
||||||
|
f"sed -i 's/^Version: {version}$/Version: 0.2.0/' /var/lib/dpkg/status",
|
||||||
|
)
|
||||||
# Re-install triggers upgrade path
|
# Re-install triggers upgrade path
|
||||||
rc, out = _container_exec(
|
rc, out = _container_exec(
|
||||||
container,
|
container,
|
||||||
"dpkg --force-confnew -i /tmp/fenris_*.deb 2>&1 || "
|
f"dpkg --force-confnew -i /pkg/{pkg_name} 2>&1 || "
|
||||||
"apt-get install -f -y 2>&1 || true",
|
"apt-get install -f -y 2>&1 || true",
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
rc, out = _container_exec(
|
# RPM: manually invoke the post scriptlet with upgrade arguments ($1=2)
|
||||||
|
# because faking a different version in the binary RPM database is not
|
||||||
|
# practical — we only need to verify the scriptlet's upgrade behaviour.
|
||||||
|
_container_exec(
|
||||||
container,
|
container,
|
||||||
"rpm -Uvh /tmp/fenris-*.rpm 2>&1 || true",
|
f"rpm -q --scripts -p /pkg/{pkg_name} "
|
||||||
|
"| sed -n '/^postinstall scriptlet/,/^preuninstall/"
|
||||||
|
"{/^preuninstall/d;/^postinstall scriptlet/d;p}' | sh -s 2 2",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Snapshot should exist
|
# Snapshot should exist
|
||||||
@@ -408,7 +442,7 @@ def test_migration_guard(skip_no_docker, image, fmt, version):
|
|||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
python3 python3-minimal smartmontools systemd systemd-sysv dbus && \
|
python3 python3-minimal smartmontools systemd systemd-sysv dbus && \
|
||||||
rm -rf /var/lib/apt/lists/*
|
rm -rf /var/lib/apt/lists/*
|
||||||
COPY dist/{pkg_name} /tmp/{pkg_name}
|
COPY dist/{pkg_name} /pkg/{pkg_name}
|
||||||
# Plant make-install remnant BEFORE installing
|
# Plant make-install remnant BEFORE installing
|
||||||
RUN mkdir -p /var/lib/fenris && echo '# manifest' > /var/lib/fenris/manifest.txt
|
RUN mkdir -p /var/lib/fenris && echo '# manifest' > /var/lib/fenris/manifest.txt
|
||||||
""")
|
""")
|
||||||
@@ -418,7 +452,7 @@ def test_migration_guard(skip_no_docker, image, fmt, version):
|
|||||||
RUN dnf install -y --setopt=install_weak_deps=False \
|
RUN dnf install -y --setopt=install_weak_deps=False \
|
||||||
python3 smartmontools systemd dbus && \
|
python3 smartmontools systemd dbus && \
|
||||||
dnf clean all
|
dnf clean all
|
||||||
COPY dist/{pkg_name} /tmp/{pkg_name}
|
COPY dist/{pkg_name} /pkg/{pkg_name}
|
||||||
RUN mkdir -p /etc/systemd/system && \\
|
RUN mkdir -p /etc/systemd/system && \\
|
||||||
echo '[Unit]' > /etc/systemd/system/fenris-collect.timer
|
echo '[Unit]' > /etc/systemd/system/fenris-collect.timer
|
||||||
""")
|
""")
|
||||||
@@ -444,7 +478,7 @@ def test_migration_guard(skip_no_docker, image, fmt, version):
|
|||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
_assert_migration_guard(container, fmt)
|
_assert_migration_guard(container, fmt, pkg_name)
|
||||||
finally:
|
finally:
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
["docker", "rm", "-f", container],
|
["docker", "rm", "-f", container],
|
||||||
@@ -497,7 +531,7 @@ def test_upgrade_semantics(skip_no_docker, image, fmt, version):
|
|||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
_assert_upgrade_semantics(container, fmt)
|
_assert_upgrade_semantics(container, fmt, pkg_name, version)
|
||||||
finally:
|
finally:
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
["docker", "rm", "-f", container],
|
["docker", "rm", "-f", container],
|
||||||
|
|||||||
Reference in New Issue
Block a user