fluxdelivery

package
v1.2.0 Latest Latest
Warning

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

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

Documentation

Overview

Package fluxdelivery is the Go half of kcl/lib/flux_delivery.k: gitless Flux delivery through a flux-operator ResourceSet.

A ledger decides which bundle digest a delivery unit runs. Its writer turns that decision into one [Input], checks it with Validate, and writes it into a ResourceSet whose template (Template) flux-operator renders into the unit's OCIRepository and one Kustomization per applier. The control-plane hub writes inline inputs (ResourceSet); self-hosted forge writes a ResourceSetInputProvider whose values are the same map ([Input.Map]).

What lives here, and why in forge

  • The TEMPLATE, as a generated constant (template_gen.go) of the one string in kcl/lib/flux_delivery.k, so a hub writer in Go and a self-hosted render in KCL emit the same objects. TestTemplateMatchesKCL pins the pair.
  • The INPUT, typed. Nobody writes the input map by hand: [Input.Map] is its JSON form, field names fixed by tags.
  • The NAMES of the rendered objects ([BundleObjectName] and friends), moved from control-plane's fluxtenant. The observer reads these objects back by name, so a name composed twice is a name that can disagree, and the template does not derive them: they are inputs, computed here.
  • VALIDATION (Validate): the structural refusals that make a bad input unwritable rather than a green environment over nothing. These are facts about the Flux objects the template emits, so they sit beside it.
  • An in-process RENDERER (Render) that reproduces flux-operator's ResourceSet builder, for a writer's pre-write check and for tests. It never applies anything, and it does not import flux-operator (whose builder is internal/, and whose module is AGPL). The real engine is held to it by an oracle test that runs the pinned `flux-operator build resourceset`.

The safety properties

Every rendered object carries `fluxcd.controlplane.io/prune: disabled` unless its input retires it, and every Kustomization carries `spec.deletionPolicy: Orphan` unless the input says teardown. So deleting the ResourceSet, emptying its inputs or a render that drops an applier orphans Flux objects and never deletes a workload. CheckRendered asserts these properties on a render; a writer runs it before every write.

Index

Constants

View Source
const (
	// APIVersion is flux-operator's API group/version for ResourceSet and
	// ResourceSetInputProvider (FLUX_DELIVERY_API_VERSION).
	APIVersion = "fluxcd.controlplane.io/v1"
	// KindResourceSet is the ResourceSet kind.
	KindResourceSet = "ResourceSet"
	// ServiceAccount is the identity a delivery ResourceSet runs as, and the
	// name of its Role and RoleBinding (FLUX_DELIVERY_SA).
	ServiceAccount = "flux-delivery"
	// PruneAnnotation / PruneDisabled are flux-operator's per-object
	// garbage-collection opt-out (FLUX_DELIVERY_PRUNE_ANNOTATION /
	// FLUX_DELIVERY_PRUNE_DISABLED). flux-operator checks it against the LIVE
	// object, on garbage collection and on ResourceSet deletion alike.
	PruneAnnotation = "fluxcd.controlplane.io/prune"
	PruneDisabled   = "disabled"
	// RetireOrphan / RetireTeardown are the retire states an input may carry
	// (FLUX_DELIVERY_RETIRE_ORPHAN / FLUX_DELIVERY_RETIRE_TEARDOWN). See
	// [Input.Retire].
	RetireOrphan   = "orphan"
	RetireTeardown = "teardown"
	// InputLabel is the label a self-hosted ResourceSetInputProvider carries
	// so the delivery ResourceSet's inputsFrom selector finds it
	// (FLUX_DELIVERY_INPUT_LABEL).
	InputLabel = "forge.dev/delivery"
)

The strings shared with kcl/lib/flux_delivery.k. TestKCLConstantsAgree pins every one against the KCL declaration of the same value.

View Source
const (
	SourceAPIVersion    = "source.toolkit.fluxcd.io/v1"
	KustomizeAPIVersion = "kustomize.toolkit.fluxcd.io/v1"
	KindOCIRepository   = "OCIRepository"
	KindKustomization   = "Kustomization"
	// KubeConfigKey is the key inside every destination kubeconfig Secret the
	// template names. control-plane's fluxhub writes the Secret with the same
	// key; a disagreement reads to kustomize-controller as a missing
	// kubeconfig.
	KubeConfigKey = "value"
	// ShardLabel and its two values. The template derives the value from
	// whether the applier names a kubeconfig, so the connected shard and the
	// kubeConfig field cannot disagree. Same spellings as forge/pkg/flux.
	ShardLabel     = "sharding.fluxcd.io/key"
	ShardMain      = "shard0"
	ShardConnected = "connected"
)

