Compare commits

...
2 Commits
Author SHA1 Message Date
flan 5b9c888b72 Add CHANGELOG for v0.0.1 and v0.0.2 2026-06-19 19:01:14 +00:00
flan 1c41df8fec Fix get_restic_config not applied when guard misfires; detect stubs in native check
The guard in the b2.py patch block skipped setting get_restic_config when
something caused B2RcloneRemote.__dict__ to already contain it at import
time (e.g. a TrueNAS version that adds a NotImplementedError stub). Remove
the guard and always assign, which is safe: the native-support kill switch
already prevents patching when TrueNAS ships a real implementation.

Also update the native-support check to distinguish a stub (source contains
NotImplementedError) from a working implementation, so a stub does not
trigger the kill switch and block all future patching.
2026-06-19 18:59:45 +00:00
2 changed files with 53 additions and 4 deletions
+42
View File
@@ -0,0 +1,42 @@
# Changelog
## v0.0.2 — 2026-06-19
### Fixed
- **B2 backup failing with `NotImplementedError` after a TrueNAS update.**
The patch block's guard (`if "get_restic_config" not in B2RcloneRemote.__dict__`)
could misfire and silently skip injecting the method — most likely when a
TrueNAS version adds a stub that raises `NotImplementedError`, causing the dict
check to return `False`. The guard is removed; the assignment is now
unconditional. This is safe because the native-support kill switch already
prevents patching when TrueNAS ships a real, working implementation.
- **Native-support check falsely triggering kill switch on stubs.**
The check now inspects the source of any pre-existing `get_restic_config`
before concluding that TrueNAS has shipped native B2 support. If the method
body contains `NotImplementedError` it is treated as a stub and patching
continues; only a method that does not raise `NotImplementedError` triggers
the kill switch and auto-disable.
---
## v0.0.1 — 2026-06-16
Initial public release. Extends TrueNAS SCALE's TrueCloud Backup feature to
work with S3-compatible providers and native Backblaze B2 in addition to Storj,
using volatile overlayfs patching of the TrueNAS middleware that persists across
system updates via a PREINIT initshutdownscript.
### Included
- `install.sh` — registers the PREINIT boot hook and applies patches immediately
- `patch/apply.sh` — PREINIT script; mounts writable overlays, patches `b2.py`
and `restic.py`, patches the Angular UI bundle to widen the credential dropdown
- `patch/create_task.py` — CLI to create TrueCloud Backup tasks with S3 or B2
credentials, bypassing the Storj-only restriction in the UI
- `recover.sh` — emergency recovery; sets the kill switch and restarts middlewared
- `uninstall.sh` — full removal of the patch and PREINIT hook
- Kill switch support (`disabled` file) for safe degradation
- Auto-disable when TrueNAS ships native B2 restic support
- `hook_status.json` written on each boot for `create_task.py verify`
+11 -4
View File
@@ -111,7 +111,15 @@ try:
print('no') print('no')
else: else:
src = open(inspect.getfile(_b2_mod), encoding='utf-8', errors='replace').read() src = open(inspect.getfile(_b2_mod), encoding='utf-8', errors='replace').read()
print('no' if 'TRUECLOUD_PATCH' in src else 'yes') if 'TRUECLOUD_PATCH' in src:
print('no')
else:
# Distinguish a real implementation from a stub that raises NotImplementedError.
try:
method_src = inspect.getsource(B2RcloneRemote.get_restic_config)
except (OSError, TypeError):
method_src = ''
print('no' if 'NotImplementedError' in method_src else 'yes')
except Exception: except Exception:
print('no') print('no')
" 2>/dev/null || echo "no") " 2>/dev/null || echo "no")
@@ -184,9 +192,8 @@ def _tc_get_restic_config(task):
p = task["credentials"]["provider"] p = task["credentials"]["provider"]
return "", {"B2_ACCOUNT_ID": p["account"], "B2_ACCOUNT_KEY": p["key"]} return "", {"B2_ACCOUNT_ID": p["account"], "B2_ACCOUNT_KEY": p["key"]}
if "get_restic_config" not in B2RcloneRemote.__dict__: B2RcloneRemote.get_restic_config = staticmethod(_tc_get_restic_config)
B2RcloneRemote.get_restic_config = staticmethod(_tc_get_restic_config) B2RcloneRemote.restic = True
B2RcloneRemote.restic = True
""" """
path = sys.argv[1] path = sys.argv[1]