Auto-disable when TrueNAS ships native B2 support; assume immutable /usr
apply.sh now inspects B2RcloneRemote.__dict__ before patching. If TrueNAS has shipped get_restic_config natively, it sets the kill switch, unmounts overlays, and logs a clear instruction to run uninstall.sh. Also: drop all conditional 'if read-only' language — overlay is always mounted unconditionally since /usr is always immutable on TrueNAS SCALE. README updated with auto-disable behaviour and revised native-support table.
This commit is contained in:
@@ -47,7 +47,8 @@ By installing this patch you accept the following:
|
||||
- **No warranty.** This software is provided as-is. See the LICENSE file.
|
||||
|
||||
If TrueNAS adds native B2 or S3 support to TrueCloud Backup, the patch
|
||||
detects it and degrades gracefully — see [Native support](#if-truenas-adds-native-support) below.
|
||||
detects it at boot, disables itself, and tells you to run `uninstall.sh` —
|
||||
see [Native support](#if-truenas-adds-native-support) below.
|
||||
|
||||
---
|
||||
|
||||
@@ -79,14 +80,11 @@ support and the reason is logged to `apply.log` in your repo root.
|
||||
TrueNAS SCALE updates replace `/usr/` entirely. The patch survives by keeping
|
||||
this repository on a **persistent ZFS pool** (your data pool, not `/tmp` or a
|
||||
system path) and registering a **PREINIT initshutdownscript** in the TrueNAS
|
||||
database. On every boot, `patch/apply.sh` runs from the repo before
|
||||
`middlewared` starts, patching `b2.py` and `restic.py` directly in the overlay
|
||||
and re-patching the UI bundle.
|
||||
|
||||
If `/usr` is a read-only filesystem, `apply.sh` handles this automatically by
|
||||
mounting a writable [overlayfs](https://docs.kernel.org/filesystems/overlayfs.html)
|
||||
on top of the relevant directories. The overlay lives in `/run` (tmpfs) and is
|
||||
recreated on every boot. No extra configuration is needed.
|
||||
database. On every boot, `patch/apply.sh` runs before `middlewared` starts. It
|
||||
mounts a writable [overlayfs](https://docs.kernel.org/filesystems/overlayfs.html)
|
||||
over the relevant directories (upper layer in `/run`, recreated each boot), then
|
||||
patches `b2.py` and `restic.py` directly in that overlay and re-patches the UI
|
||||
bundle. No extra configuration is needed.
|
||||
|
||||
---
|
||||
|
||||
@@ -257,22 +255,32 @@ restic -r "$REPO" ls latest
|
||||
|
||||
## If TrueNAS adds native support
|
||||
|
||||
When a TrueNAS update ships native B2 or S3 support in TrueCloud Backup, the
|
||||
patch handles each component as follows:
|
||||
`apply.sh` checks at every boot whether TrueNAS has shipped native B2 restic
|
||||
support (by inspecting `B2RcloneRemote.__dict__`). If it has:
|
||||
|
||||
| Component | What happens | Action needed |
|
||||
1. The kill switch (`disabled` file) is set — no patching on any future boot.
|
||||
2. Any active overlays are unmounted immediately.
|
||||
3. The following message is written to `apply.log`:
|
||||
|
||||
```
|
||||
NOTICE: TrueNAS now provides native B2 restic support — truecloud-patch is no longer needed.
|
||||
NOTICE: Setting kill switch; patching will be skipped on all future boots.
|
||||
NOTICE: Run the following to fully remove the patch:
|
||||
NOTICE: bash /mnt/tank/truenas-truecloud-patch/uninstall.sh
|
||||
```
|
||||
|
||||
Check the log after any TrueNAS update:
|
||||
```bash
|
||||
cat /mnt/tank/truenas-truecloud-patch/apply.log | tail -20
|
||||
```
|
||||
|
||||
**Scenarios where the auto-detect may not fire** (manual check needed):
|
||||
|
||||
| Scenario | What happens | Action |
|
||||
|---|---|---|
|
||||
| **B2 `get_restic_config`** added directly to `B2RcloneRemote` | `__dict__` guard detects it; our method is **not attached** | None — native version used automatically |
|
||||
| **restic.py URL builder** fixed to emit `b2:bucket:path` directly | Our wrapper sees no `/` to fix; it becomes a **no-op** | None — correct URL passes through unchanged |
|
||||
| **`get_restic_config` moved** out of `restic.py` entirely | `NameError` guard in the patched file catches it; wrapper silently does nothing | None — but run `verify` to confirm state |
|
||||
| **B2 credential schema changed** (e.g. `provider["account"]` renamed) | Our B2 config function raises `KeyError`; backup task fails | Uninstall or update the patch |
|
||||
| **B2 `get_restic_config`** added to a **base class** (not `B2RcloneRemote`) | `__dict__` check misses it; our method is attached and **shadows** the native one | Uninstall the patch |
|
||||
|
||||
**Recommended check after any TrueNAS update that adds TrueCloud provider
|
||||
support**: run `python3 /mnt/tank/truenas-truecloud-patch/patch/create_task.py verify`
|
||||
and attempt a B2 backup. If both pass, the patch is coexisting correctly. If
|
||||
the backup fails with a credential or URL error that worked before the update,
|
||||
uninstall the patch — TrueNAS has shipped a conflicting implementation.
|
||||
| B2 support added to a **base class** (not `B2RcloneRemote` directly) | `__dict__` check misses it; our method shadows native | Uninstall manually |
|
||||
| B2 **credential schema changed** (e.g. `provider["account"]` renamed) | `KeyError` on first backup | Uninstall or update the patch |
|
||||
| **URL builder** fixed but B2 class unchanged | URL wrapper becomes a no-op; no harm, but patch is dead weight | Uninstall at your convenience |
|
||||
|
||||
---
|
||||
|
||||
|
||||
+34
-6
@@ -57,10 +57,10 @@ _ensure_writable() {
|
||||
mkdir -p "$upper" "$work"
|
||||
if mount -t overlay "truecloud-${tag}" \
|
||||
-o "lowerdir=$dir,upperdir=$upper,workdir=$work" "$dir" 2>/dev/null; then
|
||||
echo "OK: Mounted writable overlay on $dir (immutable filesystem)"
|
||||
echo "OK: Mounted writable overlay on $dir"
|
||||
return 0
|
||||
fi
|
||||
echo "WARNING: $dir is read-only and overlay mount failed."
|
||||
echo "WARNING: overlay mount failed on $dir — backend patch will be skipped."
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -97,11 +97,41 @@ find_mw_python() {
|
||||
|
||||
# ── Step 1: backend patch ─────────────────────────────────────────────────────
|
||||
|
||||
echo "--- backend patch ---"
|
||||
|
||||
PYTHON=$(find_mw_python)
|
||||
echo "Using Python: $PYTHON"
|
||||
|
||||
# ── Native support check ──────────────────────────────────────────────────────
|
||||
# If TrueNAS has shipped native B2 restic support, this patch is no longer
|
||||
# needed. Set the kill switch and instruct the user to uninstall cleanly.
|
||||
|
||||
_tc_native=$("$PYTHON" -c "
|
||||
try:
|
||||
from middlewared.rclone.remote.b2 import B2RcloneRemote
|
||||
print('yes' if 'get_restic_config' in B2RcloneRemote.__dict__ else 'no')
|
||||
except Exception:
|
||||
print('no')
|
||||
" 2>/dev/null || echo "no")
|
||||
|
||||
if [ "$_tc_native" = "yes" ]; then
|
||||
echo "NOTICE: TrueNAS now provides native B2 restic support — truecloud-patch is no longer needed."
|
||||
echo "NOTICE: Setting kill switch; patching will be skipped on all future boots."
|
||||
echo "NOTICE: Run the following to fully remove the patch:"
|
||||
echo "NOTICE: bash $PATCH_DIR/uninstall.sh"
|
||||
touch "$PATCH_DIR/disabled"
|
||||
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)
|
||||
umount "$_mnt" 2>/dev/null && echo "NOTICE: Unmounted overlay on $_mnt" || true
|
||||
fi
|
||||
done
|
||||
echo "=== done ==="
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ── Step 1: backend patch ─────────────────────────────────────────────────────
|
||||
|
||||
echo "--- backend patch ---"
|
||||
|
||||
# Derive site-packages from where middlewared actually lives.
|
||||
# getsitepackages()[0] may return the wrong directory; using middlewared.__file__
|
||||
# ensures we patch files in the directory Python will actually read.
|
||||
@@ -109,8 +139,6 @@ SITE_PKG=$("$PYTHON" -c "
|
||||
import os
|
||||
try:
|
||||
import middlewared
|
||||
# e.g. /usr/lib/python3/dist-packages/middlewared/__init__.py
|
||||
# -> /usr/lib/python3/dist-packages/
|
||||
print(os.path.dirname(os.path.dirname(os.path.abspath(middlewared.__file__))))
|
||||
except ImportError:
|
||||
import site
|
||||
|
||||
Reference in New Issue
Block a user