reconcile

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Jul 6, 2026 License: AGPL-3.0 Imports: 6 Imported by: 0

Documentation

Overview

Package reconcile computes the 3-way plan (git desired ↔ committed baseline ↔ live ABM) for bidirectional, newest-wins sync. Phase 1 computes + reports the plan only; Phase 2 executes it.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Action

type Action string

Action is the reconcile verb chosen for one config in the plan.

const (
	Create    Action = "create-abm"   // new in git → POST
	Update    Action = "update-abm"   // changed in git → PATCH
	Pull      Action = "pull-git"     // changed in ABM → write into git
	PullNew   Action = "pull-new-git" // new in ABM (console-created) → write into git
	DeleteABM Action = "delete-abm"   // removed from git → DELETE (prune, gated)
	DeleteGit Action = "delete-git"   // removed from ABM → remove git file
	Conflict  Action = "conflict"     // changed in BOTH → newest-wins (resolved at apply)
)

Reconcile actions, one per planned change to a config.

type Applier

type Applier interface {
	CreateConfiguration(name, xml string, platforms []string) (id, updated string, err error)
	UpdateConfiguration(id, name, xml string) (updated string, err error)
	DeleteConfiguration(id string) error
	AddBlueprintMembers(bpID, rel, memberType string, ids []string) error
	RemoveBlueprintMembers(bpID, rel, memberType string, ids []string) error
}

Applier is the subset of the Apple Business write API the executor needs. It is an interface so Apply can be unit-tested with a fake — no production writes.

type Archiver

type Archiver interface {
	Archive(name, reason string, xml []byte, meta map[string]string) (path string, err error)
}

Archiver files a pre-overwrite live profile + sidecar (see internal/archive). Injected so Apply stays disk-free and testable; it returns the archive path.

type BlueprintAction

type BlueprintAction string

BlueprintAction is the reconcile verb for a blueprint or one of its config members.

const (
	Attach        BlueprintAction = "attach-config"   // config in git, not in ABM → POST membership
	Detach        BlueprintAction = "detach-config"   // config in ABM, not in git → DELETE membership (gated --prune)
	BlueprintNew  BlueprintAction = "blueprint-new"   // blueprint in git, not in ABM → can't auto-create (reported)
	BlueprintGone BlueprintAction = "blueprint-adopt" // blueprint in ABM, not in git → run seed to adopt (reported)
)

Blueprint reconcile actions.

type BlueprintItem

type BlueprintItem struct {
	Blueprint string          `json:"blueprint"`
	BPID      string          `json:"bp_id,omitempty"`
	Action    BlueprintAction `json:"action"`
	Config    string          `json:"config,omitempty"`
	ConfigID  string          `json:"config_id,omitempty"`
	Detail    string          `json:"detail"`
}

BlueprintItem is one planned blueprint change.

type BlueprintOutcome

type BlueprintOutcome struct {
	Blueprint string          `json:"blueprint"`
	Config    string          `json:"config,omitempty"`
	Action    BlueprintAction `json:"action"`
	Status    string          `json:"status"` // "done" | "skipped" | "error"
	Detail    string          `json:"detail"`
}

BlueprintOutcome records what happened to one planned blueprint item.

type BlueprintPlan

type BlueprintPlan struct {
	Items []BlueprintItem `json:"items"`
}

BlueprintPlan is the ordered set of planned blueprint changes.

func ComputeBlueprints

func ComputeBlueprints(desired map[string]gitops.BlueprintSpec, live []ab.LiveBlueprint, cfgIDByName map[string]string) *BlueprintPlan

ComputeBlueprints diffs the git blueprint manifests against live ABM blueprints, per config membership. cfgIDByName resolves a config NAME → its ABM id (built from live configs, or from the post-apply baseline so freshly-created configs resolve). A blueprint is matched by name across git and ABM.

func (*BlueprintPlan) HasChanges

func (p *BlueprintPlan) HasChanges() bool

HasChanges reports whether the plan contains any items (actionable or reported).

func (*BlueprintPlan) HasReconcilableChanges

func (p *BlueprintPlan) HasReconcilableChanges() bool

