Consolidate MCPOIDCConfig ReferencingWorkloads ownership#5544
Merged
Conversation
The MCPServer, VirtualMCPServer, and MCPRemoteProxy controllers each wrote the referenced MCPOIDCConfig's status via a full r.Status().Update to append themselves to ReferencingWorkloads. This had two problems: - A full PUT carries the entire Status.Conditions array, so a write landing between the MCPOIDCConfig controller's Get and Patch clobbers the conditions that controller owns (the hazard #5511 tracks, exposed by the #5509 MutateAndPatchStatus migration). - The writes were append-only — they never removed stale entries. Only the MCPOIDCConfig controller, which watches all three workload kinds and recomputes the full list via findReferencingWorkloads, handles removals. Remove the three redundant cross-controller writers and let the MCPOIDCConfig controller be the sole owner of its ReferencingWorkloads. It already watches MCPServer/VirtualMCPServer/MCPRemoteProxy and recomputes the authoritative list (additions and removals) on every relevant change, so the field stays correct without the workload controllers touching the config's status. Drop the unit tests that exercised the removed helpers; the config controller's own reference-tracking tests (and the mcp-oidc-config envtest suites, which use Eventually) cover the consolidated behavior. Closes #5511 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #5544 +/- ##
==========================================
+ Coverage 69.87% 69.88% +0.01%
==========================================
Files 648 648
Lines 65873 65870 -3
==========================================
+ Hits 46029 46034 +5
+ Misses 16500 16490 -10
- Partials 3344 3346 +2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Follow-up to the ReferencingWorkloads consolidation, addressing review feedback: - Remove the now-unused +kubebuilder:rbac mcpoidcconfigs/status (get;update;patch) markers from the MCPServer, VirtualMCPServer, and MCPRemoteProxy controllers. They read the config's status via a plain Get on the main resource (covered by the mcpoidcconfigs get marker), so the /status subresource verbs are dead. This is marker-accuracy hygiene: the operator runs as a single ServiceAccount whose aggregated role still grants mcpoidcconfigs/status write via the MCPOIDCConfig controller's own marker, so the generated ClusterRole is unchanged. - Add TestMCPServerReconciler_handleOIDCConfig_DoesNotWriteConfigStatus, which asserts the MCPServer reconcile leaves the referenced config's status (ResourceVersion, ReferencingWorkloads, conditions) untouched. Since RBAC does not enforce the boundary under a shared ServiceAccount, this test is the actual guard against a future reintroduced cross-controller writer. - Align the VirtualMCPServer/MCPRemoteProxy call-site comments with the fuller MCPServer wording (clobber + append-only rationale). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jhrozek
approved these changes
Jun 17, 2026
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
The #5509 migration made each config controller patch-safe, but the
MCPOIDCConfigcontroller is still not the sole owner of its status: theMCPServer,VirtualMCPServer, andMCPRemoteProxycontrollers each do a fullr.Status().Updateagainst the referencedMCPOIDCConfigto append themselves toReferencingWorkloads. A full PUT carries the wholeStatus.Conditionsarray, so a write landing between the config controller's Get and Patch clobbers the conditions that controller owns. The writes are also append-only — they never remove stale entries. This removes them and lets theMCPOIDCConfigcontroller (which already watches all three workload kinds and recomputes the full list with removals) be the sole owner.Closes #5511
Medium level
updateOIDCConfigReferencingWorkloadsand its call site from theMCPServer,VirtualMCPServer, andMCPRemoteProxycontrollers. Each call site keeps the rest of its OIDC handling (ref validation + hash tracking on the workload's own status); only the cross-controller write to the config object is gone.MCPOIDCConfigcontroller is unchanged: it alreadyWatchesMCPServer/VirtualMCPServer/MCPRemoteProxy and recomputes the authoritativeReferencingWorkloads(additions and removals) viafindReferencingWorkloadson every relevant change.ReferencingWorkloadsnow has a single owner.Low level
cmd/thv-operator/controllers/mcpserver_controller.goupdateOIDCConfigReferencingWorkloads+ call; explanatory comment referencing #5511cmd/thv-operator/controllers/virtualmcpserver_controller.gocmd/thv-operator/controllers/mcpremoteproxy_controller.go*_test.go(3 files)expectReferencingServertable assertionType of change
Test plan
task buildpassestask testpasses forcmd/thv-operator/controllers(unit)MCPOIDCConfigcontroller retainsReferencingWorkloadscoverage:TestMCPOIDCConfigReconciler_ReferenceCountUpdatedWithWorkloads(population) andTestMCPOIDCConfigReconciler_handleDeletion_BlocksWhenReferenced(deletion-block), plus themcp-oidc-configenvtest suites which assert population and cleanup viaEventuallytask lintblocked by the known golangci-lint go1.24-vs-go1.26 toolchain mismatch; CI runs lintgofmtcleanSpecial notes for reviewers
Behavioral change worth a careful look:
MCPOIDCConfig.ReferencingWorkloadsis now populated only by the config controller's watch-driven reconcile, not synchronously during the referencing workload's reconcile. This is eventually-consistent (the watch fires near-immediately) and is the intended design — the field is informational status, and deletion-blocking already recomputes the live list rather than reading stored status. Themcp-oidc-configenvtest suites already assert this viaEventually, so they continue to exercise the consolidated path.MCPExternalAuthConfigandMCPAuthzConfignever had cross-controller writers, so they needed no change.Generated with Claude Code
Review follow-up (addressed in latest commit):
mcpoidcconfigs/statusmarkers from the three workload controllers. Note this is marker-accuracy hygiene, not a privilege reduction — the operator is a single ServiceAccount and its aggregated role still grantsmcpoidcconfigs/statuswrite via the MCPOIDCConfig controller's own marker (regeneratingrole.yamlproduces no diff).WithInterceptorFuncstest — there is no longer a full-PUT path in these controllers to regress against. Because RBAC does not enforce the boundary under a shared ServiceAccount, the newTestMCPServerReconciler_handleOIDCConfig_DoesNotWriteConfigStatusis the actual guard: it fails if a workload reconcile ever writes the config's status again.