The Flux identities the template renders, pinned to Flux v2.9.5 (source- and kustomize-controller v1.9.5) by the CRD-schema test.

View Source
const (
	VerifyOff     = "off"
	VerifyAudit   = "audit"
	VerifyEnforce = "enforce"
)

The verification modes (Verify.Mode): the effective delivery policy's `flux_verify`. Only enforce renders `spec.verify`; audit is the writer's business (it records the publisher's verdict) and renders nothing.

View Source
const (
	ForgeTierInProgress = `has(status.conditions) &&
status.conditions.exists(c, c.type == 'Reconciling' && c.status == 'True')`

	ForgeTierFailed = `has(status.conditions) &&
status.conditions.exists(c, c.type == 'Stalled' && c.status == 'True')`

	ForgeTierCurrent = `` /* 340-byte string literal not displayed */

)

A STOPPED TIER IS SETTLED, NOT STILL ROLLING OUT.

A forge.dev tier whose compute was stopped on purpose (its owner suspended it, or a billing lapse did) carries Suspended=True and Ready=False. kstatus reads a custom resource's Ready=False as InProgress, so an applier waiting on a suspended tier timed out its health check and its tree read failed (control-plane prod's egresscanary env, 2026-11-01: FluxTreeNotReady for an environment exactly as its owner left it).

