refactor(media): mount the view inside /var/lib/media-meta instead of under /mnt
The merged door was /mnt/media, which read like a third volume and split the host state across two trees. It is now /var/lib/media-meta/view, so the array layer, the SSD upper layer, the kernel workdir, the reap trash and the merged tree are one directory — and the mount table gains no new /mnt entry beside the array. systemd names a mount unit after its ESCAPED path, so the repo copy keeps the readable name media-meta-view.mount while `install` writes the host file as var-lib-media\x2dmeta-view.mount; `plan` prints both and the page records them, so nobody hunts for a unit that "does not exist". install also creates the mount point itself, because systemd will not. The seam now additionally pins that the unit's Where= is exactly the path both containers bind — guard and bind cannot drift apart. The Quadlet Lifecycle phantom-path example is corrected too: it named an invented /mnt/media/*, while the real phantom is the legacy /opt/jellyfin/* that dankserver/compose.yaml still carries for a service whose roots moved to /home/dank/Containers/jellyfin.
This commit is contained in:
1 parent
9706f5d2de
commit
3b29e173c2
13 files changed
+93
-67
No files matched your search
+21
-14
@@ -18,7 +18,8 @@ load helpers
|
||||
VIEW="$REPO_ROOT/scripts/media-view.sh"
|
||||
JELLY="$REPO_ROOT/dankserver/jellyfin/quadlet/jellyfin.container"
|
||||
PLEX="$REPO_ROOT/dankserver/plex/quadlet/plex.container"
|
||||
UNIT="$REPO_ROOT/dankserver/host/systemd/mnt-media.mount"
|
||||
MOUNT=/var/lib/media-meta/view
|
||||
UNIT="$REPO_ROOT/dankserver/host/systemd/media-meta-view.mount"
|
||||
|
||||
@test "the plan names an overlay with the spindle below and the SSD above" {
|
||||
run bash "$VIEW" plan
|
||||
@@ -28,8 +29,10 @@ UNIT="$REPO_ROOT/dankserver/host/systemd/mnt-media.mount"
|
||||
[ "${lines[0]}" = "lowerdir=/mnt/entropy" ]
|
||||
printf '%s\n' "${lines[@]}" | grep -qx 'upperdir=/var/lib/media-meta/upper'
|
||||
printf '%s\n' "${lines[@]}" | grep -qx 'workdir=/var/lib/media-meta/work'
|
||||
printf '%s\n' "${lines[@]}" | grep -qx 'mountpoint=/mnt/media'
|
||||
printf '%s\n' "${lines[@]}" | grep -qx 'unit=/etc/systemd/system/mnt-media.mount'
|
||||
printf '%s\n' "${lines[@]}" | grep -Fqx "mountpoint=$MOUNT"
|
||||
# systemd names a mount unit after its escaped path, backslash and all.
|
||||
printf '%s\n' "${lines[@]}" | grep -Fqx \
|
||||
'unit=/etc/systemd/system/var-lib-media\x2dmeta-view.mount'
|
||||
}
|
||||
|
||||
@test "install refuses a caller who cannot write the unit directory or mount" {
|
||||
@@ -44,33 +47,33 @@ UNIT="$REPO_ROOT/dankserver/host/systemd/mnt-media.mount"
|
||||
}
|
||||
|
||||
@test "verify refuses a view that is not mounted" {
|
||||
# Every consumer trusts /mnt/media to BE the library. On a host where it is a
|
||||
# plain empty directory, verify must fail loudly: that is the difference
|
||||
# Every consumer trusts the mount point to BE the library. On a host where it
|
||||
# is a plain empty directory, verify must fail loudly: that is the difference
|
||||
# between a scan that finds nothing and a scan that empties a library DB.
|
||||
if mountpoint -q /mnt/media; then
|
||||
skip "/mnt/media is a real mountpoint on this host"
|
||||
if mountpoint -q "$MOUNT"; then
|
||||
skip "$MOUNT is a real mountpoint on this host"
|
||||
fi
|
||||
run bash "$VIEW" verify
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"/mnt/media"* ]]
|
||||
[[ "$output" == *"$MOUNT"* ]]
|
||||
}
|
||||
|
||||
@test "both media servers bind the view and refuse to start without it" {
|
||||
for unit in "$JELLY" "$PLEX"; do
|
||||
grep -q '^Volume=/mnt/media:' "$unit" || return 1
|
||||
grep -q '^UnitConfigItem=ConditionPathIsMountPoint=/mnt/media$' "$unit" || return 1
|
||||
grep -q "^Volume=$MOUNT:" "$unit" || return 1
|
||||
grep -q "^UnitConfigItem=ConditionPathIsMountPoint=$MOUNT$" "$unit" || return 1
|
||||
# The raw array must no longer be a container source: that bind is what
|
||||
# made sidecar sharing impossible (read-only) and metadata slow (seeks).
|
||||
! grep -q '^Volume=/mnt/entropy' "$unit"
|
||||
done
|
||||
# Jellyfin is the writer, so it needs the view writable; Plex only reads the
|
||||
# sidecars, and keeping its bind read-only costs nothing.
|
||||
grep -q '^Volume=/mnt/media:/data$' "$JELLY"
|
||||
grep -q '^Volume=/mnt/media:/mnt/entropy:ro$' "$PLEX"
|
||||
grep -q "^Volume=$MOUNT:/data$" "$JELLY"
|
||||
grep -q "^Volume=$MOUNT:/mnt/entropy:ro$" "$PLEX"
|
||||
}
|
||||
|
||||
@test "only the two media units reference the view" {
|
||||
run grep -rl --include='*.container' '/mnt/media' "$REPO_ROOT"/dankserver \
|
||||
run grep -rl --include='*.container' "$MOUNT" "$REPO_ROOT"/dankserver \
|
||||
"$REPO_ROOT"/dankclaw "$REPO_ROOT"/frankbook-pro 2>/dev/null
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$(printf '%s\n' "$output" | wc -l)" -eq 2 ]
|
||||
@@ -82,6 +85,10 @@ UNIT="$REPO_ROOT/dankserver/host/systemd/mnt-media.mount"
|
||||
[ -f "$UNIT" ]
|
||||
run grep -E '^Options=lowerdir=/mnt/entropy,upperdir=/var/lib/media-meta/upper,workdir=/var/lib/media-meta/work' "$UNIT"
|
||||
[ "$status" -eq 0 ]
|
||||
# The unit must mount exactly where the two containers bind, or the guard and
|
||||
# the bind disagree and a media server browses an empty directory.
|
||||
run grep -Fqx "Where=$MOUNT" "$UNIT"
|
||||
[ "$status" -eq 0 ]
|
||||
# It must wait for the array to be unlocked: mounting an overlay whose lower
|
||||
# dir is an empty /mnt/entropy would present an empty library, not an error.
|
||||
run grep -E '^RequiresMountsFor=/mnt/entropy$' "$UNIT"
|
||||
@@ -102,7 +109,7 @@ UNIT="$REPO_ROOT/dankserver/host/systemd/mnt-media.mount"
|
||||
|
||||
@test "the planner routes the view files to both media services" {
|
||||
run python3 "$REPO_ROOT/scripts/deploy-plan.py" --target dankserver --paths \
|
||||
scripts/media-view.sh dankserver/host/systemd/mnt-media.mount
|
||||
scripts/media-view.sh dankserver/host/systemd/media-meta-view.mount
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$output" = "jellyfin
|
||||
plex" ]
|
||||
|
||||
Reference in new issue
Block a user