queue

package
v0.39.2 Latest Latest
Warning

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

Go to latest
Published: Jul 27, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

Documentation

Index

Constants

View Source
const DefaultAttributionFactTTL = 10 * time.Minute

DefaultAttributionFactTTL is how long an attribution fact is retained in Redis waiting for the matching watch event to join it. Facts are never object state, so they expire on their own — nothing deletes them. After it elapses a miss is simply "absent": the v3 schema keeps no tombstone, so an aged-out fact is indistinguishable from one that never arrived. Configurable via --author-attribution-ttl.

View Source
const DefaultKeyPrefix = "gitops-reverser"

DefaultKeyPrefix is the root namespace every Redis key (cursors, facts, and command author records alike) carries when --redis-key-prefix is not set. It is also the value every release before the flag existed used, so the default is a no-op upgrade.

Variables

This section is empty.

Functions

func ValidateKeyPrefix

func ValidateKeyPrefix(prefix string) (string, error)

ValidateKeyPrefix checks a --redis-key-prefix value and returns its normalized form.

Two independent constraints shape the allowed character set:

  • The attribution telemetry gauge SCANs "<prefix>:author:v1:audit:*". Redis glob metacharacters (*, ?, [, ], \) in the prefix would silently make that pattern match the wrong keyspace, so they are rejected rather than escaped — a prefix is an operator-chosen identifier, not user data.
  • Key fields (uid, resourceVersion, namespace) are ':'-delimited and %-escaped by escapeKeyField. '%' in the prefix would make an escaped key ambiguous with an unescaped one, so it is rejected too.

':' is allowed, because a prefix like "cell-a:tenant-7" is a natural nesting and every suffix constant already begins with ':'. A trailing ':' is normalized away so "tenant-7:" and "tenant-7" name the same keyspace rather than two that differ by an empty segment.

An empty prefix is rejected: an unprefixed keyspace collides with Redis's own key namespace conventions and, more importantly, silently un-namespaces an install that meant to set the flag and passed the empty string. Use DefaultKeyPrefix to opt out.

Types

type AttributionIndex

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

AttributionIndex is the optional Redis-backed lookup table that names a commit author from audit facts. It is built from a RedisStore (sharing its connection) only when author attribution is enabled, and stores only attribution facts keyed for a join against watch events — never object state, and never the resume cursors (those belong to RedisStore, which is required regardless of this index).

func (*AttributionIndex) LookupAuthor

func (a *AttributionIndex) LookupAuthor(
	ctx context.Context,
	auditRoute string,
	gvr schema.GroupVersionResource,
	uid types.UID,
	rv string,
	exactCapable bool,
) (AuthorFact, bool)

LookupAuthor finds the strongest attribution fact for a watch event. ok=false means no fact matched (yet) — the caller ships as committer. exactCapable selects the join policy: see LookupAuthorResolution.

func (*AttributionIndex) LookupAuthorResolution

func (a *AttributionIndex) LookupAuthorResolution(
	ctx context.Context,
	auditRoute string,
	gvr schema.GroupVersionResource,
	uid types.UID,
	rv string,
	exactCapable bool,
) AuthorResolution

LookupAuthorResolution finds the strongest attribution fact and classifies the match. It is event-kind-aware:

  • An exact-capable event (ADDED / MODIFIED) tries only the immutable exact key object:<uid>:<rv> and the rv-only escape hatch; it never falls through to the last-writer-wins :last pointer, because that pointer may name a different, older author than the create/update this event represents.
  • A known RV-mismatch event (DELETED, deletecollection-expanded removal) additionally consults object:<uid>:last, whose RV deliberately never matches.

A miss returns AttributionAbsent; there is no tombstone and so no expired outcome.

func (*AttributionIndex) RecordDeleteCollectionFacts

func (a *AttributionIndex) RecordDeleteCollectionFacts(
	ctx context.Context,
	auditRoute string,
	event auditv1.Event,
) error

RecordDeleteCollectionFacts expands a deletecollection response body into one uid-latest (:last) attribution fact per listed object, joined by UID against the per-object removal watch event. It is a no-op for any other verb, or when the body is absent, hollow, or unparseable — an aggregated / metadata-only deletecollection then degrades to a committer-authored removal.

