postgres

package
v0.14.0-rc.1 Latest Latest
Warning

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

Go to latest
Published: Aug 1, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

Documentation

Overview

Package postgres is the production controlplane.Database adapter, backed by Postgres running in the cluster (ADR-0012). It implements the deploy-record seam (ADR-0007): the durable history of releases and the rollback handles, independent of cluster state. Schema changes are applied by embedded goose migrations on startup, gated to single-minor-step upgrades (ADR-0013).

It uses the pgx driver through the standard database/sql interface (not pgxpool, not the maintenance-mode lib/pq), so one *sql.DB serves both the migrations and the app. It lives under controlplane/ (not controlplane/internal) so cmd/burrowd and the managed module can wire it; it is licensed Apache-2.0 (LICENSING.md, ADR-0033).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Store

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

Store is a Postgres-backed controlplane.Database.

func Open

func Open(ctx context.Context, dsn string) (*Store, error)

Open connects to the database at dsn and verifies the connection. The caller closes the Store with Close. It does not apply the schema; call Migrate for that.

func (*Store) Addon

func (s *Store) Addon(ctx context.Context, name string) (controlplane.AddonInfo, error)

Addon returns the add-on with the given name, or ErrNotFound. The returned info has Ready false — readiness is probed live, never read from the registry.

func (*Store) Addons

func (s *Store) Addons(ctx context.Context) ([]controlplane.AddonInfo, error)

Addons returns all registered add-ons, name order. Each row has Ready false — readiness is a live property, probed by the caller, not stored.

func (*Store) AppEnv

func (s *Store) AppEnv(ctx context.Context, app string) (map[string]string, error)

AppEnv returns the non-secret environment store for app (ADR-0028). An app with no env yields an empty map and no error.

func (*Store) AppHook

func (s *Store) AppHook(ctx context.Context, app, env string, phase controlplane.HookPhase) ([]string, error)

AppHook returns the command app runs at phase in env (ADR-0072 §1). A phase with no row yields a nil command and no error: unset means no hook and today's behaviour exactly, so absence is the ordinary answer rather than ErrNotFound.

func (*Store) AppHooks

func (s *Store) AppHooks(ctx context.Context, app, env string) ([]controlplane.Hook, error)

AppHooks returns every hook configured for app in env. None yields an empty slice and no error. Rows come back in phase order so a listing is deterministic; the engine re-orders them into the order the phases fire in, which is its business and not the store's.

func (*Store) AppendAudit

func (s *Store) AppendAudit(ctx context.Context, e controlplane.AuditEntry) error

AppendAudit appends one audit row (ADR-0027). The log is append-only: there is only this INSERT and the Audit SELECT — no update or delete path. The store assigns id.

func (*Store) Audit

Audit returns audit rows matching filter, newest first, capped by filter.Limit (a default when unset). The filter clauses are optional and ANDed; an empty filter returns the latest rows up to the cap.

func (*Store) AutoDeployCandidates added in v0.13.0

func (s *Store) AutoDeployCandidates(ctx context.Context) ([]controlplane.AppEnvRef, error)

AutoDeployCandidates returns the distinct (app, environment) pairs that have at least one recorded release — the set the pull-based watcher may reconcile (ADR-0052 Phase 4b). Auto-deploy is on by default, so candidacy is "has a running release" (a version to compare a registry tag against), not "has an app_autodeploy row"; the poller reads each pair's level and skips those set to off. Rows are ordered by app then environment for a deterministic reconcile order.

func (*Store) AutoDeployLevel added in v0.13.0

func (s *Store) AutoDeployLevel(ctx context.Context, app, env string) (controlplane.AutoDeployLevel, error)

AutoDeployLevel returns app's auto-deploy level in env (ADR-0052 §2). A missing row resolves to DefaultAutoDeployLevel (off): the table holds only opt-ins, so an app runs at the default level with no row (auto-deploy is opt-in — ADR-0058).

func (*Store) AutoDeployReason added in v0.13.0

func (s *Store) AutoDeployReason(ctx context.Context, app, env string) (string, error)

AutoDeployReason returns the stored disable reason for app in env, or "" when the level was human-set or is the default (no row) — the reason surfaced next to an off level (ADR-0052 §5).

