installer 1.24.0: a PRE-EXISTING backup target is granted too (R-185)
gates / gates (push) Successful in 7s
gates / gates (push) Successful in 7s
configure_backup_target has two arms and only one granted. Case A creates the
storage and grants in the same breath; the Scenario-F arm ('the target already
exists') returned without granting. A box whose felhom-backup pre-dated the
install therefore pointed local_backup_target at a storage its own token could
not read — measured on BOTH demo boxes: {"data":[]} through the token while root
lists three archives. That tier was never restore-tested and nothing said so,
because an empty listing is also what a brand-new tier returns.
The reuse arm now ensures the ACL through the same guarded wrapper. Scenario F is
unviolated: the storage DEFINITION is untouched, and pveum acl modify is
idempotent. BACKUP_TARGET_ID is deliberately NOT added to PVE_STORAGES — that
list is granted a step before the target is resolved, and --acl-storages entries
are preflight-checked for existence; the comment now says so.
A gate asserts it: every arm that resolves the target must also grant on it.
Red-proved by reverting the arm.
This commit is contained in:
@@ -202,6 +202,37 @@ if wp is not None:
|
||||
fail("manifests/webpage.yaml no longer tracks main for the website — pinning the SITE to "
|
||||
"the installer tag turns every copy edit into a release")
|
||||
|
||||
# ── R-185: every path that RESOLVES the backup target must also grant on it ──────────────────
|
||||
#
|
||||
# THE DEFECT THIS WOULD HAVE CAUGHT, measured on both demo boxes 2026-08-03. `configure_backup_target`
|
||||
# has two arms. The CASE A arm creates the storage and grants in the same breath. The Scenario-F arm —
|
||||
# "the target already exists, leave it alone" — returned WITHOUT granting, so a box whose target
|
||||
# pre-dated the install pointed `local_backup_target` at a storage its own token could not read. The
|
||||
# API answered `{"data":[]}` while root saw three archives, and nothing said so, because an empty
|
||||
# listing is also what a brand-new tier returns.
|
||||
#
|
||||
# The assertion is deliberately about the FUNCTION, not about PVE_STORAGES: the target's grant belongs
|
||||
# with the target's resolution (PVE_STORAGES is granted a step earlier, before the target exists), so
|
||||
# what must hold is that no arm of that function can resolve a target and skip the grant.
|
||||
fn = re.search(r'^configure_backup_target\(\)\s*\{(.*?)^\}', src, re.S | re.M)
|
||||
if not fn:
|
||||
fail("cannot find configure_backup_target() — the backup-target ACL assertion cannot run, and a "
|
||||
"check that cannot run must never report OK (R-185)")
|
||||
else:
|
||||
body = fn.group(1)
|
||||
resolutions = len(re.findall(r'BACKUP_TARGET_RESOLVED="\$BACKUP_TARGET_ID"', body))
|
||||
grants = len(re.findall(r'felhom-backup-target-apply grant', body))
|
||||
if resolutions == 0:
|
||||
fail("configure_backup_target no longer resolves BACKUP_TARGET_ID anywhere — re-read it")
|
||||
elif grants >= resolutions:
|
||||
ok("every arm that resolves the backup target also grants on it (%d resolution(s), %d grant(s))"
|
||||
% (resolutions, grants))
|
||||
else:
|
||||
fail("configure_backup_target resolves the backup target in %d place(s) but grants in only %d "
|
||||
"— an arm resolves a target the agent may not READ. That is R-185: the tier's archives are "
|
||||
"invisible to the agent, it is never restore-tested, and an empty listing looks exactly "
|
||||
"like a brand-new tier." % (resolutions, grants))
|
||||
|
||||
print()
|
||||
if fails:
|
||||
print("hostinstall gates: %d FAILURE(S)" % len(fails))
|
||||
|
||||
Reference in New Issue
Block a user