These judge the tier kinds as kstatus would, plus one arm: Suspended=True at the current generation is Current. Reconciling=True is InProgress, Stalled=True is Failed (a controller's fail-fast), and Ready=True is Current. Current also requires status.observedGeneration to equal metadata.generation itself: Flux holds a stale observedGeneration InProgress before any expression runs, but the expression does not lean on that, so a Ready=True left over from a previous spec never reads settled for a newer one.

View Source
const (
	// KindInputProvider is flux-operator's ResourceSetInputProvider.
	KindInputProvider = "ResourceSetInputProvider"
	// InputProviderStatic is the provider type whose one exported input is
	// its `spec.defaultValues`.
	InputProviderStatic = "Static"
	// PointerFenceAnnotation carries the writer's fence on the pointer, as
	// decimal digits: the live pointer's fence plus one, allocated by the
	// write that is conditional on that pointer, so the sequence is the
	// cluster's and shared by every deployer. A write that would move it
	// backwards is refused ([ErrStaleFence]); the self-hosted writer also
	// refuses one that reuses it for another promotion.
	PointerFenceAnnotation = "forge.dev/delivery-fence"
	// PointerPromotionAnnotation names the ledger promotion the pointer
	// projects.
	PointerPromotionAnnotation = "forge.dev/delivery-promotion"
	// GrantsRecord is the ConfigMap in a self-hosted delivery namespace that
	// records what the bootstrap granted the apply identities, so a deploy
	// can compare a release's needs with it before it writes anything.
	GrantsRecord = "forge-delivery-grants"
)

The self-hosted pointer's kind and the annotations a writer reads back.

View Source
const (
	// ChartName is the bootstrap platform chart's name, the `--target` that
	// installs it (FLUX_DELIVERY_CHART_NAME).
	ChartName = "flux-delivery"
	// PreServiceAccount, ApplyServiceAccount and NoneServiceAccount are the
	// apply identities in an env's delivery namespace (FLUX_DELIVERY_PRE_SA,
	// FLUX_DELIVERY_APPLY_SA, FLUX_DELIVERY_NONE_SA).
	PreServiceAccount   = "forge-delivery-pre"
	ApplyServiceAccount = "forge-delivery-apply"
	NoneServiceAccount  = "forge-delivery-none"
	// SelfHostedPullSecret is the copy of the registry pull Secret the
	// bootstrap writes into the delivery namespace; an input names it as
	// source.pullSecret (FLUX_DELIVERY_PULL_SECRET).
	SelfHostedPullSecret = "forge-delivery-pull"
	// EnvLabel carries the env on every self-hosted delivery object
	// (FLUX_DELIVERY_ENV_LABEL).
	EnvLabel = "forge.dev/delivery-env"
)

The names a self-hosted bootstrap and its writer share with kcl/lib/flux_delivery.k. TestKCLConstantsAgree pins each one.

View Source
const (
	ProviderGeneric = "generic"
	ProviderAWS     = "aws"
	ProviderAzure   = "azure"
	ProviderGCP     = "gcp"
)

The pull providers Flux's OCIRepository accepts.

View Source
const ExternalSecretCurrent = `` /* 296-byte string literal not displayed */

ExternalSecretCurrent is the health expression for an ExternalSecret (moved from control-plane's fluxtenant). ESO leaves Ready=True from the preceding sync while reconciling a pin bump; its syncedResourceVersion is "<generation>-<metadata hash>" and advances only after the target Secret is written. The delimiter is part of the match (generation 1 must not accept generation 10). A missing status field is not current, and an absent variable is a CEL error, which Flux also treats as not current.

View Source
const FenceAnnotation = "deploy.reliant.dev/generation"

FenceAnnotation is stamped on the OCIRepository with BindingInput.Fence: the annotation the hub's converger already writes there (control-plane's deployconverge.GenerationAnnotation), which its observer and fence reader read back. Kept under the same key so adopting a live object is a no-op.

View Source
const Schema = "forge.dev/flux-delivery/v1"

Schema is the version of the input shape. The template's first action is to refuse (sprig `fail`) any other value, so an input written by a newer writer stalls the ResourceSet instead of misrendering. Any change to the input's shape bumps it — and with it the template.

View Source
const Template = `` /* 3502-byte string literal not displayed */

Template is kcl/lib/flux_delivery.k's FLUX_DELIVERY_TEMPLATE: the ResourceSet resourcesTemplate. Regenerate with `go generate ./pkg/fluxdelivery`.

Variables

View Source
var (
	// ErrUnretiredApplierDropped is a write that drops an applier the live
	// input holds without having retired it first. The dropped
	// Kustomization would leave the ResourceSet's inventory still live and
	// still reading the shared source, and when the source moves to a digest
	// without its path, Flux prunes everything it applied.
	ErrUnretiredApplierDropped = errors.New("fluxdelivery: the write drops an applier that was never retired")
	// ErrPathChanged is a write that keeps an applier's name and changes its
	// path. The name is a function of the same coordinates the path is, so
	// this can only be a bug, and a Kustomization whose path moves prunes
	// everything the old path applied.
	ErrPathChanged = errors.New("fluxdelivery: an applier kept its name and changed its path")
	// ErrStaleFence is a write whose fence is older than the live one: a
	// writer that lost a race would move the environment backwards.
	ErrStaleFence = errors.New("fluxdelivery: the write's fence is older than the live input's")
)

The writer's refusals on a transition from the live input to the next.

View Source
var (
	// ErrInvalidInput is an input whose shape is wrong: schema, a required
	// field, a retire value, a duration.
	ErrInvalidInput = errors.New("fluxdelivery: invalid input")
	// ErrBindingUnsupported is a non-empty binding: the ledger has no
	// per-binding promotion yet, so nothing may write one.
	ErrBindingUnsupported = errors.New("fluxdelivery: the ledger has no per-binding promotion yet; binding must be \"\"")
	// ErrInvalidName is a name, label or annotation Kubernetes would refuse,
	// or one that disagrees with the function that composes it.
	ErrInvalidName = errors.New("fluxdelivery: invalid name")
	// ErrNotDigestPinned is a source not pinned by a sha256 manifest digest.
	ErrNotDigestPinned = errors.New("fluxdelivery: the source must be pinned by its sha256 manifest digest, never a tag")
	// ErrUnknownProvider is a pull provider outside Flux's enum.
	ErrUnknownProvider = errors.New("fluxdelivery: source provider must be one of generic, aws, azure, gcp")
	// ErrNoAppliers is an input with no applier. A source alone fetches
	// bytes, applies nothing and reports Ready: an environment built that
	// way looks healthy forever and never deploys.
	ErrNoAppliers = errors.New("fluxdelivery: the input has no applier, so nothing would apply the bundle")
	// ErrPathMismatch is an applier whose path is not release's answer for
	// its coordinates — the green-over-nothing guard: Flux reconciles a path
	// the artifact does not hold as "no resources" and reports Ready.
	ErrPathMismatch = errors.New("fluxdelivery: applier path disagrees with release.BundleTreePath / BundleClusterPath")
	// ErrNoClusterCredential is an applier with neither a kubeconfig nor an
	// explicit in-cluster identity, or with both. An absent kubeConfig makes
	// kustomize-controller apply to its OWN cluster.
	ErrNoClusterCredential = errors.New("fluxdelivery: an applier needs exactly one of kubeConfigSecret and inCluster")
	// ErrServiceAccountOnConnected is a serviceAccountName beside a
	// kubeconfig. The connected shard applies as the kubeconfig's identity
	// and never as a service account; the hub's admission policy refuses it.
	ErrServiceAccountOnConnected = errors.New("fluxdelivery: a kubeconfig applier must not name a serviceAccountName")
	// ErrDuplicateApplier is two appliers with one cluster or one name: the
	// second Kustomization would overwrite the first.
	ErrDuplicateApplier = errors.New("fluxdelivery: two appliers collide")
	// ErrInvalidTree is a dependency outside the input, an unknown health
	// kind, a flat cluster given tree coordinates or the reverse.
	ErrInvalidTree = errors.New("fluxdelivery: invalid applier tree")
	// ErrInvalidVerify is a verify block enforce mode cannot render.
	ErrInvalidVerify = errors.New("fluxdelivery: invalid verify settings")
)

The refusals, moved from control-plane's fluxtenant.BuildBundle and validateBundleTrees. Each is a fact about the Flux objects the template emits, so a writer refuses before it writes anything — a refusal never leaves a half-applied environment.

View Source
var ErrUnsafeRender = errors.New("fluxdelivery: unsafe render")

ErrUnsafeRender is a render that breaks one of the template's safety properties. Reaching it means the template or the renderer is wrong, never the input: Validate has already accepted the input.

Functions

func BoundLabelValue

func BoundLabelValue(name string) string

BoundLabelValue fits a composed name into a label value (63 characters) WITHOUT merging distinct inputs.

A name that fits is returned unchanged. A longer one becomes a readable prefix, a dot, and the first 12 hex characters of the SHA-256 of the WHOLE name. Plain truncation would merge two inputs that differ past the cut into one object, and the second would overwrite the first; the hash covers every byte. The separator is a dot for the collision proof above, and trimming the cut's trailing `-`/`.` keeps the result a legal subdomain and label value.

func CheckRendered

func CheckRendered(in BindingInput, objs []*unstructured.Unstructured) error

CheckRendered asserts the safety properties on objs, the render of in (Render). A writer runs it on the would-be ResourceSet before every write, so a template or renderer bug is caught in the writer, attributed to the binding, before flux-operator ever sees it. Every golden in this package is held to it.

The properties:

  • exactly one OCIRepository (the input's source) and one Kustomization per applier, in order, and nothing else — no Secret, no RBAC, no Namespace: the template renders only objects one binding owns;
  • every object in the input's namespace, carrying the input's labels;
  • the prune opt-out on every object the input does not retire;
  • deletionPolicy Orphan on every Kustomization unless the binding is torn down (then Delete), and suspend exactly when retired as orphan or carried suspended;
  • the shard label and kubeConfig agree, every Kustomization applies as the input's ServiceAccount, and one without a kubeConfig impersonates a named one;
  • the source is digest-pinned to the input's digest, selects the manifests layer, carries the fence, and verifies exactly when the input enforces;
  • every Kustomization reads the input's source at the applier's path.

func CheckRenderedSelfHosted

func CheckRenderedSelfHosted(in BindingInput, objs []*unstructured.Unstructured, mode SelfHosted) error

CheckRenderedSelfHosted is CheckRendered for a self-hosted binding (ValidateSelfHosted): a delivery without identities names no ServiceAccount, because kustomize-controller applies as itself there.

func CheckTransition

func CheckTransition(live *BindingInput, next BindingInput) error

CheckTransition refuses a write of next over live (nil: nothing is live, a first write). The three rules, each the writer's alone to enforce:

  • the fence never moves backwards (ErrStaleFence);
  • every applier the live input holds is in next, unless the live input had already retired it (that applier, or the whole binding) — the first of the two writes that drop one (ErrUnretiredApplierDropped);
  • an applier present in both keeps its path (ErrPathChanged).

It does not check that a live retire has been APPLIED before the drop: that is an observation of the renderer, which the caller makes.

func HealthAPIVersion

func HealthAPIVersion(kind string) (string, bool)

HealthAPIVersion is the apiVersion of a kind an applier may be judged on, and whether the kind is known. The set the hub's trees use; a kind outside it has no apiVersion to name, and an unknown health check never reports.

func InputProvider

func InputProvider(in BindingInput) (map[string]any, error)

InputProvider is the pointer object for in: a Static ResourceSetInputProvider in in.Namespace, labelled so the delivery ResourceSet selects it, with in as its `defaultValues` and the fence and promotion as annotations.

It validates nothing: call ValidateSelfHosted first. It refuses only an input it cannot encode.

func InputProviderName

func InputProviderName(env, binding string) string

InputProviderName is the name of a binding's pointer: the same as its ResourceSet's, in its own kind.

func KustomizationName

func KustomizationName(org, env, binding, cluster string) string

KustomizationName is the name of a FLAT (layout v1) cluster's Kustomization (control-plane's fluxtenant.BundleKustomizationName).

The cluster is sanitized through release.BundlePathSegment — the function the path uses, so the name and the path agree about what a cluster's identity reduces to — then folded to the subdomain alphabet (LowerDNS) and bounded as a label value (BoundLabelValue): kustomize-controller stamps `kustomize.toolkit.fluxcd.io/name: <this>` on every object it applies, and an over-long name fails every apply naming the APPLIED object.

func LabelValue

func LabelValue(s string) string

LabelValue is the label value for a cluster or unit name, the way the hub labels an applier with its cluster (`reliant.dev/cluster`) and unit.

func LowerDNS

func LowerDNS(s string) string

LowerDNS folds a path segment into the RFC-1123 subdomain alphabet: lower case, and `_` (legal in a path, illegal in a name) to `-`.

A DOT IS LEFT ALONE: folding it would collapse two genuinely different contexts (`a.b` and `a-b`) onto one object name, the silent overwrite Validate refuses. Leaving it means the only collisions are ones the refusal catches.

func Render

func Render(rs map[string]any, providers ...Provider) ([]*unstructured.Unstructured, error)

Render builds the objects flux-operator would apply for rs, a ResourceSet object (as ResourceSet or kcl's flux_delivery_resourceset returns it), with providers standing in for the ResourceSetInputProviders its inputsFrom selects. The objects carry the owner labels and commonMetadata flux-operator stamps before it applies.

func ResourceSet

func ResourceSet(in BindingInput) (map[string]any, error)

ResourceSet is the ResourceSet that renders one binding, with the binding's input inline — the hub's shape, where the writer owns the object. It is the object kcl's `flux_delivery_resourceset(ResourceSetName(env, binding), namespace, inputs = [in.Map()])` returns; the KCL agreement test pins the two.

It validates nothing: call Validate first. It refuses only an input it cannot encode.

func ResourceSetName

func ResourceSetName(env, binding string) string

ResourceSetName is the name of the ResourceSet that renders a binding, in the org's hub namespace: `delivery.<env>`, or `delivery.<env>.<binding>`. Bounded as a label value because flux-operator stamps it on every object it applies (`resourceset.fluxcd.controlplane.io/name`).

func Rules

func Rules() []rbacv1.PolicyRule

Rules are the verbs the delivery ServiceAccount holds in its own namespace: the two Flux kinds the template renders, and nothing else. No Secret verb at all — `create` cannot be bounded by resourceNames, and a Secret `create` in a hub namespace mints a token for any ServiceAccount there. `delete` is the retire protocol and ResourceSet uninstall; the prune opt-out bounds it.

kcl/lib/flux_delivery.k's flux_delivery_rbac() declares the same rules for a self-hosted bootstrap; the KCL agreement test pins the pair.

func SelfHostedNamespace

func SelfHostedNamespace(project, env string) string

SelfHostedNamespace is an env's default delivery namespace: forge-delivery-<project>-<env>, the same composition as KCL's flux_delivery_namespace. A Bundle may name its own (FluxDelivery.namespace); the render reports the one in effect, and a writer reads that rather than recomposing it.

func SelfHostedServiceAccount

func SelfHostedServiceAccount(phase release.Phase) string

SelfHostedServiceAccount is the identity a self-hosted tree (with identities) of phase applies as: PreServiceAccount for `pre`, ApplyServiceAccount for rollout and post.

func SourceName

func SourceName(org, env, binding string) string

SourceName is the name of a binding's OCIRepository: `<org>.env.<env>` (control-plane's fluxtenant.BundleObjectName) for the environment's binding.

func TreeKustomizationName

func TreeKustomizationName(org, env, binding, cluster, unit string, phase release.Phase) string

TreeKustomizationName is the name of one layout-v2 tree's Kustomization (control-plane's fluxtenant.BundleTreeKustomizationName). The unit sits between cluster and phase exactly as it does in the path.

