controller

package
v0.49.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 22, 2026 License: Apache-2.0 Imports: 44 Imported by: 0

Documentation

Overview

Package controller contains shared constants for all controllers.

Index

Constants

View Source
const (
	ClusterWatchRuleReasonValidating            = "Validating"
	ClusterWatchRuleReasonGitProviderNotFound   = "GitRepoConfigNotFound"
	ClusterWatchRuleReasonGitRepoConfigNotReady = "GitRepoConfigNotReady"
	ClusterWatchRuleReasonAccessDenied          = "AccessDenied"
	ClusterWatchRuleReasonGitTargetNotFound     = "GitTargetNotFound"
	ClusterWatchRuleReasonGitDestinationInvalid = "GitDestinationInvalid"
	ClusterWatchRuleReasonReady                 = ReasonSucceeded
	ClusterWatchRuleReasonResourcesResolved     = "Resolved"
	ClusterWatchRuleReasonUnresolvedResources   = "UnresolvedResources"

	// ClusterWatchRuleReasonGitTargetNamespaceNotAuthorized is the terminal reason when the
	// referenced GitTarget's namespace is not admitted by that target's ClusterProvider. It is
	// re-exported from internal/watch, where the shared compile path both bootstrap and this
	// reconciler call decides it, so the two can never drift.
	ClusterWatchRuleReasonGitTargetNamespaceNotAuthorized = watch.ClusterWatchRuleReasonGitTargetNamespaceNotAuthorized
)

ClusterWatchRule status condition reasons.

