Switch to overlay-only patching; remove sitecustomize.py
Patches to b2.py and restic.py are applied directly in the overlayfs at PREINIT boot time. The sitecustomize.py import hook was belt-and-suspenders that succeeded or failed alongside the file patch every time, providing no genuine fallback. - Delete patch/sitecustomize.py entirely - apply.sh: remove sitecustomize install step; flatten if/elif/else structure; restore self-contained URL-fix logic in the restic.py BLOCK; rename overlay tag 'sc' -> 'mw' - recover.sh: unmount overlays to restore original files immediately, no reboot required; kill-switch file prevents re-application on next boot - uninstall.sh: remove sitecustomize.py removal section; update overlay tag - install.sh: update preflight to check patch/apply.sh, not sitecustomize.py - README: remove sitecustomize references throughout; update recovery docs
This commit is contained in:
+42
-71
@@ -7,7 +7,6 @@
|
||||
# TrueNAS updates replace /usr/ entirely; this script re-applies two patches:
|
||||
#
|
||||
# 1. Backend — b2.py and restic.py are patched directly in the overlay.
|
||||
# sitecustomize.py is also installed as belt-and-suspenders.
|
||||
#
|
||||
# 2. Angular JS bundle — Widens the TrueCloud Backup credential dropdown
|
||||
# from Storj-only to include S3 and B2.
|
||||
@@ -39,10 +38,9 @@ if [ -f "$PATCH_DIR/disabled" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# On TrueNAS 25.x+, /usr is an immutable read-only filesystem.
|
||||
# This function mounts a writable overlayfs on $1 using /run (tmpfs) for the
|
||||
# upper/work dirs. The overlay is volatile per boot; this PREINIT script
|
||||
# recreates it on every boot before middlewared starts.
|
||||
# Mounts a writable overlayfs on $1 using /run (tmpfs) for the upper/work dirs
|
||||
# when the directory is read-only. The overlay is volatile per boot; this
|
||||
# PREINIT script recreates it on every boot before middlewared starts.
|
||||
# Returns 0 if the directory is now writable, 1 if it could not be made so.
|
||||
_ensure_writable() {
|
||||
local dir="$1" tag="$2"
|
||||
@@ -90,7 +88,7 @@ find_mw_python() {
|
||||
echo "WARNING: '$py' cannot import middlewared; falling back to python3" >&2
|
||||
py="python3"
|
||||
if ! "$py" -c "import middlewared" 2>/dev/null; then
|
||||
echo "WARNING: 'python3' also cannot import middlewared; sitecustomize.py may be installed in the wrong location" >&2
|
||||
echo "WARNING: 'python3' also cannot import middlewared; backend patch will be skipped" >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -105,10 +103,8 @@ PYTHON=$(find_mw_python)
|
||||
echo "Using Python: $PYTHON"
|
||||
|
||||
# Derive site-packages from where middlewared actually lives.
|
||||
# On TrueNAS 25.x, middlewared is in /usr/lib/python3/dist-packages/, while
|
||||
# getsitepackages()[0] typically returns /usr/local/lib/python3.11/dist-packages/
|
||||
# — the wrong directory. Using middlewared.__file__ ensures we install
|
||||
# sitecustomize.py and patch files in the directory Python will actually read.
|
||||
# getsitepackages()[0] may return the wrong directory; using middlewared.__file__
|
||||
# ensures we patch files in the directory Python will actually read.
|
||||
SITE_PKG=$("$PYTHON" -c "
|
||||
import os
|
||||
try:
|
||||
@@ -137,54 +133,13 @@ _restic_ok=0
|
||||
if [ -z "$SITE_PKG" ]; then
|
||||
echo "WARNING: Cannot determine site-packages directory; skipping backend patch."
|
||||
echo " Run: $PYTHON -c \"import site; print(site.getsitepackages())\""
|
||||
elif ! _ensure_writable "$SITE_PKG" "mw"; then
|
||||
echo "WARNING: Cannot make site-packages writable; skipping backend patch."
|
||||
elif [ -z "$_MW_DIR" ]; then
|
||||
echo "WARNING: Cannot determine middlewared directory; skipping backend patch."
|
||||
else
|
||||
_can_install=true
|
||||
# On immutable OS, ensure site-packages is writable via overlay before
|
||||
# attempting any writes.
|
||||
if ! _ensure_writable "$SITE_PKG" "sc"; then
|
||||
_can_install=false
|
||||
fi
|
||||
|
||||
# Back up any pre-existing sitecustomize.py that isn't ours.
|
||||
if [ "$_can_install" = true ] && \
|
||||
[ -f "$SITE_PKG/sitecustomize.py" ] && \
|
||||
! grep -q "truecloud-patch" "$SITE_PKG/sitecustomize.py" 2>/dev/null; then
|
||||
if cp "$SITE_PKG/sitecustomize.py" \
|
||||
"$SITE_PKG/sitecustomize.py.pre-truecloud-patch"; then
|
||||
echo "OK: Backed up existing sitecustomize.py"
|
||||
else
|
||||
echo "WARNING: Could not back up existing sitecustomize.py; skipping install to avoid data loss."
|
||||
_can_install=false
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$_can_install" = true ]; then
|
||||
# Substitute PATCH_DIR into the source so sitecustomize.py knows where
|
||||
# to write hook_status.json and check the kill switch at runtime.
|
||||
_sc_tmp="$SITE_PKG/sitecustomize.py.truecloud-tmp"
|
||||
if TRUECLOUD_PATCH_DIR="$PATCH_DIR" \
|
||||
"$PYTHON" -c "
|
||||
import os, sys
|
||||
d = os.environ['TRUECLOUD_PATCH_DIR']
|
||||
with open(d + '/patch/sitecustomize.py', encoding='utf-8') as fh:
|
||||
sys.stdout.write(fh.read().replace('/data/truecloud-patch', d))
|
||||
" > "$_sc_tmp" && mv "$_sc_tmp" "$SITE_PKG/sitecustomize.py"; then
|
||||
echo "OK: Installed sitecustomize.py → $SITE_PKG/sitecustomize.py"
|
||||
else
|
||||
rm -f "$_sc_tmp"
|
||||
echo "WARNING: Failed to write $SITE_PKG/sitecustomize.py (permission error?)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Direct file patching ──────────────────────────────────────────────────
|
||||
# Patch b2.py and restic.py directly in the overlay (primary approach).
|
||||
# Each run strips any existing TRUECLOUD_PATCH block and rewrites it fresh,
|
||||
# so a bugfix in the block takes effect immediately on the next apply.sh run
|
||||
# without needing to manually clear the overlay.
|
||||
|
||||
if [ -n "$_MW_DIR" ] && [ "$_can_install" = true ]; then
|
||||
_B2_PY="$_MW_DIR/rclone/remote/b2.py"
|
||||
_RESTIC_PY="$_MW_DIR/plugins/cloud_backup/restic.py"
|
||||
_B2_PY="$_MW_DIR/rclone/remote/b2.py"
|
||||
_RESTIC_PY="$_MW_DIR/plugins/cloud_backup/restic.py"
|
||||
|
||||
# ── b2.py ─────────────────────────────────────────────────────────────
|
||||
if [ -f "$_B2_PY" ]; then
|
||||
@@ -231,7 +186,6 @@ import sys
|
||||
|
||||
BLOCK = """
|
||||
# TRUECLOUD_PATCH — added by truenas-truecloud-patch/patch/apply.sh
|
||||
# URL fix logic lives in sitecustomize._tc_fix_restic_cmd (single source of truth).
|
||||
try:
|
||||
_tc_orig_get_restic_config = get_restic_config
|
||||
except NameError:
|
||||
@@ -240,13 +194,35 @@ else:
|
||||
def get_restic_config(cloud_backup):
|
||||
import dataclasses as _dc
|
||||
result = _tc_orig_get_restic_config(cloud_backup)
|
||||
try:
|
||||
import sitecustomize as _sc
|
||||
return _sc._tc_fix_restic_cmd(result, _dc)
|
||||
except Exception as _e:
|
||||
import sys as _sys
|
||||
_sys.stderr.write(f"[truecloud-patch] restic URL fix failed: {_e}\n")
|
||||
return result
|
||||
cmd = list(result.cmd)
|
||||
for i, part in enumerate(cmd):
|
||||
if part.startswith("--repo=") or part.startswith("--repository="):
|
||||
pfx, _, url = part.partition("=")
|
||||
pfx += "="
|
||||
elif i and cmd[i - 1] in ("-r", "--repo", "--repository"):
|
||||
pfx = None
|
||||
url = part
|
||||
else:
|
||||
continue
|
||||
scheme, sep, rest = url.partition(":")
|
||||
if not sep:
|
||||
break
|
||||
changed = False
|
||||
if rest.startswith("/") and not rest.startswith("//"):
|
||||
rest = rest[1:]
|
||||
changed = True
|
||||
if scheme == "b2" and "/" in rest:
|
||||
rest = rest.replace("/", ":", 1)
|
||||
changed = True
|
||||
if changed:
|
||||
new_url = scheme + ":" + rest
|
||||
cmd[i] = pfx + new_url if pfx is not None else new_url
|
||||
try:
|
||||
return _dc.replace(result, cmd=cmd)
|
||||
except TypeError:
|
||||
return result._replace(cmd=cmd)
|
||||
break
|
||||
return result
|
||||
|
||||
get_restic_config._truecloud_patched = True
|
||||
"""
|
||||
@@ -272,12 +248,7 @@ PYEOF
|
||||
else
|
||||
echo "WARNING: restic.py not found at $_RESTIC_PY"
|
||||
fi
|
||||
elif [ -z "$_MW_DIR" ]; then
|
||||
echo "WARNING: Cannot determine middlewared directory; skipping direct file patch."
|
||||
fi
|
||||
|
||||
# Write hook_status.json so 'verify' reflects the current patch state
|
||||
# without requiring a backup run to trigger the import hook.
|
||||
# Write hook_status.json so 'verify' reflects the current patch state.
|
||||
"$PYTHON" -c "
|
||||
import json, os, sys, time
|
||||
b2_ok = sys.argv[1] == '1'
|
||||
|
||||
Reference in New Issue
Block a user