func Validate

func Validate(in BindingInput) error

Validate refuses an input the template must not render. It is pure, and it runs before anything is written: the hub converger and `forge env deploy` call the same function.

It does NOT judge the input against the LIVE ResourceSet (the shrink, path-invariance and fence refusals): those compare two inputs of one binding and belong to the writer.

func ValidateSelfHosted

func ValidateSelfHosted(in BindingInput, mode SelfHosted) error

ValidateSelfHosted is Validate for a self-hosted binding: every structural refusal of the hub, except that an in-cluster TREE is the norm here rather than a breach, plus the identity rules above. A self-hosted input that names a kubeconfig is refused: there is no other cluster to reach.

Types

type Applier

type Applier struct {
	// Name is [TreeKustomizationName] for a tree, [KustomizationName] for a
	// flat cluster.
	Name string `json:"name"`
	// Source is the OCIRepository the applier reads, and must equal
	// Source.Name: one input, one source.
	Source string `json:"source"`
	// Cluster, Unit and Phase are the applier's coordinates in the bundle:
	// the rendered cluster name as the bundle's paths index it (a kube
	// context, not necessarily a DNS label), the delivery unit and the phase
	// of a tree. Unit and Phase are "" for a flat cluster. [Validate] derives
	// Name and Path from them; the template reads neither.
	Cluster string        `json:"cluster"`
	Unit    string        `json:"unit"`
	Phase   release.Phase `json:"phase"`
	// Path is release.BundleTreePath (tree) or release.BundleClusterPath
	// (flat) of the coordinates — CHECKED, because a Kustomization at a path
	// the artifact does not hold reconciles as "no resources" and reports
	// Ready.
	Path string `json:"path"`
	// Retire retires this applier alone: "" or [RetireOrphan]. The first of
	// the two writes that drop an applier.
	Retire string `json:"retire"`
	// Labels are stamped on this Kustomization beside BindingInput.Labels
	// (the hub: cluster, delivery unit, phase — [LabelValue] of each).
	Labels map[string]string `json:"labels"`
	// KubeConfigSecret names the destination kubeconfig Secret in Namespace
	// (key [KubeConfigKey]). Exactly one of KubeConfigSecret and InCluster.
	KubeConfigSecret string `json:"kubeConfigSecret"`
	// InCluster applies to the namespace's own cluster with the controller's
	// in-cluster identity, impersonating ServiceAccountName. Explicit rather
	// than inferred from an empty KubeConfigSecret: an absent kubeConfig is
	// a destination breach when it happens by omission.
	InCluster bool `json:"inCluster"`
	// ServiceAccountName is the identity an in-cluster apply impersonates.
	// Required with InCluster, refused beside a kubeconfig (the connected
	// shard applies as the kubeconfig's identity and nothing else). Rendered
	// even when empty, as the hub's objects already carry it.
	ServiceAccountName string `json:"serviceAccountName"`
	// TargetNamespace forces every applied object into one namespace (a
	// hosted destination); "" leaves the bundle's own namespaces.
	TargetNamespace string `json:"targetNamespace"`
	// DependsOn are applier names IN THIS INPUT that must be Ready first.
	DependsOn []string `json:"dependsOn"`
	// ExternalDependsOn are Kustomization names OUTSIDE this binding, in the
	// same namespace. Not resolved here; empty today.
	ExternalDependsOn []string `json:"externalDependsOn"`
	// Health is what the applier is judged on (instead of Wait).
	Health []HealthCheck `json:"health"`
	// HealthExprs override kstatus for kinds whose Ready alone does not prove
	// the current spec reconciled.
	HealthExprs []HealthCheckExpr `json:"healthExprs"`
	// Wait judges every applied object; a tree waits on Health instead.
	Wait bool `json:"wait"`
	// Timeout and RetryInterval are canonical Go durations ("1m30s");
	// RetryInterval "" leaves Flux's default.
	Timeout       string `json:"timeout"`
	RetryInterval string `json:"retryInterval"`
	// Suspended carries a live suspend into the input (adoption, or a tree
	// added suspended).
	Suspended bool `json:"suspended"`
	// CommonLabels are stamped on every object the applier applies (a hosted
	// destination's identity).
	CommonLabels map[string]string `json:"commonLabels"`
	// Patches are kustomize patches on the applied objects (a hosted
	// destination's deployment ids), in a stable order.
	Patches []Patch `json:"patches"`
}

