R-106 follow-up: mergeConfig dropped the pbs namespace, so v0.118.0's fix was inert (v0.118.1)

Live validation caught what the tests could not. On demo-felhom the recipe read
namespace "root" with namespace_state "resolved" — confident and wrong, a worse
shape than the original defect.

mergeConfig overlays the cluster storage config onto the node entry through a
hand-listed set of fields and Namespace was not among them. NodeStorage does not
return the namespace at all, so PBSNamespace always read "" and latestPBSCoord
correctly treated that as the root namespace.

Every v0.118.0 test built StorageTarget values directly — including the two
through Collector.Collect(), which inject a fakeObserver — so nothing crossed the
merge. Two new tests drive the real Observe path with PVE's actual split returns
and table the merge itself. Red-proof: dropping the added line fails both.

Suite rc=0, 29 packages, 0 FAIL.
This commit is contained in:
2026-07-30 13:23:21 +02:00
parent 1c8a67eece
commit 6b5dade4dc
3 changed files with 104 additions and 0 deletions
+30
View File
@@ -1,3 +1,33 @@
## v0.118.1 — R-106 follow-up: the namespace was still being lost in the merge (2026-07-30)
**v0.118.0's R-106 fix was incomplete and live validation caught it.** Deployed to demo-felhom, the
recipe read:
"pbs": { ..., "namespace": "root", "namespace_state": "resolved", ... }
`namespace_state: resolved` while the value was still the wrong `"root"` — which is a worse shape than
the original defect, because it asserts confidence in a wrong answer. The new state field is what made it
legible at a glance; without it this would have looked identical to the pre-fix output.
**Cause.** `mergeConfig` (`internal/storage/observe.go:457`) overlays the CLUSTER storage config onto the
NODE entry via a hand-listed set of type-specific fields — `Type`, `Server`, `Export`, `Share`,
`Datastore`, `Fingerprint`, `VGName`, `ThinPool`, `Path`, `Content` — and `Namespace` was not on that list.
`NodeStorage` does not return the namespace at all (it is cluster-config only), so the merged entry's
`Namespace` was always empty and `StorageTarget.PBSNamespace` read `""`. `latestPBSCoord` then treated an
empty configured namespace as the genuine root namespace, which is correct logic fed a wrong input.
**Why the tests did not catch it.** Every test added in v0.118.0 constructs `StorageTarget` values
directly — including the two that run `Collector.Collect()`, because they inject a `fakeObserver`. The
break was UPSTREAM of the collector, in the observer's merge, so "the production generation path" as I had
drawn it started one layer too late. Two new tests fix that: `TestObserve_CarriesPBSNamespaceThroughMerge`
drives the real `Observe` with the split PVE returns reproduced (namespace present in the cluster list,
absent from the node list — what PVE actually does), and `TestMergeConfig_CarriesPBSNamespace` tables the
merge itself including fill-if-empty vs never-clobber. Red-proof: removing the one added line fails both,
the first quoting the exact live symptom. Suite rc=0, 29 packages, 0 FAIL.
`Username` is the one remaining unmerged type-specific field; nothing in the observer path consumes it, and
the comment on the merge now says to add it here the moment something does.
## v0.118.0 — R-106 + R-109: the DR recipe stops guessing (2026-07-30)
**The recipe is read at the worst possible moment — by an operator rebuilding a machine that is gone —