From 172e574a00be7fd57e04b85b694006e646d5eec0 Mon Sep 17 00:00:00 2001 From: mwiegand Date: Fri, 8 May 2026 12:28:00 +0200 Subject: [PATCH] chore(deploy): drop fuse-overlayfs apt dep + one-shot migrate upper/work MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop fuse-overlayfs / fuse3 from the apt/dnf install line — the new mount path is kernel overlayfs via the left4me-overlay helper, no fuse userspace needed. Add a one-shot migration block gated by /var/lib/left4me/.kernel-overlay-migrated that runs before daemon-reload: stop gameservers + web service, force- unmount any leftover fuse or overlay mounts under runtime/, then wipe and recreate empty upper/ and work/ for every instance. fuse-overlayfs running as a non-root user used user.fuseoverlayfs.* xattrs that kernel overlayfs ignores, so a pre-existing upper/ from the fuse era would resurrect "deleted" files. Co-Authored-By: Claude Opus 4.7 (1M context) --- deploy/deploy-test-server.sh | 20 ++++++++++++++++++-- deploy/tests/test_deploy_artifacts.py | 24 ++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/deploy/deploy-test-server.sh b/deploy/deploy-test-server.sh index 1d4c359..5bc48af 100755 --- a/deploy/deploy-test-server.sh +++ b/deploy/deploy-test-server.sh @@ -79,9 +79,9 @@ fi if command -v apt-get >/dev/null 2>&1; then $sudo_cmd apt-get update - $sudo_cmd apt-get install -y python3 python3-venv python3-pip curl ca-certificates tar gzip fuse-overlayfs fuse3 sudo + $sudo_cmd apt-get install -y python3 python3-venv python3-pip curl ca-certificates tar gzip util-linux sudo elif command -v dnf >/dev/null 2>&1; then - $sudo_cmd dnf install -y python3 python3-pip curl ca-certificates tar gzip fuse-overlayfs fuse3 sudo + $sudo_cmd dnf install -y python3 python3-pip curl ca-certificates tar gzip util-linux sudo else printf 'Unsupported package manager: expected apt-get or dnf\n' >&2 exit 1 @@ -178,6 +178,22 @@ if [ -f "$remote_tmp/admin_username" ] && [ -f "$remote_tmp/admin_password" ]; t fi fi +# One-shot migration: fuse-overlayfs running as the left4me user used +# user.fuseoverlayfs.* xattrs for whiteouts and opaque-dir markers; kernel +# overlayfs ignores those entirely, so a pre-existing upper/ from the fuse +# era would resurrect "deleted" files. Wipe upper/ and work/ for every +# instance once, gated by a sentinel file so reruns are no-ops. +overlay_sentinel=/var/lib/left4me/.kernel-overlay-migrated +if [ ! -e "$overlay_sentinel" ]; then + $sudo_cmd sh -c "systemctl stop 'left4me-server@*.service' 2>/dev/null || true" + $sudo_cmd systemctl stop left4me-web.service 2>/dev/null || true + $sudo_cmd sh -c "findmnt -t fuse.fuse-overlayfs -o TARGET --noheadings 2>/dev/null | xargs -r -n1 umount -l 2>/dev/null || true" + $sudo_cmd sh -c "findmnt -t overlay -o TARGET --noheadings 2>/dev/null | grep '/var/lib/left4me/runtime/' | xargs -r -n1 umount -l 2>/dev/null || true" + $sudo_cmd sh -c 'for d in /var/lib/left4me/runtime/*/; do [ -d "$d" ] || continue; rm -rf "$d/upper" "$d/work"; mkdir -p "$d/upper" "$d/work"; chown left4me:left4me "$d/upper" "$d/work"; done' + $sudo_cmd touch "$overlay_sentinel" + $sudo_cmd chown left4me:left4me "$overlay_sentinel" +fi + $sudo_cmd systemctl daemon-reload $sudo_cmd systemctl enable --now left4me-web.service $sudo_cmd systemctl restart left4me-web.service diff --git a/deploy/tests/test_deploy_artifacts.py b/deploy/tests/test_deploy_artifacts.py index 626bedd..394f84a 100644 --- a/deploy/tests/test_deploy_artifacts.py +++ b/deploy/tests/test_deploy_artifacts.py @@ -179,6 +179,30 @@ def test_deploy_script_installs_overlay_helper_with_executable_mode(): assert "chmod 0755" in script and "left4me-overlay" in script +def test_deploy_script_does_not_install_fuse_overlayfs_apt_dep(): + # fuse-overlayfs / fuse3 were the previous mount engine; kernel overlayfs + # replaces them. Comments in the migration block may legitimately mention + # the names, so scope this to the actual apt-get / dnf install lines. + install_lines = [ + line for line in DEPLOY_SCRIPT.read_text().splitlines() + if ("apt-get install" in line or "dnf install" in line) + ] + assert install_lines, "expected at least one apt/dnf install line" + for line in install_lines: + assert "fuse-overlayfs" not in line, line + assert "fuse3" not in line, line + + +def test_deploy_script_runs_one_shot_kernel_overlay_migration(): + script = DEPLOY_SCRIPT.read_text() + assert "/var/lib/left4me/.kernel-overlay-migrated" in script + # Migration should stop services + force-unmount stale mounts + wipe upper/work + assert "systemctl stop 'left4me-server@" in script + assert "systemctl stop left4me-web.service" in script + assert "findmnt -t overlay" in script + assert "/runtime/" in script and "rm -rf" in script and 'upper"' in script and 'work"' in script + + def test_env_templates_contain_required_defaults(): host_env = HOST_ENV.read_text() assert "Deployment units use fixed /var/lib/left4me paths" in host_env