Applier is one Kustomization.

type BindingInput

type BindingInput struct {
	// Schema is [Schema].
	Schema string `json:"schema"`
	// Org is the owning organization. RFC-1123 label.
	Org string `json:"org"`
	// Env is the environment id. RFC-1123 label. (Not `id`: flux-operator
	// reserves inputs.id and inputs.provider.)
	Env string `json:"env"`
	// Binding is the ledger binding within the environment. "" — the whole
	// environment — is the only value [Validate] accepts until the ledger has
	// per-binding promotion; the key exists so nothing re-shapes later.
	Binding string `json:"binding"`
	// Namespace is where the ResourceSet and everything it renders live
	// (`flux-<org>` on the hub, `flux-system` self-hosted).
	Namespace string `json:"namespace"`
	// Fence is the writer's lease fence at the last write that moved
	// something, as decimal digits (inputs are JSON). Stamped on the
	// OCIRepository as [FenceAnnotation].
	Fence string `json:"fence"`
	// Promotion is the ledger row this input projects.
	Promotion string `json:"promotion"`
	// Retire is "", [RetireOrphan] or [RetireTeardown], and only ever from a
	// ledger-recorded intent. Binding-wide; an applier may also be retired
	// alone ([Applier.Retire]).
	Retire string `json:"retire"`
	// Labels are stamped on EVERY rendered object. Computed by the writer
	// (the hub: managed-by, org, environment; a binding label only for a
	// non-empty binding); the template adds only the shard label.
	Labels map[string]string `json:"labels"`
	// Source is the binding's ONE OCIRepository.
	Source Source `json:"source"`
	// Appliers are one Kustomization each: a layout-v2 tree, or a flat
	// (layout v1) cluster.
	Appliers []Applier `json:"appliers"`
}

