Migrate sibling config controllers to MutateAndPatchStatus/Spec#5509
Open
ChrisJBurns wants to merge 6 commits into
Open
Migrate sibling config controllers to MutateAndPatchStatus/Spec#5509ChrisJBurns wants to merge 6 commits into
ChrisJBurns wants to merge 6 commits into
Conversation
Status writes now flow through controllerutil.MutateAndPatchStatus and finalizer writes through controllerutil.MutateAndPatchSpec, matching the MCPAuthzConfig controller migration in #4777. The previous r.Status().Update calls sent full PUT bodies that would clobber conditions written by any disjoint owner of Status.Conditions on this CRD; the previous r.Update calls had no optimistic-lock guard around the finalizer array. Condition and field mutations move inside the helper closures so the pre-mutate snapshot reflects the live state rather than already containing the change — a MutateAndPatchStatus prerequisite. A small helper, setOIDCConfigValidTrueCondition, factors out the Valid=True transition. Add a PreservesForeignConditions regression test mirroring the MCPAuthzConfig guard, asserting a foreign-owned condition survives a reconcile. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Status writes now flow through controllerutil.MutateAndPatchStatus and finalizer writes through controllerutil.MutateAndPatchSpec, matching the MCPAuthzConfig and MCPOIDCConfig controllers. The previous r.Status().Update calls sent full PUT bodies that would clobber conditions owned by any disjoint writer of Status.Conditions on this CRD; the previous r.Update calls had no optimistic-lock guard around the finalizer array. The IdentitySynthesized advisory was previously computed once in memory before validation. Because MutateAndPatchStatus snapshots the object on entry and any pre-mutate change is dropped from the merge patch, that upfront mutation is removed; the advisory is now recomputed inside each status-write closure via a new setValidTrueAndSynthesized helper (and directly on the validation-failure and setInvalid paths). applyIdentitySynthesizedCondition is idempotent on the same spec, so this preserves the advisory transition on every path. The setInvalid doc comment is updated to drop the stale upfront-mutation reference. Add a PreservesForeignConditions regression test mirroring the sibling guards. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #5509 +/- ##
=======================================
Coverage 69.71% 69.71%
=======================================
Files 645 645
Lines 65598 65616 +18
=======================================
+ Hits 45729 45745 +16
- Misses 16523 16526 +3
+ Partials 3346 3345 -1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
The advisory helper's bool return was consumed only by the upfront in-memory call removed in the MutateAndPatchStatus migration; every remaining caller ignores it inside a status-write closure. Drop the return value to satisfy the unparam linter and refresh the doc comment. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address review feedback on the status-write migration: - Gate the OIDC steady-state reference refresh on a captured findErr == nil rather than the referencingWorkloads != nil sentinel, decoupling the guard from findReferencingWorkloads' nil-on-error / non-nil-empty-on-success contract. - Assert the migrated DeletionBlocked condition (and reference bookkeeping) is persisted in the OIDC blocking-deletion test, which previously only checked the requeue and finalizer retention. - Use meta.FindStatusCondition in the externalauth PreservesForeignConditions test for parity with the OIDC and MCPAuthzConfig sibling guards. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
10 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
MCPAuthzConfigReconcilerwas migrated in #4777 to write status throughctrlutil.MutateAndPatchStatusand finalizers throughctrlutil.MutateAndPatchSpec, because a rawr.Status().Updatesends a full PUT that clobbersStatus.Conditionsentries owned by any disjoint writer (JSON merge-patch is the only form that honors the+listType=mapmarker), and a rawr.Updateon finalizers has no optimistic-lock guard. The two sibling controllers still used the older anti-pattern. This brings them to parity per.claude/rules/operator.md§"Status Writes" and §"Spec / metadata patching".Closes #5497
Medium level
r.Status().Update→MutateAndPatchStatusand every finalizerr.Update→MutateAndPatchSpec. Condition/field mutations move inside the helper closures (the helper snapshots the object on entry, so pre-mutate changes are dropped from the diff). Added asetOIDCConfigValidTrueConditionhelper to factor out the Valid=True transition.IdentitySynthesizedadvisory was previously computed once in memory before validation; since that pre-mutate change would be dropped byMutateAndPatchStatus, it is now recomputed inside each status-write closure via a newsetValidTrueAndSynthesizedhelper (and on the validation-failure andsetInvalidpaths).applyIdentitySynthesizedConditionis idempotent on the same spec, so the advisory transition is preserved on every path.setInvalidalready used the helper correctly; its doc comment was updated to drop a stale reference to the removed upfront mutation.PreservesForeignConditionsregression test for each sibling, mirroring theMCPAuthzConfigguard: a foreign-owned condition must survive a reconcile alongside the controller-ownedValidcondition.Low level
cmd/thv-operator/controllers/mcpoidcconfig_controller.goMutateAndPatchSpec; all status writes viaMutateAndPatchStatus; newsetOIDCConfigValidTrueConditionhelpercmd/thv-operator/controllers/mcpexternalauthconfig_controller.gosetValidTrueAndSynthesizedhelper; fold advisory into every status closure; update stalesetInvaliddoc commentcmd/thv-operator/controllers/mcpoidcconfig_controller_test.goTestMCPOIDCConfigReconciler_PreservesForeignConditionscmd/thv-operator/controllers/mcpexternalauthconfig_controller_test.goTestMCPExternalAuthConfigReconciler_PreservesForeignConditionsType of change
Test plan
task buildpassestask testpasses forcmd/thv-operator/...unit packages (envtest integration suites excluded — they require the localetcdenvtest binary)PreservesForeignConditionsregression test added and passing for each sibling controllerIdentitySynthesizedtransition guards (...TransitionsOnValidationFailure,...OBO_ClearsStaleIdentitySynthesized) still passgrepACs: nor.Status().Update/client.Status().Updateand no spec/finalizerr.Update(ctxremain in the three config controllerstask lintblocked by a known golangci-lint go1.24-vs-go1.26 toolchain mismatch; CI runs lintSpecial notes for reviewers
The externalauth migration carries the only non-mechanical subtlety: the
IdentitySynthesizedadvisory must be recomputed from the current spec inside each status patch (not before it), orMutateAndPatchStatuswould snapshot the pre-mutate object and silently drop the transition. The existingIdentitySynthesizedTransitionsOnValidationFailuretest pins this contract and continues to pass.Residual gap (out of scope, tracked in #5511): three workload controllers (
MCPServer,VirtualMCPServer,MCPRemoteProxy) still do a fullr.Status().Updateagainst these config objects to appendReferencingWorkloads. A full PUT carries the wholeStatus.Conditionsarray, so until those writers are migrated too, the config controllers are not yet the sole owners of their own status — the protection this PR adds is necessary but not by itself sufficient.Generated with Claude Code