controllerutils

package
v0.0.0-...-b75bfc0 Latest Latest
Warning

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

Go to latest
Published: Aug 26, 2026 License: Apache-2.0 Imports: 24 Imported by: 0

Documentation

Overview

Package controllerutils holds controller helpers shared across services (backend, kube-applier, fleet, ...) that all build Cosmos-backed informer-driven controllers and want consistent cadence/gating behavior.

The cooldown gate exposed here was originally backend/pkg/utils/controllerutils' TimeBasedCooldownChecker; it lives in internal/ so other services can use the same implementation without re-importing the backend module.

Index

Constants

View Source
const (
	HcpClusterAzureResourceIdAnnotation = "azure.microsoft.com/hcp-cluster-azure-resource-id"
)

Variables

This section is empty.

Functions

func BuildReadDesire

func BuildReadDesire(resourceIDString string, managementCluster *azcorearm.ResourceID, target kubeapplierapi.ResourceReference) *kubeapplierapi.ReadDesire

BuildReadDesire produces the desired-state ReadDesire for a resource. The status section is intentionally zero — the kube-applier owns status.

func NeedsUpdate

func NeedsUpdate(existing, desired any) bool

NeedsUpdate reports whether `desired` differs from `existing` in any way that should cause us to write `desired` back to Cosmos. It is a strict-but-server-managed-fields-aware semantic equality check: all the fields that actually persist must match, but cosmos-managed values like the document etag are ignored, as are Go-level representation differences (RawExtension Raw vs Object, parent pointer chains in ResourceID, etc.).

func ReadDesireNeedsWork

func ReadDesireNeedsWork(existing, desired *kubeapplierapi.ReadDesire) bool

ReadDesireNeedsWork reports whether existing matches desired in the fields the controller writes (Spec.ManagementCluster, Spec.TargetItem). A nil existing means "doesn't exist yet" — work is required.

func ResourceIDsEqual

func ResourceIDsEqual(a, b *azcorearm.ResourceID) bool

ResourceIDsEqual compares two *azcorearm.ResourceID for equality by their canonical string form. Both may be nil; non-nil values are compared by String(), so independently-parsed instances with different parent pointer chains still compare equal when they represent the same ARM ID.

Types

type AfterEnqueuer

type AfterEnqueuer interface {
	EnqueueAfter(keyObj any, duration time.Duration)
}

AfterEnqueuer allows scheduling a workqueue item for processing after an explicit delay. Validation controllers use this to implement EarliestRetryAfter semantics.

type CooldownChecker

type CooldownChecker interface {
	CanSync(ctx context.Context, key any) bool
}

CooldownChecker decides whether a key may be (re-)queued.

Implementations must be safe to call concurrently — informer event handlers, periodic resyncs, and worker goroutines may all consult the same checker.

CanSync takes a context so implementations may emit logs or use context-bound services; the time-based variant ignores it. A true return is taken as "the caller WILL sync this key" and the implementation is free to record state (e.g. stamp a next-allowed time) on that basis.

type GenericSyncer

type GenericSyncer[T comparable] interface {
	SyncOnce(ctx context.Context, keyObj T) error
	CooldownChecker() CooldownChecker
	MakeKey(resourceID *azcorearm.ResourceID) T
}

type GenericWatchingController

type GenericWatchingController[T comparable] struct {
	// contains filtered or unexported fields
}

func NewGenericWatchingController

func NewGenericWatchingController[T comparable](name string, resourceType azcorearm.ResourceType, syncer GenericSyncer[T], reconcileTotal *prometheus.CounterVec) *GenericWatchingController[T]

NewGenericWatchingController creates a controller that watches Cosmos-backed informers and delegates reconciliation to syncer.

func (*GenericWatchingController[T]) EnqueueAfter

func (c *GenericWatchingController[T]) EnqueueAfter(keyObj any, duration time.Duration)

func (*GenericWatchingController[T]) EnqueueResourceIDAdd

func (c *GenericWatchingController[T]) EnqueueResourceIDAdd(resourceID *azcorearm.ResourceID, changed bool)

EnqueueResourceIDAdd is equivalent to calling EnqueueResourceIDAddWithMaxDepth with a maxDepth of -1. See EnqueueResourceIDAddWithMaxDepth for more details. It is exposed so that individual controllers can add other items to requeue based on easily.

func (*GenericWatchingController[T]) EnqueueResourceIDAddWithMaxDepth

func (c *GenericWatchingController[T]) EnqueueResourceIDAddWithMaxDepth(resourceID *azcorearm.ResourceID, changed bool, maxDepth int)

