Documentation
¶
Index ¶
- Constants
- func ArchiveCompressor(masterKey []byte, extra ...[]byte) (*compression.Compressor, error)
- func ArchiveCompressorFromKeyFile(path string) (*compression.Compressor, []byte, error)
- func ConnectToRunningInstance(ctx context.Context, deps *Dependencies, instanceName string, ...) (*pgx.Conn, error)
- func DescribeScheduleSet(hasSnapshotPlan bool, backupCrons int) string
- func GetActiveOffsiteSettingsDecrypted(deps *Dependencies) (*offsite.OffsiteSettings, error)
- func NormalizeSnapshotFormat(s string) (string, error)
- func OffsiteApply(deps *Dependencies, params *OffsiteApplyParams) error
- func OffsiteLogs(deps *Dependencies, params *OffsiteLogsParams) ([]offsite.OffsiteLog, error)
- func OffsiteRemove(deps *Dependencies) error
- func RemoveSnapshotLocal(ctx context.Context, deps *Dependencies, id int) error
- func RemoveSnapshotRemote(ctx context.Context, deps *Dependencies, id int) error
- func RestoreClusterFromArchive(ctx context.Context, deps *Dependencies, p RestoreClusterParams) (int, error)
- func SnapshotDownloadsDir(backupDir string) string
- func StreamInstanceLogs(ctx context.Context, deps *Dependencies, params GetLogsParams, w io.Writer) error
- func SweepSnapshotDownloads(backupDir string) (int, error)
- func TestPostgreSQLConnectivityWithPassword(ctx context.Context, port int, password string) error
- type AddDatabaseUserParams
- type AddDatabaseUserResult
- type ArchiveOrigin
- type BackupRDBMSParams
- type BackupRDBMSResult
- type ChecklistArchive
- type ChecklistBackupCron
- type ChecklistCoverage
- type ChecklistHealth
- type ChecklistInstance
- type ChecklistNotificationConfig
- type ChecklistNotificationEvent
- type ChecklistNotifications
- type ChecklistOp
- type ChecklistResult
- type ChecklistSnapCopy
- type ChecklistSnapshots
- type ConnectOptions
- type ConsistencyCheckOp
- type ConsistencyStatus
- type CreateDatabaseParams
- type CreateDatabaseResult
- type CreateRDBMSOp
- type CreateRDBMSParams
- type CreateRDBMSResult
- type CronBackupCreateOp
- type CronBackupDeleteOp
- type CronBackupListOp
- type CronLogsParams
- type CronLogsResult
- type CronTaskOp
- type DatabaseInfo
- type DatabaseMeta
- type DeleteDatabaseUserParams
- type DeleteDatabaseUserResult
- type DeleteRDBMSOp
- type DeleteRDBMSParams
- type Dependencies
- type DownloadBackupParams
- type DownloadBackupResult
- type DownloadSnapshotResult
- type DropAllBackupsResult
- type Executor
- func (e *Executor) AddDatabaseUserOp(ctx context.Context, deps *Dependencies, params AddDatabaseUserParams) (*AddDatabaseUserResult, error)
- func (e *Executor) Close()
- func (e *Executor) CreateDatabaseOp(ctx context.Context, deps *Dependencies, params CreateDatabaseParams) (*CreateDatabaseResult, error)
- func (e *Executor) DeleteDatabaseUserOp(ctx context.Context, deps *Dependencies, params DeleteDatabaseUserParams) (*DeleteDatabaseUserResult, error)
- func (e *Executor) Drain(ctx context.Context) error
- func (e *Executor) Execute(ctx context.Context, op Operation) error
- func (e *Executor) ExecuteAsync(ctx context.Context, op Operation) <-chan error
- func (e *Executor) ExecuteRead(ctx context.Context, op Operation) error
- func (e *Executor) ListDatabasesOp(ctx context.Context, deps *Dependencies, params ListDatabasesParams) (*ListDatabasesResult, error)
- func (e *Executor) ResetDatabaseUserPasswordOp(ctx context.Context, deps *Dependencies, ...) (*ResetDatabaseUserPasswordResult, error)
- type FetchResult
- type GetLogsParams
- type GetLogsResult
- type GetPasswordOp
- type GetPasswordResult
- type GetRDBMSOp
- type InstanceMeta
- type ListBackupsParams
- type ListBackupsResult
- type ListDatabasesParams
- type ListDatabasesResult
- type ListRDBMSOp
- type MakeSnapshotParams
- type MakeSnapshotResult
- type NotificationAddParams
- type NotificationAddResult
- type NotificationEditParams
- type NotificationEditResult
- type NotificationGetParams
- type NotificationGetResult
- type NotificationListParams
- type NotificationListResult
- type NotificationLogsParams
- type NotificationLogsResult
- type NotificationRemoveParams
- type NotificationRemoveResult
- type NotificationTestParams
- type NotificationTestResult
- type OffsiteApplyParams
- type OffsiteGetResult
- type OffsiteInfoResult
- type OffsiteLogsParams
- type OffsiteTestResult
- type OpType
- type Operation
- type ParameterGroupDeleteParams
- type ParameterGroupDeleteResult
- type ParameterGroupGetParams
- type ParameterGroupGetResult
- type ParameterGroupInfo
- type ParameterGroupListParams
- type ParameterGroupListResult
- type ParameterGroupPutParams
- type ParameterGroupPutResult
- type PostgreSQLStatus
- type PreflightCheck
- type PullImageOp
- type PullImageParams
- type PullImageResult
- type ReconfigureRDBMSOp
- type ReconfigureRDBMSParams
- type ReconfigureRDBMSResult
- type RemoteSnapshotObject
- type RemoteSnapshotSpec
- type RemoveLocalBackupParams
- type RemoveLocalBackupResult
- type RemoveRemoteBackupParams
- type RemoveRemoteBackupResult
- type ResetDatabaseUserPasswordParams
- type ResetDatabaseUserPasswordResult
- type RestoreArchiveSource
- type RestoreClusterParams
- type RestoreDatabaseParams
- type RestoreDatabaseResult
- type RestoreInstanceParams
- type RestoreInstanceResult
- type RestoreRDBMSParams
- type RestoreRDBMSResult
- type SetPasswordOp
- type SetPasswordResult
- type SnapshotApplyParams
- type SnapshotApplyPlan
- type SnapshotApplyResult
- type SnapshotCaptureFailure
- type SnapshotCronTaskOp
- type SnapshotInstanceEntry
- type SnapshotManifest
- type SwitchRDBMSOp
- type SwitchRDBMSParams
- type SwitchRDBMSResult
- type UpdateRDBMSOp
- type UpdateRDBMSParams
- type UpdateStateOp
- type UpdateStateParams
- type UpgradeRDBMSOp
- type UpgradeRDBMSParams
- type UpgradeRDBMSResult
- type UploadBackupParams
- type UploadBackupResult
- type UploadSnapshotResult
Constants ¶
const ( ArchiveOriginFile = "file" ArchiveOriginCatalogue = "catalogue" ArchiveOriginCatalogueDownloaded = "catalogue-downloaded" ArchiveOriginS3Download = "s3-download" ArchiveOriginS3Cached = "s3-cached" )
Archive origin kinds, reported to the operator.
const ( SnapshotFormatPhysical = "physical" SnapshotFormatLogical = "logical" )
Snapshot formats. "Physical" is a byte-level copy of each cluster (pg_basebackup, or a cold file copy for a stopped instance); "logical" is the portable pg_dump-based format. Physical is the default: it is cheaper to capture, restores byte-for-byte (per-database GUCs, database-level ACLs and ICU collations survive, which logical restore cannot reproduce), and a DR restore lands on the same image/arch anyway. Logical remains the right tool for cross-major, cross-architecture and single-database restores.
const ( SnapshotFilePrefix = "snapshot-" SnapshotStagingPrefix = ".snapshot-" )
SnapshotFilePrefix begins the filename of every snapshot archive, and SnapshotStagingPrefix every in-progress staging directory. Both are exported because the daemon's startup sweep needs to recognise them: staging dirs are orphans to delete, finished snapshots are deliberately unreferenced by backup_history and must not be reported as stray archives.
const LegacyBackupNewScheduleRefusal = "per-instance backups are deprecated and will be removed in the first release after " + LegacyBackupRemovalAfter + ", so new backup schedules are no longer accepted. Schedule snapshots instead — " + "one schedule covers every instance: 'oddk snapshot setup-cron --utc-hour <hour>'. " + "Existing backup schedules keep running and can still be changed, paused, resumed or removed; " + "'oddk snapshot migrate-from-backups' moves them over"
LegacyBackupNewScheduleRefusal is the daemon's answer to a request for a new per-instance backup schedule.
const LegacyBackupRemovalAfter = "2026-12-31"
Per-instance backups are deprecated in favour of whole-deployment snapshots and will be REMOVED in the first release after LegacyBackupRemovalAfter.
Until then every backup command keeps working — restore, list, download and dangerously-drop-all must, since existing archives have to stay usable — with three exceptions that stop NEW adoption without breaking any deployment's protection:
- every `oddk backup ...` command prints a notice to stderr naming its replacement (the CLI; stdout and --json output are untouched);
- `backup setup-cron` refuses to create a NEW schedule (the daemon). Existing schedules keep running and can still be edited, paused, resumed or removed; `snapshot migrate-from-backups` moves them over;
- the README and CHANGELOG announce the removal.
What removal will mean: the `backup` commands, the per-instance backup schedules and the backup_history catalogue. NOT the dump engine (BackupRDBMS / stageInstanceDump): `instance major-upgrade` takes a full backup as its rollback artifact, and logical snapshots stage through it.
const MaxRemoteSnapshotListing = 1000
MaxRemoteSnapshotListing bounds a bucket listing; truncation is reported, never silent.
const SnapshotCronInstance = "*snapshot*"
SnapshotCronInstance is the identity a scheduled snapshot uses in cron_logs and in the task tracker's dedup queue.
Snapshots cover the whole deployment, so they have no instance name — but the tracker dedups by name and cron_logs.instance_name is NOT NULL. '*' cannot appear in a real instance name (letters, digits, '-' and '_' only, per util.ValidateInstanceName), so this can never collide with one.
const SnapshotDownloadsDirName = "downloads"
SnapshotDownloadsDirName is the managed area under the backup directory for snapshot archives fetched from S3 that no catalogue row references — a foreign deployment's archive pulled for restore-instance, or apply's own download on a DR host. Deliberately NOT dot-prefixed: the startup sweep deletes dot-prefixed backup-dir entries as orphaned staging. As a subdirectory it is invisible to the top-level unreferenced-archive warnings, to ReconcileLocalLocations, and to retention — which is the point: nothing in here is catalogued, because a foreign row in snapshot_history would drive every checklist coverage verdict and displace real archives from the retention newest-2 floor.
const SnapshotDownloadsTTL = 7 * 24 * time.Hour
SnapshotDownloadsTTL is how long a fetched archive is kept. Everything in the downloads area is re-fetchable from S3 by construction, so pruning loses nothing; reuse bumps the file's mtime, so an archive an operator is actively restoring from does not age out mid-series.
const ( // SnapshotFormatVersion is the newest archive format this binary // understands. Bump it only for a change an older reader cannot cope with. SnapshotFormatVersion = snapshotFormatVersionPhysical )
Snapshot archive format versions. `snapshot apply` refuses anything newer than SnapshotFormatVersion, which is what stops a pre-physical binary from hunting for globals.sql inside a physical archive and failing confusingly.
A LOGICAL snapshot still stamps v1 because its layout genuinely is v1 — the version describes the archive, not the binary that wrote it. NOTE this does NOT make new logical archives applyable by older binaries: the pre-existing OddkVersion gate refuses any archive created by a newer oddk regardless of format. --logical's real benefits are portability across architectures and single-database workflows, not older readers.
const SnapshotS3Prefix = "*snapshots*"
SnapshotS3Prefix is the first key segment for snapshots, mirroring how a backup's key starts with its instance name. Snapshots belong to no single instance, and "*" cannot collide with a real one because instance names are restricted to letters, digits, '-' and '_'.
Variables ¶
This section is empty.
Functions ¶
func ArchiveCompressor ¶ added in v0.1.94
func ArchiveCompressor(masterKey []byte, extra ...[]byte) (*compression.Compressor, error)
ArchiveCompressor returns the compressor every archive goes through. It writes to the archive key derived from masterKey (this host's), and reads with that key plus any extra master keys — a foreign archive's, from --master-key. Nil and repeated keys are skipped; with none at all it can read only plaintext archives from ODDK <= 0.1.93 and refuses to write. See internal-docs/archive-encryption.md.
func ArchiveCompressorFromKeyFile ¶ added in v0.1.94
func ArchiveCompressorFromKeyFile(path string) (*compression.Compressor, []byte, error)
ArchiveCompressorFromKeyFile reads an operator-supplied master.key and returns the compressor for its archives, plus the key itself (callers also decrypt the snapshot's stored credentials with it).
func ConnectToRunningInstance ¶
func ConnectToRunningInstance(ctx context.Context, deps *Dependencies, instanceName string, opts ...ConnectOptions) (*pgx.Conn, error)
ConnectToRunningInstance connects to a PostgreSQL instance, ensuring it's running first
func DescribeScheduleSet ¶ added in v0.1.79
DescribeScheduleSet names a set of schedules — "snapshot schedule + 2 backup schedule(s)" — for the apply progress line and the CLI's closing warning, so the two cannot drift.
Deliberately neutral about WHY the set is interesting: it renders both the schedules apply paused and, under --no-pause-schedules, the ones it left running. It was called DescribePausedSchedules until the second caller existed, at which point the name would have been describing the opposite of what it printed.
func GetActiveOffsiteSettingsDecrypted ¶
func GetActiveOffsiteSettingsDecrypted(deps *Dependencies) (*offsite.OffsiteSettings, error)
GetActiveOffsiteSettingsDecrypted returns the active offsite settings with the SecretAccessKey decrypted, ready to hand to an S3 client. It returns a copy so the stored ciphertext is never mutated, and never lets the encrypted secret reach an S3 auth path (the bug that left cron remote cleanup unable to delete). For EC2 IAM-role mode (or an empty secret) the secret is left empty. Returns (nil, nil) when no offsite configuration is active.
func NormalizeSnapshotFormat ¶ added in v0.1.61
NormalizeSnapshotFormat maps the wire value to a canonical format, with the empty string meaning "the default" (physical). Every entry point — HTTP handler, cron plan, CLI — funnels through this so the accepted vocabulary cannot drift between them.
func OffsiteApply ¶
func OffsiteApply(deps *Dependencies, params *OffsiteApplyParams) error
func OffsiteLogs ¶
func OffsiteLogs(deps *Dependencies, params *OffsiteLogsParams) ([]offsite.OffsiteLog, error)
func OffsiteRemove ¶
func OffsiteRemove(deps *Dependencies) error
func RemoveSnapshotLocal ¶ added in v0.1.59
func RemoveSnapshotLocal(ctx context.Context, deps *Dependencies, id int) error
RemoveSnapshotLocal deletes the local archive, keeping any offsite copy.
When there is no offsite copy the RECORD is removed too, because snapshot_history CHECKs that a row describes at least one location — the same semantics RemoveLocalCopy implements for backups.
func RemoveSnapshotRemote ¶ added in v0.1.59
func RemoveSnapshotRemote(ctx context.Context, deps *Dependencies, id int) error
RemoveSnapshotRemote deletes the offsite copy, keeping any local one.
func RestoreClusterFromArchive ¶ added in v0.1.59
func RestoreClusterFromArchive(ctx context.Context, deps *Dependencies, p RestoreClusterParams) (int, error)
RestoreClusterFromArchive replays globals and every database from an extracted backup archive into a fresh cluster, then verifies that all expected roles and databases exist. It returns the number of user databases restored.
This is the full-fidelity restore path: unlike RestoreRDBMS (which restores a single database across clusters and therefore strips ownership), it recreates each database with its original owner, encoding and collation and restores WITH ownership and privileges. It assumes the cluster is empty, so the caller owns the decision that overwriting is safe.
Callers are responsible for status bookkeeping and for wrapping errors with any recovery hint — every failure here leaves a cluster that is partially restored at best.
func SnapshotDownloadsDir ¶ added in v0.1.65
SnapshotDownloadsDir returns the managed downloads area under backupDir.
func StreamInstanceLogs ¶
func StreamInstanceLogs(ctx context.Context, deps *Dependencies, params GetLogsParams, w io.Writer) error
StreamInstanceLogs streams container logs to w until ctx is cancelled or the container stops.
func SweepSnapshotDownloads ¶ added in v0.1.65
SweepSnapshotDownloads prunes the managed downloads area under backupDir: finished archives older than SnapshotDownloadsTTL (everything here is re-fetchable from S3 by construction, and reuse bumps mtime so an archive in active use stays), and .tmp-* partials older than an hour (orphaned by a crash — a live download keeps its mtime fresh). A missing directory is a no-op. The directory itself is left in place; delete/create churn buys nothing.
Types ¶
type AddDatabaseUserParams ¶
type AddDatabaseUserResult ¶
type AddDatabaseUserResult struct {
Username string `json:"username"`
Password string `json:"password"`
Database string `json:"database"`
ReadOnly bool `json:"readOnly"`
Message string `json:"message"`
}
func AddDatabaseUser ¶
func AddDatabaseUser(ctx context.Context, deps *Dependencies, params AddDatabaseUserParams) (*AddDatabaseUserResult, error)
type ArchiveOrigin ¶ added in v0.1.65
type ArchiveOrigin struct {
Kind string `json:"kind"`
Path string `json:"path"`
DownloadedBytes int64 `json:"downloadedBytes,omitempty"`
// CredentialSource says what authenticated an S3 fetch:
// "offsite-settings", "request", or "instance-role". Empty for local kinds.
CredentialSource string `json:"credentialSource,omitempty"`
// ExpectedSHA256 is the digest the catalogue recorded when this host
// wrote the archive, for an archive resolved THROUGH the catalogue (--id,
// or an s3 URI that is a catalogue row's remote copy). The restore checks
// the file against it before extracting, because a local copy is only
// ever stat'd here and an intact file that was REPLACED would otherwise
// restore as if it were the catalogued snapshot. Empty: nothing to check
// (a --file / uncatalogued --s3-uri source, or a pre-022 row).
ExpectedSHA256 string `json:"-"`
}
ArchiveOrigin reports where the restore's archive actually came from, so the CLI can say honestly what happened (downloaded vs reused vs local) and where the file now lives.
func ResolveRestoreInstanceArchive ¶ added in v0.1.65
func ResolveRestoreInstanceArchive(ctx context.Context, deps *Dependencies, src *RestoreArchiveSource, backupDir string, progress io.Writer) (string, bool, *ArchiveOrigin, error)
ResolveRestoreInstanceArchive turns a restore source into a local archive path. The returned foreign flag is true when the archive did NOT come from this host's own catalogue — such a restore must never backdate created_at for checklist coverage, because this host's snapshots do not hold that data.
- ArchivePath passes through untouched (existing --file behavior).
- SnapshotID uses the row's surviving local copy, else downloads it via DownloadSnapshot — whose refusals (no offsite config, no remote copy, an unreferenced file squatting on the target path) pass through unchanged, and whose SetLocalLocation makes the download the row's managed local copy, subject to normal retention.
- Remote first checks whether the URI IS some catalogue row's remote copy and treats that as the SnapshotID case (one archive should not exist in two places); otherwise it fetches into the managed downloads area and the archive stays uncatalogued.
type BackupRDBMSParams ¶
type BackupRDBMSParams struct {
Name string
BackupDir string
Comment string // Optional comment for the backup
}
BackupRDBMSParams contains parameters for backing up an RDBMS instance
type BackupRDBMSResult ¶
BackupRDBMSResult contains the result of backing up an RDBMS instance
func BackupRDBMS ¶
func BackupRDBMS(ctx context.Context, deps *Dependencies, params *BackupRDBMSParams) (*BackupRDBMSResult, error)
BackupRDBMS creates a backup of an RDBMS instance
type ChecklistArchive ¶ added in v0.1.62
type ChecklistArchive struct {
ID int `json:"id"`
Timestamp string `json:"timestamp"`
SizeBytes int64 `json:"sizeBytes"`
Location string `json:"location"` // "local", "s3", "local+s3"
Comment string `json:"comment,omitempty"`
}
ChecklistArchive describes one snapshot archive: the newest completed one globally, and the one an instance's coverage refers to.
type ChecklistBackupCron ¶
type ChecklistBackupCron struct {
UTCHour int `json:"utcHour"`
CleanupLocalDays int `json:"cleanupLocalDays"`
CleanupRemoteDays int `json:"cleanupRemoteDays"`
}
ChecklistBackupCron describes a still-scheduled per-instance daily backup. Legacy: `snapshot migrate-from-backups` removes these; one surviving is an outstanding migration task, which is the only reason it is still reported.
type ChecklistCoverage ¶ added in v0.1.62
type ChecklistCoverage struct {
State string `json:"state"`
Snapshot *ChecklistArchive `json:"snapshot,omitempty"` // set for covered/config-only
}
ChecklistCoverage reports whether this instance's DATA is in the newest completed snapshot. States:
- "covered": the newest snapshot holds this instance's data.
- "config-only": the newest snapshot holds only this instance's configuration — it was stopped during a logical capture, its container was missing/unsafe during a physical one, or its capture was ATTEMPTED AND FAILED (since 0.1.71 that degrades the entry instead of aborting the whole snapshot, and this verdict is one of the signals that keeps it from being silent). A restore from it would produce no database contents, so this must NOT read as protection.
- "not-captured": the instance post-dates the newest snapshot (also the case for an instance destroyed and re-created under the same name — the archive holds the predecessor's data, not this incarnation's).
- "no-snapshots": no snapshot has ever completed.
Snapshot names which instances had data since migration 019; for an older newest-record the state is decided by creation-time alone (optimistically "covered"), which self-corrects at the first post-upgrade capture.
type ChecklistHealth ¶
type ChecklistHealth struct {
Overall string `json:"overall"` // "healthy", "degraded", "unhealthy", "checking", "unknown"
CheckedAt string `json:"checkedAt,omitempty"`
HostHealthy bool `json:"hostHealthy"`
FailDetails string `json:"failDetails,omitempty"`
}
ChecklistHealth summarizes the latest daemon-wide health check.
type ChecklistInstance ¶
type ChecklistInstance struct {
Name string `json:"name"`
Version string `json:"version"`
Status string `json:"status"`
Health string `json:"health"` // "ok", "failing", "not-checked", "unknown"
ParameterGroup string `json:"parameterGroup"`
LegacyBackupCron *ChecklistBackupCron `json:"legacyBackupCron,omitempty"`
SnapshotCoverage ChecklistCoverage `json:"snapshotCoverage"`
}
ChecklistInstance is the per-instance audit row.
Per-instance backups are a legacy feature and deliberately absent from the audit: the protection question is answered by snapshot coverage alone. The one backup-shaped field left is LegacyBackupCron — a per-instance backup schedule that still exists marks an un-migrated deployment, and an audit that silently hid it would bury the one signal that migration isn't done.
type ChecklistNotifications ¶
type ChecklistNotifications struct {
Configured []ChecklistNotificationConfig `json:"configured"`
LastEvent *ChecklistNotificationEvent `json:"lastEvent,omitempty"`
LastError *ChecklistNotificationEvent `json:"lastError,omitempty"`
}
ChecklistNotifications summarizes notification configuration and recent delivery activity. Notifications are global (not per-instance).
type ChecklistOp ¶
type ChecklistOp struct {
// contains filtered or unexported fields
}
ChecklistOp aggregates a read-only audit snapshot across all instances: health, parameter group, snapshot coverage (plus a warning for legacy per-instance backup schedules) and global snapshot + notification status.
func NewChecklistOp ¶
func NewChecklistOp(deps *Dependencies) *ChecklistOp
func (*ChecklistOp) GetResult ¶
func (op *ChecklistOp) GetResult() *ChecklistResult
func (*ChecklistOp) Name ¶
func (op *ChecklistOp) Name() string
func (*ChecklistOp) Type ¶
func (op *ChecklistOp) Type() OpType
type ChecklistResult ¶
type ChecklistResult struct {
GeneratedAt string `json:"generatedAt"`
Health ChecklistHealth `json:"health"`
Instances []ChecklistInstance `json:"instances"`
Snapshots ChecklistSnapshots `json:"snapshots"`
Notifications ChecklistNotifications `json:"notifications"`
}
ChecklistResult is the audit snapshot returned by GET /api/checklist.
type ChecklistSnapCopy ¶ added in v0.1.59
type ChecklistSnapCopy struct {
LocalAndRemote int `json:"localAndRemote"`
RemoteOnly int `json:"remoteOnly"`
LocalOnly int `json:"localOnly"`
None int `json:"none"`
}
ChecklistSnapCopy breaks snapshots down by where they actually exist, the same way BackupCopies does — "we have 30 snapshots" means nothing if they are all on the machine that just died.
type ChecklistSnapshots ¶ added in v0.1.59
type ChecklistSnapshots struct {
Scheduled bool `json:"scheduled"`
UTCHour int `json:"utcHour,omitempty"`
IntervalHours int `json:"intervalHours,omitempty"`
Format string `json:"format,omitempty"`
LastSnapshot *ChecklistArchive `json:"lastSnapshot,omitempty"`
Total int `json:"total"`
Copies ChecklistSnapCopy `json:"copies"`
// Stale is set when a schedule exists but the newest snapshot is older than
// two intervals. Without it a schedule that has been failing for weeks reads
// green: the newest COMPLETED snapshot is still shown, and a failed capture
// writes no record at all, so there is nothing to make the gap visible.
Stale bool `json:"stale"`
StaleDetail string `json:"staleDetail,omitempty"`
// Paused reports a schedule that exists but is suspended, which `snapshot
// apply` does automatically so a restored host cannot write into the source
// deployment's offsite bucket. It is a PROBLEM, not a neutral state: the
// deployment is taking no snapshots, and the whole risk is that someone
// restores a host and never resumes it. Scheduled stays true — the schedule
// exists — so a reader must check both.
Paused bool `json:"paused"`
PausedSince string `json:"pausedSince,omitempty"`
PausedReason string `json:"pausedReason,omitempty"`
// PausedBackupCrons counts suspended per-instance backup schedules, which
// `snapshot apply` pauses for the same reason.
PausedBackupCrons []string `json:"pausedBackupCrons,omitempty"`
}
ChecklistSnapshots summarizes whole-deployment snapshot state. Like notifications, this is global rather than per-instance — a snapshot covers every instance, so there is exactly one schedule.
It belongs on the checklist because a snapshot is what a host migration or disaster recovery actually restores from. An audit that reports healthy per-instance backups while no snapshot has ever been taken is describing a deployment that cannot be rebuilt.
type ConnectOptions ¶
type ConnectOptions struct {
Database string
}
ConnectOptions allows specifying optional parameters for database connections
type ConsistencyCheckOp ¶
type ConsistencyCheckOp struct {
// contains filtered or unexported fields
}
ConsistencyCheckOp validates RDBMS instance consistency
func NewConsistencyCheckOp ¶
func NewConsistencyCheckOp(deps *Dependencies, instance *instances.RDBMSInstance) *ConsistencyCheckOp
func (*ConsistencyCheckOp) Execute ¶
func (op *ConsistencyCheckOp) Execute(ctx context.Context) error
Execute inspects the instance and reports what it finds. It WRITES NOTHING.
It is declared OpTypeRead and reached from the three most-run read commands (`oddk list`, `oddk instance status`, `oddk checklist`), and it runs on the executor's lock-free read path — so writing here would both break the OpTypeRead contract and race with whatever write operation is in flight.
It used to persist. First the PostgreSQL probe verdict, which latched: one blip wrote "broken-port" from an ordinary `oddk list`, and the recovery path had no branch that wrote "running" back, so every later run observed a healthy server and re-persisted the stale value. That was narrowed in 0.1.66 to container-derived state only, and is now removed entirely — because even the narrow version had the same shape of bug: an out-of-band `docker stop`, once observed by any `oddk list`, wrote "stopped" and thereby removed the instance from health monitoring (the checker skips anything not "running"), so no degraded notification could ever fire for it. A read command silently switching off monitoring is exactly the disease this file caught.
Startup reconciliation owns store correction now (see daemon.decideReconcile). Between restarts a stale row is harmless: snapshot capture asks Docker directly, the checklist and `oddk list` display the corrected status computed here in memory, and an instance whose container really did stop now fails its health check and alerts — which is the truthful outcome.
The one status the container does NOT get to correct is "error" — see deriveDisplayStatus.
func (*ConsistencyCheckOp) GetInstance ¶
func (op *ConsistencyCheckOp) GetInstance() *instances.RDBMSInstance
func (*ConsistencyCheckOp) GetStatus ¶
func (op *ConsistencyCheckOp) GetStatus() *ConsistencyStatus
func (*ConsistencyCheckOp) Name ¶
func (op *ConsistencyCheckOp) Name() string
func (*ConsistencyCheckOp) Type ¶
func (op *ConsistencyCheckOp) Type() OpType
type ConsistencyStatus ¶
type ConsistencyStatus struct {
ContainerExists bool
ContainerRunning bool
PostgreSQLReady bool
OverallHealthy bool
Issues []string
}
ConsistencyStatus represents the health status of an RDBMS instance
type CreateDatabaseParams ¶
type CreateDatabaseParams struct {
InstanceName string
DatabaseName string
// Username, when set, also creates the database's owner user in the
// same operation — the right setup for a service that runs its own
// migrations. If database creation fails, the just-created user is
// rolled back (best-effort; a failed rollback is logged and reported).
// Other access levels are deliberately not offered here — a read-only
// user on a brand-new empty database has nothing to read; add those
// with add-db-user once the database has content.
Username string
}
type CreateDatabaseResult ¶
type CreateDatabaseResult struct {
DatabaseName string `json:"databaseName"`
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
Message string `json:"message"`
}
func CreateDatabase ¶
func CreateDatabase(ctx context.Context, deps *Dependencies, params CreateDatabaseParams) (*CreateDatabaseResult, error)
type CreateRDBMSOp ¶
type CreateRDBMSOp struct {
// contains filtered or unexported fields
}
CreateRDBMSOp creates a new RDBMS instance
func NewCreateRDBMSOp ¶
func NewCreateRDBMSOp(deps *Dependencies, params CreateRDBMSParams) *CreateRDBMSOp
NewCreateRDBMSOp creates a new create RDBMS operation
func (*CreateRDBMSOp) GetResult ¶
func (op *CreateRDBMSOp) GetResult() *CreateRDBMSResult
GetResult returns the operation result
func (*CreateRDBMSOp) Name ¶
func (op *CreateRDBMSOp) Name() string
func (*CreateRDBMSOp) Type ¶
func (op *CreateRDBMSOp) Type() OpType
type CreateRDBMSParams ¶
type CreateRDBMSParams struct {
Name string
Version string
Image string
Port int
CPUCores int
RAMMB int
ParameterGroup string
}
CreateRDBMSParams contains parameters for creating an RDBMS instance
type CreateRDBMSResult ¶
type CreateRDBMSResult struct {
Instance *instances.RDBMSInstance
Password string // Plaintext password for initial response
}
CreateRDBMSResult contains the result of creating an RDBMS instance
type CronBackupCreateOp ¶
type CronBackupCreateOp struct {
// contains filtered or unexported fields
}
func NewCronBackupCreateOp ¶
func NewCronBackupCreateOp(deps *Dependencies, instanceName string, utcHour, cleanupLocalDays, cleanupRemoteDays int) *CronBackupCreateOp
func (*CronBackupCreateOp) Execute ¶
func (op *CronBackupCreateOp) Execute(ctx context.Context) error
func (*CronBackupCreateOp) GetResult ¶
func (op *CronBackupCreateOp) GetResult() *cron.CronPlan
func (*CronBackupCreateOp) Name ¶
func (op *CronBackupCreateOp) Name() string
func (*CronBackupCreateOp) Type ¶
func (op *CronBackupCreateOp) Type() OpType
type CronBackupDeleteOp ¶
type CronBackupDeleteOp struct {
// contains filtered or unexported fields
}
func NewCronBackupDeleteOp ¶
func NewCronBackupDeleteOp(deps *Dependencies, instanceName string) *CronBackupDeleteOp
func (*CronBackupDeleteOp) Execute ¶
func (op *CronBackupDeleteOp) Execute(ctx context.Context) error
func (*CronBackupDeleteOp) Name ¶
func (op *CronBackupDeleteOp) Name() string
func (*CronBackupDeleteOp) Type ¶
func (op *CronBackupDeleteOp) Type() OpType
type CronBackupListOp ¶
type CronBackupListOp struct {
// contains filtered or unexported fields
}
func NewCronBackupListOp ¶
func NewCronBackupListOp(deps *Dependencies) *CronBackupListOp
func (*CronBackupListOp) GetResult ¶
func (op *CronBackupListOp) GetResult() []*cron.CronPlan
func (*CronBackupListOp) Name ¶
func (op *CronBackupListOp) Name() string
func (*CronBackupListOp) Type ¶
func (op *CronBackupListOp) Type() OpType
type CronLogsParams ¶ added in v0.1.74
type CronLogsParams struct {
// Instance filters to one instance's runs. Empty returns every instance's,
// including the whole-deployment snapshot runs recorded under
// SnapshotCronInstance.
Instance string
Limit int
}
CronLogsParams selects which scheduled-run history to return.
type CronLogsResult ¶ added in v0.1.74
CronLogsResult is the scheduled-run history.
func CronLogs ¶ added in v0.1.74
func CronLogs(deps *Dependencies, params CronLogsParams) (*CronLogsResult, error)
CronLogs returns the recorded history of scheduled backups and snapshots.
cron_logs is the evidence that scheduled protection actually ran — the table the 365-day retention window exists to preserve for SOC 2 / PCI sampling — and until this operation existed it had no reader anywhere in the API or the CLI. A failed run recorded its reason and returned nil, leaving that reason reachable only through journalctl or a manual sqlite3 session.
type CronTaskOp ¶
type CronTaskOp struct {
// contains filtered or unexported fields
}
func NewCronTaskOp ¶
func NewCronTaskOp(deps *Dependencies, instanceName string) *CronTaskOp
func (*CronTaskOp) Name ¶
func (op *CronTaskOp) Name() string
func (*CronTaskOp) Type ¶
func (op *CronTaskOp) Type() OpType
type DatabaseInfo ¶
type DatabaseMeta ¶
type DatabaseMeta struct {
Name string `json:"name"`
Owner string `json:"owner"`
Encoding string `json:"encoding"`
Collate string `json:"collate"`
Ctype string `json:"ctype"`
LocProvider string `json:"locProvider"` // "c" (libc), "i" (icu), "b" (builtin); "c" assumed pre-PG15
CreateGrantees []string `json:"createGrantees,omitempty"`
}
DatabaseMeta captures the attributes needed to faithfully recreate a database (its encoding/collation and owner), plus CREATE privileges that pg_dump does not preserve when restore uses --no-privileges. It is written into the backup archive at backup time and consumed by restore and major-upgrade.
type DeleteDatabaseUserResult ¶
type DeleteDatabaseUserResult struct {
Username string `json:"username"`
Message string `json:"message"`
}
func DeleteDatabaseUser ¶
func DeleteDatabaseUser(ctx context.Context, deps *Dependencies, params DeleteDatabaseUserParams) (*DeleteDatabaseUserResult, error)
type DeleteRDBMSOp ¶
type DeleteRDBMSOp struct {
// contains filtered or unexported fields
}
DeleteRDBMSOp deletes an RDBMS instance
func NewDeleteRDBMSOp ¶
func NewDeleteRDBMSOp(deps *Dependencies, params DeleteRDBMSParams) *DeleteRDBMSOp
func (*DeleteRDBMSOp) Name ¶
func (op *DeleteRDBMSOp) Name() string
func (*DeleteRDBMSOp) Type ¶
func (op *DeleteRDBMSOp) Type() OpType
type DeleteRDBMSParams ¶
type DeleteRDBMSParams struct {
Name string
}
DeleteRDBMSParams contains parameters for deleting an RDBMS instance
type Dependencies ¶
type Dependencies struct {
Store *store.Store
Docker *docker.Client
MasterKey []byte
// Archives writes and reads this host's archives, encrypted to the key
// derived from MasterKey (ArchiveCompressor). Built once, with MasterKey.
Archives *compression.Compressor
DataDir string // Added for backup operations
BackupDir string // Added for backup operations
Logger *log.Logger
}
Dependencies holds all the dependencies needed by operations
type DownloadBackupParams ¶
DownloadBackupParams contains parameters for downloading a backup from S3
type DownloadBackupResult ¶
type DownloadBackupResult struct {
Message string `json:"message"`
Location string `json:"location"`
Size int64 `json:"size"`
}
DownloadBackupResult contains the result of downloading a backup
func DownloadBackup ¶
func DownloadBackup(ctx context.Context, deps *Dependencies, params DownloadBackupParams) (*DownloadBackupResult, error)
DownloadBackup downloads a backup from S3 to local storage
type DownloadSnapshotResult ¶ added in v0.1.59
type DownloadSnapshotResult struct {
ID int `json:"id"`
Filename string `json:"filename"`
Size int64 `json:"size"`
LocalPath string `json:"localPath"`
}
DownloadSnapshotResult describes a retrieved archive.
func DownloadSnapshot ¶ added in v0.1.59
func DownloadSnapshot(ctx context.Context, deps *Dependencies, id int, backupDir string) (*DownloadSnapshotResult, error)
DownloadSnapshot pulls a snapshot back from S3 into backupDir.
Without this the offsite copy is write-only: a snapshot is uploaded, local retention then removes the local copy, and the primary disaster-recovery artifact can no longer be obtained through ODDK at all — exactly when it is needed. `backup download` has always existed for the per-instance case; this is its counterpart.
type DropAllBackupsResult ¶ added in v0.1.63
type DropAllBackupsResult struct {
RecordsTotal int `json:"recordsTotal"`
RecordsDropped int `json:"recordsDropped"`
RecordsKept int `json:"recordsKept"`
LocalFilesDeleted int `json:"localFilesDeleted"`
LocalFilesMissing int `json:"localFilesMissing"`
LocalBytesFreed int64 `json:"localBytesFreed"`
RemoteObjectsDeleted int `json:"remoteObjectsDeleted"`
RemoteRefsDroppedNoOffsite int `json:"remoteRefsDroppedNoOffsite"`
Errors []string `json:"errors"`
Message string `json:"message"`
}
DropAllBackupsResult reports what DropAllBackups did. Errors are collected per record rather than aborting the sweep: the command exists to decommission the legacy per-instance backup system, and dying on record 3 of 200 would leave the operator guessing what remains. A record whose copies could not all be dealt with is KEPT, so the run is re-entrant.
func DropAllBackups ¶ added in v0.1.63
func DropAllBackups(ctx context.Context, deps *Dependencies) (*DropAllBackupsResult, error)
DropAllBackups deletes EVERY per-instance backup: local archive files, S3 objects, and the backup_history records — across all instances, including records whose instance has since been destroyed (which is why this cannot be a client-side loop over the per-ID remove endpoints: those refuse when the instance no longer exists). Snapshot archives are untouched — this walks backup_history only.
Copy-handling rules, chosen so a partial run can always be re-run:
- A local file that is already gone is fine (the record is still dropped); any other unlink failure keeps the record and is reported.
- With offsite configured, an S3 delete failure keeps the record (unlike per-ID remove-remote's warn-and-clear): silently mass-orphaning objects in the bucket is exactly what a bulk sweep must not do. If the local copy was already removed, the record's local location is cleared so it reflects reality (the CHECK constraint holds — remote is still set).
- With offsite NOT configured, remote references are dropped and counted: ODDK has no way to reach those objects, and keeping the records forever would make the sweep unfinishable. Mirrors remove-remote's semantics; the caller is told the objects themselves survive in the bucket.
type Executor ¶
type Executor struct {
// contains filtered or unexported fields
}
Executor manages sequential execution of operations
func (*Executor) AddDatabaseUserOp ¶
func (e *Executor) AddDatabaseUserOp(ctx context.Context, deps *Dependencies, params AddDatabaseUserParams) (*AddDatabaseUserResult, error)
AddDatabaseUserOp executes the add database user operation
func (*Executor) Close ¶ added in v0.1.74
func (e *Executor) Close()
Close stops the executor accepting NEW operations. Whatever is in flight is left alone to finish — use Drain to wait for it.
Reads are deliberately NOT refused after Close: they take no lock, change nothing, and staying answerable during a shutdown that is waiting on a long operation is the point of having a read path at all.
func (*Executor) CreateDatabaseOp ¶
func (e *Executor) CreateDatabaseOp(ctx context.Context, deps *Dependencies, params CreateDatabaseParams) (*CreateDatabaseResult, error)
CreateDatabaseOp executes the create database operation
func (*Executor) DeleteDatabaseUserOp ¶
func (e *Executor) DeleteDatabaseUserOp(ctx context.Context, deps *Dependencies, params DeleteDatabaseUserParams) (*DeleteDatabaseUserResult, error)
DeleteDatabaseUserOp executes the delete database user operation
func (*Executor) Drain ¶ added in v0.1.74
Drain blocks until no operation is running, or ctx is done.
This is the graceful-shutdown half of "operations are uninterruptible": killing the process mid-operation is exactly what the design forbids, so a stop must wait. Scheduled operations (cron backups, snapshots) run in their own goroutine rather than an HTTP handler, so http.Server.Shutdown does not see them — this does.
func (*Executor) Execute ¶
Execute runs an operation, ensuring sequential execution.
Callers from HTTP handlers should pass context.Background(), not r.Context(): operations are uninterruptible by HTTP client disconnect by design — a half-aborted pg_dump, pg_restore, S3 upload, or container-create would leave debris (orphan helper containers, partial files, inconsistent state). The ctx here is only useful if a caller intentionally wires in cancellation.
Note that concurrent callers QUEUE on the mutex for the whole duration of whatever is running — they are not rejected. (An earlier "another operation is already running" fast-fail was unreachable dead code: the mutex is held for the operation's entire duration, so the flag it checked could never be set when the check ran.)
func (*Executor) ExecuteAsync ¶
ExecuteAsync runs an operation asynchronously but still sequentially
func (*Executor) ExecuteRead ¶ added in v0.1.74
ExecuteRead runs a read-only operation WITHOUT taking the serialization lock, so it answers while a long write operation is in flight.
Why this exists: Execute holds one process-wide mutex for an operation's whole duration, and the read handlers took it too. During a whole-deployment snapshot — minutes to hours — `oddk list`, `oddk backup list` and `oddk checklist` blocked on that mutex and then died at the HTTP WriteTimeout with a bare EOF. The commands an operator reaches for to find out what is happening were exactly the ones that stopped answering while something was happening. OpType has been declared on every operation since the beginning and consulted nowhere; this is what it was for.
The safety argument has three parts, and all three must hold for an operation to be routed here:
- It must not write. Not to SQLite, not to Docker, not to disk. A read that mutates could interleave with the write operation it is running alongside. ConsistencyCheckOp used to persist a corrected status from `oddk list`; that write is gone, and startup reconciliation owns store correction now.
- Its reads must tolerate a concurrent writer. SQLite is opened in WAL mode with busy_timeout=5000 (see store.New), so readers do not block on the writer and vice versa. Docker inspects and PostgreSQL connects are naturally concurrent.
- A torn view is acceptable. A read here may observe a deployment mid-operation — an instance that is being recreated, a backup row that is about to exist. That is strictly better than the previous behaviour of answering nothing at all, and it is what an operator watching a long operation actually wants.
The OpTypeWrite guard is a programming-error check, not input validation: a write routed here would silently lose the serialization the whole design rests on, so it fails loudly instead.
func (*Executor) ListDatabasesOp ¶
func (e *Executor) ListDatabasesOp(ctx context.Context, deps *Dependencies, params ListDatabasesParams) (*ListDatabasesResult, error)
ListDatabasesOp executes the list databases operation
func (*Executor) ResetDatabaseUserPasswordOp ¶
func (e *Executor) ResetDatabaseUserPasswordOp(ctx context.Context, deps *Dependencies, params ResetDatabaseUserPasswordParams) (*ResetDatabaseUserPasswordResult, error)
ResetDatabaseUserPasswordOp executes the reset database user password operation
type FetchResult ¶ added in v0.1.65
FetchResult describes a fetched (or reused) archive in the downloads area.
func FetchRemoteSnapshot ¶ added in v0.1.65
func FetchRemoteSnapshot(ctx context.Context, c *compression.Compressor, client *s3service.Client, uri, key, destDir string, progress io.Writer) (*FetchResult, error)
FetchRemoteSnapshot downloads bucket-root key into destDir (created lazily).
- a missing object refuses with a list-remote hint before anything streams;
- an existing file with the object's exact size is REUSED (mtime bumped so the TTL sweep cannot reap an archive mid-restore-series) — retrying a failed restore or restoring several instances out of one archive must not download it again. A reused file is NOT re-verified: it was verified when it was downloaded, and every restore path verifies again at extract time, so paying to decompress a multi-gigabyte archive on each instance of a restore series would buy nothing;
- an existing file with a different size is atomically replaced: ODDK owns the downloads area exclusively and everything in it is re-fetchable, so a refusal here would only break retry-after-corruption;
- the stream lands in a dot-prefixed temp name and is renamed into place, so a crash can never leave a partial file at the final name.
uri is the operator's original s3:// form, used verbatim in messages (key may have had a configured bucket path stripped for the client to re-add). c verifies the download, so it must hold the archive's key.
func FetchSnapshotForApply ¶ added in v0.1.65
func FetchSnapshotForApply(ctx context.Context, spec *RemoteSnapshotSpec, masterKeyPath, backupDir string, progress io.Writer) (*FetchResult, error)
FetchSnapshotForApply downloads the archive for a daemon-less apply using this process's own ambient AWS credential chain (env vars, shared config/profile, ECS, EC2 instance role). Stored offsite settings cannot serve here even in principle: they live encrypted inside the SNAPSHOT's oddk.db, which is exactly what a DR host does not have yet.
Every failure ends with the no-changes reassurance, matching preflight refusals — the download is additive and nothing of ODDK's state has been touched.
masterKeyPath is apply's --master-key: the download is verified as it lands, which for an encrypted archive needs its key.
type GetLogsParams ¶
GetLogsParams contains parameters for getting instance logs
type GetLogsResult ¶
type GetLogsResult struct {
Logs string
}
GetLogsResult contains the result of getting instance logs
func GetInstanceLogs ¶
func GetInstanceLogs(ctx context.Context, deps *Dependencies, params GetLogsParams) (*GetLogsResult, error)
GetInstanceLogs fetches the container logs for a given instance
type GetPasswordOp ¶
type GetPasswordOp struct {
// contains filtered or unexported fields
}
GetPasswordOp is an operation to get the decrypted password for an instance
func NewGetPasswordOp ¶
func NewGetPasswordOp(deps *Dependencies, name string) *GetPasswordOp
NewGetPasswordOp creates a new operation to get password
func (*GetPasswordOp) GetResult ¶
func (op *GetPasswordOp) GetResult() *GetPasswordResult
func (*GetPasswordOp) Name ¶
func (op *GetPasswordOp) Name() string
func (*GetPasswordOp) Type ¶
func (op *GetPasswordOp) Type() OpType
type GetPasswordResult ¶
type GetPasswordResult struct {
Password string `json:"password"`
}
type GetRDBMSOp ¶
type GetRDBMSOp struct {
// contains filtered or unexported fields
}
GetRDBMSOp gets a single RDBMS instance
func NewGetRDBMSOp ¶
func NewGetRDBMSOp(deps *Dependencies, name string) *GetRDBMSOp
func (*GetRDBMSOp) GetResult ¶
func (op *GetRDBMSOp) GetResult() *instances.RDBMSInstance
func (*GetRDBMSOp) Name ¶
func (op *GetRDBMSOp) Name() string
func (*GetRDBMSOp) Type ¶
func (op *GetRDBMSOp) Type() OpType
type InstanceMeta ¶ added in v0.1.59
type InstanceMeta struct {
Name string `json:"name"`
Version string `json:"version"`
Image string `json:"image"`
Port int `json:"port"`
CPUCores int `json:"cpuCores"`
RAMMB int `json:"ramMb"`
ParameterGroup string `json:"parameterGroup"`
// ParameterGroupDefinition inlines the group rather than only naming it:
// the target host has no reason to have a group by that name, and
// recreating the container needs the actual parameters. Nil when the group
// could not be read at backup time (see captureInstanceMetadata).
ParameterGroupDefinition *parameters.ParameterGroup `json:"parameterGroupDefinition,omitempty"`
}
InstanceMeta captures the instance's own configuration — everything needed to recreate the container it ran in, as opposed to the data inside it. Written at backup time and consumed when rebuilding an instance on a different host.
Deliberately absent:
- The postgres password, in either form. The plaintext is never written to disk, and the encrypted form lives in oddk.db, which a snapshot carries separately. Putting it here would copy a credential into every per-instance archive, including ones uploaded individually to S3.
- Status and container ID, which describe this host and mean nothing on another one.
type ListBackupsParams ¶
type ListBackupsParams struct {
InstanceName string
}
ListBackupsParams contains parameters for listing backups
type ListBackupsResult ¶
type ListBackupsResult struct {
Backups []backup.BackupRecord
}
ListBackupsResult contains the result of listing backups
func ListBackups ¶
func ListBackups(ctx context.Context, deps *Dependencies, params ListBackupsParams) (*ListBackupsResult, error)
ListBackups lists all backups for an RDBMS instance with consistency checks If InstanceName is empty, lists all backups across all instances
type ListDatabasesParams ¶
type ListDatabasesParams struct {
InstanceName string
}
type ListDatabasesResult ¶
type ListDatabasesResult struct {
Databases []DatabaseInfo `json:"databases"`
}
func ListDatabases ¶
func ListDatabases(ctx context.Context, deps *Dependencies, params ListDatabasesParams) (*ListDatabasesResult, error)
type ListRDBMSOp ¶
type ListRDBMSOp struct {
// contains filtered or unexported fields
}
ListRDBMSOp lists all RDBMS instances
func NewListRDBMSOp ¶
func NewListRDBMSOp(deps *Dependencies) *ListRDBMSOp
func (*ListRDBMSOp) GetResult ¶
func (op *ListRDBMSOp) GetResult() []instances.RDBMSInstance
func (*ListRDBMSOp) Name ¶
func (op *ListRDBMSOp) Name() string
func (*ListRDBMSOp) Type ¶
func (op *ListRDBMSOp) Type() OpType
type MakeSnapshotParams ¶ added in v0.1.59
type MakeSnapshotParams struct {
BackupDir string // where the snapshot archive is written
Comment string // free-text note stored with the catalogue record
// Format is SnapshotFormatPhysical or SnapshotFormatLogical; empty means
// physical (callers should have gone through NormalizeSnapshotFormat).
Format string
// SpreadCheckpoint selects pg_basebackup's spread checkpoint, which paces
// the initial checkpoint over minutes instead of spiking I/O. Scheduled
// runs set it; interactive runs use a fast checkpoint because an operator
// is waiting.
SpreadCheckpoint bool
}
MakeSnapshotParams configures a snapshot.
type MakeSnapshotResult ¶ added in v0.1.59
type MakeSnapshotResult struct {
ID int `json:"id,omitempty"`
Path string `json:"path"`
Size int64 `json:"size"`
Timestamp time.Time `json:"timestamp"`
Format string `json:"format"`
Instances []SnapshotInstanceEntry `json:"instances"`
InstancesWithData int `json:"instancesWithData"`
ConfigOnly int `json:"configOnly"`
// SHA256 of the archive file, as recorded in the catalogue.
SHA256 string `json:"sha256,omitempty"`
// CaptureFailures is non-empty when the archive was written but is NOT a
// complete capture of the deployment. Callers must treat that as a failed
// run — see CaptureFailureError — while still keeping, shipping and
// cataloguing the archive.
CaptureFailures []SnapshotCaptureFailure `json:"captureFailures,omitempty"`
}
MakeSnapshotResult describes the produced archive.
func MakeSnapshot ¶ added in v0.1.59
func MakeSnapshot(ctx context.Context, deps *Dependencies, params *MakeSnapshotParams) (*MakeSnapshotResult, error)
MakeSnapshot captures the whole deployment — every instance's databases and roles, plus the control plane's own oddk.db — into a single archive that `snapshot apply` can rebuild a host from.
A stopped instance is captured configuration-only rather than failing the whole snapshot: its data cannot be dumped without a live server, but letting one stopped instance block disaster-recovery capture for every other instance would be the wrong trade. Such entries are marked in the manifest and warned about, so they cannot quietly look like a complete capture.
Instances are dumped sequentially, so a snapshot is NOT a single point in time across instances. That is acceptable for host moves and DR, and must be stated wherever this is documented for users.
func (*MakeSnapshotResult) CaptureFailureError ¶ added in v0.1.74
func (r *MakeSnapshotResult) CaptureFailureError() error
CaptureFailureError summarises the per-instance capture failures as one error, or nil when the capture was complete. It is what turns a degraded archive into a reported failure at every layer above this one.
type NotificationAddParams ¶
type NotificationAddParams struct {
Name string `json:"name"`
Type notifications.NotificationType `json:"type"`
Config json.RawMessage `json:"config"`
}
type NotificationAddResult ¶
type NotificationAddResult struct {
Notification *notifications.Notification `json:"notification"`
}
func NotificationAdd ¶
func NotificationAdd(ctx context.Context, deps *Dependencies, params NotificationAddParams) (*NotificationAddResult, error)
type NotificationEditParams ¶
type NotificationEditParams struct {
Name string `json:"name"`
Type string `json:"type"`
Config json.RawMessage `json:"config"`
}
type NotificationEditResult ¶
type NotificationEditResult struct {
Notification *notifications.Notification `json:"notification"`
}
func NotificationEdit ¶
func NotificationEdit(ctx context.Context, deps *Dependencies, params NotificationEditParams) (*NotificationEditResult, error)
type NotificationGetParams ¶
type NotificationGetParams struct {
Name string `json:"name"`
}
type NotificationGetResult ¶
type NotificationGetResult struct {
Notification *notifications.Notification `json:"notification"`
}
func NotificationGet ¶
func NotificationGet(ctx context.Context, deps *Dependencies, params NotificationGetParams) (*NotificationGetResult, error)
type NotificationListParams ¶
type NotificationListParams struct{}
type NotificationListResult ¶
type NotificationListResult struct {
Notifications []notifications.Notification `json:"notifications"`
}
func NotificationList ¶
func NotificationList(ctx context.Context, deps *Dependencies, params NotificationListParams) (*NotificationListResult, error)
type NotificationLogsParams ¶
type NotificationLogsParams struct {
Limit int `json:"limit"`
}
type NotificationLogsResult ¶
type NotificationLogsResult struct {
Logs []notifications.NotificationLog `json:"logs"`
}
func NotificationLogs ¶
func NotificationLogs(ctx context.Context, deps *Dependencies, params NotificationLogsParams) (*NotificationLogsResult, error)
type NotificationRemoveParams ¶
type NotificationRemoveParams struct {
Name string `json:"name"`
}
type NotificationRemoveResult ¶
type NotificationRemoveResult struct {
Message string `json:"message"`
}
func NotificationRemove ¶
func NotificationRemove(ctx context.Context, deps *Dependencies, params NotificationRemoveParams) (*NotificationRemoveResult, error)
type NotificationTestParams ¶
type NotificationTestParams struct{}
type NotificationTestResult ¶
type NotificationTestResult struct {
Message string `json:"message"`
}
func NotificationTest ¶
func NotificationTest(ctx context.Context, deps *Dependencies, params NotificationTestParams) (*NotificationTestResult, error)
type OffsiteApplyParams ¶
type OffsiteApplyParams struct {
ConfigJSON []byte
}
type OffsiteGetResult ¶
type OffsiteGetResult struct {
Config offsite.OffsiteSettingsJSON `json:"config"`
}
func OffsiteGet ¶
func OffsiteGet(deps *Dependencies) (*OffsiteGetResult, error)
type OffsiteInfoResult ¶
type OffsiteInfoResult struct {
Active bool `json:"active"`
Config *offsite.OffsiteSettings `json:"config,omitempty"`
}
func OffsiteInfo ¶
func OffsiteInfo(deps *Dependencies) (*OffsiteInfoResult, error)
type OffsiteLogsParams ¶
type OffsiteLogsParams struct {
Limit int
}
type OffsiteTestResult ¶
type OffsiteTestResult struct {
Success bool `json:"success"`
Message string `json:"message"`
Error string `json:"error,omitempty"`
}
func OffsiteTest ¶
func OffsiteTest(deps *Dependencies) (*OffsiteTestResult, error)
type Operation ¶
type Operation interface {
// Name returns a human-readable name for the operation
Name() string
Execute(ctx context.Context) error
// Type returns whether this is a read or write operation
Type() OpType
}
Operation represents a unit of work that can be executed
type ParameterGroupDeleteParams ¶
type ParameterGroupDeleteParams struct {
Name string
}
ParameterGroupDeleteParams contains parameters for deleting a parameter group
type ParameterGroupDeleteResult ¶
type ParameterGroupDeleteResult struct {
Message string `json:"message"`
}
func ParameterGroupDelete ¶
func ParameterGroupDelete(ctx context.Context, deps *Dependencies, params ParameterGroupDeleteParams) (*ParameterGroupDeleteResult, error)
type ParameterGroupGetParams ¶
type ParameterGroupGetParams struct {
Name string
}
ParameterGroupGetParams contains parameters for getting a parameter group
type ParameterGroupGetResult ¶
type ParameterGroupGetResult struct {
GroupName string `json:"groupName"`
Parameters []parameters.ResolvedParameter `json:"parameters"`
}
func ParameterGroupGet ¶
func ParameterGroupGet(ctx context.Context, deps *Dependencies, params ParameterGroupGetParams) (*ParameterGroupGetResult, error)
type ParameterGroupInfo ¶
type ParameterGroupInfo struct {
Name string `json:"name"`
Parameters []parameters.ResolvedParameter `json:"parameters"`
}
type ParameterGroupListParams ¶
type ParameterGroupListParams struct{}
type ParameterGroupListResult ¶
type ParameterGroupListResult struct {
Groups []ParameterGroupInfo `json:"groups"`
}
func ParameterGroupList ¶
func ParameterGroupList(ctx context.Context, deps *Dependencies, params ParameterGroupListParams) (*ParameterGroupListResult, error)
type ParameterGroupPutParams ¶
ParameterGroupPutParams contains parameters for creating a parameter group
type ParameterGroupPutResult ¶
type ParameterGroupPutResult struct {
Message string `json:"message"`
}
func ParameterGroupPut ¶
func ParameterGroupPut(ctx context.Context, deps *Dependencies, params ParameterGroupPutParams) (*ParameterGroupPutResult, error)
type PostgreSQLStatus ¶
type PostgreSQLStatus int
PostgreSQL connectivity status constants
const ( PostgreSQLStatusOK PostgreSQLStatus = iota PostgreSQLStatusBrokenPort PostgreSQLStatusBrokenAuth PostgreSQLStatusOther )
func TestPostgreSQLConnectivity ¶
func TestPostgreSQLConnectivity(ctx context.Context, deps *Dependencies, instanceName string) PostgreSQLStatus
TestPostgreSQLConnectivity tests PostgreSQL connectivity and returns detailed status
type PreflightCheck ¶ added in v0.1.59
PreflightCheck is one line of the pre-apply report. Every check runs before anything on the host is modified.
type PullImageOp ¶
type PullImageOp struct {
// contains filtered or unexported fields
}
PullImageOp pulls a PostgreSQL image from Docker Hub
func NewPullImageOp ¶
func NewPullImageOp(deps *Dependencies, params PullImageParams) *PullImageOp
NewPullImageOp creates a new pull image operation
func (*PullImageOp) GetResult ¶
func (op *PullImageOp) GetResult() *PullImageResult
GetResult returns the operation result
func (*PullImageOp) Name ¶
func (op *PullImageOp) Name() string
func (*PullImageOp) Resolve ¶
func (op *PullImageOp) Resolve() error
Resolve computes the target image name and validates version/image consistency. It is a cheap, side-effect-free pre-flight so the HTTP handler can return a clean 4xx before committing to a streaming response. Execute calls it implicitly if it hasn't run yet.
func (*PullImageOp) SetProgressWriter ¶
func (op *PullImageOp) SetProgressWriter(w io.Writer)
SetProgressWriter sets the destination for raw Docker pull progress frames. Call before Execute. If unset, progress is discarded.
func (*PullImageOp) Type ¶
func (op *PullImageOp) Type() OpType
type PullImageParams ¶
type PullImageParams struct {
Version string
Image string
// IfMissing pulls only when the image is not already present locally;
// when present it is a no-op (no network). Used by create/switch to
// auto-provision an image without forcing a re-pull of a cached one.
// Standalone pull and update leave this false so a moving tag is refreshed.
IfMissing bool
}
PullImageParams contains parameters for pulling a Docker image
type PullImageResult ¶
PullImageResult contains the result of pulling a Docker image
type ReconfigureRDBMSOp ¶
type ReconfigureRDBMSOp struct {
// contains filtered or unexported fields
}
ReconfigureRDBMSOp rebuilds an existing instance's container with a new shape.
It is the same recreate-or-roll-back path that switch and update use (applyClusterChange): everything that can be refused is refused before the running container is touched, a container that is built but never reaches readiness is replaced by the previous one, and the store records the new shape only once PostgreSQL accepts connections on it. The data volume is never touched. RAM is more than a cgroup limit here: parameter groups resolve against it, so a RAM change also re-resolves shared_buffers and friends, and the shared-memory fit is re-checked against the NEW size.
func NewReconfigureRDBMSOp ¶
func NewReconfigureRDBMSOp(deps *Dependencies, params ReconfigureRDBMSParams) *ReconfigureRDBMSOp
NewReconfigureRDBMSOp creates a new reconfigure RDBMS operation
func (*ReconfigureRDBMSOp) Execute ¶
func (op *ReconfigureRDBMSOp) Execute(ctx context.Context) error
func (*ReconfigureRDBMSOp) GetResult ¶
func (op *ReconfigureRDBMSOp) GetResult() *ReconfigureRDBMSResult
GetResult returns the operation result
func (*ReconfigureRDBMSOp) Name ¶
func (op *ReconfigureRDBMSOp) Name() string
func (*ReconfigureRDBMSOp) Type ¶
func (op *ReconfigureRDBMSOp) Type() OpType
type ReconfigureRDBMSParams ¶
type ReconfigureRDBMSParams struct {
Name string
ParameterGroup string
CPUCores *int
RAMMB *int
Port *int
}
ReconfigureRDBMSParams describes an in-place change to an instance's shape: its parameter group, CPU allocation, RAM, port, or any combination. Every field is optional and an unset one keeps the current value, so moving the port does not require restating the RAM. At least one must be set.
type ReconfigureRDBMSResult ¶
type ReconfigureRDBMSResult struct {
Instance *instances.RDBMSInstance
}
ReconfigureRDBMSResult contains the result of reconfiguring an RDBMS instance
type RemoteSnapshotObject ¶ added in v0.1.65
type RemoteSnapshotObject struct {
URI string `json:"uri"`
Key string `json:"key"`
Size int64 `json:"size"`
LastModified time.Time `json:"lastModified"`
}
RemoteSnapshotObject is one list-remote entry; URI is pre-formatted for copy-paste into restore-instance/apply.
func ListRemoteSnapshots ¶ added in v0.1.65
func ListRemoteSnapshots(ctx context.Context, client *s3service.Client, bucket, prefix string) ([]RemoteSnapshotObject, bool, error)
ListRemoteSnapshots lists objects under the client-relative prefix, newest first, rendered as bucket-root URIs.
type RemoteSnapshotSpec ¶ added in v0.1.65
type RemoteSnapshotSpec struct {
URI string // s3://bucket/key of a snapshot archive
Region string // "" = credential chain's region, then us-east-1
Endpoint string // "" = AWS S3
// Credentials is the CLIENT-resolved ambient triple, when the CLI's
// environment had one. Nil means none were supplied. Never persisted,
// never logged.
Credentials *s3service.StaticCredentials
// Profile selects a shared-config profile for the AMBIENT chain. Only
// meaningful on daemon-less paths (apply, list-remote), where this process
// resolves credentials itself.
Profile string
}
RemoteSnapshotSpec names one S3 object plus how to reach it.
type RemoveLocalBackupParams ¶
type RemoveLocalBackupResult ¶
type RemoveLocalBackupResult struct {
Message string `json:"message"`
}
func RemoveLocalBackup ¶
func RemoveLocalBackup(ctx context.Context, deps *Dependencies, params RemoveLocalBackupParams) (*RemoveLocalBackupResult, error)
type RemoveRemoteBackupResult ¶
type RemoveRemoteBackupResult struct {
Message string `json:"message"`
}
func RemoveRemoteBackup ¶
func RemoveRemoteBackup(ctx context.Context, deps *Dependencies, params RemoveRemoteBackupParams) (*RemoveRemoteBackupResult, error)
type ResetDatabaseUserPasswordResult ¶
type ResetDatabaseUserPasswordResult struct {
Username string `json:"username"`
Password string `json:"password"`
Message string `json:"message"`
}
func ResetDatabaseUserPassword ¶
func ResetDatabaseUserPassword(ctx context.Context, deps *Dependencies, params ResetDatabaseUserPasswordParams) (*ResetDatabaseUserPasswordResult, error)
type RestoreArchiveSource ¶ added in v0.1.65
type RestoreArchiveSource struct {
ArchivePath string // a path on this host's filesystem
SnapshotID int // a row in this host's snapshot catalogue
Remote *RemoteSnapshotSpec // an s3:// object, possibly foreign
// MasterKeyPath is a foreign archive's master key (--master-key), on the
// daemon's filesystem. A Remote fetch is verified as it lands, which for
// an encrypted archive needs its key.
MasterKeyPath string
}
RestoreArchiveSource selects where restore-instance's archive comes from. Exactly one field must be set (the HTTP handler enforces it; this layer trusts but verifies).
type RestoreClusterParams ¶ added in v0.1.59
type RestoreClusterParams struct {
InstanceName string // names the ephemeral helper containers
Image string // image the helper containers run
Major int // the cluster's PostgreSQL major (gates pgRestoreEnv)
Port int // cluster port on the bridge gateway
Password string // postgres superuser password (plaintext)
CPUCores int // caps pg_restore -j
ExtractedDir string // root of the extracted backup archive
Databases []DatabaseMeta // per-database metadata from databases.json
ExpectedRoles []string // roles globals.sql must create
}
RestoreClusterParams describes a fresh, empty, running PostgreSQL cluster and the extracted backup archive to replay into it.
Password is the plaintext postgres superuser password the cluster was created with. It must be the same password the archive's globals.sql carries a hash of — normally guaranteed because both come from the same oddk.db row. If it is not, applying globals rewrites the postgres role to a hash whose plaintext is unknown and the cluster becomes unreachable.
type RestoreDatabaseParams ¶ added in v0.1.93
type RestoreDatabaseParams struct {
ArchivePath string
InstanceName string // the target instance: the live one the database is created on
// SourceInstance is the snapshot entry the database is read from. Empty
// means InstanceName — restoring an instance's own database. Naming
// another lets prod's database from last night land on staging.
SourceInstance string
DatabaseName string // the database inside the snapshot
RestoreAs string // optional: the name to create it under
// MasterKeyPath is the SOURCE host's master.key. Only a physical entry
// needs a key at all (to open the scratch copy of the cluster, whose roles
// are the source's), and only when the snapshot came from elsewhere.
MasterKeyPath string
BackupDir string
Progress io.Writer
// ExpectedSHA256 is the catalogue's digest for the archive, when it was
// resolved through the catalogue (see ArchiveOrigin.ExpectedSHA256).
ExpectedSHA256 string
}
RestoreDatabaseParams describes restoring ONE database out of a snapshot into a live instance — the "someone dropped a table" path, and with RestoreAs the "give me yesterday's copy next to today's" path.
It is the snapshot counterpart of `backup restore --database`, and shares its core (restoreDatabaseFromDumpTree). Almost every hazard of restore-instance is absent by construction: the instance's data volume, its other databases, its roles, its postgres password and its configuration are all untouched. The one thing written is a database that did not exist before.
type RestoreDatabaseResult ¶ added in v0.1.93
type RestoreDatabaseResult struct {
Instance string `json:"instance"`
SourceInstance string `json:"sourceInstance"`
SourceDatabase string `json:"sourceDatabase"`
TargetDatabase string `json:"targetDatabase"`
Format string `json:"format"`
SourceHost string `json:"sourceHost"`
SnapshotAt time.Time `json:"snapshotAt"`
// SkippedGrantRoles are roles the source granted CREATE on the database
// that do not exist on the target, so the grant could not be replayed.
SkippedGrantRoles []string `json:"skippedGrantRoles,omitempty"`
// Warnings are things the operator must know that did not fail the
// restore — e.g. a scratch copy of the instance's data that could not be
// removed.
Warnings []string `json:"warnings,omitempty"`
// ArchiveOrigin is set by the caller that resolved the archive source.
ArchiveOrigin *ArchiveOrigin `json:"archiveOrigin,omitempty"`
}
RestoreDatabaseResult describes what was restored.
func RestoreDatabaseFromSnapshot ¶ added in v0.1.93
func RestoreDatabaseFromSnapshot(ctx context.Context, deps *Dependencies, params *RestoreDatabaseParams) (_ *RestoreDatabaseResult, err error)
RestoreDatabaseFromSnapshot restores one database out of a snapshot entry into a live instance — the same-named one by default, or any other (SourceInstance), e.g. prod's database into staging.
A LOGICAL entry already holds per-database dumps, laid out exactly like a backup archive, so it is replayed directly. A PHYSICAL entry holds a copy of the whole cluster: it is started as an isolated scratch cluster (no network; see docker.CreateScratchCluster), the one database is pg_dumped out of it, and that dump is replayed. Either way the target sees the same thing a `backup restore` produces: a new database, objects owned by postgres, no object privileges, database-level CREATE grants replayed where the role exists.
type RestoreInstanceParams ¶ added in v0.1.59
type RestoreInstanceParams struct {
ArchivePath string
InstanceName string
// MasterKeyPath is the SOURCE host's master.key. Empty means "use this
// host's own key", which is correct for the common case of restoring an
// instance from a snapshot this same host took.
MasterKeyPath string
// ForeignSource marks an archive that did NOT come from this host's own
// snapshot catalogue (an s3Uri fetch with no matching catalogue row). Such
// a restore must never backdate created_at for checklist coverage — this
// host's snapshots do not hold that data, so "not yet captured" is the
// truthful verdict. MasterKeyPath already implies foreignness, but a
// cloned-master-key fleet (or a deleted catalogue row) can be foreign
// without needing a key, which is the hole this flag closes.
ForeignSource bool
BackupDir string
// ExpectedSHA256 is the catalogue's digest for the archive, when it was
// resolved through the catalogue (see ArchiveOrigin.ExpectedSHA256).
ExpectedSHA256 string
// Progress receives human-readable lines. Nil is fine (emitLine no-ops).
Progress io.Writer
}
RestoreInstanceParams describes restoring ONE instance out of a snapshot archive into a LIVE deployment.
This is the counterpart to `snapshot apply`, not a variant of it. Apply rebuilds a whole host, runs daemon-less, and demands an empty deployment because it replaces oddk.db and master.key wholesale. This restores a single instance into a deployment that keeps running, so it runs THROUGH the daemon (for executor serialization and health-check pausing) and touches neither the live oddk.db's other rows nor the live master.key.
type RestoreInstanceResult ¶ added in v0.1.59
type RestoreInstanceResult struct {
Instance string `json:"instance"`
Created bool `json:"created"`
Replaced bool `json:"replaced"`
Databases int `json:"databases"`
SourceHost string `json:"sourceHost"`
SnapshotAt time.Time `json:"snapshotAt"`
Port int `json:"port"`
CPUCores int `json:"cpuCores"`
RAMMB int `json:"ramMb"`
Image string `json:"image"`
// PasswordChanged reports that the instance's stored postgres password was
// replaced by the snapshot's. This is not optional and not a side effect to
// hide: globals.sql sets the postgres role to the SOURCE's hash, so the only
// plaintext that can still authenticate is the source's.
PasswordChanged bool `json:"passwordChanged"`
// Format is how the entry was captured ("physical" or "logical").
Format string `json:"format"`
// FinalStatus is the instance's status after the restore: "running", or
// "stopped" for a physical cold capture — the instance was stopped when
// the snapshot was taken, and the restore reproduces that (after starting
// once to verify the cluster actually recovers).
FinalStatus string `json:"finalStatus"`
// ArchiveOrigin reports where the archive came from (a daemon-side path,
// the catalogue, or an S3 fetch). Set by the caller that resolved the
// source, not by RestoreInstanceFromSnapshot, which only ever sees a local
// path.
ArchiveOrigin *ArchiveOrigin `json:"archiveOrigin,omitempty"`
}
RestoreInstanceResult describes what was rebuilt.
func RestoreInstanceFromSnapshot ¶ added in v0.1.59
func RestoreInstanceFromSnapshot(ctx context.Context, deps *Dependencies, params *RestoreInstanceParams) (result *RestoreInstanceResult, err error)
RestoreInstanceFromSnapshot rebuilds one instance from a snapshot archive.
If the instance does not exist it is created from the archive's instance.json. If it does exist, its cluster is destroyed and rebuilt — the data is replaced, not merged, because RestoreClusterFromArchive requires an empty cluster and a partial overlay would silently mix two deployments' data.
The instance's recorded configuration (port, resources, image, parameter group) comes from the SNAPSHOT, not from the live row. A restore that resurrected the data but kept a since-changed shape would not be a restore of that instance. Callers must show the operator what will be applied.
type RestoreRDBMSParams ¶
type RestoreRDBMSParams struct {
// Either BackupID or FilePath must be provided (mutually exclusive)
BackupID int // ID from backup_history table
FilePath string // Direct path to a .tar.zst.age (or pre-0.1.94 .tar.zst) file
InstanceName string // Target instance to restore to
DatabaseName string // Database name inside the backup to restore
RestoreAs string // Optional: restore under a different name
BackupDir string // Backup directory for resolving relative paths
}
RestoreRDBMSParams contains parameters for restoring a database from backup
type RestoreRDBMSResult ¶
type RestoreRDBMSResult struct {
TargetDatabase string `json:"targetDatabase"`
SourceBackup string `json:"sourceBackup"`
Message string `json:"message"`
}
RestoreRDBMSResult contains the result of restoring a database
func RestoreRDBMS ¶
func RestoreRDBMS(ctx context.Context, deps *Dependencies, params *RestoreRDBMSParams) (*RestoreRDBMSResult, error)
RestoreRDBMS restores a single database from a backup archive
type SetPasswordOp ¶
type SetPasswordOp struct {
// contains filtered or unexported fields
}
SetPasswordOp is an operation to set a new password for an instance
func NewSetPasswordOp ¶
func NewSetPasswordOp(deps *Dependencies, name, password string) *SetPasswordOp
NewSetPasswordOp creates a new operation to set password
func (*SetPasswordOp) GetResult ¶
func (op *SetPasswordOp) GetResult() *SetPasswordResult
func (*SetPasswordOp) Name ¶
func (op *SetPasswordOp) Name() string
func (*SetPasswordOp) Type ¶
func (op *SetPasswordOp) Type() OpType
type SetPasswordResult ¶
type SnapshotApplyParams ¶ added in v0.1.59
type SnapshotApplyParams struct {
ArchivePath string
MasterKeyPath string
DataDir string
BackupDir string
DaemonPort int
Docker *docker.Client
// NoPauseSchedules skips the automatic pause of every restored schedule.
//
// The pause exists because the restored oddk.db carries the SOURCE host's
// offsite settings alongside its schedules, so an unpaused restore uploads
// into — and runs RETENTION against — a bucket the source may still be
// using. Apply cannot tell a rehearsal from a real failover, so it fails
// safe by default.
//
// This flag is the operator asserting the one fact apply cannot check: that
// no other host is writing to that bucket. It is for the genuine
// disaster-recovery case, where the source is gone and the replacement must
// start protecting itself immediately rather than waiting for somebody to
// notice a paused schedule. Set it when the source is dead; leave it alone
// for a rehearsal or a staged migration.
NoPauseSchedules bool
// Progress receives human-readable status lines during preflight (nil
// discards them).
Progress io.Writer
// PullProgress receives raw Docker pull frames, for a caller that wants to
// render layer progress. nil discards them; an image pull on a DR host can
// take minutes, so a CLI should supply one.
PullProgress io.Writer
}
SnapshotApplyParams configures a whole-deployment restore.
type SnapshotApplyPlan ¶ added in v0.1.59
type SnapshotApplyPlan struct {
Manifest *SnapshotManifest
ExtractedDir string
MasterKey []byte
Instances []*InstanceMeta
Checks []PreflightCheck
// LogicalInputs holds, per logically captured instance, what its dumps
// declare (databases.json, the roles in globals.sql). Read in preflight so
// a truncated logical archive is refused before oddk.db and master.key are
// replaced, and carried into the rebuild so it is read exactly once.
LogicalInputs map[string]*logicalRestoreInputs
// ExistingKeyPath is set when the target already has a master.key that
// will be displaced (the normal case: the installer started the daemon,
// which generated one).
ExistingKeyPath string
// PulledImages lists images preflight fetched onto this host. Recorded so
// the summary can be honest that preflight was not purely read-only.
PulledImages []string
// contains filtered or unexported fields
}
SnapshotApplyPlan is the validated result of preflight — everything the destructive phase needs, resolved while the host is still untouched.
func PreflightSnapshotApply ¶ added in v0.1.59
func PreflightSnapshotApply(ctx context.Context, params *SnapshotApplyParams) (*SnapshotApplyPlan, error)
PreflightSnapshotApply validates that this snapshot can be applied to this host and prepares everything the destructive phase needs. It modifies nothing. On success the caller owns plan.ExtractedDir and must Cleanup().
Checks run cheapest-first so an obvious refusal (wrong version, running daemon, non-empty deployment) costs a second rather than the time to decompress the whole archive.
func (*SnapshotApplyPlan) Cleanup ¶ added in v0.1.59
func (p *SnapshotApplyPlan) Cleanup()
Cleanup removes the extracted staging tree. Safe to call more than once.
type SnapshotApplyResult ¶ added in v0.1.59
type SnapshotApplyResult struct {
Restored []string // instances rebuilt with their data
ConfigOnly []string // instances whose snapshot held no data
ReplacedKeyAs string
ReplacedDBAs string
TokensReplaced bool
// Backup catalogue reconciliation. BackupsDangling counts records left with
// neither a local nor a remote copy — the daemon's startup sweep will drop
// those, so the operator is told rather than finding history gone.
BackupsRepointed int
BackupsLocalCleared int
BackupsDangling int
// Snapshot catalogue reconciliation, same shape and same reason: the
// restored rows carry the source host's paths.
SnapshotsRepointed int
SnapshotsLocalCleared int
SnapshotsDangling int
// Schedules suspended by this apply. The restored oddk.db carries the SOURCE
// host's schedule AND its offsite settings, so an unpaused restore starts
// writing into — and running RETENTION against — a bucket the source may
// still be using. SnapshotCronPaused is false when the archive carried no
// snapshot schedule at all.
SnapshotCronPaused bool
BackupCronsPaused int
// SchedulesLeftRunning records that --no-pause-schedules suppressed the
// pause, so the CLI can warn about the bucket collision the operator has
// just taken responsibility for instead of the paused schedules they do not
// have.
SchedulesLeftRunning bool
}
SnapshotApplyResult reports what was rebuilt.
func ExecuteSnapshotApply ¶ added in v0.1.59
func ExecuteSnapshotApply(ctx context.Context, plan *SnapshotApplyPlan, progress io.Writer) (*SnapshotApplyResult, error)
ExecuteSnapshotApply performs the destructive phase: it installs the snapshot's oddk.db and master.key over this host's, then rebuilds every instance that carries data.
Everything before this point was read-only. Call it only with a plan from a successful PreflightSnapshotApply, and only after the operator has confirmed.
The daemon check is repeated here, not merely inherited from preflight: the systemd unit sets Restart=always, so a daemon killed rather than stopped can return within seconds — potentially between preflight and now.
type SnapshotCaptureFailure ¶ added in v0.1.74
type SnapshotCaptureFailure struct {
Instance string `json:"instance"`
Stage string `json:"stage"` // "base backup", "cold copy", "dump", "decrypt password"
Error string `json:"error"`
}
SnapshotCaptureFailure is one instance whose data capture was ATTEMPTED and FAILED, as opposed to one that had nothing to capture (no container, stopped under --logical, paused). Both end up configuration-only in the archive; only this one means something is broken and needs fixing.
type SnapshotCronTaskOp ¶ added in v0.1.59
type SnapshotCronTaskOp struct {
// contains filtered or unexported fields
}
SnapshotCronTaskOp is the scheduled whole-deployment snapshot: capture, ship offsite, retry anything that failed to ship, then apply retention.
It reuses cron_logs rather than introducing a parallel table, so snapshots inherit the audit trail and the 365-day retention sweep that already exist — and so a crashed attempt leaves evidence instead of only a reclaimed staging directory. The four phase columns line up: backup -> snapshot, upload, cleanup -> local retention, remote_cleanup -> offsite retention.
func NewSnapshotCronTaskOp ¶ added in v0.1.59
func NewSnapshotCronTaskOp(deps *Dependencies, backupDir string) *SnapshotCronTaskOp
func (*SnapshotCronTaskOp) Execute ¶ added in v0.1.59
func (op *SnapshotCronTaskOp) Execute(ctx context.Context) error
Execute runs every phase, recording each and never aborting the chain on a phase failure — retention must still run on a night the capture failed, or a broken snapshot job would also silently stop pruning.
func (*SnapshotCronTaskOp) Name ¶ added in v0.1.59
func (op *SnapshotCronTaskOp) Name() string
func (*SnapshotCronTaskOp) Type ¶ added in v0.1.59
func (op *SnapshotCronTaskOp) Type() OpType
type SnapshotInstanceEntry ¶ added in v0.1.59
type SnapshotInstanceEntry struct {
Name string `json:"name"`
Version string `json:"version"`
Image string `json:"image"`
// HasData is false for a configuration-only entry: the instance's
// configuration was captured but its databases were not.
HasData bool `json:"hasData"`
// Format says how this entry's data was captured: "physical"
// (pg_basebackup / cold copy under basebackup/) or "logical"
// (globals.sql + databases/). Empty means logical — pre-0.1.61 archives
// predate the field. Restore paths branch on this, never on a flag.
Format string `json:"format,omitempty"`
// CaptureMode distinguishes physical captures: "basebackup" was taken from
// the live server, "cold" is a file copy of a stopped instance's data dir.
// A cold entry is restored to a STOPPED instance — the deployment's shape
// is reproduced, not just its bytes.
CaptureMode string `json:"captureMode,omitempty"`
// SkipReason explains a configuration-only entry, and is shown by apply so
// the operator is never surprised by an instance coming back empty.
SkipReason string `json:"skipReason,omitempty"`
}
SnapshotInstanceEntry records what the snapshot holds for one instance.
type SnapshotManifest ¶ added in v0.1.59
type SnapshotManifest struct {
FormatVersion int `json:"formatVersion"`
OddkVersion string `json:"oddkVersion"`
CreatedAt time.Time `json:"createdAt"`
SourceHost string `json:"sourceHost"`
// SourceArch is runtime.GOARCH on the capturing host. Physical data
// directories are only supported on the same platform, so apply and
// restore-instance refuse a physical entry on a different architecture
// (logical archives restore anywhere; the field is informational there).
// Empty in pre-0.1.61 archives, which are all logical anyway.
SourceArch string `json:"sourceArch,omitempty"`
// Migrations is the applied-migration list from the source's oddk.db. It
// describes the embedded store's schema more precisely than OddkVersion.
Migrations []string `json:"migrations"`
Instances []SnapshotInstanceEntry `json:"instances"`
}
SnapshotManifest is the first entry in a snapshot archive, so a reader can check compatibility from the first few kilobytes rather than streaming through every dump to find it.
func ReadSnapshotManifest ¶ added in v0.1.59
func ReadSnapshotManifest(extractedDir string) (*SnapshotManifest, error)
ReadSnapshotManifest reads manifest.json from an extracted snapshot directory. Unlike the per-instance metadata readers, absence is an error: every snapshot has one, so a missing manifest means the archive is not a snapshot (or is truncated).
func ReadSnapshotManifestFromArchive ¶ added in v0.1.59
func ReadSnapshotManifestFromArchive(c *compression.Compressor, archivePath string) (*SnapshotManifest, error)
ReadSnapshotManifestFromArchive streams the archive only as far as its manifest, which MakeSnapshot writes first precisely so this is cheap. The manifest is inside the encryption, so c must hold the archive's key.
type SwitchRDBMSOp ¶
type SwitchRDBMSOp struct {
// contains filtered or unexported fields
}
SwitchRDBMSOp switches an existing RDBMS instance to a different Docker image
func NewSwitchRDBMSOp ¶
func NewSwitchRDBMSOp(deps *Dependencies, params SwitchRDBMSParams) *SwitchRDBMSOp
NewSwitchRDBMSOp creates a new switch RDBMS operation
func (*SwitchRDBMSOp) GetResult ¶
func (op *SwitchRDBMSOp) GetResult() *SwitchRDBMSResult
GetResult returns the operation result
func (*SwitchRDBMSOp) Name ¶
func (op *SwitchRDBMSOp) Name() string
func (*SwitchRDBMSOp) Type ¶
func (op *SwitchRDBMSOp) Type() OpType
type SwitchRDBMSParams ¶
type SwitchRDBMSParams struct {
Name string
Image string
Version string // empty = keep current instance version
}
SwitchRDBMSParams contains parameters for switching an instance's image
type SwitchRDBMSResult ¶
type SwitchRDBMSResult struct {
Instance *instances.RDBMSInstance
}
SwitchRDBMSResult contains the result of switching an instance's image
type UpdateRDBMSOp ¶
type UpdateRDBMSOp struct {
// contains filtered or unexported fields
}
UpdateRDBMSOp re-pulls the image for an instance's current tag and recreates the container if the pull produced a newer image (e.g. a patch release with security fixes). It is the one-step equivalent of `pull` followed by `instance switch` onto the same tag. Like pull, it streams raw Docker progress frames to a writer so the CLI can render live progress; it appends a final status frame describing the outcome.
func NewUpdateRDBMSOp ¶
func NewUpdateRDBMSOp(deps *Dependencies, params UpdateRDBMSParams) *UpdateRDBMSOp
NewUpdateRDBMSOp creates a new update RDBMS operation.
func (*UpdateRDBMSOp) GetResult ¶
func (op *UpdateRDBMSOp) GetResult() *SwitchRDBMSResult
GetResult returns the operation result.
func (*UpdateRDBMSOp) Name ¶
func (op *UpdateRDBMSOp) Name() string
func (*UpdateRDBMSOp) SetProgressWriter ¶
func (op *UpdateRDBMSOp) SetProgressWriter(w io.Writer)
SetProgressWriter sets the destination for raw Docker pull progress frames and the final status frame. Call before Execute. If unset, progress is discarded.
func (*UpdateRDBMSOp) Type ¶
func (op *UpdateRDBMSOp) Type() OpType
type UpdateRDBMSParams ¶
UpdateRDBMSParams contains parameters for updating an instance to the latest image for its tag (re-pull + recreate if a newer patch is available).
type UpdateStateOp ¶
type UpdateStateOp struct {
// contains filtered or unexported fields
}
UpdateStateOp updates the state of an RDBMS instance
func NewUpdateStateOp ¶
func NewUpdateStateOp(deps *Dependencies, params UpdateStateParams) *UpdateStateOp
func (*UpdateStateOp) Name ¶
func (op *UpdateStateOp) Name() string
func (*UpdateStateOp) Type ¶
func (op *UpdateStateOp) Type() OpType
type UpdateStateParams ¶
UpdateStateParams contains parameters for updating instance state
type UpgradeRDBMSOp ¶
type UpgradeRDBMSOp struct {
// contains filtered or unexported fields
}
UpgradeRDBMSOp performs a major-version PostgreSQL upgrade of an instance using a logical dump/restore: it takes a verified full backup, destroys the old cluster volume, creates a fresh cluster at the target version (which automatically picks the correct data-dir mount for the major), then replays globals and every database into it.
A newer PostgreSQL server cannot start against an older major's data directory, so an in-place restart is impossible; dump/restore is the only universal path that also works with custom images (pgvector, postgis) where pg_upgrade would need both majors' binaries in one image.
func NewUpgradeRDBMSOp ¶
func NewUpgradeRDBMSOp(deps *Dependencies, params UpgradeRDBMSParams) *UpgradeRDBMSOp
NewUpgradeRDBMSOp creates a new major-upgrade operation
func (*UpgradeRDBMSOp) GetResult ¶
func (op *UpgradeRDBMSOp) GetResult() *UpgradeRDBMSResult
GetResult returns the operation result
func (*UpgradeRDBMSOp) Name ¶
func (op *UpgradeRDBMSOp) Name() string
func (*UpgradeRDBMSOp) Type ¶
func (op *UpgradeRDBMSOp) Type() OpType
type UpgradeRDBMSParams ¶
type UpgradeRDBMSParams struct {
Name string
TargetVersion string // target PostgreSQL major version, e.g. "18"
Image string // optional target image; required for custom (non-postgres:) images
}
UpgradeRDBMSParams contains parameters for a major-version upgrade of an instance
type UpgradeRDBMSResult ¶
type UpgradeRDBMSResult struct {
Instance *instances.RDBMSInstance `json:"instance"`
FromVersion string `json:"fromVersion"`
ToVersion string `json:"toVersion"`
BackupID int `json:"backupId"`
DatabasesRestored int `json:"databasesRestored"`
}
UpgradeRDBMSResult contains the result of a major-version upgrade
type UploadBackupParams ¶
UploadBackupParams contains parameters for uploading a backup to S3
type UploadBackupResult ¶
UploadBackupResult contains the result of uploading a backup
func UploadBackup ¶
func UploadBackup(ctx context.Context, deps *Dependencies, params UploadBackupParams) (*UploadBackupResult, error)
UploadBackup uploads a backup to S3
type UploadSnapshotResult ¶ added in v0.1.59
type UploadSnapshotResult struct {
ID int `json:"id"`
Filename string `json:"filename"`
Size int64 `json:"size"`
RemoteLocation string `json:"remoteLocation"`
}
UploadSnapshotResult describes a completed offsite upload.
func UploadSnapshot ¶ added in v0.1.59
func UploadSnapshot(ctx context.Context, deps *Dependencies, id int) (*UploadSnapshotResult, error)
UploadSnapshot ships one snapshot archive offsite.
Mirrors UploadBackup: same client, same log table, same s3:// location format (prefix baked in, which is why RelativeKey exists on the read side).
Source Files
¶
- archive_keys.go
- backup_deprecation.go
- backup_drop_all.go
- backup_remove_local.go
- checklist.go
- cluster_recreate.go
- cron_backup_create.go
- cron_backup_delete.go
- cron_backup_list.go
- cron_logs.go
- cron_notify.go
- cron_task.go
- database_metadata.go
- executor.go
- helper_container.go
- instance_metadata.go
- notification_add.go
- notification_edit.go
- notification_get.go
- notification_list.go
- notification_logs.go
- notification_remove.go
- notification_verify.go
- offsite.go
- parameter_group_delete.go
- parameter_group_get.go
- parameter_group_list.go
- parameter_group_put.go
- ports.go
- postgres_helpers.go
- pull_image.go
- rdbms_add_database_user.go
- rdbms_backup.go
- rdbms_consistency_check.go
- rdbms_create.go
- rdbms_create_database.go
- rdbms_delete.go
- rdbms_delete_database_user.go
- rdbms_download_backup.go
- rdbms_get.go
- rdbms_get_password.go
- rdbms_list.go
- rdbms_list_backups.go
- rdbms_list_databases.go
- rdbms_logs.go
- rdbms_major_upgrade.go
- rdbms_reconfigure.go
- rdbms_recreate.go
- rdbms_reset_database_user_password.go
- rdbms_restore.go
- rdbms_set_password.go
- rdbms_switch.go
- rdbms_update.go
- rdbms_update_state.go
- rdbms_upload_backup.go
- restore_cluster.go
- retention.go
- s3_location.go
- snapshot_apply.go
- snapshot_cron_task.go
- snapshot_fetch.go
- snapshot_make.go
- snapshot_physical.go
- snapshot_rebuild.go
- snapshot_restore_database.go
- snapshot_restore_instance.go
- snapshot_retrieve.go
- snapshot_upload.go
- types.go