func (*Store) Close

func (s *Store) Close() error

Close releases the database connections.

func (*Store) CreateEnvironment added in v0.7.0

func (s *Store) CreateEnvironment(ctx context.Context, name, namespace string) error

CreateEnvironment registers a named environment mapping name to namespace (ADR-0035 phase 2). The name is the primary key, so a duplicate is rejected: the INSERT ... ON CONFLICT DO NOTHING affects no rows, which is reported as an ErrInvalid-wrapped duplicate error.

func (*Store) DeleteAddon

func (s *Store) DeleteAddon(ctx context.Context, name string) error

DeleteAddon removes the add-on row with the given name, or ErrNotFound if no such row exists.

func (*Store) DeleteAppHooks

func (s *Store) DeleteAppHooks(ctx context.Context, app string) error

DeleteAppHooks removes every hook for app across every environment — the durable side of an app teardown, beside DeleteReleases. Deleting the hooks of an app that has none is a no-op.

func (*Store) DeleteDependencyCheckSettings

func (s *Store) DeleteDependencyCheckSettings(ctx context.Context, app string) error

DeleteDependencyCheckSettings removes app's recorded setting across all environments — the durable side of an app teardown.

func (*Store) DeleteEnvironment

func (s *Store) DeleteEnvironment(ctx context.Context, name string) error

DeleteEnvironment removes the registered environment with the given name, or ErrNotFound when no such environment is registered. The default environment `prod` is stored here but is never removed: the engine rejects it before this call (ADR-0067 §2).

func (*Store) DeleteExposure

func (s *Store) DeleteExposure(ctx context.Context, app, env string) error

DeleteExposure removes the recorded exposure for app in env. Removing one that is not recorded is a no-op: an unexpose must succeed whether or not the intent row survived.

func (*Store) DeleteHealthEndpoints

func (s *Store) DeleteHealthEndpoints(ctx context.Context, app string) error

DeleteHealthEndpoints removes every declared endpoint for app across all environments — the durable side of an app teardown.

func (*Store) DeleteReleases

func (s *Store) DeleteReleases(ctx context.Context, app string) error

DeleteReleases removes every release record for app. Deleting the releases of an app that has none is a no-op (no RowsAffected check) — absence is fine.

func (*Store) DependencyChecksEnabled

func (s *Store) DependencyChecksEnabled(ctx context.Context, app, env string) (bool, error)

DependencyChecksEnabled reports whether the deploy-time dependency check runs for app in env.

A MISSING ROW IS TRUE. The check is Burrow's default, so a row exists only where somebody decided otherwise; this is read on every deploy, and returning ErrNotFound for the ordinary case would make the caller special-case the state every app is in.

func (*Store) DisableAutoDeploy added in v0.13.0

func (s *Store) DisableAutoDeploy(ctx context.Context, app, env, reason string) error

DisableAutoDeploy sets app's auto-deploy level in env to off and records why (e.g. "disabled by rollback") — the safety stop of ADR-0052 §5, so the watcher does not fight a deliberate downgrade. It is keyed by (app, environment) and upserts, overwriting any prior level and reason.

func (*Store) Exposure

func (s *Store) Exposure(ctx context.Context, app, env string) (controlplane.Exposure, error)

Exposure returns the recorded exposure for app in env, or ErrNotFound when the app is not published there — the read behind ADR-0076 §3's conservative default, since an exposure's port is the only container port Burrow knows for an app.

func (*Store) Exposures

func (s *Store) Exposures(ctx context.Context) ([]controlplane.Exposure, error)

Exposures returns every recorded exposure, ordered by app then environment.

func (*Store) ExtendObservationWindow

func (s *Store) ExtendObservationWindow(ctx context.Context, id int64, until time.Time, degraded string) error

ExtendObservationWindow advances a window's coverage and counts the sweep. A degraded note counts the sweep as incomplete and replaces the window's most recent note; a clean sweep leaves any earlier note in place, because the window is still one that had a degraded stretch in it.

