Help us shape the future of Go! Take the 2026 Go Developer Survey

admission

package
v0.22.1 Latest Latest
Warning

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

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

Documentation

Overview

Package admission is the control-plane admission framework (ADR-0063): a two-phase pipeline (mutate-all → validate-all) of single-purpose Admissions run on every resource write, after authz and before the store. It generalizes the former inline obj.Validate() admit step (ADR-0018) and opens the cross-resource/Delete extension point (ADR-0064 links consume it).

It is a near-leaf: it imports api/types, internal/auth, api/fault only — it nests under internal/controlplane but must NOT import its parent package (no cycle).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func FindCycle added in v0.10.0

func FindCycle(start string, adj map[string][]string) string

FindCycle runs a coloured DFS from start over adj and returns the cycle path (or "" if none reachable). A self-edge (start → start) is detected as a degenerate cycle. link-validity and app-requires (ADR-0219) use it.

func Members added in v0.4.0

func Members(ctx context.Context, r StoreReader, ns v1.NamespaceName, group v1.ResourceGroupName) ([]v1.Object, error)

Members returns the members of ResourceGroup group in ns (ADR-0170 Decision 7): every object of a namespaced kind other than ResourceGroup, in ns, whose metadata.resourceGroup is group and that no controller owns (a controlled child goes with its owner). Platform-written records (WorkflowRuns, Invocations) count.

Types

type Admission

type Admission interface {
	// Name identifies the admission in errors and ordering (stable, kebab-case).
	Name() string
	// Phase is Mutating or Validating.
	Phase() Phase
	// Handles reports whether this admission applies to the given GVK + operation.
	Handles(gvk v1.GroupVersionKind, op Operation) bool
	// Admit reviews req and returns the (possibly-mutated) object, or a fault error denying the write.
	Admit(ctx context.Context, req Request) (v1.Object, error)
}

Admission reviews a write before it is persisted. Deterministic and side-effect-free. A Validating admission returns req.Object on allow, or a fault error (Invalid/Forbidden/Conflict/PayloadTooLarge) on deny. A Mutating admission returns the object to carry forward, or a fault error.

func NewBackupScheduleAdmission added in v0.11.0

func NewBackupScheduleAdmission(storeURL string) Admission