View Source
const (
	// ConditionTypeReady indicates whether the resource is ready.
	ConditionTypeReady = "Ready"
	// ConditionTypeResourcesResolved indicates whether rule resources resolved to concrete GVRs.
	ConditionTypeResourcesResolved = "ResourcesResolved"
	// ConditionTypeReconciling is the kstatus progress condition. It is abnormal-true.
	ConditionTypeReconciling = "Reconciling"
	// ConditionTypeStalled is the kstatus blocked condition. It is abnormal-true.
	ConditionTypeStalled = "Stalled"
	// ConditionTypeStreamsRunning indicates whether watched type streams are routing live events.
	ConditionTypeStreamsRunning = "StreamsRunning"
	// ConditionTypeGitPathAccepted indicates whether the GitTarget path is safe to materialize.
	ConditionTypeGitPathAccepted = "GitPathAccepted"
	// ConditionTypeRenderMatchesLive indicates whether every current render scope agrees with live.
	ConditionTypeRenderMatchesLive = "RenderMatchesLive"
	// ConditionTypeGitTargetReady indicates whether the referenced GitTarget is ready for writes.
	ConditionTypeGitTargetReady = "GitTargetReady"
	// ConditionTypeSourceNamespaceAuthorized reports whether a rule's EFFECTIVE source namespace
	// is authorized for the observed generation. It is positive and state-style, and it is set
	// even for legacy own-namespace rules (reason LegacySourceNamespace) so the effective
	// authorization is always visible and automation has ONE condition to inspect.
	//
	// It is deliberately distinct from GitTargetReady, which stays the health of the referenced
	// GitTarget and must never be reused for source authorization. Its three values are not
	// interchangeable: False is a refusal (terminal, Stalled=True), while Unknown covers both "the
	// answer is still being established" and "a rule with an already-resolved scope has lost the
	// ability to re-evaluate its policy and is retaining that scope" — neither of which may be
	// rendered as a permanent failure.
	ConditionTypeSourceNamespaceAuthorized = "SourceNamespaceAuthorized"
	// ConditionTypeStreamsReady is a source-compatibility alias for StreamsRunning.
	ConditionTypeStreamsReady = ConditionTypeStreamsRunning
	// ConditionTypeAuthorAttributed indicates whether a CommitRequest's commit author
	// was named from the submitter captured at admission. It is binary and immediately
	// settled (no Unknown, no timeout): True (AttributedFromAdmission) when the
	// validate-operator-types webhook recorded the submitter, False (CommitterFallback) when
	// capture ran but found no record, or False (AuthorCaptureDisabled) when capture is off.
	// False is not a failure and does not affect Ready: the
	// request claims no actor and can attach only to an unnamed watch window
	// (docs/architecture.md#commitrequest-finalize).
	ConditionTypeAuthorAttributed = "AuthorAttributed"
	// ConditionTypePushed indicates whether a CommitRequest's commit reached the
	// remote repository.
	ConditionTypePushed = "Pushed"

	// ClusterProviderConditionValidated reports whether a ClusterProvider's inputs are safe and
	// resolvable: the in-cluster "default" provider is trivially Validated; a remote provider is
	// Validated once its kubeconfig Secret is present, keyed, and passes the exec/TLS safety
	// policy. It is asserted WITHOUT a network dial — runtime reachability/discovery health are
	// deferred until authenticated remote ingest wires them from the watch engine.
	ClusterProviderConditionValidated = "Validated"

	// ClusterProviderConditionAuditFactsReceived reports whether an attribution fact has ever been
	// published on this provider's audit route (spec.attribution.auditRoute, defaulting to the
	// provider's name). It is Unknown from creation until the first fact, with the reason naming
	// which silence it is (see the three below), and True (ReasonFactsReceived) from then on. It
	// NEVER regresses: the persisted status is the latch, so a restart — or the memory transport,
	// whose in-process state is lost with the process — keeps it True. Later silence is a quiet cluster, not a fault, which is why there is
	// no False state, no grace window and no timer.
	//
	// It is deliberately NOT part of Ready: a provider reading a route nobody posts under mirrors
	// perfectly and loses only the commit AUTHOR, so reporting it through Ready would tell every
	// kstatus reader the object is broken. It is absent entirely when the operator runs with
	// --author-attribution=false, where no fact was ever expected.
	ClusterProviderConditionAuditFactsReceived = "AuditFactsReceived"

	// ReasonTransportUnavailable is the AuditFactsReceived=Unknown reason when the last append to
	// the fact transport failed. Nothing can be concluded about any route's configuration while
	// this holds. The fix is the operator's transport, not the reader's audit webhook.
	ReasonTransportUnavailable = "TransportUnavailable"
	// ReasonNoAuditDelivery is the AuditFactsReceived=Unknown reason when no audit request has ever
	// reached this operator, on any route. Nothing is posting to it, so the fix is the API server's
	// audit webhook configuration and its connectivity, never this provider's route.
	ReasonNoAuditDelivery = "NoAuditDelivery"
	// ReasonRouteUnused is the AuditFactsReceived=Unknown reason when audit requests ARE arriving
	// and nothing has ever landed on this provider's route. That is the one case where the route
	// itself is the thing to look at.
	//
	// It deliberately does not claim the facts went to some OTHER route, which would be the sharper
	// message: requests can also arrive and produce no fact anywhere, when the audit policy's level
	// or verbs leave nothing attributable. This reason stays true for both.
	ReasonRouteUnused = "RouteUnused"
	// ReasonFactsReceived is the AuditFactsReceived=True reason: a fact has arrived on the route,
	// and that answer is latched.
	ReasonFactsReceived = "Received"

	// ReasonValidated is the Validated=True reason.
	ReasonValidated = "Validated"
	// ReasonInCluster is the Validated=True reason for the in-cluster "default" provider.
	ReasonInCluster = "InCluster"
	// ReasonKubeConfigInvalid is the Validated=False reason for a malformed or unsafe kubeconfig
	// whose specific cause is carried in the message.
	ReasonKubeConfigInvalid = "KubeConfigInvalid"

	// MsgSnapshotCompleted is returned as the condition message when the initial
	// cluster snapshot has been successfully committed to Git.
	MsgSnapshotCompleted = "Initial snapshot reconciliation completed"

	// RequeueSteadyInterval is the unified control-plane periodic reconcile fallback.
	// The control plane no longer watches Secrets (docs/rbac.md),
	// so out-of-band credential and age-key changes are picked up on this steady cadence
	// instead of via a Secret informer. It replaces the former split of a 2-minute
	// transient-retry, a 5-minute auth/secret, and a 10-minute revalidation interval with
	// a single 5-minute fallback for the GitProvider, GitTarget, WatchRule, and
	// ClusterWatchRule reconcilers. The fast stream-settle loop below is separate.
	RequeueSteadyInterval = 5 * time.Minute
	// RequeueStreamSettleInterval is the requeue interval while a Ready GitTarget still
	// has streams pending replay completion. Stream status is computed during reconcile, so
	// this keeps status.streams fresh while watches converge.
	RequeueStreamSettleInterval = 10 * time.Second
	// RequeueWriteLostInterval is how soon a reconcile whose status write lost the optimistic-lock
	// race comes back. It is short because there is nothing to wait FOR: the write was rejected
	// against a resourceVersion this reconcile had already read, so the only thing that has to
	// happen before the retry can succeed is the informer cache catching up with a write that is
	// already durable — milliseconds, not a settle window.
	//
	// It used to borrow RequeueStreamSettleInterval, which put the recovery ten seconds out and
	// made every lost write a ten-second window of published-but-stale status. Two unrelated
	// cadences shared one constant; only the coincidence that both were "soon" hid it.
	RequeueWriteLostInterval = 100 * time.Millisecond

	// RetryInitialDuration is the initial duration for exponential backoff retry.
	RetryInitialDuration = 100 * time.Millisecond
	// RetryBackoffFactor is the multiplicative factor for exponential backoff.
	RetryBackoffFactor = 2.0
	// RetryBackoffJitter is the jitter factor for retry backoff.
	RetryBackoffJitter = 0.1
	// RetryMaxSteps is the maximum number of retry attempts.
	RetryMaxSteps = 5

	// ReasonSucceeded is the reason on a healthy, fully reconciled object. It replaces the former
	// "OK"/"Ready" spellings, which restated the condition type instead of answering "why".
	ReasonSucceeded = fluxmeta.SucceededReason
	// ReasonProgressing indicates that a stream or control-plane gate is still converging.
	ReasonProgressing = fluxmeta.ProgressingReason

	// ReasonChecking indicates that the controller is checking the resource status.
	ReasonChecking = "Checking"
	// ReasonReconciling indicates that reconciliation is still making progress.
	ReasonReconciling = "Reconciling"
	// ReasonStalled indicates that reconciliation is blocked until a human fixes the object or dependency.
	ReasonStalled = "Stalled"
	// ReasonSecretNotFound indicates that the referenced secret was not found.
	ReasonSecretNotFound = "SecretNotFound"
	// ReasonSecretMalformed indicates that the referenced secret is invalid.
	ReasonSecretMalformed = "SecretMalformed"
	// ReasonConnectionFailed indicates that the connection to the provider failed.
	ReasonConnectionFailed = "ConnectionFailed"
	// ReasonCommitConfigInvalid indicates the commit configuration is invalid.
	ReasonCommitConfigInvalid = "CommitConfigInvalid"
	// ReasonEncryptionConfigInvalid indicates encryption configuration is invalid.
	ReasonEncryptionConfigInvalid = "EncryptionConfigInvalid"
)
View Source
const (
	GitTargetConditionReady                = ConditionTypeReady
	GitTargetConditionReconciling          = ConditionTypeReconciling
	GitTargetConditionStalled              = ConditionTypeStalled
	GitTargetConditionValidated            = "Validated"
	GitTargetConditionEncryptionConfigured = "EncryptionConfigured"
	GitTargetConditionGitPathAccepted      = ConditionTypeGitPathAccepted
	GitTargetConditionRenderMatchesLive    = ConditionTypeRenderMatchesLive
	// GitTargetConditionStreamsRunning is the source data-plane axis: True when every tracked type's
	// watch has crossed its replay watermark or resumed from a durable cursor.
	GitTargetConditionStreamsRunning = ConditionTypeStreamsRunning
)
View Source
const (
	// GitTargetReasonOK is the healthy reason. It is the shared Succeeded vocabulary rather than
	// a per-kind spelling; the name is kept for call-site stability.
	GitTargetReasonOK                   = ReasonSucceeded
	GitTargetReasonProviderNotFound     = "ProviderNotFound"
	GitTargetReasonBranchNotAllowed     = "BranchNotAllowed"
	GitTargetReasonTargetConflict       = "TargetConflict"
	GitTargetReasonNotChecked           = "NotChecked"
	GitTargetReasonBlocked              = "Blocked"
	GitTargetReasonNotStarted           = "NotStarted"
	GitTargetReasonNotRequired          = "NotRequired"
	GitTargetReasonMissingSecret        = "MissingSecret"
	GitTargetReasonInvalidConfig        = "InvalidConfig"
	GitTargetReasonSecretCreateDisabled = "SecretCreateDisabled"
	GitTargetReasonGitPathAccepted      = "GitPathAccepted"
	GitTargetReasonUnsupportedContent   = "UnsupportedContent"
	// GitTargetReasonIgnoreShadowsManagedPath is the terminal reason for the one
	// unrecoverable .gittargetignore footgun (docs/spec/gitpath-foreign-content-stringency.md
	// §4.3): an ignore pattern matches a path the operator writes, which would blind it to its
	// own file. The writer's write-plan precondition refuses the flush before any byte is
	// written and the GitTarget is failed with this reason. The string must stay in sync with
	// the watch package's gitPathRefusalReason.
	GitTargetReasonIgnoreShadowsManagedPath = "IgnoreShadowsManagedPath"
	// GitTargetReasonWriteBoundaryRefused is the reason for a write the operator refused
	// because it had nowhere safe to land, rather than because the folder holds content the
	// operator cannot manage
	// (docs/design/support-boundary/gittarget-granularity-and-cross-environment-edits.md §1): a
	// planned write escaping spec.path (L1), or an in-place edit of a source file more than
	// one kustomize render root reaches (L2, write-fan-in > 1). Nothing was committed. The
	// string must stay in sync with the watch package's gitPathRefusalReason.
	GitTargetReasonWriteBoundaryRefused = "WriteBoundaryRefused"
	// GitTargetReasonMultipleSourceNamespaces is the terminal reason for a target that declared
	// its folder namespace-free (spec.serializeNamespace: false) and that more than one source
	// namespace reaches. Two namespaces produce documents whose identities are equal once the
	// namespace is stripped, so each write flips one document between two live objects; the writer
	// refuses the flush before any byte is written. The remedy is a GitTarget or WatchRule edit.
	// The string must stay in sync with manifestanalyzer.GitPathRefusalReason.
	GitTargetReasonMultipleSourceNamespaces = "MultipleSourceNamespaces"
	// GitTargetReasonUnrenderedPlacement is the terminal reason for a placement under
	// spec.placement.useKustomize that no kustomization would render: the folder already has a
	// render root and the resolved path is outside it. Nothing is written, because a document in
	// Git that nothing applies looks mirrored and is not. The remedy is the target's own template
	// or path. The string must stay in sync with manifestanalyzer.GitPathRefusalReason.
	GitTargetReasonUnrenderedPlacement    = "UnrenderedPlacement"
	GitTargetReasonRenderMatchesLive      = "RenderMatchesLive"
	GitTargetReasonRenderDoesNotMatchLive = "RenderDoesNotMatchLive"
	GitTargetReasonRenderRechecking       = "Rechecking"

	GitTargetReadyReasonValidationFailed        = "ValidationFailed"
	GitTargetReadyReasonEncryptionNotConfigured = "EncryptionNotConfigured"
	GitTargetReadyReasonWorkerUnavailable       = "WorkerUnavailable"
	// GitTargetReasonWatchPlanFailing is Ready=False/Reconciling=True while the watch-plane
	// owner's pass for this GitTarget keeps failing and being retried on its backoff ladder. It
	// is deliberately a PROGRESSING reason rather than a stall: a pass fails on a source cluster
	// that is unreachable or a catalog that is not observable yet, both of which recover on their
	// own. What it buys is that such a target does not read as idle.
	GitTargetReasonWatchPlanFailing = "WatchPlanFailing"

	GitTargetStreamsRunningReasonNotReady = "NotReady"

	// GitTargetReasonSuspended is Ready=True on a target whose spec.suspend stops it writing. It
	// is True on purpose: suppressing writes on request is a configured outcome, not ill health.
	GitTargetReasonSuspended = "Suspended"
)
View Source
const (
	// GitTargetReasonAmbiguousLayout is a GitTarget path covering more than one kustomize render
	// root. The string must stay in sync with the watch package's gitPathRefusalReason, which
	// maps the corresponding write refusal onto GitPathAccepted.
	GitTargetReasonAmbiguousLayout = "AmbiguousLayout"
	// GitTargetReasonLayoutNotScanned is the pre-scan state: the target has not read its folder
	// yet, so nothing is known about its layout. Distinct from a folder that resolved to nothing.
	GitTargetReasonLayoutNotScanned = "NotScanned"
)
View Source
const (
	// GitTargetConditionSourceClusterReachable is the RUNTIME reachability of the source
	// cluster a GitTarget mirrors from: True (reason LocalCluster) when kubeConfig is omitted,
	// Unknown before the data plane's first discovery, False after a real failed attempt. It is
	// distinct from Validated: a missing/malformed kubeconfig is Validated=False (an input the
	// controller reads without a network dial); an otherwise-valid kubeconfig whose API server
	// cannot be contacted is SourceClusterReachable=False.
	GitTargetConditionSourceClusterReachable = "SourceClusterReachable"
	// GitTargetConditionGitProviderReady projects the referenced GitProvider's Ready onto the
	// GitTarget, so one `kubectl get gittarget` separates source-side (SourceClusterReachable)
	// from destination-side (GitProviderReady) failure.
	GitTargetConditionGitProviderReady = "GitProviderReady"

	// GitTargetReasonGitProviderNotReady is the GitProviderReady=False reason.
	GitTargetReasonGitProviderNotReady = "GitProviderNotReady"
	// GitTargetReasonGitProviderReady is the GitProviderReady=True reason.
	GitTargetReasonGitProviderReady = "GitProviderReady"

	// GitTargetConditionClusterProviderReady projects the referenced ClusterProvider's Ready onto
	// the GitTarget, so one `kubectl get gittarget` shows whether the SOURCE cluster's provider is
	// healthy — distinct from SourceClusterReachable (the data plane's runtime reach) and
	// GitProviderReady (the destination). It follows the GitProviderReady contract: only an
	// EXPLICIT Ready=False downgrades the GitTarget; a provider that has not reported readiness yet
	// is Unknown and does not. A MISSING provider never reaches this projection at all — that is the
	// Validated gate's hard denial (GitTargetReasonClusterProviderNotFound), not a readiness axis.
	GitTargetConditionClusterProviderReady = "ClusterProviderReady"
	// GitTargetReasonClusterProviderNotReady is the ClusterProviderReady=False/Unknown reason.
	GitTargetReasonClusterProviderNotReady = "ClusterProviderNotReady"
	// GitTargetReasonClusterProviderReady is the ClusterProviderReady=True reason.
	GitTargetReasonClusterProviderReady = "ClusterProviderReady"
)
View Source
const (
	WatchRuleReasonValidating            = "Validating"
	WatchRuleReasonGitProviderNotFound   = "GitRepoConfigNotFound"
	WatchRuleReasonGitRepoConfigNotReady = "GitRepoConfigNotReady"
	WatchRuleReasonAccessDenied          = "AccessDenied"
	WatchRuleReasonGitTargetNotFound     = "GitTargetNotFound"
	WatchRuleReasonGitDestinationInvalid = "GitDestinationInvalid"
	WatchRuleReasonReady                 = ReasonSucceeded
	WatchRuleReasonResourcesResolved     = "Resolved"
	WatchRuleReasonUnresolvedResources   = "UnresolvedResources"
)