Extending a window that no longer exists is ErrNotFound rather than a silent no-op: the caller's correct response is to open a fresh window, and a no-op here would have it record no coverage at all while believing it was.

func (*Store) FailBackup

func (s *Store) FailBackup(ctx context.Context, id, reason, detail string) error

FailBackup marks a recorded backup failed and records the closed reason and the Burrow-authored detail beside it (ADR-0063 §7). The size is zeroed: a failed backup has no bytes anybody can restore, and leaving a length on the row would make it read like a partial success.

Neither reason nor detail may carry a secret value or a vendor response body — the caller's contract, restated here because this is the write that makes them durable.

func (*Store) Failures

func (s *Store) Failures(ctx context.Context, filter controlplane.FailureFilter) ([]controlplane.Failure, error)

Failures returns ledger rows matching filter, oldest first — the order ADR-0074 §5 asks for, because the earliest first_seen in a cascade is the likeliest thing to actually fix. The filter clauses are optional and ANDed; the zero filter returns the active rows up to the cap.

func (*Store) GetBackup

func (s *Store) GetBackup(ctx context.Context, id string) (controlplane.Backup, error)

GetBackup returns the backup with the given id, or ErrNotFound.

func (*Store) GetEnvironment added in v0.7.0

func (s *Store) GetEnvironment(ctx context.Context, name string) (controlplane.Environment, error)

GetEnvironment returns the registered environment with the given name, or ErrNotFound.

func (*Store) HealthEndpoint

func (s *Store) HealthEndpoint(ctx context.Context, app, env string) (controlplane.HealthEndpoint, error)

HealthEndpoint returns the endpoint declared for app in env, or the ZERO VALUE when none was declared. A missing row is deliberately not ErrNotFound: no endpoint is the state every app starts in (§5 is opt-in), and the deploy path reads this on every apply, so an error there would turn the ordinary case into something a caller has to special-case.

func (*Store) LatestRelease

func (s *Store) LatestRelease(ctx context.Context, app, env string) (controlplane.Release, error)

LatestRelease returns the most recently saved release for app in env, keyed per (app, environment) — env is the canonical environment name (ADR-0052 Phase 4a).

func (*Store) ListBackups

func (s *Store) ListBackups(ctx context.Context, app, env string) ([]controlplane.Backup, error)

ListBackups returns recorded backups, newest first. An empty app lists every app's backups and an empty env every environment's; a non-empty value restricts to that app or environment (ADR-0067 §1). No matches yields an empty slice and no error.

func (*Store) ListEnvironments added in v0.7.0

func (s *Store) ListEnvironments(ctx context.Context) ([]controlplane.Environment, error)

ListEnvironments returns the registered environments ordered by name (ADR-0035 phase 2), including the default environment `prod`, which burrowd writes here at startup (ADR-0067 §2).

func (*Store) ListReleases added in v0.13.0

func (s *Store) ListReleases(ctx context.Context, app, env string) ([]controlplane.Release, error)

ListReleases returns every release for app in env, newest first (by insertion order) — the deploy timeline the history surface reads. It reads the same rows deploys write, ordered the opposite way from Releases. Releases are keyed per (app, environment) (ADR-0052 Phase 4a), so it selects by both.

func (*Store) ManagedApps

func (s *Store) ManagedApps(ctx context.Context) ([]controlplane.AppEnvRef, error)

ManagedApps returns the (app, environment) pairs whose release history shows a rollout that reached the cluster — a release that was deployed, or one superseded by a later deploy, which only happens after the later one succeeded. An app whose only deploy failed before any workload was applied is deliberately excluded: it has no Deployment to be missing, and reporting one would be a false positive on the day someone is already debugging a failed deploy (ADR-0074 §6).

func (*Store) Migrate

func (s *Store) Migrate(ctx context.Context, appVersion string) error

Migrate brings the database schema up to date and records appVersion as the version that last migrated it. It first enforces the single-minor-step upgrade gate (ADR-0013): if the database was last migrated by a different version, the upgrade must be exactly one minor step forward within the same major, or Migrate refuses before changing anything. Migrations are applied under a Postgres advisory lock, so concurrent control-plane replicas do not race. Migrate is safe to call on every startup; with no pending migrations it only re-stamps the version.