HasReconcilableChanges reports whether the plan has drift that sync can act on — an attach or a detach. Reported-only advisories (blueprint-new / blueprint-adopt, which sync never applies) are excluded, so they don't make --exit-on-diff loop forever or trigger a confirm-then-do-nothing apply.

func (*BlueprintPlan) ReconcilableCount

func (p *BlueprintPlan) ReconcilableCount() int

ReconcilableCount is the number of attach/detach items (for a confirm prompt).

type BlueprintResult

type BlueprintResult struct {
	Outcomes []BlueprintOutcome `json:"outcomes"`
	Writes   int                `json:"writes"`
	Errors   int                `json:"errors"`
	Skipped  int                `json:"skipped"`
}

BlueprintResult summarizes a blueprint apply run.

type Engine

type Engine struct {
	Client   Applier
	Archiver Archiver
	Files    FileStore
}

Engine executes a reconcile plan against a live tenant. It archives before every overwrite or delete and keeps the committed baseline exact so the next 3-way diff is correct.

func (*Engine) Apply

func (e *Engine) Apply(p *Plan, desired map[string][]byte, live []ab.LiveConfig, base *state.State, opts Opts) *Result

Apply executes the plan. Every error is captured per-item in the Result (Errors count) rather than aborting the run, so independent configs still converge; the baseline (base) is mutated in place and should be saved by the caller only after Apply returns. Archiving always precedes the write it protects — if the archive fails, the write is skipped so the audit trail is never bypassed.

func (*Engine) ApplyBlueprints

func (e *Engine) ApplyBlueprints(p *BlueprintPlan, opts Opts, priorWrites int) *BlueprintResult

ApplyBlueprints executes the blueprint membership plan: attach (always) and detach (only with --prune) config members via per-member POST/DELETE (the relationship is additive/merges, so this converges). priorWrites is the tenant writes already spent this run (e.g. by config apply) so --limit-writes is a single shared budget. Reported-only items (create/adopt) are surfaced as skips.

type FileStore

type FileStore interface {
	WriteConfig(name string, content []byte) error
	RemoveConfig(name string) error
}

FileStore is the git-side profile tree (see internal/gitops). Pull writes a file; an ABM-side delete removes one.

type Item

type Item struct {
	Name   string `json:"name"`
	Action Action `json:"action"`
	Detail string `json:"detail"`
}

Item is one planned change: the config name, the action, and a human-readable detail.

type Opts

type Opts struct {
	Prune       bool     // enable DeleteABM (off by default — never prune unasked)
	LimitWrites int      // circuit breaker: max tenant writes per run (0 = unlimited)
	Platforms   []string // configuredForPlatforms for newly-created configs
	// GitTime resolves the git-side timestamp of a config (last commit time, else
	// file mtime) for newest-wins conflict resolution. ok=false → the timestamp is
	// unknown, so the conflict is skipped rather than guessed (never clobber a side
	// on missing information).
	GitTime func(name string) (t time.Time, ok bool)
}

Opts tunes one apply run.

type Outcome

type Outcome struct {
	Name    string `json:"name"`
	Action  Action `json:"action"` // the effective action (a Conflict resolves to Update or Pull)
	Status  string `json:"status"` // "done" | "skipped" | "error"
	Detail  string `json:"detail"`
	Archive string `json:"archive,omitempty"` // archive path, when one was written
}

Outcome records what happened to one planned item.

type Plan

type Plan struct {
	Items []Item `json:"items"`
}

Plan is the ordered set of changes the reconcile produced.

func Compute

func Compute(desired map[string][]byte, base *state.State, live []ab.LiveConfig) *Plan

Compute diffs desired (git lib/) vs baseline (state) vs live (ABM). Only changed items are returned; unchanged configs are omitted.

func (*Plan) HasChanges

func (p *Plan) HasChanges() bool

HasChanges reports whether the plan contains any changes.

type Result

type Result struct {
	Outcomes []Outcome `json:"outcomes"`
	Writes   int       `json:"writes"`  // tenant writes performed (POST/PATCH/DELETE)
	Errors   int       `json:"errors"`  // items that failed
	Skipped  int       `json:"skipped"` // items intentionally not applied (prune off / limit reached / unresolved)
}

Result summarizes an apply run.

Jump to

Keyboard shortcuts

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