rollback

package
v0.2.1 Latest Latest
Warning

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

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

Documentation

Overview

Package rollback plans and applies a return to an earlier control plane release. Applying is host-side only: the control plane process never replaces its own binary (see adr/028-release-rollback.md).

Index

Constants

View Source
const (
	OutcomeApplied   = "applied"
	OutcomeRecovered = "recovered"
)

Outcomes of Apply.

View Source
const (
	StepBackup      = "backup"
	StepFetch       = "fetch"
	StepVerify      = "verify"
	StepStop        = "stop"
	StepRestore     = "restore_backup"
	StepSwap        = "swap_binary"
	StepStart       = "start"
	StepHealth      = "verify_health"
	StepAutoRecover = "auto_recover"
)

Step codes in a Plan. The dashboard and CLI translate them.

View Source
const (
	SourceRetained = "retained"
	SourceManifest = "manifest"
	SourceUnknown  = "unknown"
)

Schema sources for a target.

View Source
const KeepCount = 3

KeepCount is how many release binaries stay on disk for instant, offline rollback.

Variables

View Source
var (
	ErrConfirmVersion     = errors.New("typed confirmation does not match the target version")
	ErrNotRetained        = errors.New("target release is not retained on this host")
	ErrSchemaNewer        = errors.New("database schema is newer than the target release supports")
	ErrSchemaUnknown      = errors.New("target release schema version is unknown")
	ErrRestoreNotNeeded   = errors.New("a backup restore is not needed for this target")
	ErrBackupIncompatible = errors.New("backup is not compatible with the target release")
	ErrConfirmDataLoss    = errors.New("data loss confirmation does not match the backup name")
	ErrRecovered          = errors.New("target release failed to start, previous release restored")
)

Refusals from Apply. Each is raised before anything is changed.

View Source
var ErrChecksum = errors.New("retained binary does not match its recorded checksum")

ErrChecksum means a retained binary no longer matches its recorded SHA-256.

View Source
var ErrInvalidVersion = errors.New("not a release version")

ErrInvalidVersion means a version string is not a release tag.

Functions

func DBSchemaVersion

func DBSchemaVersion(ctx context.Context, dbPath string) (int, error)

DBSchemaVersion reads the schema version of the database file read-only. It never migrates, so it is safe against a database newer or older than the running binary.

func Dir

func Dir(exe string) string

Dir returns the directory that holds retained binaries for the executable at exe.

func FileSHA256

func FileSHA256(path string) (string, int64, error)

FileSHA256 returns the hex SHA-256 and size of the file at path.

func Guard

func Guard(plan Plan, opts Options) error

Guard decides whether opts may proceed against plan, before any change.

func SwapInBackup

func SwapInBackup(dbPath, backupPath string, now time.Time) (string, error)

SwapInBackup moves the live database (and its WAL files) aside under a timestamped name and copies backupPath into place. It returns the aside path for UndoSwap.

func UndoSwap

func UndoSwap(dbPath, aside string) error

UndoSwap puts the database kept by SwapInBackup back in place.

func ValidVersion

func ValidVersion(v string) bool

ValidVersion reports whether v looks like a release tag. It also keeps tags safe to use inside a file name.

func Verify

func Verify(r Retained) error

Verify recomputes r's SHA-256 and compares it with the recorded one. A binary with no recorded checksum is refused: it was not retained by the verified install path.

func WaitHealthy

func WaitHealthy(ctx context.Context, base string, timeout time.Duration) error

WaitHealthy polls base+"/healthz" and base+"/readyz" until both return 2xx or timeout passes.

func WriteAudit

func WriteAudit(ctx context.Context, dbPath, actor, path string, now time.Time) error

WriteAudit inserts one audit_log row straight into the database file, without running migrations: the host process may be newer than the schema it just rolled back to, and opening the store would migrate it forward.

Types

type BackupOption

type BackupOption struct {
	Name          string    `json:"name"`
	CreatedAt     time.Time `json:"created_at"`
	SchemaVersion int       `json:"schema_version"`
	Compatible    bool      `json:"compatible"`
}

BackupOption is a database backup a restore could use.

func ListBackups

func ListBackups(ctx context.Context, dataDir string) ([]BackupOption, error)

ListBackups returns the newest local control plane backups with their schema versions (-1 when a file cannot be inspected).

type Deps