WatchRule status condition reasons.

View Source
const (
	// WatchRuleReasonLegacySourceNamespace is the True reason when every item watches the rule's
	// own namespace.
	WatchRuleReasonLegacySourceNamespace = authz.ReasonLegacySourceNamespace
	// WatchRuleReasonSourceNamespaceAllowed is the True reason when every item is admitted and at
	// least one names a namespace other than the rule's own — an authorized override or the
	// cluster-wide wildcard.
	WatchRuleReasonSourceNamespaceAllowed = authz.ReasonSourceNamespaceAllowed
	// WatchRuleReasonSourceNamespaceNotAllowed is the TERMINAL False reason for a refusal.
	WatchRuleReasonSourceNamespaceNotAllowed = authz.ReasonSourceNamespaceNotAllowed
)

SourceNamespaceAuthorized condition reasons, re-exported from internal/authz so the decision and the status surface can never drift apart.

Three of the six went with the source-side selector: the condition can no longer be Unknown, because the gate reads only control-plane objects this reconcile already has, and it can no longer report an empty resolved scope, because "*" is one cluster-wide stream rather than a set that could come back empty.

View Source
const GitTargetConditionLayoutResolved = "LayoutResolved"

GitTargetConditionLayoutResolved reports what the last scan resolved about the folder's shape: which kustomize render root governs new documents, or that there is none, or that there are several. It is an OBSERVATION and writes no part of the kstatus trio — a folder with no kustomization at all is a perfectly healthy folder, and most of them are.