EnqueueResourceIDAddWithMaxDepth traverses resourceID and its parents according to maxDepth until it finds a resourceID that is of the resource type of c.resourceType and adds it if found. Each walk to Parent consumes one level. maxDepth is the maximum number of parent hops to traverse when searching for a resourceID of type c.resourceType. maxDepth 0 means only the resourceID itself is considered. maxDepth -1 (or any negative value) means no limit. The parent walk continues until a match or nil parent is reached. It is exposed so that individual controllers can add other items to requeue based on easily. When there's a match of resourceType: when changed is true, the resourceID is added to the queue immediately. Otherwise, the resourceID is added to the queue only if the cooldown checker allows it.

func (*GenericWatchingController[T]) QueueForInformers

func (c *GenericWatchingController[T]) QueueForInformers(resyncDuration time.Duration, notifiers ...Notifier) error

QueueForInformers is equivalent to calling QueueForInformersWithMaxDepth with maxDepth of -1. See QueueForInformersWithMaxDepth for more details.

func (*GenericWatchingController[T]) QueueForInformersWithMaxDepth

func (c *GenericWatchingController[T]) QueueForInformersWithMaxDepth(resyncDuration time.Duration, maxDepth int, notifiers ...Notifier) error

QueueForInformersWithMaxDepth adds event handlers to the notifiers for the controller with a given max depth. maxDepth is the maximum number of parent hops to traverse when searching for a resourceID whose type is c.resourceType. Each walk to Parent consumes one level. maxDepth 0 means only the resourceID itself is considered. maxDepth -1 (or any negative value) means no limit. The parent walk continues until a match or nil parent is reached. It is exposed so that individual controllers can add other items to requeue based on easily.

func (*GenericWatchingController[T]) Run

func (c *GenericWatchingController[T]) Run(ctx context.Context, threadiness int)

func (*GenericWatchingController[T]) SyncOnce

func (c *GenericWatchingController[T]) SyncOnce(ctx context.Context, keyObj any) error

type Notifier

type Notifier interface {
	AddEventHandlerWithOptions(handler cache.ResourceEventHandler, options cache.HandlerOptions) (cache.ResourceEventHandlerRegistration, error)
}

type SettableCooldownChecker

type SettableCooldownChecker struct {
	// contains filtered or unexported fields
}

SettableCooldownChecker is a cooldown gate where the per-key cooldown duration is set explicitly by the caller via SetCooldown, rather than being fixed at construction time. A key with no cooldown set is always allowed.

func NewSettableCooldownChecker

func NewSettableCooldownChecker() *SettableCooldownChecker

func (*SettableCooldownChecker) CanSync

func (c *SettableCooldownChecker) CanSync(_ context.Context, key any) bool

func (*SettableCooldownChecker) SetClock

func (c *SettableCooldownChecker) SetClock(clock utilsclock.PassiveClock)

func (*SettableCooldownChecker) SetCooldown

func (c *SettableCooldownChecker) SetCooldown(key any, duration time.Duration)

SetCooldown records that the given key should not be re-synced until now+duration has elapsed.

func (*SettableCooldownChecker) TimeUntilReady

func (c *SettableCooldownChecker) TimeUntilReady(key any) time.Duration

TimeUntilReady returns the duration until the key's cooldown expires. Returns 0 if the key has no cooldown set or the cooldown has already expired.

type TimeBasedCooldownChecker

type TimeBasedCooldownChecker struct {
	// contains filtered or unexported fields
}

TimeBasedCooldownChecker is a fixed-interval cooldown gate: after CanSync returns true for a key, subsequent calls for the same key return false until cooldownDuration has elapsed since the allowed call.

The next-exec map is an LRU rather than an unbounded map so that a long-running process whose keys come and go does not leak memory. 1M entries is far above any realistic management cluster's resource count.

func NewTimeBasedCooldownChecker

func NewTimeBasedCooldownChecker(cooldownDuration time.Duration) *TimeBasedCooldownChecker

NewTimeBasedCooldownChecker constructs a checker bound to the real wall-clock and a 1M-entry LRU. Tests should call SetClock to inject a fake clock.

func (*TimeBasedCooldownChecker) CanSync

func (c *TimeBasedCooldownChecker) CanSync(_ context.Context, key any) bool

CanSync stamps now+cooldownDuration on a true return so subsequent calls within the cooldown window return false. A key with no record is always allowed. ctx is part of the CooldownChecker interface but unused here.

func (*TimeBasedCooldownChecker) SetClock

SetClock substitutes the time source used to evaluate the cooldown. Intended for tests; production code should use the real clock from the constructor.

Jump to

Keyboard shortcuts

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