chore(deploy): drop fuse-overlayfs apt dep + one-shot migrate upper/work

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) <noreply@anthropic.com>
This commit is contained in:
mwiegand 2026-05-08 12:28:00 +02:00
parent 93a60befb6
commit 172e574a00
No known key found for this signature in database
2 changed files with 42 additions and 2 deletions

View file

@ -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

View file

@ -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