View Source
const GitTargetConditionStreamsReady = GitTargetConditionStreamsRunning
View Source
const GitTargetReasonClusterProviderNotFound = authz.ReasonClusterProviderNotFound

GitTargetReasonClusterProviderNotFound is the Validated=False reason when a GitTarget's referenced ClusterProvider does not exist. This is a HARD GATE: a GitTarget may mirror a source cluster ONLY through an existing ClusterProvider, "default" included. The operator never creates one, so a target whose provider was never declared is held NotReady and its data plane stopped rather than mirroring on an implicit local identity.

View Source
const GitTargetReasonConflict = GitTargetReasonTargetConflict
View Source
const GitTargetReasonNamespaceNotAuthorized = authz.ReasonNamespaceNotAuthorized

GitTargetReasonNamespaceNotAuthorized is the Validated=False reason when a GitTarget's namespace is not admitted by its referenced ClusterProvider's spec.accessFrom. It runs on every reconcile, so a policy tightened AFTER a GitTarget was created stops that target's watches too.

View Source
const GitTargetReasonReady = GitTargetConditionReady

GitTargetReasonReady is a backward-compatible alias used by existing tests.

View Source
const GitTargetStreamsReadyReasonNotReady = GitTargetStreamsRunningReasonNotReady
View Source
const LegacyClusterProviderFinalizer = "configbutler.ai/clusterprovider-fact-purge"