NewBackupScheduleAdmission returns the Validating admission of a BackupSchedule's recipients and target: the recipients follow ADR-0204 Decision 2's rules (envelope.Parse), and a target inside the blob store, storeURL (blob.target or the file:// URL of blob.dir), is refused naming spec.target.url. A target on the store's provider is stored; the schedule's Ready reason says SameProvider.

func NewBackupSpecImmutableAdmission added in v0.11.0

func NewBackupSpecImmutableAdmission() Admission

NewBackupSpecImmutableAdmission returns the Validating admission that refuses a change to a Backup's or a Restore's spec: a run record and a request are fixed at creation, as a Site's prefix is.

func NewBackupTTLAdmission added in v0.11.0

func NewBackupTTLAdmission(r StoreReader) Admission

NewBackupTTLAdmission returns the Validating admission that refuses a Backup whose spec.ttl exceeds its schedule's ttl. Without the schedule it admits: the run fails ScheduleNotFound (no link-validity admission, ADR-0121).

func NewBindingDeclaredAdmission added in v0.15.0

func NewBindingDeclaredAdmission(r StoreReader) Admission

NewBindingDeclaredAdmission returns the Validating admission "binding-declared" (ADR-0248 Decision 2): every store, catalog, ConfigMap and Secret a Function binds, its catalog entries' bindings included, is declared at its root, and every binding of an image step is declared at its Workflow's (ADR-0250 Decision 2). A part of an App binds what the App declares too: Request.Declared under app-parts, else read from r for an object an App controls, so a direct write of a part passes as the App's own did. A step Function binds what its Workflow declares. A Site's one bucket is Site.Validate's. A standalone CatalogService's blob, config and secret bindings are declared at its root (ADR-0251 Decision 2); an App's catalog binds buckets the App declares. It replaces ADR-0229's workflow-step-stores.

func NewBucketDeletionProtectionAdmission

func NewBucketDeletionProtectionAdmission(r StoreReader, p BlobProber) Admission

NewBucketDeletionProtectionAdmission returns the Validating admission that protects Bucket data (ADR-0080). On Delete: Conflict if any Function.spec.bindings.blob names the bucket OR any prefix still holds objects. On Update: Conflict if a prefix removed from spec.prefixes[] is still named by some Function.spec.bindings.blob OR still holds objects. (Clone of kvstore-deletion-protection, scanning spec.bindings.blob.) The prober is optional — a nil prober skips the data-emptiness check (binding-protection still applies).

func NewBucketQuotaAdmission

func NewBucketQuotaAdmission(r StoreReader, maxPerNamespace int) Admission

NewBucketQuotaAdmission returns the Validating admission that rejects creating a Bucket when its namespace already holds maxPerNamespace buckets (ADR-0080 bucket-count quota). maxPerNamespace ≤ 0 disables the quota.

func NewCatalogStoreOwnerAdmission added in v0.15.0

func NewCatalogStoreOwnerAdmission() Admission

NewCatalogStoreOwnerAdmission returns the Mutating admission "catalog-store-owner" (ADR-0251 Decisions 1 and 4), the twin of function-store-owner: an omitted owner of a prefix a standalone CatalogService defines at its root becomes the CatalogService, and a bindings.config entry naming a ConfigMap it defines becomes that ConfigMap's stored name.

func NewFunctionStoreOwnerAdmission added in v0.15.0

func NewFunctionStoreOwnerAdmission() Admission

NewFunctionStoreOwnerAdmission returns the Mutating admission "function-store-owner": an omitted owner of a table, a prefix or a database the Function defines at its root becomes the Function (Decision 4), and a bindings.config entry naming a ConfigMap it defines becomes that ConfigMap's stored name (Decision 5), also when it names an earlier stored name of it, so the environment, the pool key and the GC read the name that holds the data.

func NewKVStoreDeletionProtectionAdmission

func NewKVStoreDeletionProtectionAdmission(r StoreReader, p KVProber) Admission

NewKVStoreDeletionProtectionAdmission returns the Validating admission that protects KVStore data (ADR-0073). On Delete: Conflict if any Function.spec.bindings.kv names the store OR it still holds keys. On Update: Conflict if a table removed from spec.tables[] is still named by some Function.spec.bindings.kv, or if an added table still holds the data of its removed namesake (not yet reclaimed by the reconciler, which only diffs the current spec against the live data). (Clone of link-deletion-protection, scanning spec.bindings.kv.)

func NewKVStoreQuotaAdmission

func NewKVStoreQuotaAdmission(r StoreReader, maxPerNamespace int) Admission

NewKVStoreQuotaAdmission returns the Validating admission that rejects creating a KVStore when its namespace already holds maxPerNamespace stores (ADR-0072 store-count quota). maxPerNamespace ≤ 0 disables the quota.

func NewLinkDeletionProtectionAdmission

func NewLinkDeletionProtectionAdmission(r StoreReader) Admission

NewLinkDeletionProtectionAdmission returns the Validating admission that rejects deleting a Function that is another Function's link target (ADR-0064). It reads Request.Old.

func NewLinkValidityAdmission

func NewLinkValidityAdmission(r StoreReader) Admission

NewLinkValidityAdmission returns the Validating admission that enforces ADR-0064's cross-resource link rules on Create/Update: every link target exists in the namespace, and the namespace link graph stays acyclic with the applied function (a self-link is a degenerate cycle).

func NewPolicyValidityAdmission

func NewPolicyValidityAdmission() Admission

NewPolicyValidityAdmission returns the Validating admission that enforces ADR-0074's Policy rules on a Policy Create/Update: spec.cedar PARSES as Cedar and references only the curated actions + entity types (kv::read/kv::write; Function/KVStore/KVTable), and its scopes name no entity of another namespace (ADR-0177). An un-parseable, off-schema or foreign-namespace Policy is rejected (fault.Invalid) before it reaches the metastore — so the cedar driver can rely on every stored Policy compiling. (Shape cloned from the link/KV validity admissions.)

func NewResourceGroupDeletionProtectionAdmission added in v0.4.0

func NewResourceGroupDeletionProtectionAdmission(r StoreReader) Admission

NewResourceGroupDeletionProtectionAdmission returns the Validating admission that refuses deleting a ResourceGroup while it has a member (ADR-0170 Decision 7), naming up to five members and the count.

func NewRestoreInPlaceAdmission added in v0.11.0

func NewRestoreInPlaceAdmission(authz auth.Authorizer, r StoreReader, h *hold.Hold) Admission

NewRestoreInPlaceAdmission returns the Validating admission of a Restore with an inPlace member: its create needs update on the Namespace (the built-in RBAC gives it to an admin only) and is refused while another in-place Restore of the namespace has not ended, since they would share the namespace's restore hold; its delete is refused until it has ended, so the hold keeps the Restore that names it. A create is also refused while the namespace carries a restore marker, which only hold release --namespace lifts once its load did not complete; h nil holds nothing. It reads Request.Old on a delete.

func NewSQLDatabaseDeletionProtectionAdmission added in v0.13.0

func NewSQLDatabaseDeletionProtectionAdmission(r StoreReader, p SQLProber) Admission

NewSQLDatabaseDeletionProtectionAdmission returns the Validating admission that refuses deleting a SQLDatabase that a Function's spec.bindings.sql binds (ADR-0233 Decision 7), or whose engine holds a user table, or whose data cannot be checked (fail closed). A controller's delete through the store is not checked.

func NewSQLDatabaseQuotaAdmission added in v0.13.0

func NewSQLDatabaseQuotaAdmission(r StoreReader, maxPerNamespace int) Admission

NewSQLDatabaseQuotaAdmission returns the Validating admission that rejects creating a SQLDatabase when its namespace already holds maxPerNamespace databases. maxPerNamespace ≤ 0 disables the quota.

func NewSitePrefixImmutableAdmission

func NewSitePrefixImmutableAdmission() Admission

NewSitePrefixImmutableAdmission returns the Validating admission that rejects any change to spec.bindings.blob[0].prefix on Update. The reconciler only ever adds prefixes to the Bucket, so a changed prefix would orphan the old one with its objects (ADR-0139 Decision §7). It needs no dependencies — Old and Object are both on the Request — and it never consults Old.Status, which an apply wipes.

func NewValidateAdmission

func NewValidateAdmission() Admission

NewValidateAdmission returns the built-in Validating admission that runs obj.Validate() for every kind on Create and Update.

func NewWorkflowRunContractAdmission

func NewWorkflowRunContractAdmission(r workflowGetter) Admission

NewWorkflowRunContractAdmission returns the Validating admission that checks a WorkflowRun's spec.input against its parent Workflow's CACHED status.contract.input (ADR-0098) — zero registry I/O. A parent that is absent or not-yet-Ready (no cached contract) is allowed: the engine's run-start gate is the backstop, and a dangling spec.workflow is the run reconciler's concern.

func NewWorkflowRunPayloadAdmission

func NewWorkflowRunPayloadAdmission(limit int64) Admission

NewWorkflowRunPayloadAdmission returns the Validating admission that rejects a WorkflowRun whose spec.input exceeds the payload cap (ADR-0094: run payloads are size-bounded control-plane metadata; larger data travels by reference on the blob substrate). limit ≤ 0 disables the cap.

func NewWorkflowRunSpecImmutableAdmission added in v0.2.1

func NewWorkflowRunSpecImmutableAdmission() Admission

NewWorkflowRunSpecImmutableAdmission returns the Validating admission that rejects an Update changing a WorkflowRun's spec.workflow, spec.input or spec.replay. The engine runs a run's spec once, so a second run applied under an existing name is rejected (ADR-0094 duplicate-run-name-rejected) instead of replacing the record of what ran; spec.paused and spec.cancel stay mutable.

type BlobProber

type BlobProber interface {
	// HasAny reports whether any object exists under the prefix of the namespace's bucket.
	HasAny(ctx context.Context, ns v1.NamespaceName, bucket v1.ObjectName, prefix string) (bool, error)
}

BlobProber probes whether a Bucket prefix holds any objects (ADR-0080): the deletion-protection admission uses it to block deleting a bucket (or removing a prefix) that still holds data. Declared HERE (like KVProber/StoreReader) so the admission package stays a near-leaf; the wiring resolves the Bucket to the substrate view the s3gateway writes, so the key layout has one owner. Optional — a nil prober skips the data-emptiness check (binding-protection still applies).

type Declared added in v0.15.0

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

Declared is what a resource's root declares, by kind and name (ADR-0248 Decision 2): its name and ref entries, a defined ConfigMap under its name and its stored name, and its Secret declarations.

func RootDeclared added in v0.15.0

func RootDeclared(kv []v1.KVStoreEntry, buckets []v1.BucketEntry, sql []v1.SQLDatabaseEntry, catalogs []v1.CatalogEntry,
	configMaps []v1.ConfigMapEntry, secrets []v1.SecretDeclaration) *Declared

RootDeclared is what these root entries declare.

func (*Declared) Add added in v0.15.0

func (d *Declared) Add(kind v1.Kind, name v1.ObjectName)

Add declares name of kind.

func (*Declared) Has added in v0.15.0

func (d *Declared) Has(kind v1.Kind, name v1.ObjectName) bool

Has reports whether name of kind is declared; a nil Declared declares nothing.

type KVProber

type KVProber interface {
	// HasAny reports whether any key exists under prefix.
	HasAny(ctx context.Context, prefix string) (bool, error)
}

KVProber probes whether a KVStore holds any keys (ADR-0072): the deletion-protection admission uses it to block deleting a store that still holds data. Declared HERE (like StoreReader) so the admission package stays a near-leaf; the wiring adapts the kvstore driver's List to it.

type NamespaceReading added in v0.4.0

type NamespaceReading interface {
	ReadsNamespace() bool
}

NamespaceReading marks an admission that needs the namespace's admission lock (ADR-0147): two concurrent writes it admits could store a state no sequential order of them allows. The control plane holds the lock from the Old fetch through the store write.

type Operation

type Operation string

Operation is the resource write being admitted.

const (
	Create Operation = "create"
	Update Operation = "update"
	Delete Operation = "delete"
)

type Phase

type Phase int

Phase orders an admission: all Mutating run (threading the object), then all Validating.

const (
	// Mutating admissions may return a changed object; they run first.
	Mutating Phase = iota
	// Validating admissions are read-only checks; they run on the final object.
	Validating
)

type Pipeline

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

Pipeline runs the registered admissions for a write: all Mutating (threading the object), then all Validating. Construct once at server start with NewPipeline; it is read-only thereafter.

func NewPipeline

func NewPipeline(admissions ...Admission) *Pipeline

NewPipeline registers admissions, partitioning by Phase; registration order within a phase is preserved (it is the deterministic tie-break).

func (*Pipeline) Admit

func (p *Pipeline) Admit(ctx context.Context, req Request) (v1.Object, error)

Admit runs all Mutating admissions (threading req.Object — each may transform it), then all Validating ones (each sees the final object), skipping any whose Handles is false. The first fault error short-circuits. Returns the final (possibly-mutated) object.

func (*Pipeline) Handles

func (p *Pipeline) Handles(gvk v1.GroupVersionKind, op Operation) bool

Handles reports whether any registered admission applies to (gvk, op) — lets a caller skip an Old-object fetch on Delete when nothing admits deletes.

func (*Pipeline) ReadsNamespace added in v0.4.0

func (p *Pipeline) ReadsNamespace(gvk v1.GroupVersionKind, op Operation) bool

ReadsNamespace reports whether any admission handling (gvk, op) implements NamespaceReading and returns true: the write must hold its namespace's admission lock (ADR-0147).

type Request

type Request struct {
	Operation Operation
	GVK       v1.GroupVersionKind
	Object    v1.Object
	Old       v1.Object
	Identity  auth.Identity
	Declared  *Declared
}

Request is one admission review. Object is the incoming desired object (nil on Delete); Old is the stored object (nil on Create; set on Update/Delete). GVK is the route's kind (authoritative, not body-derived). Identity is the authenticated caller (authz already passed). Declared is set when app-parts reviews an App's part: what the App's root declares, which the part binds without a root entry of its own (ADR-0248 Decision 2).

type SQLProber added in v0.13.0

type SQLProber interface {
	HasTables(ctx context.Context, ns v1.NamespaceName, name v1.ObjectName) (bool, error)
}

SQLProber reports whether a SQLDatabase's engine holds a user table (ADR-0232 Decision 7). It fails Unavailable when the engine cannot answer but the database has data on disk.

type StoreReader

type StoreReader interface {
	List(ctx context.Context, gvk v1.GroupVersionKind, ns v1.NamespaceName) ([]v1.Object, error)
}

StoreReader is the read-only store view a cross-resource admission needs. It is a subset of store.Store declared here so the admission package stays a near-leaf (the wiring adapts the real store.Store to this interface).

Jump to

Keyboard shortcuts

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