type Deps struct {
	BinaryPath  string
	ReleasesDir string
	BinaryName  string
	// DBSchema reads the live database's schema version.
	DBSchema func(ctx context.Context) (int, error)
	// Backups lists restore points with their schema versions.
	Backups func(ctx context.Context) ([]BackupOption, error)
	// Backup takes a fresh backup of the live database and returns its name.
	Backup func(ctx context.Context) (string, error)
	Stop   func(ctx context.Context) error
	Start  func(ctx context.Context) error
	// WaitHealthy returns nil once the control plane answers its health and
	// readiness probes, or an error when timeout passes first.
	WaitHealthy func(ctx context.Context, timeout time.Duration) error
	// RestoreDB swaps the named backup in and returns the path the previous
	// database was kept at. UndoRestoreDB puts that path back.
	RestoreDB     func(ctx context.Context, backupName string) (string, error)
	UndoRestoreDB func(ctx context.Context, keptPath string) error
	// WriteMarker leaves the context the next boot records in upgrade history,
	// and ClearMarker withdraws it when the target did not stay. Both optional.
	WriteMarker func(ctx context.Context, backupName string) error
	ClearMarker func()
	// Audit records the outcome in the audit log. Best effort.
	Audit func(ctx context.Context, detail string) error
	Log   *slog.Logger
	Now   func() time.Time
}

Deps are the host operations Apply drives. Tests fake them; the CLI wires real ones (systemd, sqlite, filesystem).

type Options

type Options struct {
	CurrentVersion  string
	ConfirmVersion  string
	RestoreBackup   string
	ConfirmDataLoss string
	AcceptUnknown   bool
	HealthTimeout   time.Duration
}

Options are the operator's choices for one rollback.

type Plan

type Plan struct {
	CurrentVersion       string                `json:"current_version"`
	CurrentSchemaVersion int                   `json:"current_schema_version"`
	Target               Target                `json:"target"`
	Verdict              upgrade.SchemaVerdict `json:"verdict"`
	Steps                []string              `json:"steps"`
	Warnings             []string              `json:"warnings"`
	DowntimeSeconds      int                   `json:"downtime_seconds"`
	RestoreRequired      bool                  `json:"restore_required"`
	Backups              []BackupOption        `json:"backups"`
	LossSince            *time.Time            `json:"data_loss_since,omitempty"`
	Command              string                `json:"command"`
	RestoreCommand       string                `json:"restore_command,omitempty"`
}

Plan is everything an operator needs to decide on a rollback.

func BuildPlan

func BuildPlan(in PlanInput) Plan

BuildPlan derives the verdict, steps, warnings and downtime for a rollback. It is pure: callers supply the schema versions and backup list.

type PlanInput

type PlanInput struct {
	CurrentVersion       string
	CurrentSchemaVersion int
	DBSizeBytes          int64
	Target               Target
	Backups              []BackupOption
	ProgramName          string
}

PlanInput is the facts BuildPlan needs, gathered by the caller.

type Result

type Result struct {
	Outcome     string `json:"outcome"`
	From        string `json:"from"`
	To          string `json:"to"`
	BackupName  string `json:"backup_name,omitempty"`
	Restored    string `json:"restored_backup,omitempty"`
	KeptDB      string `json:"previous_database,omitempty"`
	Plan        Plan   `json:"plan"`
	HealthError string `json:"health_error,omitempty"`
}

Result reports what Apply did.

func Apply

func Apply(ctx context.Context, d Deps, plan Plan, opts Options) (Result, error)

Apply performs the rollback: guard, fresh backup, optional restore, binary swap, restart, health verification, and automatic recovery. Nothing changes until Guard and the target checksum pass.

type Retained

type Retained struct {
	Version       string    `json:"version"`
	Path          string    `json:"-"`
	SHA256        string    `json:"sha256"`
	SizeBytes     int64     `json:"size_bytes"`
	SchemaVersion int       `json:"schema_version"`
	RetainedAt    time.Time `json:"retained_at"`
}

Retained is one release binary kept on this host.

func Find

func Find(dir, name, version string) (Retained, bool, error)

Find returns the retained binary for version.

func List

func List(dir, name string) ([]Retained, error)

List returns retained binaries newest first, with the SHA-256 recorded when each was retained (not recomputed: Verify does that before use).

func Retain

func Retain(dir, name, version, src string, schemaVersion int, now time.Time) (Retained, error)

Retain copies src into dir as the binary for version, records its SHA-256 and schema version beside it, then prunes to KeepCount, never removing version itself. schemaVersion is -1 when unknown.

type Target

type Target struct {
	Version       string `json:"version"`
	SchemaVersion int    `json:"schema_version"`
	SchemaSource  string `json:"schema_source"`
	Retained      bool   `json:"retained"`
	SHA256        string `json:"sha256,omitempty"`
}

Target describes the release being rolled back to.

Jump to

Keyboard shortcuts

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