LegacyClusterProviderFinalizer is the fact-purge finalizer this controller USED to take. It is no longer added; it is only ever REMOVED, so an object created by an older operator can still be deleted after an upgrade instead of stranding in Terminating.

DO NOT REINTRODUCE A FINALIZER HERE. It was dropped because purge-on-delete is a promise only a living operator can keep, and the two cases that matter break it. `helm uninstall` removes the manager and the ClusterProvider together, so nothing detaches the finalizer, the object strands in Terminating forever, and the next `helm install --wait` then fails on it — recovering needs a manual kubectl patch. That was hit on a live cluster and is why this shipped. An operator that is simply down blocks the delete instead, and then loses the purge anyway if the object is force-removed, so the finalizer does not even reliably deliver the guarantee it exists for.

The purge it protected is not needed either, and is now unsafe. Facts are keyed by (audit route, group/resource, uid, resourceVersion) and expire on their own, so a re-provisioned cluster produces different object UIDs and cannot join a stale fact on the exact key. And since spec.attribution.auditRoute is what partitions them, SEVERAL ClusterProviders may share one route (an API server posts audit under exactly one), so purging on a single provider's deletion would drop the facts of every other provider on that route, and of the operator's own cluster, which was never torn down. A purge would have to be re-derived against the route, not the object.

View Source
const ReconcileRequestAnnotation = "reconcile.configbutler.ai/requestedAt"

ReconcileRequestAnnotation asks a GitTarget to be reconciled at once, and to re-read its Git folder rather than wait for the periodic pass. Set it to any value that changes — a timestamp is the convention — and the change is what triggers; the value itself carries no meaning.

The spelling is Flux's, deliberately: `reconcile.fluxcd.io/requestedAt` is what this ecosystem already types, and `flux reconcile` is the muscle memory a user brings. Nothing else in this repository had a reconcile-request convention to be consistent with instead.