func (*Store) ObservationWindows

func (s *Store) ObservationWindows(ctx context.Context, since time.Time, limit int) ([]controlplane.ObservationWindow, error)

ObservationWindows returns the windows whose coverage ends at or after since, oldest first. The gaps between them are what the caller is actually reading.

func (*Store) OperationalConfig

func (s *Store) OperationalConfig(ctx context.Context) (controlplane.OperationalConfig, error)

OperationalConfig returns every stored operational limit value, keyed by the code it was set under — a bare code for a cluster value, `<env>.<code>` for an environment one (ADR-0068 §1). An empty table yields an empty configuration, in which every limit resolves to its built-in default.

func (*Store) PendingBackups

func (s *Store) PendingBackups(ctx context.Context, before time.Time) ([]controlplane.Backup, error)

PendingBackups returns the backups still `pending` that were recorded before the cutoff, oldest first — the candidates for a Job that is gone (ADR-0074 §6). The cutoff is the caller's: it is what separates a backup still running from one nothing is going to finish.

func (*Store) Policy

func (s *Store) Policy(ctx context.Context) (controlplane.Policy, error)

Policy returns the current guardrail policy: the built-in defaults with any stored guardrail dispositions overlaid (ADR-0020). An empty table yields DefaultPolicy.

func (*Store) Provider

func (s *Store) Provider(ctx context.Context, name string) (controlplane.Provider, error)

Provider returns the provider with the given name, or ErrNotFound.

func (*Store) Providers

func (s *Store) Providers(ctx context.Context) ([]controlplane.Provider, error)

Providers returns all configured providers, name order.

func (*Store) PruneLedger

func (s *Store) PruneLedger(ctx context.Context, before time.Time) (controlplane.LedgerPruneResult, error)

PruneLedger enforces the retention bound (ADR-0074 §4). It removes RESOLVED failures and elapsed coverage windows; an active failure survives however old it is, because a thing that is still broken is not history. Both deletes run in one transaction so a partial prune cannot leave the ledger and its coverage record describing different periods.

func (*Store) RecordBackup

func (s *Store) RecordBackup(ctx context.Context, b controlplane.Backup) error

RecordBackup persists a new backup row (ADR-0032). burrowd records it pending before starting the backup Job, then SetBackupStatus moves it to completed or FailBackup to failed when the Job finishes. An existing row with the same id is overwritten. The row names the app, the claim and the path within it, the destination it is being written to, and the status — never a credential.

func (*Store) RecordExposure

func (s *Store) RecordExposure(ctx context.Context, ex controlplane.Exposure) error

RecordExposure upserts the intent behind an expose, keyed by (app, environment) — what was asked for, never whether it currently works (ADR-0074 §6).

func (*Store) RecordFailure

func (s *Store) RecordFailure(ctx context.Context, obs controlplane.FailureObservation) error

RecordFailure opens or extends the row for one observed failure (ADR-0074 §4). The upsert targets the PARTIAL unique index on active rows, which is what makes the whole shape work: a sighting of a failure that is already active advances last_seen and the count and leaves first_seen — the answer to "when did it start" — untouched, while a sighting of one that had been RESOLVED conflicts with nothing and opens a new row, so a thing that broke, recovered and broke again reads as two episodes rather than one long outage.

func (*Store) Release

func (s *Store) Release(ctx context.Context, id string) (controlplane.Release, error)

func (*Store) Releases

func (s *Store) Releases(ctx context.Context, app, env string) ([]controlplane.Release, error)

Releases returns every release for app in env, oldest first, keyed per (app, environment).

func (*Store) ResolveFailures

func (s *Store) ResolveFailures(ctx context.Context, at time.Time, keep []controlplane.FailureKey, skip []controlplane.ObjectRef) error

ResolveFailures closes every active row that is neither still observed (keep) nor unreadable (skip). Both lists arrive as text keys because Postgres compares an array of composites far less readably than an array of joined strings, and the separator cannot occur in any part.

An empty keep list resolves everything not skipped, which is correct and is the normal case once a cluster is healthy: nothing observed active means nothing is active.

func (*Store) SaveAddon