It writes ONLY the :last key: the body item's resourceVersion is the pre-delete RV, which no watch removal event ever presents, so the exact and rv-only keys would be dead. Finalizer-pending items are NOT skipped — under the deletion-as-intent rule a deletionTimestamp already removes the file, so the actor who ran the collection delete is credited with that removal even while Kubernetes finalization is still in flight. See docs/spec/deletecollection-attribution-expander.md.

func (*AttributionIndex) RecordFact

func (a *AttributionIndex) RecordFact(ctx context.Context, auditRoute string, event auditv1.Event) error

RecordFact stores the attribution fact for one accepted, mutating audit event. A UID-bearing fact writes the immutable exact key (uid+rv) and overwrites the :last pointer; a fact that has an RV but no UID writes the type-scoped rv-only key instead. It is a no-op for events without an objectRef, a resolvable name, or a user — those can never name an author. The caller (the audit handler) has already rejected reads, failures, dry-runs, and non-ResponseComplete stages.

type AttributionResult

type AttributionResult string

AttributionResult is the bounded resolver outcome recorded for each watch event.

const (
	// AttributionExactUser is an exact UID+resourceVersion match for a human user.
	AttributionExactUser AttributionResult = "exact_user"
	// AttributionExactServiceAccount is an exact UID+resourceVersion match for a named service account.
	AttributionExactServiceAccount AttributionResult = "exact_serviceaccount"
	// AttributionWeak is a non-exact match: the uid-latest :last pointer or the rv-only
	// escape hatch, used by known RV-mismatch events and no-UID facts respectively.
	AttributionWeak AttributionResult = "weak"
	// AttributionExactDeleteCollectionItem is a match to a fact expanded from a
	// deletecollection response body — a precise per-object credit for one member of a
	// collection delete, joined by UID via :last (the body item's RV is the pre-delete
	// RV and never matches the removal event's RV). The reason is driven by the value's
	// verb, not by which key matched.
	AttributionExactDeleteCollectionItem AttributionResult = "exact_deletecollection_item"
	// AttributionAbsent means no usable author fact matched before the grace elapsed.
	AttributionAbsent AttributionResult = "absent"
)

type AuthorFact

type AuthorFact struct {
	GroupResource    string `json:"groupResource,omitempty"`
	Namespace        string `json:"namespace,omitempty"`
	Name             string `json:"name,omitempty"`
	UID              string `json:"uid,omitempty"`
	Author           string `json:"author"`
	DisplayName      string `json:"displayName,omitempty"`
	Email            string `json:"email,omitempty"`
	Verb             string `json:"verb,omitempty"`
	Subresource      string `json:"subresource,omitempty"`
	AuditID          string `json:"auditID,omitempty"`
	ResourceVersion  string `json:"resourceVersion,omitempty"`
	StageTimestamp   string `json:"stageTimestamp,omitempty"`
	IsServiceAccount bool   `json:"isServiceAccount,omitempty"`
}

AuthorFact is the minimal attribution fact stored per accepted, mutating audit event and read back by the watch-event resolver. It names an author candidate and carries the evidence needed to decide confidence; it is never object state. v3 moves the object identity (group-resource, namespace, name, uid) off the key and into the value, so the fact is self-describing.

type AuthorResolution

type AuthorResolution struct {
	Fact   AuthorFact
	Result AttributionResult
}

AuthorResolution is the structured result of an attribution lookup.

type CommandAuthor

type CommandAuthor struct {
	Author      string `json:"author"`
	DisplayName string `json:"displayName,omitempty"`
	Email       string `json:"email,omitempty"`
	RequestedAt string `json:"requestedAt,omitempty"` // RFC3339Nano, for lag metrics/debug
}

CommandAuthor is the minimal authorship captured at admission for one command object. It carries only what a git commit author needs — no RV, no auditID, no conflict bit: this is a 1:1 command capture, not a post-persist join.

type CommandAuthorStore

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