It matters most for a folder someone else edits. The resolution only refreshes when this target scans, and a resolution that only refreshes on the periodic cadence is one you cannot iterate with: edit the folder, request a reconcile, read status.placement.

Variables

This section is empty.

Functions

This section is empty.

Types

type AuditRouteFacts added in v0.44.0

type AuditRouteFacts interface {
	// FirstFactAt reports when the first fact was published on a route IN THIS PROCESS. A restart
	// empties it, which is why the condition latches in the persisted status instead.
	FirstFactAt(route string) (time.Time, bool)
	// FirstFactEvents carries a ClusterProvider-shaped event naming the ROUTE whose first fact just
	// landed, so the providers reading it re-reconcile within seconds.
	FirstFactEvents() <-chan event.GenericEvent
	// TransportFailing and AuditDelivered are PROCESS-WIDE, not per route. They exist only to say
	// which of the three silences a provider with no facts is in; neither is ever asserted as this
	// provider's own status, because a fault shared by the whole pipeline must not become a verdict
	// repeated on every object that reads it.
	TransportFailing() bool
	AuditDelivered() bool
	// TransportName is the configured transport ("redis" or "memory"), used only to point the
	// reader at the right thing while publishes are failing. Empty yields neutral wording.
	TransportName() string
}

AuditRouteFacts is the per-route "has a fact ever been published here" registry the AuditFactsReceived condition reads. internal/attribution.RouteHealth implements it; a nil reconciler field means author attribution is off, and the condition is then absent rather than Unknown — nothing was ever expected on the route.

type ClusterProviderReconciler added in v0.38.0

type ClusterProviderReconciler struct {
	client.Client

	Scheme *runtime.Scheme

	// OperatorNamespace is the namespace a remote provider's kubeConfig Secret is pinned to (the
	// operator's own namespace). A cluster-scoped provider has no namespace of its own, so the
	// credential for a cluster is always read from here — never from the source cluster.
	OperatorNamespace string

	// KubeConfigSafety gates exec-auth and insecure-TLS kubeconfigs (reject-not-strip), matching
	// what the watch engine's resolver enforces, so Validated agrees with what a watch would use.
	KubeConfigSafety kubeconfig.SafetyPolicy

	// AuditFacts is the per-audit-route fact registry the AuditFactsReceived condition latches on.
	// Nil means the operator runs with author attribution disabled, and the condition is then
	// absent rather than Unknown.
	AuditFacts AuditRouteFacts

	// Recorder emits a Kubernetes Event on every persisted Ready transition; nil disables Events.
	Recorder record.EventRecorder
	// contains filtered or unexported fields
}

ClusterProviderReconciler reconciles a ClusterProvider object. It is the read-side peer of the GitProviderReconciler: it validates the cluster's connectivity inputs (spec.kubeConfig) without dialing, and owns the per-cluster status the watch engine and GitTargets project from. The in-cluster "default" provider has no kubeConfig and is trivially Validated.

func (*ClusterProviderReconciler) Reconcile added in v0.38.0

Reconcile validates a ClusterProvider's inputs and updates its status.

func (*ClusterProviderReconciler) SetupWithManager added in v0.38.0

func (r *ClusterProviderReconciler) SetupWithManager(mgr ctrl.Manager) error

SetupWithManager sets up the controller with the Manager.

type ClusterWatchRuleReconciler

type ClusterWatchRuleReconciler struct {
	client.Client

	Scheme       *runtime.Scheme
	RuleStore    *rulestore.RuleStore
	WatchManager WatchManagerInterface
	// Recorder emits a Kubernetes Event on every persisted Ready transition; nil disables Events.
	Recorder record.EventRecorder
}

ClusterWatchRuleReconciler reconciles a ClusterWatchRule object.

func (*ClusterWatchRuleReconciler) Reconcile

func (*ClusterWatchRuleReconciler) SetupWithManager

func (r *ClusterWatchRuleReconciler) SetupWithManager(mgr ctrl.Manager) error

SetupWithManager sets up the controller with the Manager.

type CommandAuthorLookup

type CommandAuthorLookup interface {
	LookupCommandAuthor(ctx context.Context, uid types.UID) (queue.CommandAuthor, bool)
}

CommandAuthorLookup resolves the author of a CommitRequest from the submitter captured at admission by the validate-operator-types webhook, keyed by the persisted object's UID. *queue.CommandAuthorStore satisfies it without adaptation. The lookup is present-or-never (docs/spec/commitrequest-admission-authorship.md): a miss is immediate and final — the webhook is not configured (or a best-effort write missed) — and the request claims no actor with no wait.

type CommitRequestFinalizer

type CommitRequestFinalizer interface {
	ServiceCommitRequest(ctx context.Context, attach git.AttachCommitRequest) (git.FinalizeResult, bool, error)
}

CommitRequestFinalizer is the EventRouter seam the reconciler drives, using the attach-then-poll protocol (docs/spec/commitrequest-design.md): ServiceCommitRequest registers the attach idempotently on the GitTarget's branch worker (bind the message to the author's open window, finalize after the grace) and returns the request's current outcome — resolved=false means keep polling. watch.EventRouter satisfies it without adaptation.

