Audit ----- - create_task.py verify failed on a DEFAULT install. hook_status.json emitted a per-file entry for the nested module with ok:false whenever the feature was switched off -- the default -- so verify printed [FAIL] and exited 1, right after the README tells users to run it. Status is now per MODULE with an `active` flag, and verify renders an inactive module as [SKIP]. - A partial apply suppressed the middlewared restart. The exit code conflated "nothing applied" with "one module applied, one failed", so a failing providers patch would prevent the restart that a freshly-applied nested patch needs, leaving it on disk and never loaded. Exit 2 now means partial and the restart still fires. - The native-nested probe could never fire. It scanned crud.py for the guard message, but our own injected block quotes that message, so once applied the probe would always conclude the guard was still present. It now reads only the stock portion of the file. - recover.sh did not unmount staging trees, so an emergency recovery left bind mounts pinning ZFS snapshots that could then never be destroyed. - uninstall.sh deleted sidecar files without reading them. A sidecar is the only record that an interrupted run's snapshot tree is still on disk; both scripts now name the snapshot before clearing it. - Removed a dead branch in the restart gate (unreachable: the kill switch exits). Refactor -------- - Staging teardown had been copy-pasted into uninstall.sh and recover.sh -- two untested shell copies of the fiddly depth-ordering and lazy-umount logic. Both now call `python3 patch/truecloud_nested.py cleanup`, so there is exactly one implementation and it is the one under test. - Dropped the in-memory ACTIVE dict. The sidecar file was already the source of truth; a second in-process record could only desync -- and it is precisely the middlewared-restart case (which empties it) that must not orphan a snapshot tree. One record, on disk, or none. Not done: the overlay-unmount loop is duplicated across apply.sh/uninstall.sh/ recover.sh. It is pre-existing, and apply.sh runs at PREINIT under a tight timeout -- giving it a source dependency would trade 10 lines of duplication for a boot-time failure mode. 74 tests, ruff and shellcheck clean.
86 lines
3.0 KiB
Bash
Executable File
86 lines
3.0 KiB
Bash
Executable File
#!/bin/bash
|
|
# recover.sh — emergency recovery if middlewared won't start after installing truecloud-patch.
|
|
#
|
|
# Run this from the TrueNAS shell (local console, SSH, or debug shell):
|
|
#
|
|
# bash /mnt/tank/truenas-truecloud-patch/recover.sh
|
|
#
|
|
# What it does:
|
|
# 1. Creates a "disabled" file in the repo root — apply.sh checks for this
|
|
# file at boot and skips all patching, so the next boot is always clean.
|
|
# 2. Unmounts any active truecloud overlays so the original /usr files are
|
|
# visible immediately (no reboot required).
|
|
# 3. Restarts middlewared against the unpatched files.
|
|
#
|
|
# To re-enable the patch after investigating:
|
|
# rm /mnt/tank/truenas-truecloud-patch/disabled
|
|
# bash /mnt/tank/truenas-truecloud-patch/patch/apply.sh
|
|
# systemctl restart middlewared
|
|
|
|
VERSION="0.3.0"
|
|
|
|
PATCH_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
|
|
|
|
echo "=== TrueNAS TrueCloud Provider Patch v${VERSION} — Recover ==="
|
|
echo ""
|
|
|
|
if [ "$(id -u)" -ne 0 ]; then
|
|
echo "ERROR: must be run as root." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [ ! -d "$PATCH_DIR" ]; then
|
|
echo "ERROR: $PATCH_DIR not found — truecloud-patch may not be installed." >&2
|
|
exit 1
|
|
fi
|
|
|
|
touch "$PATCH_DIR/disabled"
|
|
echo "Kill switch set: $PATCH_DIR/disabled created."
|
|
|
|
echo "Unmounting truecloud overlays ..."
|
|
_any=0
|
|
for _tag in mw ui; do
|
|
if mount | grep -qF "truecloud-${_tag} on "; then
|
|
_mnt=$(mount | grep "truecloud-${_tag} on " | awk '{print $3}' | head -1)
|
|
if umount "$_mnt" 2>/dev/null; then
|
|
echo " Unmounted: $_mnt"
|
|
_any=1
|
|
else
|
|
echo " WARNING: Could not unmount $_mnt — a reboot will restore original files."
|
|
fi
|
|
fi
|
|
done
|
|
[ "$_any" -eq 0 ] && echo " No overlays active."
|
|
|
|
# Nested-snapshot staging trees are bind mounts that PIN their ZFS snapshots, so
|
|
# leaving them mounted blocks those snapshots from ever being destroyed. The
|
|
# overlays above are volatile, but these are not self-healing without a reboot,
|
|
# and recover.sh is expected to work without one.
|
|
echo "Unmounting nested-snapshot staging trees ..."
|
|
# Best-effort: never block recovery. Same tested implementation as uninstall.sh.
|
|
python3 "$PATCH_DIR/patch/truecloud_nested.py" cleanup || true
|
|
|
|
# Cancel a deferred boot restart if one is still queued — we restart ourselves.
|
|
systemctl stop truecloud-mw-restart.service 2>/dev/null
|
|
systemctl reset-failed truecloud-mw-restart.service 2>/dev/null
|
|
|
|
echo "Restarting middlewared ..."
|
|
if systemctl restart middlewared; then
|
|
echo ""
|
|
echo "middlewared started successfully."
|
|
echo "Your system is back to normal (Storj-only TrueCloud Backup)."
|
|
else
|
|
echo ""
|
|
echo "WARNING: middlewared did not start cleanly even with the patch disabled."
|
|
echo "The problem is unrelated to truecloud-patch."
|
|
echo "Check the system log for details:"
|
|
echo " journalctl -u middlewared -n 50"
|
|
exit 1
|
|
fi
|
|
echo ""
|
|
echo "To re-enable the patch once you have investigated:"
|
|
echo " rm $PATCH_DIR/disabled"
|
|
echo " bash $PATCH_DIR/patch/apply.sh"
|
|
echo " systemctl restart middlewared"
|