CommandAuthorStore records and reads command authorship. It shares RedisStore's connection but is wired whenever the validate-operator-types webhook is enabled — independent of --author-attribution, which only governs mirrored-resource attribution.

func (*CommandAuthorStore) LookupCommandAuthor

func (s *CommandAuthorStore) LookupCommandAuthor(
	ctx context.Context, uid types.UID,
) (CommandAuthor, bool)

LookupCommandAuthor is the controller-side read, keyed by the persisted object's UID. ok=false means no record was captured — the validate-operator-types webhook is not configured (or a best-effort write missed) — and the controller finalizes as the committer, immediately.

func (*CommandAuthorStore) RecordCommandAuthor

func (s *CommandAuthorStore) RecordCommandAuthor(
	ctx context.Context, uid types.UID, author CommandAuthor,
) error

RecordCommandAuthor is the admission-side write: capture the authenticated submitter the instant a command CREATE is admitted, before it persists. Last-write-wins (a CREATE fires admission once; a retried admission re-asserts the same user).

type RedisStore

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

RedisStore is the required Redis/Valkey-backed store. It owns the connection and persists each GitTarget watch shard's resume cursor (state continuity / work re-pickup), and the readiness gate pings it. It is a hard dependency in every mode and knows nothing about attribution: author attribution is an optional layer built on the same connection via AttributionIndex.

func NewRedisStore

func NewRedisStore(cfg RedisStoreConfig) (*RedisStore, error)

NewRedisStore opens the Redis/Valkey connection that backs the resume cursors.

func (*RedisStore) AttributionIndex

func (s *RedisStore) AttributionIndex(factTTL time.Duration) *AttributionIndex

AttributionIndex builds the optional author-attribution fact index on this store's connection. Call it only when author attribution is enabled — the store itself, and the resume cursors it holds, never depend on it.

func (*RedisStore) CommandAuthorStore

func (s *RedisStore) CommandAuthorStore() *CommandAuthorStore

CommandAuthorStore builds the command-authorship store on this connection. Wire it when the validate-operator-types webhook is enabled; it does not depend on attribution. The record lives in the same top-level author domain as audit facts but in the separate command subfamily, with its own fixed cleanup TTL.

func (*RedisStore) KeyPrefix

func (s *RedisStore) KeyPrefix() string

KeyPrefix returns the root namespace every key this store's family owns is written under. Reported at startup so an operator can confirm which keyspace a pod claims.

func (*RedisStore) LookupWatchCursor

func (s *RedisStore) LookupWatchCursor(
	ctx context.Context,
	gitTargetUID string,
	gvr schema.GroupVersionResource,
	namespace string,
) (string, bool)

LookupWatchCursor returns the last resourceVersion durably processed for one GitTarget watch shard. A miss means the watch must rebuild from a fresh replay.

func (*RedisStore) Ping

func (s *RedisStore) Ping(ctx context.Context) error

Ping checks liveness of the underlying Redis/Valkey connection. The readiness gate uses it so the pod does not report ready before its resume-cursor store is reachable.

func (*RedisStore) RecordWatchCursor

func (s *RedisStore) RecordWatchCursor(
	ctx context.Context,
	gitTargetUID string,
	gvr schema.GroupVersionResource,
	namespace, rv string,
) error

RecordWatchCursor stores the last resourceVersion durably processed for one GitTarget watch shard, refreshing watchCursorTTL on each write. The cursor is keyed by GitTarget UID and bounded by the TTL, so it never needs explicit deletion: a live watch keeps it fresh, and a dead one's cursor simply expires.

type RedisStoreConfig

type RedisStoreConfig struct {
	Addr       string
	Username   string
	AuthValue  string
	DB         int
	TLSEnabled bool
	// KeyPrefix is the root namespace for every key this connection's stores own. Empty
	// means DefaultKeyPrefix. Several reversers can then share one Redis/Valkey without
	// sharing a logical database — Redis offers only 16 of those, and --redis-db was the
	// only separator this operator provided.
	KeyPrefix string
}

RedisStoreConfig configures the Redis/Valkey connection that backs the required watch-resume cursor store.

Jump to

Keyboard shortcuts

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