reconcile

package
v1.0.0-rc2 Latest Latest
Warning

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

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

Documentation

Overview

Package reconcile is the pull loop: for each application, fetch the repository, render it, plan against the swarm, and — when the sync policy says so — apply.

Each application runs on its own schedule in its own goroutine. One application whose repository is unreachable must not stall the others, and a shared queue would make exactly that happen.

The set of applications is mutable while the loop runs: Add, Remove and Replace start, stop and retune per-application loops under the reconciler's lock. This is what lets the app set itself be reconciled from git (issue #47); the loop that drives those operations from a diff is a separate concern.

Index

Constants

View Source
const (
	// DefaultInterval matches ArgoCD's. Every tick costs a git fetch, a full
	// render of every release, and a read of the swarm's release records, so
	// the default is deliberately not aggressive; an application that needs to
	// be quicker sets its own.
	DefaultInterval = 3 * time.Minute
)

Variables

View Source
var ErrNotPlanned = errors.New("no plan yet")

ErrNotPlanned is returned when an application has not been reconciled yet, so there is nothing to diff.

Functions

This section is empty.

Types

type Builder

type Builder interface {
	Build(ctx context.Context, app string, spec application.Source, co git.Checkout) (*source.Built, error)
}

Builder turns a working tree into a plan's inputs. *source.Builder implements it.

type Engine

type Engine interface {
	PlanApply(ctx context.Context, rf *charts.ReleaseFile, src charts.ChartSource, opts charts.PlanOptions) (*charts.Plan, error)
	Apply(ctx context.Context, plan *charts.Plan, opts charts.InstallOptions) ([]charts.ApplyResult, error)
	History(ctx context.Context, release string) ([]charts.Release, error)
	// Uninstall removes a release's stack and its recorded revisions, keeping
	// its volumes unless asked. Used only by prune, so an application whose
	// sync policy does not enable it never reaches this.
	Uninstall(ctx context.Context, release string, purgeVolumes bool) (*charts.UninstallResult, error)
}

Engine is the part of the chart engine this loop uses. *charts.Engine implements it.

type Fetcher

type Fetcher interface {
	Fetch(ctx context.Context, app string, src application.Source) (git.Checkout, error)
}

Fetcher brings an application's repository to a revision. *git.Sourcer implements it.

type Options

type Options struct {
	Fetcher   Fetcher
	Builder   Builder
	Swarms    swarms.Registry
	NewEngine func(charts.Backend) Engine
	Interval  time.Duration
	Log       *slog.Logger
	Now       func() time.Time
	// RegistryAuth resolves an application's image-pull credential, keyed by
	// application name. An application absent from the map deploys public images
	// only. Built at startup by regauth.Load, which is where a missing or
	// unparseable secret becomes a startup error.
	RegistryAuth map[string]regauth.Resolver
	// ForbiddenSecretMounts names the controller's own mounted secrets, which no
	// reconciled stack may mount. Controller-wide; built at startup from the
	// controller's /run/secrets. Empty disables the check.
	ForbiddenSecretMounts map[string]struct{}

	// ControllerID is the identity half of the owner stamp this reconciler
	// writes, so that a second controller on the same swarm does not read these
	// releases as its own. Empty is application.DefaultControllerID.
	ControllerID string
}

Options configures a Reconciler. Everything has a working default except the fetcher and the builder, which have no sensible one.

type Reconciler

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

Reconciler runs the loop and holds what it last observed.

func New

func New(apps []application.Spec, o Options) *Reconciler

New returns a Reconciler for the given applications.

func (*Reconciler) Add

func (r *Reconciler) Add(spec application.Spec) error

Add starts reconciling a new application. It returns an error if one of that name is already present: the caller that drives the set from a git diff (issue #52) tells add from replace itself and does not rely on this being an upsert.

func (*Reconciler) Diffs

func (r *Reconciler) Diffs(app string) ([]application.ReleaseDiff, error)

Diffs returns the manifest changes the last plan found. It does not re-render: what it reports is what the status reports, which is the point.

func (*Reconciler) History

func (r *Reconciler) History(ctx context.Context, app string) (application.History, error)

History returns the recorded revisions of every release the application declares, newest first.

It reads the swarm rather than the status cache: history is the one thing that survives a controller restart with no database of its own, because the engine keeps one Docker Config per revision in Raft. Serving it from memory would make it disappear on exactly the restart it is most useful after.

func (*Reconciler) Remove

func (r *Reconciler) Remove(name string) error

Remove stops reconciling an application and drops what was observed of it. It cancels the application's loop and waits for that goroutine to return before reporting done, so a removed application is provably no longer reconciling — a caller replacing the whole set can rely on that. The deployed stack is left running and becomes unmanaged (D-e); pruning it is separate (issue #54).

func (*Reconciler) Replace

func (r *Reconciler) Replace(spec application.Spec) error

Replace swaps the spec a running application reconciles against, keyed by name, keeping its recorded status and its loop: the next tick reads the new spec, so a healthy loop is retuned rather than restarted. A change of name is not a replace — it is a Remove of the old and an Add of the new, which the caller does, because the two report as different applications.

func (*Reconciler) Run

func (r *Reconciler) Run(ctx context.Context) error

Run reconciles until ctx is cancelled.

It starts a loop for every application in the set and then blocks, so that applications added while it runs are supervised under the same context and a removed one's loop is cancelled with it. A destination the swarm registry cannot resolve is not fatal here: it fails its own application on the next tick, surfaced on that application's status, rather than stopping the loop that observes every other one.

func (*Reconciler) SetRegistryAuth

func (r *Reconciler) SetRegistryAuth(app string, resolver regauth.Resolver)

SetRegistryAuth installs the image-pull credential an application's images are pulled with, replacing whatever it had. A nil resolver removes it, which is what an application that has just dropped its registryAuth needs — otherwise the credential it no longer declares would keep being sent.

It exists because the set is mutable: regauth.Load resolves the applications declared at startup, and an application that joins later brings a credential that map has never seen. The caller driving the set resolves it and puts it here before the application is added, so the first reconcile already has it.

func (*Reconciler) Sync

func (r *Reconciler) Sync(ctx context.Context, app string) error

Sync reconciles one application now, applying if its policy allows.

func (*Reconciler) SyncNow

func (r *Reconciler) SyncNow(ctx context.Context, app string) error

SyncNow reconciles one application now and applies whatever the plan contains, whether or not the policy is automated. It is what the API's sync action calls: a manual policy means "do not deploy on a schedule", not "never deploy".

func (*Reconciler) View

func (r *Reconciler) View(app string) (application.View, bool)

View returns one application's spec and last observed status.

func (*Reconciler) Views

func (r *Reconciler) Views() []application.View

Views returns every application, in the order they were declared or added.

Jump to

Keyboard shortcuts

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