reconcile

package
v0.2.0-beta Latest Latest
Warning

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

Go to latest
Published: Sep 12, 2026 License: Apache-2.0 Imports: 5 Imported by: 0

Documentation

Overview

Package reconcile implements the core convergence loop: desired state in, observed state diffed against it, idempotent and level-triggered controllers converge the two. Reconcilers never assume they're continuing a previous partial operation: every call re-derives what to do from current observed state, which is what makes them safe to interrupt and safe to retry.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Condition

type Condition struct {
	Type               string
	Status             ConditionStatus
	Reason             string
	Message            string
	LastTransitionTime time.Time
}

Condition is a single named status a controller reports after each reconcile, always with a machine-readable Reason: every reconcile emits a status condition with a reason string.

type ConditionStatus

type ConditionStatus string

ConditionStatus mirrors Kubernetes' tri-state convention deliberately: "Unknown" is a real, distinct state from "False": a controller that hasn't finished its first reconcile yet is not the same as one that has confirmed failure.

const (
	ConditionTrue    ConditionStatus = "True"
	ConditionFalse   ConditionStatus = "False"
	ConditionUnknown ConditionStatus = "Unknown"
)

The three values a Condition's Status can hold.

type Controller

type Controller interface {
	// Name identifies the controller in logs and status lookups. Stable
	// across restarts.
	Name() string

	// Reconcile diffs desired against observed state and takes whatever
	// action is needed to converge them. It returns the conditions
	// resulting from this attempt even when it also returns an error;
	// callers should record both.
	Reconcile(ctx context.Context) (Result, error)
}

Controller manages one resource's convergence from desired to observed state. Reconcile must be idempotent: calling it repeatedly with nothing changed must be a cheap no-op, and calling it after a previous call was interrupted mid-operation must still converge correctly rather than double-apply or corrupt state.

type Engine

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

Engine runs a fixed set of controllers, triggered by Docker events with a periodic resync as a safety net for any event the stream missed. This push-primary/pull-as-safety-net shape is deliberate: Coolify's own v5 rearchitecture converged on the same pattern independently.

func NewEngine

func NewEngine(logger *slog.Logger, controllers ...Controller) *Engine

NewEngine builds an Engine over the given controllers. Order is preserved for each ReconcileAll pass but controllers must not depend on each other's side effects within a single pass: that's what makes per-resource-type controllers safe to reason about independently.

func (*Engine) LastResult

func (e *Engine) LastResult(name string) (Result, error)

LastResult returns the most recent Result and error for a controller by name, for status reporting. The zero Result and a nil error are returned if the controller has never run.

func (*Engine) ReconcileAll

func (e *Engine) ReconcileAll(ctx context.Context)

ReconcileAll runs every controller once: first the fixed set NewEngine was built with, then whatever the Source currently reports, if one is set. A single controller failing does not stop the others from running: one broken resource should never block convergence of everything else. A Source error is logged and skipped for this pass rather than treated as fatal, since the next tick or event re-derives the set again anyway.

func (*Engine) Run

func (e *Engine) Run(ctx context.Context, events <-chan docker.Event, resyncInterval time.Duration) error

Run reconciles once immediately, then keeps reconciling on every incoming Docker event and on every resyncInterval tick, until ctx is cancelled or the event stream closes. Interrupting Run at any point is safe: nothing here holds a lock across a reconcile, so the next Run (or a fresh process) picks up wherever observed state actually is.

func (*Engine) SetSource

func (e *Engine) SetSource(s Source)

SetSource attaches a dynamic controller source, reconciled in addition to the fixed controllers NewEngine was built with. Call it once before Run, if at all; not safe to call concurrently with reconciles in flight.

func (*Engine) SetStore

func (e *Engine) SetStore(s Store)

SetStore attaches persistence for reconcile status. Call it once before Run, if at all; not safe to call concurrently with reconciles in flight.

type Result

type Result struct {
	Conditions []Condition
}

Result is what a Controller returns from one Reconcile call.

type Source

type Source func(ctx context.Context) ([]Controller, error)