There is no watermark barrier: the interactive case is covered by the human gap between the edit and the save, UC2 by the collect-grace. The grace is anchored at attribution — the worker stamps finalizeAt = receipt + closeDelaySeconds — so the controller no longer holds the finalize itself.

type CommitRequestReconciler

type CommitRequestReconciler struct {
	client.Client

	Scheme *runtime.Scheme

	// APIReader performs uncached reads so a stale cache echo of our own
	// status stamp can never re-run a finalize that already reached a
	// terminal phase. Nil falls back to the (cached) Client.
	APIReader client.Reader

	// Finalizer attaches the request to the author-bound open window and reports
	// its outcome; AuthorLookup resolves the submitter captured at admission. When
	// AuthorLookup is nil (the validate-operator-types webhook is disabled), requests
	// claim no actor immediately, with AuthorAttributed=False. The attached window
	// determines the eventual Git author.
	Finalizer    CommitRequestFinalizer
	AuthorLookup CommandAuthorLookup
}

CommitRequestReconciler deliberately does NOT use reconcileStatus, which every other controller here shares. The difference is not stylistic, so it is worth saying once:

  • Its For() carries no GenerationChangedPredicate, so a status write re-enqueues the object. The lost-write hazard reconcileStatus exists to close — the winning status-only update being filtered out, leaving nothing to correct the stale answer — cannot arise.
  • It reads through APIReader (uncached) precisely so a cache echo cannot re-drive work, which is the same lag that makes reconcileStatus lose its optimistic lock in the first place.
  • The policies are opposites, and reconcileStatus's is the wrong one here. It drops a losing write and asks the caller to come back and recompute; a re-run of THIS reconcile would re-finalize an already-flushed window. So writeTerminalStatus retries in place, bounded, and gives up rather than requeue. (The worker now defends the same boundary from its side: a request whose window has committed stays identifiable until the push settles it, so a re-sent attach is recognized rather than registered afresh. This policy is still the right one, but a re-run is no longer the only thing standing between a flushed window and a NoOpenWindow that never happened.)

CommitRequestReconciler drives a CommitRequest through its state machine (docs/spec/commitrequest-design.md and docs/spec/commitrequest-admission-authorship.md):

  1. ATTRIBUTE — a single synchronous read of the submitter captured at admission (present-or-never). A hit names that submitter as the author (AuthorAttributed=True); a miss claims no actor immediately (AuthorAttributed=False). There is no wait and no requeue for the author: the record is written before the object is visible, so waiting cannot help.
  2. ATTACH + POLL — the instant the author is settled, send the attach to the GitTarget's worker (bind the message to the author's open window, finalize after the grace) and poll the outcome. The grace is anchored at attribution by the worker (finalizeAt = receipt + closeDelaySeconds), so there is no controller-side delay. A window belonging to someone else (or no window) resolves NoOpenWindow; the foreign window stays open.

func (*CommitRequestReconciler) Reconcile

func (r *CommitRequestReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error)

Reconcile advances one CommitRequest through attribute → attach + poll → terminal status. With MaxConcurrentReconciles=1 concurrent CommitRequests are serialized by construction, and the worker keys attaches by request identity so re-sends across poll requeues are idempotent.

func (*CommitRequestReconciler) SetupWithManager

func (r *CommitRequestReconciler) SetupWithManager(mgr ctrl.Manager) error

SetupWithManager sets up the controller with the Manager. MaxConcurrentReconciles is pinned to 1 on purpose: the single worker IS the multi-CommitRequest ordering design — concurrent CommitRequests for the same GitTarget are serialized exactly as a dedicated finalize-coordinator goroutine would serialize them, without the extra moving parts (see docs/spec/commitrequest-design.md).

Restart recovery is best-effort by design: the message is durable in spec.message, so on restart any non-terminal request is re-reconciled — author-resolved from the admission cache when present and re-attached — which heals the common cases. The one knowingly-accepted gap is a request whose commit was already pushed but whose terminal status was not yet written: the in-memory outcome is gone, the re-driven attach finds the work already mirrored, and it resolves Rejected/AlreadyPresent. We do not build a durable record to close that.

type GitProviderReconciler

type GitProviderReconciler struct {
	client.Client

	Scheme *runtime.Scheme

	// Recorder emits a Kubernetes Event on every persisted Ready transition; nil disables Events.
	Recorder record.EventRecorder

	// SSHHostKeys configures SSH host-key resolution (install-level default ConfigMap and the
	// dev-only missing-key opt-out) for the connectivity check's credential read, so it matches
	// what the write path uses.
	SSHHostKeys gitpkg.SSHHostKeyConfig

	// CredentialPolicy controls explicit insecure opt-ins for Git credential transports.
	CredentialPolicy gitpkg.CredentialTransportPolicy
	// contains filtered or unexported fields
}

GitProviderReconciler reconciles a GitProvider object.

