backup

package
v1.0.2 Latest Latest
Warning

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

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

Documentation

Overview

Package backup produces and manages owner-run backups: one tar per backup holding the core dump, one dump per plugin schema, the portability export and a manifest. Records live on disk (a sidecar next to each archive), so listing and deleting work with the database down and survive a restore.

Index

Constants

View Source
const (
	EnvKeep          = "HATAMOTO_BACKUP_KEEP"
	EnvKeepPreUpdate = "HATAMOTO_BACKUP_KEEP_PRE_UPDATE"
)

Environment variables that set how many ok backups of a kind are kept.

View Source
const DefaultWait = 10 * time.Minute

DefaultWait is how long a pre-update backup waits for a running one.

Variables

View Source
var ErrBusy = errors.New("another backup is running")

ErrBusy means another backup holds the lock.

View Source
var ErrNotFound = errors.New("backup not found")

ErrNotFound is returned for an unknown or invalid id, and for a download of a backup that is not ok.

View Source
var ErrRunning = errors.New("backup is running")

ErrRunning is returned when deleting a backup that is still being made.

Functions

func ErrorLine

func ErrorLine(out []byte) string

ErrorLine is what to show the owner of a failed pg_dump: the first "pg_dump: error:" line (plus a directly following detail line), because the last lines are often the LOCK TABLE query that follows it. Without one, the last non-empty line.

func KeepFromEnv

func KeepFromEnv() (manual, preUpdate int)

KeepFromEnv reads the retention counts (manual, pre-update); a missing or invalid value gives the default of 5 and 2.

func NewID

func NewID(t time.Time, kind Kind) string

NewID is the id of a backup of kind created at t.

func OSExec

func OSExec(ctx context.Context, env []string, name string, args ...string) ([]byte, error)

OSExec is the production ExecFunc.

func TryLock

func TryLock(dir string) (func(), error)

TryLock takes the one-backup-at-a-time lock on dir without waiting. It is a flock, so a process that dies frees it.

func ValidID

func ValidID(id string) bool

ValidID reports whether id has the shape NewID produces. Anything that reaches the file system goes through it first.

func WaitLock

func WaitLock(ctx context.Context, dir string, wait time.Duration) (func(), error)

WaitLock polls TryLock every 250 ms until it succeeds, ctx ends or wait passes (then ErrBusy).

Types

type Engine

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

Engine takes backups and manages the ones on disk.

func New

func New(o Options) *Engine

New builds an Engine. A data dir that cannot be created shows up as an error from every call that needs it.

func (*Engine) Begin

func (e *Engine) Begin(ctx context.Context, req Request) (Info, func(ctx context.Context) (Info, error), error)

Begin takes the lock and writes the running record before it returns, so a caller can answer "started" (or ErrBusy) at once and do the work later. The returned function does the work and releases the lock; it must be called exactly once. Pass it a lifecycle context, never a request context.

func (*Engine) Create

func (e *Engine) Create(ctx context.Context, req Request) (Info, error)

Create takes a backup and returns its record. A manual request while another backup runs gives ErrBusy at once; a pre-update request waits.

func (*Engine) Delete

func (e *Engine) Delete(id string) error

func (*Engine) FreeDisk

func (e *Engine) FreeDisk() uint64

FreeDisk is the free space of the backups directory, 0 when unknown.

func (*Engine) Get

func (e *Engine) Get(id string) (Info, error)

func (*Engine) List

func (e *Engine) List() ([]Info, error)

List, Get, Delete and Open need only the backups directory.

func (*Engine) MediaDirAbs

func (e *Engine) MediaDirAbs() string

MediaDirAbs is the absolute path of the media dir the content export reads.

func (*Engine) Open

func (e *Engine) Open(id string) (*os.File, Info, error)

type ExecFunc

type ExecFunc func(ctx context.Context, env []string, name string, args ...string) ([]byte, error)

ExecFunc runs a program and returns its combined output. env is added to the process environment.

type Info

type Info struct {
	ID      string    `json:"id"`
	Kind    Kind      `json:"kind"`
	Target  string    `json:"target,omitempty"`
	State   State     `json:"state"`
	Step    string    `json:"step,omitempty"`
	Percent int       `json:"percent"`
	Error   string    `json:"error,omitempty"`
	Created time.Time `json:"created"`
	Size    int64     `json:"size,omitempty"`
	Parts   []string  `json:"parts,omitempty"`
	Plugins []string  `json:"plugins,omitempty"`
	// MediaMissing counts media files the database references that were not
	// on disk; the backup is still OK, it just lacks them.
	MediaMissing int `json:"media_missing,omitempty"`
}

Info is the sidecar record of one backup.

type Kind

type Kind string

Kind says why a backup was taken.

const (
	KindManual    Kind = "manual"
	KindPreUpdate Kind = "pre_update"
	// KindScheduled is reserved; nothing creates it yet.
	KindScheduled Kind = "scheduled"
)

type Options

