Fix native-nested probe: guard message is split across string literals
The probe searched plugins/cloud/crud.py for the contiguous string
"no further nesting". Stock does not contain it. The message is split across
adjacent string literals:
verrors.add(f"{name}.snapshot", "This option is only available for datasets that have no further "
"nesting")
Python concatenates those at runtime, so the errmsg IS contiguous and CRUD_BLOCK's
runtime filter matches correctly -- but the SOURCE never contains the whole
phrase. The probe therefore found nothing, concluded iX had removed the guard, and
skipped the nested module as "already native" on every boot. apply.log would say
"TrueNAS now handles nesting natively" and the feature would never work.
It fails safe -- the stock guard stays in place, so no backup could be
misconfigured and no data was at risk -- but the module was 100% dead.
Verified against real middlewared on a live box: the probe returned native=yes
(wrong) before this change and native=no (correct) after.
The probe now strips whitespace and quote characters before matching, which is
robust to any wrapping or concatenation style. Added a regression test that
EXECUTES apply.sh's own probe code (extracted, not reimplemented -- a
reimplementation would pass while the shipped probe stayed broken) against the
real wrapped source, a single-line variant, and a three-way split.
75 tests, ruff and shellcheck clean.
This commit is contained in:
@@ -8,6 +8,7 @@ these tests do.
|
||||
import ast
|
||||
import os
|
||||
import re
|
||||
import textwrap
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -46,6 +47,35 @@ def extract_blocks():
|
||||
return blocks
|
||||
|
||||
|
||||
def _nested_native_detector():
|
||||
"""The REAL native-nested probe, lifted out of apply.sh.
|
||||
|
||||
Extracted rather than reimplemented: a reimplementation would happily pass
|
||||
while the shipped probe stayed broken, which is precisely the bug this guards.
|
||||
"""
|
||||
with open(APPLY_SH, encoding="utf-8") as fh:
|
||||
sh = fh.read()
|
||||
|
||||
m = re.search(
|
||||
r"^(\s*)_drop = str\.maketrans\(.*?\n\s*if 'nofurthernesting' not in "
|
||||
r"stock_src\.translate\(_drop\):\n\s*result\['native_nested'\] = 'yes'",
|
||||
sh, re.S | re.M,
|
||||
)
|
||||
assert m, "could not find the native-nested probe in apply.sh"
|
||||
|
||||
# The block lives inside a double-quoted shell string; undo bash's escaping.
|
||||
body = m.group(0)
|
||||
body = body.replace("\\\\", "\x00").replace('\\"', '"').replace("\x00", "\\")
|
||||
body = textwrap.dedent(body)
|
||||
|
||||
def detect(stock_src):
|
||||
ns = {"stock_src": stock_src, "result": {"native_nested": "no"}, "chr": chr}
|
||||
exec(body, ns) # noqa: S102 - executing our own shipped code, on purpose
|
||||
return ns["result"]["native_nested"]
|
||||
|
||||
return detect
|
||||
|
||||
|
||||
def test_heredoc_itself_compiles():
|
||||
compile(heredoc_source(), "apply.sh:PYEOF", "exec")
|
||||
|
||||
@@ -153,6 +183,38 @@ class TestIndependentModules:
|
||||
assert "'ok': (not providers_needed) or bool(b2_ok and restic_ok)" in src
|
||||
assert "'active': nested_needed" in src
|
||||
|
||||
def test_nested_native_probe_matches_the_real_wrapped_source(self):
|
||||
"""Stock splits the guard message across adjacent string literals.
|
||||
|
||||
Python concatenates them at runtime, so the errmsg is contiguous -- but the
|
||||
SOURCE never contains the whole phrase. A raw substring search finds
|
||||
nothing, concludes iX removed the guard, and silently skips this module
|
||||
forever. This is exactly what happened, and only a run against real
|
||||
middlewared caught it.
|
||||
"""
|
||||
detect = _nested_native_detector()
|
||||
|
||||
# Verbatim shape from TrueNAS plugins/cloud/crud.py.
|
||||
stock_wrapped = (
|
||||
' verrors.add(f"{name}.snapshot", '
|
||||
'"This option is only available for datasets that have no further "\n'
|
||||
' "nesting")\n'
|
||||
)
|
||||
assert detect(stock_wrapped) == "no", "guard is present; must NOT report native"
|
||||
|
||||
# Same message on a single line — must also be detected.
|
||||
assert detect('verrors.add(x, "... have no further nesting")\n') == "no"
|
||||
|
||||
# Single-quoted, three-way split — still the guard.
|
||||
assert detect(
|
||||
"verrors.add(x, 'This option is only available for '\n"
|
||||
" 'datasets that have no further '\n"
|
||||
" 'nesting')\n"
|
||||
) == "no"
|
||||
|
||||
# Guard genuinely gone -> native support.
|
||||
assert detect("def _validate(self):\n pass\n") == "yes"
|
||||
|
||||
def test_nested_native_probe_ignores_our_own_block(self):
|
||||
# CRUD_BLOCK quotes the guard message, so scanning the whole file would
|
||||
# find the string in our own patch and never detect native support.
|
||||
|
||||
Reference in New Issue
Block a user