fix(storageclass): preserve guest-only resources - #6
Conversation
|
Focused review at exact head
Evidence:
The ownership primitive itself is compatible with generic |
61ce65f to
2caf64c
Compare
|
Current-head re-review at The previous three findings are fixed on the success path: translate patches now run before the final marker write; the deletion expectation names the StorageClass GVK; selector mismatch and full controller adoption/deletion are covered. Exact-head focused and shared gates pass. P0 - a translate-patch error still persists a partially mutated, unowned mirrored object. Independent exact-head error injection proves this:
The red assertions are: Required contract: when translate patching fails, do not persist any virtual mutation. A robust shape is to build/patch/mark a deep copy and only assign it to the object after all fallible transforms succeed, while retaining the patcher's original pre-state for the final write; alternatively, make the defer skip persistence when the transform path has returned an error. Add this error-injection regression so both marker and prior fields are unchanged after the failed reconcile. Gates at exact head:
No PR #5 stacking or live rollout until this closes. |
2caf64c to
0b0186f
Compare
|
Current-head focused re-review at The error-path blocker is closed. The deterministic regression now proves both halves of the contract after an injected patch failure that deletes the marker before returning error:
The earlier findings also remain closed: final marker is after successful translate patches, owned host-delete asserts an explicit empty StorageClass GVK, selector mismatch covers owned/unowned branches, and full Independent gates at exact head:
Evidence boundary: this approves PR #6 by itself. PR #5 remains orthogonal and unstacked; a combined current-head full-Reconcile gate is still required before producing or rolling a runtime image. No live rollout is approved by this comment. |
|
Review hardening landed at
Local gates on the updated PR head:
The stacked PR #5 managed-host deletion path is validated on combined head |
weicao
left a comment
There was a problem hiding this comment.
Focused design-contract review at exact head 783dec5d338f639e221b851ba809c8d53a085332 (base apecloud-v0.35.1).
BLOCKER — the pre-delete ownership re-read is cached, so the claimed race safety is incomplete
deleteOwnedVirtual correctly re-reads ownership and uses a UID delete precondition, but the read at host_syncer.go:136 uses ctx.VirtualClient, which is the manager client. Its reads can be cache-backed while deletes go to the API server. If the live object removes the ownership annotation in place (same UID) and the cache still returns the prior owned version, lines 143-157 accept the stale owner and the UID precondition still succeeds, deleting the live unowned object.
Direct adversarial evidence on this exact head:
stale cached Get: owned annotation, UID=same-live-uid
live client state: owner annotation removed, UID=same-live-uid
SyncToHost -> deleteOwnedVirtual
expected: live unowned object preserved
actual: delete succeeds; subsequent live Get is NotFound
result: FAIL (rc1)
The PR's existing focused tests and their race run both pass, but the current owner removed from current object fixture uses an immediately consistent fake read. It does not exercise cache staleness, and the race detector cannot detect this distributed read/write race.
Please make the destructive ownership re-read live/uncached (for example, an APIReader captured from the virtual manager, or an equivalent server-authoritative gate), retain the UID precondition, and add the stale-read/same-UID owner-removal case. A same-UID metadata update is exactly the case UID preconditions do not cover.
Pre-flight: current head and two-file scope verified; root-cause contract is coherent; no runtime script/API-version issue. Evidence is source/unit only, not a product/runtime verdict.
Classes 1-8:
- silent fallback: clean; unowned objects are preserved.
- ownership write contract: clean; create/update reassert ownership after patches.
- state continuity: BLOCKER — ownership read from cache is treated as live through the delete boundary.
4/4a. sentinel/cross-field collision: clean. - conditional cleanup pre-state: BLOCKER — cleanup depends on potentially stale owner state.
- NotFound: explicit and appropriate.
- terminating vs absent: no release contract in this delta.
- precedence: clean.
Direct commands: existing focused tests rc0; focused race rc0; stale-cache/live-unowned same-UID adversarial case rc1 as above.
Summary
Fixes the vcluster defect recorded in apecloud/kubeblocks#10614.
apecloud/vclusterhas Issues disabled, so the linked KubeBlocks issue contains the scene and reproduction evidence.Root cause
hostStorageClassSyncer.SyncToHostunconditionally deleted the virtual object whenever its mapped host object was missing. The mirror mapper treats host objects as managed, while imported virtual StorageClasses carried no ownership marker. As a result, enabling fromHost StorageClass sync deleted unrelated guest-only StorageClasses, including a generated S3 StorageClass used by BackupRepo. Helm rollback cannot restore such a deletion.Constructed test scene and reproducibility
b98b1ae5a603fa504daf1e745d65784c5a0be0eawith fromHost StorageClass sync disabled.SyncToHostand is deleted.The regression test failed against the base with
virtual state mismatch: expected the guest object, existing state was empty.Contract
Tests
go test -v ./pkg/controllers/resources/storageclasses -run TestFromHostSync -count=1-> rc1 atPreserve_guest-only_storage_class_when_host_resource_is_absentgo test ./pkg/controllers/resources/storageclasses -count=1git diff --checkScope boundary
This is a vcluster StorageClass lifecycle fix. It is not an etcd restore PASS and does not authorize rollout until a focused review, patched image identity proof, and the previously defined guest/host live gates pass.