func (s *Store) SaveAddon(ctx context.Context, a controlplane.AddonInfo) error

SaveAddon upserts an add-on in the registry by name (ADR-0025). It records the non-secret registry entry — type, mode, backend, where it lives, and the capabilities it serves. Ready is a live property of the cluster and is never persisted; it is probed at list time.

func (*Store) SaveProvider

func (s *Store) SaveProvider(ctx context.Context, p controlplane.Provider) error

SaveProvider upserts a provider in the registry by name (ADR-0023). It records only the non-secret registry entry; the token lives in the burrow-credentials Secret.

func (*Store) SaveRelease

func (s *Store) SaveRelease(ctx context.Context, r controlplane.Release) error

func (*Store) SetAppEnv

func (s *Store) SetAppEnv(ctx context.Context, app, key, value string) error

SetAppEnv upserts one env key for app.

func (*Store) SetAppHook

func (s *Store) SetAppHook(ctx context.Context, app, env string, phase controlplane.HookPhase, command []string) error

SetAppHook upserts the command app runs at phase in env, replacing any command already set there. The command arrives validated by the engine, which is the only place that knows what a runnable argv is; the store records what it is given.

func (*Store) SetAutoDeployLevel added in v0.13.0

func (s *Store) SetAutoDeployLevel(ctx context.Context, app, env string, level controlplane.AutoDeployLevel) error

SetAutoDeployLevel upserts app's auto-deploy level in env, keyed by (app, environment). It CLEARS any stored disable reason: a human setting the level (the re-enable path) removes the rollback or downgrade note, because re-enabling is a deliberate human action (ADR-0052 §5).

func (*Store) SetBackupStatus

func (s *Store) SetBackupStatus(ctx context.Context, id string, status controlplane.BackupStatus, sizeBytes int64) error

SetBackupStatus updates a recorded backup's status and size (the Job-finished transition). It CLEARS any recorded failure, so a row that reaches completed cannot keep a stale reason from an earlier attempt beside it. Setting the status of an unknown backup id returns ErrNotFound.

func (*Store) SetDependencyChecks

func (s *Store) SetDependencyChecks(ctx context.Context, app, env string, enabled bool, at time.Time) error

SetDependencyChecks records whether the deploy-time dependency check runs for app in env. Enabling writes a row rather than deleting one, so "somebody looked at this and left it on" and "nobody has ever thought about it" stay distinguishable.

func (*Store) SetGuardrail

func (s *Store) SetGuardrail(ctx context.Context, code controlplane.GuardrailCode, disp controlplane.Disposition) error

SetGuardrail upserts one guardrail's disposition.

func (*Store) SetHealthEndpoint

func (s *Store) SetHealthEndpoint(ctx context.Context, ep controlplane.HealthEndpoint) error

SetHealthEndpoint upserts the declared endpoint for an app in one environment.

func (*Store) SetLimit

func (s *Store) SetLimit(ctx context.Context, code controlplane.LimitCode, value string) error

SetLimit upserts one operational limit's value. The value arrives validated and canonicalized by the engine, which is the only place that knows a limit's kind and bounds; the store records what it is given.

func (*Store) StartObservationWindow

func (s *Store) StartObservationWindow(ctx context.Context, at time.Time) (int64, error)

StartObservationWindow opens a window for one run of the observer and returns its id.

func (*Store) UnsetAppEnv

func (s *Store) UnsetAppEnv(ctx context.Context, app, key string) error

UnsetAppEnv removes one env key for app. Removing a key that is not set is a no-op.

func (*Store) UnsetAppHook

func (s *Store) UnsetAppHook(ctx context.Context, app, env string, phase controlplane.HookPhase) error

UnsetAppHook removes app's hook at phase in env. Removing one that is not set is a no-op.

func (*Store) UnsetHealthEndpoint

func (s *Store) UnsetHealthEndpoint(ctx context.Context, app, env string) error

UnsetHealthEndpoint removes the declared endpoint for app in env, returning it to the conservative default. Removing one that was never declared is a no-op: unset must be idempotent, since it is what a user reaches for when they are not sure what is set.

Jump to

Keyboard shortcuts

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