BindingInput is ONE ResourceSet input: everything one ledger binding — one promotion-row stream — decides. Today a binding is one environment (Binding == "").

Every field is computed by the ledger's writer; the template derives nothing but the safety stamps (see the package doc). Field names are fixed by the JSON tags, and BindingInput.Map is the only way to produce the map a ResourceSet carries.

func LiveInput

func LiveInput(pointer *unstructured.Unstructured) (BindingInput, error)

LiveInput reads the input a live pointer carries back into a BindingInput. A pointer whose defaultValues does not decode as one (an older schema, a hand edit) is an error: a writer must not compare against an input it cannot read.

func (BindingInput) Map

func (in BindingInput) Map() (map[string]any, error)

Map is the input as a ResourceSet carries it: `spec.inputs[i]`, or a ResourceSetInputProvider's `defaultValues`.

Every key is present, with empty maps and lists rather than null: the template runs with missingkey=error, so an absent key is a stalled render, and a writer comparing the live input with the next one must not see a difference between null and empty.

type HealthCheck

type HealthCheck struct {
	APIVersion string `json:"apiVersion"`
	Kind       string `json:"kind"`
	Name       string `json:"name"`
	Namespace  string `json:"namespace,omitempty"`
}

HealthCheck is one entry of Kustomization.spec.healthChecks.