Source dynamically supplies the current set of controllers to reconcile, called once per pass immediately before those controllers run. A Source re-derives its controller set from live desired state every call, the same level-triggered principle every controller in this codebase already follows for its own resource: nothing about which apps or databases exist is remembered between calls, everything is re-derived from what's in the store right now. This is how the engine goes from a fixed set of controllers built once at startup to one controller per app and per database, appearing and disappearing as desired state changes, without a restart.

type Store

type Store interface {
	UpsertConditions(ctx context.Context, controllerName string, conditions []Condition) error
}

Store is optional persistence for reconcile status. Every reconcile is required to emit a status condition with a reason string, stored and shown in the UI; without a Store, Engine still tracks the latest result via LastResult, but only in memory, lost on restart. internal/store.DB satisfies this interface structurally, no import of internal/store needed here (or that would be a cycle, since internal/store already imports this package for the Condition type).

Directories

Path Synopsis
Package application implements the declarative app spec's service contract and the application controller: the reconcile.Controller that converges a real, store-backed desired service to a running container, replacing nginxdemo's hardcoded desired state with the real thing.
Package application implements the declarative app spec's service contract and the application controller: the reconcile.Controller that converges a real, store-backed desired service to a running container, replacing nginxdemo's hardcoded desired state with the real thing.
Package cloudflaretunnel implements the reconcile.Controller that converges a single, platform-wide desired state (store.
Package cloudflaretunnel implements the reconcile.Controller that converges a single, platform-wide desired state (store.
Package database implements the managed database controller: the reconcile.Controller that converges a store-backed store.DesiredDatabase to a running, volume-backed container, the same architectural pattern internal/reconcile/application already establishes (level-triggered, deterministic naming, a narrow store interface for testability), applied to a database instead of a built application image.
Package database implements the managed database controller: the reconcile.Controller that converges a store-backed store.DesiredDatabase to a running, volume-backed container, the same architectural pattern internal/reconcile/application already establishes (level-triggered, deterministic naming, a narrow store interface for testability), applied to a database instead of a built application image.
Package ingress implements the ingress controller: the reconcile.Controller that keeps Caddy's config (internal/ingress, ADR 005) in sync with every service that declares domains.
Package ingress implements the ingress controller: the reconcile.Controller that keeps Caddy's config (internal/ingress, ADR 005) in sync with every service that declares domains.
Package mesh implements the mesh controller: the reconcile.Controller that keeps the WireGuard mesh and the internal DNS zone converged on whatever the store currently says the fleet and its placements look like.
Package mesh implements the mesh controller: the reconcile.Controller that keeps the WireGuard mesh and the internal DNS zone converged on whatever the store currently says the fleet and its placements look like.
Package nginxdemo is the Phase 0 exit criterion: one controller that keeps a single hardcoded nginx container running.
Package nginxdemo is the Phase 0 exit criterion: one controller that keeps a single hardcoded nginx container running.
Package nodehealth implements the node health check: the reconcile.Controller that converges a node's observed heartbeat (internal/store's last_seen_at, kept fresh by internal/agent.Server's periodic touch loop while a node's gRPC session stays open) against its recorded Status, the same architectural pattern internal/reconcile/application and internal/reconcile/database already establish (level-triggered, a narrow store interface for testability, one controller instance per resource), applied to a node instead of a service or database.
Package nodehealth implements the node health check: the reconcile.Controller that converges a node's observed heartbeat (internal/store's last_seen_at, kept fresh by internal/agent.Server's periodic touch loop while a node's gRPC session stays open) against its recorded Status, the same architectural pattern internal/reconcile/application and internal/reconcile/database already establish (level-triggered, a narrow store interface for testability, one controller instance per resource), applied to a node instead of a service or database.
Package registry implements the reconcile.Controller that converges a single, platform-wide desired state (store.RegistrySettings plus a generated password in internal/secrets) to a running or absent registry:2 container: Levelrail's own built-in image registry, so a multi-node deployment gets a BuildKit cache/distribution backend (internal/build's WithCacheRegistry) without an operator first signing up for an external one.
Package registry implements the reconcile.Controller that converges a single, platform-wide desired state (store.RegistrySettings plus a generated password in internal/secrets) to a running or absent registry:2 container: Levelrail's own built-in image registry, so a multi-node deployment gets a BuildKit cache/distribution backend (internal/build's WithCacheRegistry) without an operator first signing up for an external one.

Jump to

Keyboard shortcuts

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