release

package
v0.0.1-dev.137 Latest Latest
Warning

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

Go to latest
Published: Jul 11, 2026 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package release implements the stateful runeset release model: the 3-way reconcile plan that turns a rendered cast into create/update/prune/adopt actions against the cluster.

Design: _docs/plugins/RUNESET_STATEFUL_RELEASES.md and CAST_REFACTOR_PLAN.md. This package is the pure planning core — it computes WHAT a cast will do; the server-side ReleaseController executes the plan (apply, verify, prune-last).

Index

Constants

This section is empty.

Variables

View Source
var ErrConflicts = errors.New("release plan has unresolved conflicts; pass --adopt or resolve ownership")

ErrConflicts is returned when a plan cannot be applied because it contains unresolved ownership conflicts (resource owned elsewhere, no --adopt).

View Source
var ErrDetachWouldPrune = errors.New("--detach is not allowed for a plan that prunes resources")

ErrDetachWouldPrune is returned when --detach is requested for a plan that would delete resources (Decision C3: detach is create/update-only).

Functions

This section is empty.

Types

type Action

type Action string

Action is the reconcile verb planned for a single resource.

const (
	// ActionCreate — resource is desired and does not exist; create and own it.
	ActionCreate Action = "create"
	// ActionUpdate — resource is desired and already owned by this release.
	ActionUpdate Action = "update"
	// ActionPrune — resource was owned by the previous revision but is no longer
	// desired; delete it (runs last, after verify — Decision D3).
	ActionPrune Action = "prune"
	// ActionAdopt — resource exists but is unmanaged or owned elsewhere, and
	// --adopt was given; take ownership.
	ActionAdopt Action = "adopt"
	// ActionReference — inherently-shared cluster-scoped kind (StorageClass,
	// Namespace): ensure it exists but never own or prune it (Decision D2).
	ActionReference Action = "reference"
)

type Applier

type Applier interface {
	// Apply materializes a create / update / adopt / reference change.
	Apply(ctx context.Context, change PlannedChange) error
	// Prune deletes a no-longer-desired owned resource, honoring reclaim policy.
	Prune(ctx context.Context, ref types.OwnerRef) error
	// Verify blocks until the given owned resources are healthy (or errors).
	Verify(ctx context.Context, refs []types.OwnerRef) error
	// Revert restores a resource to the pre-image captured before this cast
	// first touched it (via Apply or Prune): a resource that did not exist is
	// deleted, one that did is restored to that prior state. Used only by
	// atomic rollback; must tolerate the change having half-applied or not
	// applied at all.
	Revert(ctx context.Context, ref types.OwnerRef) error
}

Applier executes individual plan actions against the cluster. Implemented server-side against the orchestrator + repos; faked in tests.

type Conflict

type Conflict struct {
	OwnedBy *types.OwnedBy
	Reason  string
}

Conflict marks a planned change that cannot be applied as-is: the live resource is owned by a different release (or unmanaged) and adoption was not requested. A plan containing any Conflict is not applyable.

type DesiredResource

type DesiredResource struct {
	Ref types.OwnerRef
	// ContentHash optionally lets the planner skip no-op updates. Empty means
	// "always treat as a potential update" (a finer no-op check is a TODO).
	ContentHash string
}

DesiredResource is one resource the rendered release wants to exist.

type LiveLookup

type LiveLookup interface {
	Lookup(ref types.OwnerRef) (LiveState, error)
}

LiveLookup reports the live state of a resource. Implemented server-side against the store/orchestrator; faked in tests.

type LiveState

type LiveState struct {
	Exists bool
	// OwnedBy is the OwnedBy stamp found on the live resource, or nil if the
	// resource is unmanaged (exists but carries no release ownership).
	OwnedBy *types.OwnedBy
}

LiveState is the observed live state of a single resource.

type Options

type Options struct {
	// Adopt allows taking over resources that exist but are unmanaged or owned
	// by a different release (the --adopt flag).
	Adopt bool
}

Options tune planning.

type Plan

type Plan struct {
	Release   string
	Namespace string
	Changes   []PlannedChange
}

Plan is the full set of changes a cast revision will make, in apply order (desired resources in input order, then prunes).

func BuildPlan

func BuildPlan(releaseName, namespace string, desired []DesiredResource, current *types.Release, live LiveLookup, opts Options) (*Plan, error)