type HealthCheckExpr

type HealthCheckExpr struct {
	APIVersion string `json:"apiVersion"`
	Kind       string `json:"kind"`
	// InProgress and Failed are evaluated before Current, in that order
	// (Flux RFC 0009). omitempty keeps an expression that sets only Current
	// rendering exactly as before.
	InProgress string `json:"inProgress,omitempty"`
	Failed     string `json:"failed,omitempty"`
	Current    string `json:"current"`
}

HealthCheckExpr is one entry of Kustomization.spec.healthCheckExprs.

func ForgeTierHealthExprs added in v1.0.15

func ForgeTierHealthExprs() []HealthCheckExpr

ForgeTierHealthExprs is one expression per forge.dev tier kind, for an applier that waits on every object it applies (Wait).

type OIDCIdentity

type OIDCIdentity struct {
	Issuer  string `json:"issuer"`
	Subject string `json:"subject"`
}

OIDCIdentity is one keyless signer identity, as Flux's matchOIDCIdentity.

type Patch

type Patch struct {
	Patch  string      `json:"patch"`
	Target PatchTarget `json:"target"`
}

Patch is one entry of Kustomization.spec.patches.

type PatchTarget

type PatchTarget struct {
	Group   string `json:"group,omitempty"`
	Version string `json:"version,omitempty"`
	Kind    string `json:"kind"`
	Name    string `json:"name"`
}

PatchTarget selects the object a Patch applies to.

type Provider

type Provider struct {
	Name   string
	Inputs []map[string]any
}

Provider is the exported inputs of one ResourceSetInputProvider the ResourceSet selects (the self-hosted shape). flux-operator adds the provider reference to each input; an `id` is the provider's own business and, if wanted, belongs in Inputs.

type SelfHosted

type SelfHosted struct {
	// ApplyIdentities is FluxDelivery.apply_identities: every tree names its derived
	// identity. False: no tree names one.
	ApplyIdentities bool
}

SelfHosted is how a self-hosted delivery applies, for ValidateSelfHosted.

type Source

type Source struct {
	// Name is [SourceName](org, env, binding).
	Name string `json:"name"`
	// URL is the bundle repository, `oci://` scheme included, without a tag:
	// the digest pins it.
	URL string `json:"url"`
	// Digest is the bundle's OCI MANIFEST digest ("sha256:" + 64 hex), the
	// value Flux reports as the revision.
	Digest string `json:"digest"`
	// Interval is the reconcile interval of the source and of every applier,
	// as a canonical Go duration ("5m0s"): the form the API server stores, so
	// a render equals the live object byte for byte.
	Interval string `json:"interval"`
	// Insecure allows the pull over plain HTTP. The digest still pins the
	// content; this weakens transport, not integrity.
	Insecure bool `json:"insecure"`
	// Provider is Flux's pull auth provider ("" for Flux's generic default).
	Provider string `json:"provider"`
	// PullSecret names the registry pull Secret in Namespace, written by the
	// writer (or the self-hosted bootstrap), never by the template. "" for
	// an anonymous pull.
	PullSecret string `json:"pullSecret"`
	// Verify is the signature verification the source enforces.
	Verify Verify `json:"verify"`
}

Source is the binding's OCIRepository.

type Verify

type Verify struct {
	// Mode is [VerifyOff], [VerifyAudit] or [VerifyEnforce].
	Mode string `json:"mode"`
	// Provider is Flux's verification provider, "cosign" or "notation".
	// Required in enforce mode.
	Provider string `json:"provider"`
	// SecretRef names the Secret of trusted public keys in Namespace, written
	// by the writer before an enforcing input. Key-based verification;
	// exclusive with OIDC.
	SecretRef string `json:"secretRef"`
	// OIDC are the keyless identities a signature may carry; exclusive with
	// SecretRef.
	OIDC []OIDCIdentity `json:"oidc"`
}

Verify is the source's signature verification.

Directories

Path Synopsis
internal
gentemplate command
Command gentemplate writes pkg/fluxdelivery/template_gen.go: the Go constant of kcl/lib/flux_delivery.k's FLUX_DELIVERY_TEMPLATE, evaluated through forge's own KCL seam.
Command gentemplate writes pkg/fluxdelivery/template_gen.go: the Go constant of kcl/lib/flux_delivery.k's FLUX_DELIVERY_TEMPLATE, evaluated through forge's own KCL seam.
kcleval
Package kcleval evaluates a KCL program against the forge module embedded in this binary — the module a released forge renders with — for fluxdelivery's generator and its Go/KCL agreement tests.
Package kcleval evaluates a KCL program against the forge module embedded in this binary — the module a released forge renders with — for fluxdelivery's generator and its Go/KCL agreement tests.

Jump to

Keyboard shortcuts

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