Documentation
¶
Overview ¶
Package reconcile drives a non-clobbering, idempotent sync of a desired set of Units into a ConfigHub Space. It is the engine behind the installer's "plan"/"update" and cub's "variant upload" refresh: given a desired Unit set (each with a stable merge-external-source identity), it computes adds/updates/deletes against the live Space and applies them so that operator post-install edits survive (3-way merge on update, empty-merge instead of delete on removal).
The engine never scans directories, parses manifests, or knows about AppConfig — the caller supplies the desired set already expanded, with a body (Path or Content) and a source name per Unit. All ConfigHub side effects go through a CubRunner, so callers can fork the cub binary or invoke the CLI in-process.
This engine was lifted from the installer's internal/diff. The installer still carries its own copy; migrating it onto this package is tracked by confighub issue #4806.
Index ¶
- func DefaultChangeSetSlug(t time.Time) string
- func RestoreCommand(space, slug string, updatedSlugs []string) string
- type ApplyOptions
- type ApplyResult
- type BinaryRunner
- type ChangeSetRef
- type CubRunner
- type DesiredUnit
- type Engine
- type Plan
- type PostSpaceHook
- type SlugDiff
- type SpacePlan
- type SpaceSource
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DefaultChangeSetSlug ¶
DefaultChangeSetSlug returns the conventional slug for a reconcile ChangeSet at the given time. Stable input → stable output (no nondeterminism); callers wanting the live timestamp pass time.Now().
Format: reconcile-YYYYMMDD-HHMMSS (UTC). Avoids RFC3339's colons and timezone offsets that ConfigHub slug rules disallow.
func RestoreCommand ¶
RestoreCommand returns the verbatim shell command an operator can run to revert the updates a ChangeSet captured. updatedSlugs scopes the restore to exactly the Units modified in the ChangeSet, avoiding partial-failure noise when other Units share the ownership label but were not part of it. Uses --patch because bulk restore requires it.
Types ¶
type ApplyOptions ¶
type ApplyOptions struct {
// Yes permits emptying Units that dropped out of the desired set.
// Required when the plan contains deletes.
Yes bool
// ChangeSetSlug is the slug used when opening a per-Space ChangeSet for
// updates. Defaults to DefaultChangeSetSlug(time.Now()) chosen by the
// caller — pass a non-empty, retry-stable slug for reproducibility.
ChangeSetSlug string
// ChangeSetDescription is the description on the opened ChangeSet.
// Defaults to a generic line derived from the Space's DisplayName.
ChangeSetDescription string
// Stdout/Stderr override the I/O streams; nil uses os.Stdout/os.Stderr.
Stdout io.Writer
Stderr io.Writer
// PostSpaceHook runs after each Space's changes. Nil = no-op.
PostSpaceHook PostSpaceHook
}
ApplyOptions tunes Apply. Zero value is the safe default: interactive refusal to empty Units without Yes, os.Stdout/os.Stderr, no hook.
type ApplyResult ¶
type ApplyResult struct {
Created int
Updated int
Deleted int
ChangeSetsOpened []ChangeSetRef
}
ApplyResult reports what Apply did.
type BinaryRunner ¶
type BinaryRunner struct {
Bin string
}
BinaryRunner is a CubRunner that forks the cub binary. Bin defaults to "cub" resolved from $PATH. This is the runner the installer uses; a CLI that embeds the engine can instead supply an in-process runner.
type ChangeSetRef ¶
ChangeSetRef names one opened ChangeSet so the caller can render the revert command. UpdatedSlugs is the exact set of Units modified inside it.
type CubRunner ¶
type CubRunner interface {
Run(ctx context.Context, args []string, stdout, stderr io.Writer) error
}
CubRunner executes the cub CLI. args excludes the leading "cub". Either writer may be nil (discard). Implementations either fork a cub process (BinaryRunner) or dispatch to the CLI's commands in-process.
type DesiredUnit ¶
type DesiredUnit struct {
// Slug is the Unit slug — its stable identity in the Space.
Slug string
// SourceName is the merge-external-source identity passed to cub on
// create/update. The server picks the 3-way-merge base by source type
// per Unit, so this value only labels the change, but keeping it stable
// across runs (typically the source file's basename) keeps the audit
// trail coherent. Defaults to baseName(Path), or Slug when Path is empty.
SourceName string
// Path is a file on disk holding the Unit body. Used when Content is nil.
Path string
// Content, when non-nil, is the Unit body; the engine stages it to a
// temp file for cub. Takes precedence over Path — set it for bodies that
// don't already exist as files (e.g. AppConfig raw env content).
Content []byte
// Toolchain, when set, is forwarded to `cub unit create --toolchain`.
// Empty lets cub infer it. Ignored on update (cub uses the existing Unit).
Toolchain string
// Bodyless marks a Unit that should be recognized as present — kept out
// of delete candidates — but never added or updated by this engine. Used
// for Units whose body is maintained elsewhere, e.g. an AppConfig
// placeholder populated by an Upsert link.
Bodyless bool
}
DesiredUnit is one Unit the caller wants present in the Space.
type Engine ¶
type Engine struct {
Cub CubRunner
}
Engine reconciles desired Unit sets against ConfigHub via Cub.
func (*Engine) Apply ¶
func (e *Engine) Apply(ctx context.Context, plan Plan, opts ApplyOptions) (ApplyResult, error)
Apply executes the plan. Updates run inside a per-Space ChangeSet (adds and deletes are not ChangeSet-revertable). An empty plan is a no-op.
func (*Engine) Compute ¶
Compute produces a Plan by querying ConfigHub for the current owned Unit set (per SpaceSource.ListWhere) and diffing it against the desired set. ConfigHub state is the single source of truth: an add is a desired Unit absent from the Space, an update is a desired Unit whose dry-run merge reports non-bookkeeping changes, a delete is an owned Unit absent from the desired set (excluding Ignore and Bodyless Units). Compute mutates nothing.
type Plan ¶
type Plan struct {
Spaces []SpacePlan
}
Plan is the diff between the desired Unit sets and live ConfigHub state, broken down per Space.
func (Plan) HasChanges ¶
HasChanges reports whether the plan would create, update, or delete anything.
type PostSpaceHook ¶
PostSpaceHook runs after a Space's Unit changes (adds, updates, deletes) have been applied. Callers use it to reconcile links, refresh bookkeeping records, or any per-Space follow-up. Returning an error fails Apply.
type SlugDiff ¶
type SlugDiff struct {
Slug string
SourceName string
Path string
Content []byte
Toolchain string
// DiffText is the cub -o mutations diff (ANSI-stripped, bookkeeping
// filtered). Set only for Updates.
DiffText string
}
SlugDiff is one entry in Adds/Updates/Deletes. For Deletes only Slug is set.
type SpacePlan ¶
type SpacePlan struct {
Space string
DisplayName string
DisplayVersion string
// Adds exist in Desired but not in the live Space.
Adds []SlugDiff
// Updates exist in both and cub's dry-run reports non-bookkeeping changes.
Updates []SlugDiff
// Deletes are owned Units absent from Desired (Ignore/Bodyless excluded).
Deletes []SlugDiff
Target string
CreateLabels []string
CreateAnnotations []string
UpdateAnnotations []string
}
SpacePlan is the slice of a Plan in one Space, carrying forward the stamping config from its SpaceSource so Apply is self-contained.
type SpaceSource ¶
type SpaceSource struct {
// Space is the target Space slug.
Space string
// ListWhere scopes which existing Units this reconcile owns and may
// therefore delete: the engine lists current Units matching this cub
// where-clause (e.g. "Labels.Package='web'"). Empty means every Unit in
// the Space is owned.
ListWhere string
// Ignore lists owned Unit slugs to never treat as delete candidates even
// when absent from Desired (e.g. a bookkeeping record Unit).
Ignore []string
// Desired is the target Unit set.
Desired []DesiredUnit
// Target is the target ref forwarded to `cub unit create --target` for
// adds. Empty means no target binding. Existing Units keep their binding.
Target string
// CreateLabels / CreateAnnotations are key=value pairs stamped on adds
// only — never re-applied on update, which would clobber operator edits.
CreateLabels []string
CreateAnnotations []string
// UpdateAnnotations are key=value pairs re-stamped on every update via a
// separate --patch (engine-owned bookkeeping the caller wants kept in
// sync, e.g. a version marker). Empty skips the extra patch.
UpdateAnnotations []string
// DisplayName / DisplayVersion are presentation only, echoed in Apply's
// per-Space banner and the default ChangeSet description.
DisplayName string
DisplayVersion string
}
SpaceSource is the desired state for one Space: the Unit set plus the ownership scoping and stamping the engine applies. Component/variant semantics live in the caller; the engine only reconciles Units.