type Options struct {
	Data        string // the data dir; backups live in $Data/backups
	MediaDir    string // upload dir of the content export; empty means ./uploads
	DatabaseURL string
	Pool        *pgxpool.Pool // nil means the database is not reachable
	Run         ExecFunc      // nil means OSExec
	// Free reports the bytes writable under dir; nil skips the space check.
	Free          func(dir string) (uint64, error)
	Now           func() time.Time
	Log           *slog.Logger
	Version       string // hatamoto version, recorded in the manifest
	KeepManual    int
	KeepPreUpdate int
	// SecretsDir holds secrets.env; the file is added to a backup when present.
	SecretsDir string
	// ConfigPath is the operator's env file, stored verbatim; its path is
	// recorded in the manifest as an absolute path. Empty means no
	// config part; a Request.ConfigPath wins.
	ConfigPath string
	// Trust gives the key IDs recorded in the manifest; nil means the IDs
	// embedded in this binary.
	Trust func() (TrustIDs, error)
}

Options configure an Engine.

type Plugin

type Plugin struct {
	Name   string // display name, e.g. foo-bar
	Schema string // plugin_foo_bar
	Role   string
}

Plugin is one plugin schema to dump and the role that owns it.

func (Plugin) Part

func (p Plugin) Part() string

Part is the archive file name of the plugin dump.

type Querier

type Querier interface {
	Query(ctx context.Context, sql string, args ...any) (pgx.Rows, error)
	QueryRow(ctx context.Context, sql string, args ...any) pgx.Row
}

Querier is the part of a pgx pool discovery needs.

type RefusalError

type RefusalError struct{ Reasons []string }

RefusalError lists every reason a restore was refused; nothing was written.

func (*RefusalError) Error

func (e *RefusalError) Error() string

type Request

type Request struct {
	Kind   Kind
	Target string        // the version a pre-update backup protects
	Wait   time.Duration // how long a pre-update backup waits for the lock; 0 means DefaultWait
	// ConfigPath overrides Options.ConfigPath for this backup.
	ConfigPath string
}

Request asks for one backup.

type RestoreOptions

type RestoreOptions struct {
	Data        string // the data dir; an id source is $Data/backups/<id>.tar
	MediaDir    string // must be empty or missing
	SecretsDir  string // secrets.env is written here
	DatabaseURL string // the app role on the empty target database
	Version     string // the running hatamoto version; must equal the backup's
	Run         ExecFunc
	// Q reads the target database (emptiness, server major). Nil opens a pool
	// from DatabaseURL for the checks.
	Q           Querier
	ServerMajor func(ctx context.Context) (int, error)
	// Trust gives the key IDs of this binary; nil means the embedded ones.
	Trust    func() (TrustIDs, error)
	OpenPool func(ctx context.Context, url string) (*pgxpool.Pool, error)
	// OpenAdmin connects with the admin URL (pointed at the target database)
	// for the pre-flight checks; nil connects with pgx.
	OpenAdmin func(ctx context.Context, url string) (Querier, func(), error)
	Log       *slog.Logger
	// Getenv reads the ambient environment for the libpq overrides check; nil
	// means os.Getenv.
	Getenv func(string) string
}

RestoreOptions say where a backup is restored to. Everything is checked against the archive before anything is written.

type RestorePlan

type RestorePlan struct {
	Backup   string
	Checks   []string
	Would    []string
	Warnings []string
	DryRun   bool
	Done     bool // the steps ran: Print says "done", not "would"
}

RestorePlan is what a restore checked and does (or would do).

func Restore

Restore checks a backup against this machine and, unless it is a dry run, restores it. It is not an Engine method: Engine.New creates directories, and a dry run writes nothing.

func (*RestorePlan) Print

func (p *RestorePlan) Print(w io.Writer)

Print writes the plan in the shape of the restore command output.

type RestoreRequest

type RestoreRequest struct {
	Source    string // a backup id or the path of a .tar
	AdminURL  string // superuser DSN for the role bootstrap
	ConfigOut string // where to write the recorded config; empty means the config is not written
	DryRun    bool
}

RestoreRequest is one restore.

type State

type State string

State is where a backup is in its life.

const (
	StateRunning State = "running"
	StateOK      State = "ok"
	StateFailed  State = "failed"
)

type Store

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

Store reads and writes the records in $DATA/backups. It needs no database.

func NewStore

func NewStore(data string) (*Store, error)

NewStore opens (and creates) $data/backups, mode 0700: the archives hold the whole database.

func (*Store) Delete

func (s *Store) Delete(id string) error

Delete removes the archive, then the sidecar. A backup that is really running is refused; an interrupted one reads as failed and can go.

func (*Store) Dir

func (s *Store) Dir() string

Dir is the backups directory.

func (*Store) Get

func (s *Store) Get(id string) (Info, error)

Get returns one record. A sidecar that says running while nobody holds the lock is reported (and rewritten) as failed: the process died.

func (*Store) List

func (s *Store) List() ([]Info, error)

List returns every backup, newest first. Legacy pre-*.dump files and anything hidden are not backups of this kind and are never touched.

func (*Store) Open

func (s *Store) Open(id string) (*os.File, Info, error)

Open returns the archive of an ok backup for download.

func (*Store) Prune

func (s *Store) Prune(kind Kind, keep int) error

Prune removes ok backups of kind beyond the newest keep. Failed and running ones stay for the owner to look at and delete.

func (*Store) Write

func (s *Store) Write(in Info) error

Write stores the sidecar through a temp file and a rename.

type TrustIDs

type TrustIDs struct {
	Release  []string `json:"release,omitempty"`
	Manifest []string `json:"manifest,omitempty"`
	Env      []string `json:"env,omitempty"`
}

TrustIDs are the key IDs a backup records: the release and manifest keys embedded in the binary that made it, and the operator's own keys.

Jump to

Keyboard shortcuts

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