func (*GitProviderReconciler) Reconcile

func (r *GitProviderReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error)

Reconcile is part of the main kubernetes reconciliation loop which aims to move the current state of the cluster closer to the desired state.

func (*GitProviderReconciler) SetupWithManager

func (r *GitProviderReconciler) SetupWithManager(mgr ctrl.Manager) error

SetupWithManager sets up the controller with the Manager.

type GitTargetReconciler

type GitTargetReconciler struct {
	client.Client

	Scheme        *runtime.Scheme
	WorkerManager *git.WorkerManager

	// BaseTrustMaxAge bounds two things with one duration: how long a branch worker may keep
	// believing its checkout sits at the remote tip, and how long a GitTarget may go without
	// re-reading its own folder. Zero disables it, which is the default.
	//
	// They are not the same clock, and treating them as one leaves the second unbounded. Every
	// push renews the shared checkout, so a branch-wide age expires only on a branch nobody is
	// publishing to; a target that has gone quiet beside a busy sibling would never come up. See
	// baseTrustReadTracker.
	//
	// It is enforced here rather than by a timer inside the worker because the target that needs
	// it is the IDLE one, and an idle worker has nothing arriving to check the clock on. This
	// reconcile is already a scheduled tick — a converged GitTarget requeues on
	// RequeueSteadyInterval and that pass publishes status without touching Git — so it is the
	// wake-up the age needs, with no second mechanism to own.
	BaseTrustMaxAge time.Duration
	EventRouter     *watch.EventRouter
	// Recorder emits a Kubernetes Event on every persisted Ready transition. It may be nil in
	// tests, in which case no Event is recorded and nothing else changes.
	Recorder record.EventRecorder
	// contains filtered or unexported fields
}

GitTargetReconciler reconciles a GitTarget object.

func (*GitTargetReconciler) Reconcile

func (r *GitTargetReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error)

Reconcile validates GitTarget references and drives startup lifecycle gates.

func (*GitTargetReconciler) SetupWithManager

func (r *GitTargetReconciler) SetupWithManager(mgr ctrl.Manager) error

SetupWithManager sets up the controller with the Manager.

type WatchManagerInterface

type WatchManagerInterface interface {
	// TriggerRuleChange marks the GitTarget a rule names as needing a new plan pass, and returns.
	// The watch-plane owner runs the pass once that target has been quiet for its settle window.
	//
	// It replaces a ReconcileForRuleChange that did the work inline, on the controller worker
	// that observed the rule: a discovery call, a namespace list, a full re-projection, and then a
	// replan of every running GitTarget. A rule edit now replans the ONE target the rule names.
	TriggerRuleChange(gitDest types.ResourceReference)

	// TriggerAllRuleChange marks every declared GitTarget. It is the rule-DELETION path only: the
	// object is already gone, so it cannot be read for the target it named and the owner does not
	// pretend to narrow the invalidation.
	TriggerAllRuleChange()

	ResolveWatchRuleResources(ctx context.Context, rule configv1alpha3.WatchRule) (bool, string)
	ResolveClusterWatchRuleResources(ctx context.Context, rule configv1alpha3.ClusterWatchRule) (bool, string)
	StreamSummaryForGitTarget(gitDest types.ResourceReference) watch.StreamSummary
	StreamSummaryForWatchRule(rule configv1alpha3.WatchRule) watch.StreamSummary
	StreamSummaryForClusterWatchRule(rule configv1alpha3.ClusterWatchRule) watch.StreamSummary

	// StreamStateEvents is the channel a rule controller wires via source.Channel so a stream
	// reaching (or leaving) Streaming re-reconciles the rules that project it, instead of leaving
	// them to discover it on RequeueStreamSettleInterval. A stream coming up is the last thing
	// that has to happen before a rule can honestly report StreamsRunning=True, and the data plane
	// is the only thing that knows when it did.
	//
	// Every call registers a NEW subscriber: a Go channel has one consumer, and three controllers
	// project this state. It may return nil when no data plane is wired.
	StreamStateEvents() <-chan event.GenericEvent
}

WatchManagerInterface defines the interface for watch manager reconciliation. This allows for easier testing by enabling mock implementations.

type WatchRuleReconciler

type WatchRuleReconciler struct {
	client.Client

	Scheme       *runtime.Scheme
	RuleStore    *rulestore.RuleStore
	WatchManager WatchManagerInterface
	// Recorder emits a Kubernetes Event on every persisted Ready transition; nil disables Events.
	Recorder record.EventRecorder
}

WatchRuleReconciler reconciles a WatchRule object.

func (*WatchRuleReconciler) Reconcile

func (r *WatchRuleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error)

Reconcile is part of the main kubernetes reconciliation loop which aims to move the current state of the cluster closer to the desired state.

func (*WatchRuleReconciler) SetupWithManager

func (r *WatchRuleReconciler) SetupWithManager(mgr ctrl.Manager) error

SetupWithManager sets up the controller with the Manager.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL