Repository navigation
feat!: replace ClusterProvider with a namespaced KubeProvider - #430
Merged
Merged
Conversation
KubeProvider is a namespaced source cluster: a kubeconfig Secret in its own namespace (spec.secretRef) or the operator's own identity (spec.inCluster), exactly one of the two, immutable. It admits its own namespace and those spec.accessFrom admits, and an omitted audit route is <namespace>.<name>. GitTarget, FluxTrigger and ArgoCDTrigger gain spec.kubeProviderRef. Nothing reads either yet; the references are optional until clusterProviderRef goes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Every reader names its source cluster with spec.kubeProviderRef: the GitTarget reconciler and its watch plane, the WatchRule and ClusterWatchRule gates, and the apply triggers. ClusterProvider is no longer read. A KubeProvider's kubeconfig Secret is read from its own namespace. Its spec.inCluster is honored only for a provider listed in the new --in-cluster-kube-providers flag; the resolver refuses any other with InClusterNotAuthorized, so the watches, discovery and appliers cannot reach the operator's identity through it. A provider admits its own namespace, and accessFrom admits the others. An omitted audit route is <namespace>.<name>, and a provider carrying a route another namespace holds reports AuditRouteConflict and is not used; its siblings on the route are woken when the holder goes. The watch plane keys a source cluster by the provider's namespace/name, and the resolver's version carries the provider's UID, so a provider recreated under the same name, inCluster ones included, rebuilds its clients. A credential the operator refuses keeps its reason on SourceClusterReachable. The ClusterProvider reasons and conditions are renamed: KubeProviderNotFound, KubeProviderReady, KubeProviderNotReady. The chart renders a KubeProvider named local in the release namespace and lists it in the flag. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The ClusterProvider kind and its CRD are removed: a CRD cannot change its scope, so nothing converts one into a KubeProvider. GitTarget, FluxTrigger and ArgoCDTrigger require spec.kubeProviderRef, with no default. spec.clusterProviderRef stays in each schema only so that setting it is refused, naming spec.kubeProviderRef. The triggers' Cluster column reads spec.kubeProviderRef. The settings index describes the KubeProvider. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… suite to it The reference docs, chart README, samples and layout corpus name a namespaced KubeProvider: its secretRef or inCluster credential, the --in-cluster-kube-providers consent, own-namespace admission, and audit routes held per namespace. UPGRADING describes the migration from ClusterProvider. The e2e install's KubeProvider is local in the operator's namespace, so the cluster's audit webhook posts under /audit-webhook/gitops-reverser.local. Every e2e GitTarget and trigger names it. The source-cluster corner covers a tenant's own provider and Secret, a second tenant refused without accessFrom, an inCluster provider the installation does not list, and two same-named providers in two namespaces kept apart across a recreate. The audit-route spec moves its dedicated provider into the operator's namespace and covers a provider borrowing that namespace's route. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Important Review skippedToo many files! This PR contains 226 files, which is 126 over the limit of 100. To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch. Upgrade to a paid plan to raise the limit. ⚙️ Run configuration
📒 Files selected for processing: 226
You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
…uster source_cluster carries the KubeProvider's namespace/name, so the CommitRequest spec looks for the install's provider rather than "default". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…er.spec.secretRef Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ommitted Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ranch The overlap check counted every GitTarget on a GitBranch, admitted or not, and the older one won. A GitTarget in a namespace the branch's GitProvider does not admit writes nothing, yet any namespace that knew a GitBranch's name could create one at "." first and hold every folder on that branch against the targets allowed to write it. An existing target now holds its path only while its GitProvider and KubeProvider admit it; the admission is read only for an older target that overlaps. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…tion intro up to date An accessFrom label selector admits every namespace whose labels match, so it is only as strong as the right to set those labels. security-model.md and configuration.md say so and point at platform-only labels or names. configuration.md's intro and usual flow name the GitBranch between the GitProvider and the GitTarget, and state that an unadmitted GitTarget holds no path. architecture.md lists GitBranch among the kinds, counts nine, names audit-webhook path segments as routes, and says a recreated or no longer listed inCluster provider rebuilds or drops its clients. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
sunib
added a commit
that referenced
this pull request
Oct 23, 2026
ClusterProvider became KubeProvider in #430; the overview and config-basics diagrams still said ClusterProvider, in the drawn text and the embedded Excalidraw scene alike. config-cluster.excalidraw.svg is referenced nowhere. The structural redraw of config-basics (GitBranch, KubeProvider, the optional applier claim row) is left for Excalidraw. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
sunib
added a commit
that referenced
this pull request
Oct 23, 2026
…oCDClaim (#438) * docs: plan the applier claim rename, and name coordinated folders The plan of record for renaming FluxTrigger/ArgoCDTrigger to FluxClaim/ArgoCDClaim (spec.applierClaimRef), with the doc corrections that ship alongside it. definitions.md and the bi-directional guide gain "coordinated" / "uncoordinated shared folder" for a folder's setup. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * refactor(applier)!: rename FluxTrigger/ArgoCDTrigger to FluxClaim/ArgoCDClaim The resources never declared when to apply (the branch worker decides that); they exclusively take an applier and hand it back. "Trigger" also already named watch-plan, API-surface and Argo Events triggers. Hard cut, with no users yet: - kinds FluxClaim and ArgoCDClaim (fluxclaims, argocdclaims) - GitTarget.spec.applierClaimRef replaces spec.applyTriggerRef - condition ApplierClaimReady, reasons ApplierClaimNotFound and HeldByAnotherClaim - finalizers configbutler.ai/flux-claim and configbutler.ai/argocd-claim - Go: the ApplierClaim interface, ClaimKind(), and the files with them Kept: every Applier* name (the applier is unchanged), Held and its reasons, status.operation, status.previousSuspendState, ApplyReason, the apply_* metrics and the applier_kind label. A folder an applier claim coordinates is a "coordinated folder" in comments. Also: the KubeProvider godoc describes a connection and an identity, not a source cluster, and a comment no longer mentions a sibling's flush, gone since #428. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * docs(images): name KubeProvider in the diagrams, and drop an unused one ClusterProvider became KubeProvider in #430; the overview and config-basics diagrams still said ClusterProvider, in the drawn text and the embedded Excalidraw scene alike. config-cluster.excalidraw.svg is referenced nowhere. The structural redraw of config-basics (GitBranch, KubeProvider, the optional applier claim row) is left for Excalidraw. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * docs: applier claims, and four corrections to how the docs describe them The guides, the reference pages and the definitions say applier claim, FluxClaim and ArgoCDClaim; a folder a GitTarget coordinates through one is a coordinated folder. definitions.md defines the applier claim as a declaration of control over apply timing only, keeps Bound distinct from Held, covers ArgoCDClaim in Held since #437, and adds "trigger" to the words we do not use. Design and review pages keep the names they were written with; apply-trigger.md says so at the top. Corrections: - C1: "the API is the source of truth" says for what, how a Git edit reaches the cluster, and that the applier's echoes are not mirrored. - C2: a KubeProvider is a connection and an identity, not a source cluster: an applier claim reaches a management cluster through it. - C3: the text about other GitProviders' branch workers flushing for an apply, and the exposure it described, are gone since #428. - O1: one applier claim holds an applier per KubeProvider, not per cluster; two providers for one cluster are not arbitrated, so name one. UPGRADING has the hard-cut entry. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(applier): adopt an applier anew after a hand-back that kept its value A hand-back that leaves an applier as it stood can keep the value under GitOps Reverser's field manager: spec.suspend: true for a Kustomization suspended before, enabled: false for an Application that was manual and whose switch was taken while held. Referenced again, the applier claim took that kept value for a hold in place: - an ArgoCDClaim recorded nothing, so its next hand-back gave the switch up and the Application synced on its own; - a FluxClaim recorded nothing and stamped no token, and waited forever for a suspend Flux could never report handled, since the hand-back gave its requestedAt up and Flux still reports the old one. An applier claim with no previousSuspendState, no Held=True and no operation in flight is adopting: a held field is then one a hand-back kept. Once an uncached read finds no other applier claim holding the applier, it records how the applier stands and holds it anew, a Kustomization with a fresh token, as it takes over any suspend. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(e2e): restore the playground ConfigMap's trigger key The rename turned --from-literal=trigger=tilt into two shell words, and kubectl refused the command ("exactly one NAME is required, got 2"). The key was never the apply trigger: it says what made the edit. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * lets do some graphics! * docs: show the new configuration diagrams where they explain the model config-platform-shared shows a platform namespace sharing a GitProvider and a GitReceiver with team namespaces, beside the text that says so; config-bi-directional shows a coordinated folder in the bi-directional model and under who applies this folder. config-basics, redrawn with GitBranch and KubeProvider, gets alt text that says what it shows. Also restores a sentence the rename garbled: the applier's own schedule is off, not its "own applier claims". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * docs(configuration): sharing connections across namespaces, with push routing "Why both provider types are namespaced" becomes a practical section on what a platform team shares and how teams reach it: the GitProvider and KubeProvider admit namespaces with accessFrom, the GitBranch sits beside its credential, and a GitReceiver needs no wiring at all, since a push wakes every GitProvider whose spec.url names the repository, for the branch it names. An example shows a platform namespace and a team's GitTarget writing its own branch through it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.
Phase 2 of
docs/design/gitbranch-and-kubeprovider-plan.md.What changes
KubeProvideris a namespaced kind naming a source cluster.ClusterProviderand its CRD are removed; a CRD cannot change scope, so nothing converts one. EveryClusterProvider*reason and identifier is renamed with no alias:KubeProviderNotFound,KubeProviderReady,KubeProviderNotReady.spectakes exactly one ofsecretRef(a kubeconfig Secret in the provider's own namespace) andinCluster: {}, enforced by CEL and immutable.clientis refused besideinCluster. The kubeconfig safety checks and their reasons are unchanged.inClusterneeds the installation's consent.--in-cluster-kube-providerstakes exactnamespace/nameentries (chart:controllerManager.inClusterKubeProviders, plus the chart's own provider added automatically). An unlisted provider reportsValidated=False,Stalled=True(InClusterNotAuthorized).SourceClusterReachable=Falsewith that reason, because a refused credential keeps its own reason instead of reading as unreachable.accessFromadmits the others.allowCrossNamespacekeeps its meaning.kubeProviderRefis a required, immutableNamespacedObjectReferenceonGitTarget,FluxTriggerandArgoCDTrigger, with nodefaultfallback.clusterProviderRefstays in each schema only to be refused by name, the pattern phase 1 used for the branch fields.auditRouteis<namespace>.<name>.Stalled=True(AuditRouteConflict), and authz refuses everything that would mirror or apply through it.source_clustermetric label carry<namespace>/<name>. The resolver's version token carries the provider UID, and refresh now re-resolves in-cluster sources as well. A provider deleted and recreated under the same name therefore rebuilds its clients and re-plans its watches, whichever credential arm it uses. A provider dropped from the allow-list loses the operator's identity on the next refresh.KubeProvidernamedlocalin the release namespace and lists it in the flag. The quick start references it.clusterProvider.*values are gone;kubeProvider.*replaces them.Also fixed: an unadmitted GitTarget could hold a path
Not about KubeProvider; it dates from #428/#429. The overlap check counted every GitTarget on a
GitBranch, admitted or not, and the older one won. Any namespace that knew aGitBranch's name could therefore create a GitTarget at.first and block every folder on that branch withTargetConflict. An existing target now holds its path only while itsGitProviderandKubeProvideradmit it. The docs also say that anaccessFromlabel selector is only as strong as the right to set the labels it matches.Decisions taken with the reviewer before coding
--in-cluster-kube-providers/controllerManager.inClusterKubeProviders.local.secretRef, per definitions.md rule 2.where-validation-lives.mdputs cross-object checks in the reconciler, the webhook is optional and fail-open, and admission cannot see a provider that changes its route later. The plan's "Done when" line is updated to match.Commits
Upgrade impact
Audit routes default to
<namespace>.<name>, so the chart's provider posts under/audit-webhook/<release namespace>.local, and the API server's audit webhook URL must change. The e2e cluster config does too; locally that means onetask clean-cluster. UPGRADING.md has the full migration.Tests
task lintandtask testpass.accessFrom;inClusterprovider;🤖 Generated with Claude Code