Retire the two modules independently; calm the README down
The native-support check looked only for native B2 restic support and, on
finding it, set the kill switch and disabled the whole patch. That was fine when
providers were the only thing here. With nested-dataset snapshots in the patch it
is wrong: TrueNAS is likely to ship one capability long before the other, and a
single all-or-nothing kill switch would silently take a still-needed module down
with the superseded one.
apply.sh now treats the patch as two independent modules:
providers b2.py + restic.py + the UI credential dropdown
native when B2RcloneRemote carries a real get_restic_config()
nested plugins/cloud/{snapshot,crud}.py + cloud_backup/sync.py (opt-in)
native when the "no further nesting" validation is gone from
plugins/cloud/crud.py
Each is detected and skipped on its own. The kill switch fires only once BOTH are
done (native, or nested was never enabled). The UI patch belongs to providers and
is skipped with it. The deferred middlewared restart now fires when ANY
still-needed module landed -- keying it off providers alone would have left a
freshly-patched nested module on disk and never loaded on a native-B2 box.
hook_status.json reports each module with an active flag and a reason.
README: dropped the warning boxes and the disclaimer's fear-bulleting in favour
of plain statements, documented the two-module design and the per-module
auto-disable, and added a Development section disclosing AI assistance. The one
caveat kept, as a plain sentence rather than a banner: the mount --bind staging
step has not yet been exercised by a live backup run.
67 tests, ruff and shellcheck clean.
This commit is contained in:
@@ -105,12 +105,61 @@ def test_crud_block_is_scoped_to_cloud_backup():
|
||||
assert '!= "cloud_backup"' in extract_blocks()["CRUD_BLOCK"]
|
||||
|
||||
|
||||
class TestIndependentModules:
|
||||
"""The two modules must retire independently.
|
||||
|
||||
TrueNAS may ship native B2 support long before (or after) it handles nested
|
||||
datasets. A single all-or-nothing kill switch would silently take a
|
||||
still-needed module down with the superseded one.
|
||||
"""
|
||||
|
||||
def _sh(self):
|
||||
with open(APPLY_SH, encoding="utf-8") as fh:
|
||||
return fh.read()
|
||||
|
||||
def test_native_support_is_detected_per_module(self):
|
||||
sh = self._sh()
|
||||
assert "native_b2" in sh
|
||||
assert "native_nested" in sh
|
||||
assert "no further nesting" in sh, "nested native-support probe"
|
||||
|
||||
def test_kill_switch_only_when_both_modules_are_done(self):
|
||||
sh = self._sh()
|
||||
assert '[ "$_providers_needed" = "0" ] && [ "$_nested_needed" = "0" ]' in sh
|
||||
# ...and that is the only place the kill switch is actually set. (Ignore
|
||||
# comment lines, which mention the same path.)
|
||||
code = [ln for ln in sh.splitlines() if not ln.lstrip().startswith("#")]
|
||||
sets = [ln for ln in code if 'touch "$PATCH_DIR/disabled"' in ln]
|
||||
assert len(sets) == 1, f"kill switch set in {len(sets)} places"
|
||||
|
||||
def test_each_module_is_gated_separately(self):
|
||||
src = heredoc_source()
|
||||
assert "if not providers_needed:" in src
|
||||
assert "elif nested_native:" in src
|
||||
|
||||
def test_ui_patch_is_tied_to_the_providers_module(self):
|
||||
# The UI change widens the credential dropdown; it is meaningless once B2
|
||||
# is native, but must NOT be skipped merely because nested is off.
|
||||
sh = self._sh()
|
||||
i = sh.index("--- UI patch ---")
|
||||
assert '[ "$_providers_needed" = "0" ]' in sh[i:i + 400]
|
||||
|
||||
def test_restart_fires_when_any_needed_module_landed(self):
|
||||
# Keying the restart off providers alone would leave a freshly-patched
|
||||
# nested module on disk and never loaded on a native-B2 box.
|
||||
sh = self._sh()
|
||||
i = sh.index("--- deferred restart ---")
|
||||
tail = sh[i:]
|
||||
assert '_backend_ok' in tail
|
||||
assert '"$_b2_ok"' not in tail
|
||||
|
||||
|
||||
class TestOptIn:
|
||||
"""Nested-snapshot support must be opt-in and must never self-enable."""
|
||||
|
||||
def test_heredoc_gates_on_the_opt_in_flag(self):
|
||||
src = heredoc_source()
|
||||
assert "nested_enabled = sys.argv[7]" in src
|
||||
assert re.search(r"nested_enabled = sys\.argv\[\d+\] == \"1\"", src)
|
||||
assert "if not nested_enabled:" in src
|
||||
|
||||
def test_apply_sh_reads_the_marker_file(self):
|
||||
|
||||
Reference in New Issue
Block a user