BuildPlan computes the 3-way reconcile between the desired set, the current release record (nil for a first install), and live state.

Rules:

  • Shared cluster-scoped kinds (StorageClass, Namespace) are ActionReference: ensured-present, never owned or pruned.
  • A desired resource already in current.Owns → ActionUpdate.
  • A desired resource that doesn't exist live → ActionCreate.
  • A desired resource that exists live but isn't ours → ActionAdopt if opts.Adopt, else a Conflict.
  • A resource in current.Owns no longer desired → ActionPrune.

func Reconcile

func Reconcile(ctx context.Context, spec ReleaseSpec, recs ReleaseRecords, live LiveLookup, applier Applier) (*types.Release, *Plan, error)

Reconcile executes a release spec end to end (synchronous):

plan → write pending intent → apply (ordered) → verify → prune-last → deployed

It is Prepare followed by Execute. On apply/verify failure it records the release as failed and returns; nothing is pruned (Decision D3). With spec.Atomic, the failure path additionally reverts every change this revision made before recording `failed`. Detach callers use Prepare + a background Execute instead (see releasectl.Controller.Cast).

func (*Plan) Applyable

func (p *Plan) Applyable() bool

Applyable reports whether the plan is free of unresolved conflicts.

func (*Plan) Counts

func (p *Plan) Counts() map[Action]int

Counts tallies changes by action (for the cast plan display block).

func (*Plan) HasPrune

func (p *Plan) HasPrune() bool

HasPrune reports whether the plan deletes anything. Used to refuse --detach on destructive plans (Decision C3: create/update-only detach).

type PlannedChange

type PlannedChange struct {
	Ref      types.OwnerRef
	Action   Action
	Conflict *Conflict
}

PlannedChange pairs a resource ref with its planned action.

type PreparedCast

type PreparedCast struct {
	Release *types.Release
	Plan    *Plan
	// Execute applies the prepared change set and returns the final release
	// record (deployed, or failed with the cause). Safe to call from a
	// background goroutine with a detached context.
	Execute func(ctx context.Context, applier Applier) (*types.Release, error)
}

PreparedCast is the outcome of planning a cast and recording its pending intent: the pending release, the computed plan, and an Execute closure that performs apply → verify → prune-last → deployed against the cluster.

Synchronous callers run Execute inline (see Reconcile). Detach callers (C3-a) return the pending release immediately and run Execute in the background with a detached context.

func Prepare

func Prepare(ctx context.Context, spec ReleaseSpec, recs ReleaseRecords, live LiveLookup) (*PreparedCast, error)

Prepare runs the 3-way plan, validates it (conflicts, detach-prune), and writes the pending release intent. It does NOT mutate the cluster — that is the returned PreparedCast.Execute closure's job. On a validation error it still returns a PreparedCast carrying the Plan (Release nil) so callers can surface it.

type ReleaseRecords

type ReleaseRecords interface {
	Get(ctx context.Context, namespace, name string) (rel *types.Release, found bool, err error)
	Save(ctx context.Context, rel *types.Release) error
}

ReleaseRecords is the persistence the reconciler needs (satisfied by ReleaseRepo). Get returns found=false when no record exists yet.

type ReleaseSpec

type ReleaseSpec struct {
	Name           string
	Namespace      string
	Source         types.ReleaseSource
	Manifest       types.RunesetManifest
	Values         map[string]interface{}
	RenderedDigest string
	Resources      []DesiredResource
	Options        Options

	// Detach requests create/update-only async completion (Decision C3). A
	// detach spec whose plan prunes is rejected with ErrDetachWouldPrune.
	Detach bool

	// Atomic opts into rollback-on-failure (Decision D3, Helm parity): if any
	// apply, verify, or prune step fails, every change this revision already
	// made is reverted — creates deleted, updates and prunes restored from
	// their pre-cast state — so the cast lands fully applied or not at all.
	// The release is still recorded `failed` (the rollback is noted in the
	// cause); the default (false) keeps today's leave-as-failed behavior.
	Atomic bool
}

ReleaseSpec is the rendered, ready-to-apply description of a release. It is produced client-side (resolve → render → lint) and executed by the server-side reconciler. Resource payloads are carried by the Applier implementation, keyed by ref; this spec holds identity, provenance, and the desired ref set.

Jump to

Keyboard shortcuts

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