database

package
v0.9.2 Latest Latest
Warning

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

Go to latest
Published: Nov 6, 2026 License: Apache-2.0 Imports: 36 Imported by: 0

Documentation

Overview

Package database implements the managed database controller: the reconcile.Controller that converges a store-backed store.DesiredDatabase to a running, volume-backed container, the same architectural pattern internal/reconcile/application already establishes (level-triggered, deterministic naming, a narrow store interface for testability), applied to a database instead of a built application image.

Redis and Postgres diverge deliberately:

Redis can run safely with no password configured. That is not the recommended production posture, but it is not an active vulnerability the way unauthenticated Postgres is, so this controller reconciles Redis for real: create, mount its volume, start, report Ready. That "not an active vulnerability" reasoning assumes internal-network-only reachability; PubliclyAccessible (migrations/0026_database_public_access.sql) breaks that assumption for a passwordless Redis, so the operator UI carries an explicit warning when enabling it rather than this controller silently refusing, matching the project's operator-trust posture elsewhere (no built-in service mesh or per-resource firewall).

Postgres cannot run safely without credentials: database auth needs the same envelope-encrypted secret storage the secrets design specifies (internal/secrets.Manager), which exists but is only wired into this controller when the control plane itself has a master key configured (cmd/levelrail's dynamicSource calls postgresCredentialsFor and passes the result via WithPostgresCredentials below). Without one, Reconcile refuses to start Postgres at all and reports a loud, explicit condition explaining why, mirroring how internal/deploy.requireNoUnresolvedEnv fails loudly rather than silently deploying a container missing values it needs, rather than starting it in trust-auth mode (no password) as a "temporary" workaround. Controller carries an optional postgresCredentials field, nil whenever no master key is configured or credential generation itself fails for this pass, so Postgres support activates purely by populating it, no restructuring of this controller needed either way.

Index

Constants

View Source
const (
	PostgresPasswordEnvKey   = "postgres_password"
	MySQLPasswordEnvKey      = "mysql_password"
	MongoPasswordEnvKey      = "mongo_password"
	MariaDBPasswordEnvKey    = "mariadb_password"
	ClickHousePasswordEnvKey = "clickhouse_password"
)

PostgresPasswordEnvKey, MySQLPasswordEnvKey, MongoPasswordEnvKey are the internal/secrets envKeys each engine's generated password is stored under, keyed by the database's own name as the secrets manager's "serviceName" (cmd/levelrail's postgresCredentialsFor and its two siblings). Exported and centralized here, not left as unexported consts in cmd/levelrail, so internal/deploy and internal/reconcile/application can resolve a { from: ... } env var's password against the exact same keys those credential generators already write, with no second copy of the string literals to drift.

View Source
const (
	TLSCertEnvKey = "tls_cert"
	TLSKeyEnvKey  = "tls_key"
)

TLSCertEnvKey, TLSKeyEnvKey are the internal/secrets envKeys a TLS-capable database's generated certificate and private key are stored under (cmd/levelrail's tlsMaterialFor), the same per-database keying PostgresPasswordEnvKey and its siblings already establish. Exported so internal/reconcile/application and internal/api can check whether TLS material exists for a given database without importing cmd/levelrail.

View Source
const (
	// EnvMinSafeAgentVersion is the first agent release that reports every
	// field the database drift checks compare (container labels).
	EnvMinSafeAgentVersion = "APP_AGENT_MIN_SAFE_VERSION"

	// ConditionTypeAgentCompatible is the extra condition set while a
	// database's node runs an agent too old for safe drift decisions.
	ConditionTypeAgentCompatible = "AgentCompatible"
)
View Source
const (
	EnvPoolerImage           = "APP_PG_POOLER_IMAGE"
	EnvPoolerDefaultPoolSize = "APP_PG_POOLER_DEFAULT_POOL_SIZE"
	EnvPoolerMaxClientConn   = "APP_PG_POOLER_MAX_CLIENT_CONN"
)

Env knobs for the managed Postgres pooler. The image falls back to a pinned default; operators override it with a tag or digest they have vetted.

View Source
const (
	EnvDockerWaitInterval = "APP_DB_DOCKER_WAIT_INTERVAL"
	EnvReadyProbeInterval = "APP_DB_READY_PROBE_INTERVAL"
	EnvReadyTimeout       = "APP_DB_READY_TIMEOUT"
)

Environment variables for the waiting steps of creating a database.

View Source
const (
	EnvRecreateLimit       = "APP_DB_RECREATE_LIMIT"
	EnvRecreateWindow      = "APP_DB_RECREATE_WINDOW"
	EnvRecreateBackoffBase = "APP_DB_RECREATE_BACKOFF_BASE"
	EnvRecreateBackoffMax  = "APP_DB_RECREATE_BACKOFF_MAX"
)

Env knobs for the recreate loop guard. A replace beyond the limit inside the window is delayed with an exponential backoff instead of repeating forever.

View Source
const (
	MeilisearchAPIKeyEnvKey = "meilisearch_master_key"
	QdrantAPIKeyEnvKey      = "qdrant_api_key"
	TypesenseAPIKeyEnvKey   = "typesense_api_key"
)

Secrets envKeys each engine's generated API key is stored under.

View Source
const (
	RestorePendingName   = ".restore-pending"
	RestoreImportingName = ".restore-importing"
	RestorePreviousName  = ".restore-previous"
	RestoreStagingName   = ".restore-staging"
	RestoreRollbackName  = ".restore-rollback"
	RestoreBootsName     = ".restore-boots"
)

Restore staging names inside an engine's volume, shared with internal/backup. Only an explicit rollback marker restores the previous data: an unconfirmed restore is never rolled back silently on a reboot.

View Source
const (
	PgvectorSuffix    = "-pgvector"
	TimescaleDBSuffix = "-timescaledb"
	PostGISSuffix     = "-postgis"
)

Suffixes that mark a Postgres version running an extension image, for example "17-pgvector" runs pgvector/pgvector:pg17. The engine id stays "postgres" so every Postgres code path (dump, restore, health) applies unchanged.

View Source
const DefaultPoolerImage = runtimesettings.DefaultPoolerImage

DefaultPoolerImage is used when EnvPoolerImage is unset. It is pinned to an exact version and speaks the env contract poolerEnv writes.

View Source
const EnvBinlogExpireSeconds = "APP_PITR_BINLOG_EXPIRE_SECONDS"

EnvBinlogExpireSeconds bounds how long a database keeps closed binlog files locally; shipped copies in the backup target are pruned by base backup retention.

View Source
const EnvCrashLoopRestarts = "APP_DB_CRASH_LOOP_RESTARTS"

EnvCrashLoopRestarts is how many container restarts inside the ready window count as a crash loop.

View Source
const EnvDragonflyProactorThreads = "APP_DRAGONFLY_PROACTOR_THREADS"

EnvDragonflyProactorThreads caps Dragonfly's io threads (default: one per CPU). Dragonfly refuses to start when threads times 256MiB exceeds the memory available, which a many-core host with little RAM hits.

View Source
const MySQLBinlogBasename = "mysql-bin"

MySQLBinlogBasename is the log-bin prefix inside MySQLBinlogPath.

View Source
const MySQLBinlogPath = "/var/lib/mysql-binlog"

MySQLBinlogPath is where a PITR-enabled MySQL or MariaDB database mounts its binlog volume. The backup package reads the same path to ship closed files.

View Source
const MySQLSlowQueryLogPath = "/var/lib/mysql/slow-query.log"

MySQLSlowQueryLogPath is where mysqlCommand directs the slow query log inside the container's own writable data directory. Not the container's stdout/stderr: MySQL 8's FILE log sink cannot reliably open /dev/stderr (confirmed against a real container: "Could not use /dev/stderr for logging, error 2"), so internal/api's slow-queries handler execs into the container to read this file directly instead of going through the Docker log stream database_logs.go uses.

View Source
const PoolerConditionType = "PoolerReady"

PoolerConditionType is reported next to Ready so a broken pooler never marks the database itself as down.

View Source
const PoolerPort = 6432

PoolerPort is where the pooler listens. It shares the database's network namespace, so it must not collide with Postgres' own 5432.

Variables

This section is empty.

Functions

func CheckInPlaceVersionChange added in v0.2.0

func CheckInPlaceVersionChange(engine, from, to string) error

CheckInPlaceVersionChange decides whether swapping an engine's image tag over the same data volume is safe. It is the one rule behind both the set-version route and the automatic upgrade runner.

func ClickHouseIdent added in v0.2.0

func ClickHouseIdent(name string) string

ClickHouseIdent maps a database name to a ClickHouse identifier the image entrypoint can use: it interpolates CLICKHOUSE_DB and CLICKHOUSE_USER into unquoted SQL, so a hyphen fails the CREATE and leaves no database.

func ContainerName

func ContainerName(dbName string) string

ContainerName exports containerName: the Docker container name dbName's managed database reconciles to, also the host part of a resolved { from: ... } env var (internal/reconcile/application's resolveDatabaseField).

func ContainerPort

func ContainerPort(engine string) (int, bool)

ContainerPort returns the standard container port engine's image listens on, and whether engine is recognized.

func ConventionalEnvVar

func ConventionalEnvVar(engine, field string) string

ConventionalEnvVar is the env var name an app conventionally reads field of engine from (MEILISEARCH_URL, QDRANT_API_KEY), or "" when engine has no such convention or field is not supported for it.

func CreateIsolatedRedisACLCommand added in v0.2.0

func CreateIsolatedRedisACLCommand(username, password string) []string

CreateIsolatedRedisACLCommand returns the redis-cli argv that creates or resets username's ACL entry: password-authenticated, restricted to keys under its own "username:" prefix (~prefix:* per Redis 6+ ACL key patterns), every command category except @admin/@dangerous/@scripting (scripting is excluded because a Lua script's own key access isn't reliably bound by the caller's key-pattern ACL). "reset" first clears any prior state for this username so a redeploy converges to the same rules rather than layering onto whatever ran before; idempotent, safe to run again against a username that already exists.

func CreateIsolatedRoleSQL added in v0.2.0

func CreateIsolatedRoleSQL(role, dbName, password string) string

CreateIsolatedRoleSQL returns the SQL script that creates role (if it does not already exist) and grants it read/write on every table and sequence in dbName's public schema, including ones created later. Idempotent: safe to run again against a role that already exists.

func DataVolumeName added in v0.2.0

func DataVolumeName(dbName string) string

DataVolumeName is the Docker volume holding a database's data.

func DropIsolatedRedisACLCommand added in v0.2.0

func DropIsolatedRedisACLCommand(username string) []string

DropIsolatedRedisACLCommand returns the redis-cli argv that removes username's ACL entry. ACL DELUSER on a username that doesn't exist returns 0 deleted rather than erroring, so this is idempotent, safe to retry the same way DropIsolatedRoleSQL's DROP ROLE IF EXISTS is.

func DropIsolatedRoleSQL added in v0.2.0

func DropIsolatedRoleSQL(role, dbName string) string

DropIsolatedRoleSQL returns the SQL script that revokes role's grants on dbName and drops it. Idempotent: DROP ROLE IF EXISTS tolerates a role that is already gone (a retried teardown).

func GenerateIsolatedRolePassword added in v0.2.0

func GenerateIsolatedRolePassword() (string, error)

GenerateIsolatedRolePassword returns a random password for a new isolated role, the same 24-byte/base64url shape the platform's own managed database credentials already use.

func GenerateSelfSignedCert added in v0.2.0

func GenerateSelfSignedCert(commonName string) (certPEM, keyPEM []byte, err error)

GenerateSelfSignedCert creates a new self-signed ECDSA P-256 certificate for commonName. Not ed25519: libpq's SCRAM channel binding hashes the certificate signature and fails with "could not find digest for NID UNDEF" on an ed25519 certificate, which broke psql, psycopg2 and every other libpq client connecting with sslmode=require.

func ImageRef added in v0.2.0

func ImageRef(engine, version string) string

ImageRef is the image:tag a database of engine and version runs.

func ImageTag added in v0.2.0

func ImageTag(engine, version string) string

ImageTag returns the tag part of the image a database of engine and version runs.

func IsLegacyTLSCert added in v0.2.0

func IsLegacyTLSCert(certPEM []byte) bool

IsLegacyTLSCert reports whether certPEM is one of the ed25519 certificates earlier releases generated, which libpq cannot use.

func IsolatedRoleName added in v0.2.0

func IsolatedRoleName(previewName, sourceKey string) string

IsolatedRoleName derives a deterministic, safe identifier for one preview's isolated credential on sourceKey: lowercase, only [a-z0-9_], truncated to stay well under Postgres' 63-byte identifier limit. Used both as a Postgres role name and, unchanged, as a Redis ACL username. Deterministic so a redeploy of the same preview finds the same role/user rather than minting a new one.

func LeadingMajor added in v0.2.0

func LeadingMajor(v string) (int, bool)

LeadingMajor parses the first numeric component of a version tag, accepting a "v" prefix (Dragonfly tags such as "v1.27.1").

func MemcachedCacheMB

func MemcachedCacheMB(res *store.ServiceResources) int

MemcachedCacheMB sizes the item cache at three quarters of the container limit: memcached's -m excludes connection and hash table overhead, so a cache equal to the limit gets the container OOM killed.

func MemcachedStatsCmd

func MemcachedStatsCmd() []string

MemcachedStatsCmd asks the server for its stats over the text protocol. The image ships bash but no client, so this uses bash's /dev/tcp.

func ParseMemcachedStats

func ParseMemcachedStats(out string) (map[string]string, error)

ParseMemcachedStats reads "STAT name value" lines up to END.

func ParsePgvectorVersion added in v0.2.0

func ParsePgvectorVersion(version string) (major int, isVariant bool, err error)

ParsePgvectorVersion reports whether version selects the pgvector variant and its Postgres major. Other variants return (0, false, nil).

func ParsePostgresVariant

func ParsePostgresVariant(version string) (major int, variant string, err error)

ParsePostgresVariant reports whether version selects an extension image and returns its Postgres major and variant name. A string that mentions a variant but is not exactly "<major>-<variant>" is an error; a plain version returns (0, "", nil).

func PasswordSecretKey

func PasswordSecretKey(engine string) (key string, ok bool)

PasswordSecretKey returns the internal/secrets envKey engine's generated password is stored under, and whether engine has a password at all: Redis runs passwordless (this package's own doc comment on why), so it reports ok=false rather than an empty key.

func PoolerActive

func PoolerActive(d *store.DesiredDatabase) bool

PoolerActive reports whether apps should be handed the pooled address.

func PoolerContainerName

func PoolerContainerName(dbName string) string

PoolerContainerName is the pooler child container for dbName.

func PoolerImage

func PoolerImage() string

PoolerImage is the runtime setting, else the env override, else the pinned default.

func PostgresNeedsPGDATA added in v0.2.0

func PostgresNeedsPGDATA(version string) bool

PostgresNeedsPGDATA reports whether a Postgres image tag is major 18 or newer, whose default PGDATA moved under /var/lib/postgresql/<major>/: without pinning it back, the data directory falls outside the mounted volume and a container recreate loses the data. Older images keep the unchanged env.

func ScratchSearchSpec

func ScratchSearchSpec(engine, key string, spec *docker.ContainerSpec) bool

ScratchSearchSpec configures a disposable restore target for engine (a backup drill) with the same boot script and paths and a throwaway key.

func ServerTLSEngine

func ServerTLSEngine(engine string) bool

ServerTLSEngine reports whether the server side of engine can terminate TLS here. Unlike SupportsTLS, MySQL and MariaDB clients opt in themselves, so no URL changes.

func SetPoolerImageSource

func SetPoolerImageSource(f func() string)

SetPoolerImageSource wires the live runtime setting so a saved image applies without a restart. Env and the pinned default remain the fallback.

func SupportsField

func SupportsField(engine, field string) bool

SupportsField reports whether field is resolvable for a database of engine. The single source of truth internal/deploy.Pipeline.validateEnv (deploy-time) and internal/reconcile/application's resolveDatabaseField (reconcile-time) both defer to, so which fields work for which engine can never drift between the two.

func SupportsPITR

func SupportsPITR(engine string) bool

SupportsPITR reports whether engine can run with point-in-time restore enabled.

func SupportsTLS added in v0.2.0

func SupportsTLS(engine string) bool

SupportsTLS reports whether engine's managed database gets TLS enabled by this controller (see WithTLS). Scoped to Postgres and Redis for now: both expose an "encrypt without verifying" mode entirely within a standard connection URI (sslmode=require, rediss://) that mainstream client libraries already honor with zero app-side code changes, the "no user action required" bar this feature is held to. The other engines this package reconciles (MySQL, MariaDB, MongoDB, ClickHouse, KeyDB, Dragonfly) don't share that property yet: MongoDB's own tlsAllowInvalidCertificates URI option is a plausible future candidate, KeyDB/Dragonfly fork Redis's TLS flags under different names not verified here, and MySQL/MariaDB have no driver-agnostic URI knob for "encrypt but don't verify" at all.

func TLSContainerPort added in v0.2.0

func TLSContainerPort(engine string) (int, bool)

TLSContainerPort returns the port engine's image listens on once TLS is enabled, which for Redis differs from ContainerPort's plaintext port (controller.go's own redisTLSContainerPort doc comment: --port 0 disables the plaintext port entirely once TLS is on). Postgres negotiates TLS on its one existing port, so its value is identical to ContainerPort.

func ValidateEngineVersion added in v0.2.0

func ValidateEngineVersion(engine, version string) error

ValidateEngineVersion rejects variant strings that cannot be mapped to an image. Versions without a variant marker are not constrained here.

func WALArchiveVolumeName added in v0.2.0

func WALArchiveVolumeName(dbName string) string

WALArchiveVolumeName is the Docker volume holding a database's PITR WAL archive.

Types

type APIKeyCredentials

type APIKeyCredentials struct {
	Key string
}

APIKeyCredentials is the generated key a search engine requires.

type AgentVersionLookup

type AgentVersionLookup func(ctx context.Context, nodeID string) (version string, remote bool, err error)

AgentVersionLookup returns the agent build a node reported. remote is false for the local node, which has no agent and is never outdated.

type ClickHouseCredentials

type ClickHouseCredentials struct {
	Username string
	Password string
}

ClickHouseCredentials is what ClickHouse reconciliation needs, injected as CLICKHOUSE_USER/CLICKHOUSE_PASSWORD/CLICKHOUSE_DB. Unlike MySQL/MariaDB, the image doesn't refuse to start without these, but this controller still requires them, the same "never unauthenticated" posture MongoDBCredentials' own doc comment explains.

type Controller

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

Controller converges one named database's desired state (read fresh from Store on every Reconcile, never cached) to a running, volume-backed container.

func New

func New(dbName string, dbStore Store, runtime docker.Runtime, opts ...Option) *Controller

New builds a Controller for dbName.

func (*Controller) Name

func (c *Controller) Name() string

Name implements reconcile.Controller.

func (*Controller) Reconcile

func (c *Controller) Reconcile(ctx context.Context) (reconcile.Result, error)

Reconcile implements reconcile.Controller.

func (*Controller) Teardown added in v0.2.0

func (c *Controller) Teardown(ctx context.Context) error

Teardown stops and removes this database's container, if one exists. Used both by a caller that owns this database's full lifecycle (an ephemeral preview database, internal/api/preview_environments_databases.go) and by callers that must call it themselves right after moving or deleting desired state: Reconcile treats ErrDatabaseNotFound as "not deployed yet," not "stop everything," so a moved-off-this-node or deleted database is never reconciled here again otherwise. Idempotent and safe to call again after a partial failure: InspectByName reports the container's real state fresh on every call, so a Stop that already ran (or a Remove that already succeeded) is simply skipped rather than retried into an error. Does not remove the container's data volume; see dataVolumeName's own doc comment.

type MariaDBCredentials

type MariaDBCredentials struct {
	Username string
	Password string
}

MariaDBCredentials is MySQLCredentials' counterpart for the official mariadb image. Injected as MARIADB_ROOT_PASSWORD/MARIADB_USER/ MARIADB_PASSWORD/MARIADB_DATABASE, not the MYSQL_* names: the mariadb image accepts MYSQL_* only as a legacy alias ("MARIADB_* variables will be used in preference to MYSQL_* variables" per its own docs), and from MariaDB 11 the mysqldump/mysql client binaries themselves are gone from the image, so this controller and internal/backup's dump/ restore commands both use the MariaDB-native names throughout, not the MySQL-shaped ones this image only keeps for someone else's old scripts.

type MeshPortLookup added in v0.2.0

type MeshPortLookup interface {
	GetDatabaseMeshPort(ctx context.Context, name string) (int, error)
	GetDatabasePoolerMeshPort(ctx context.Context, name string) (int, error)
}

MeshPortLookup reads a database's allocated mesh port (0 when none).

type MongoDBCredentials

type MongoDBCredentials struct {
	Username string
	Password string
}

MongoDBCredentials is what MongoDB reconciliation needs: the same dbName-scoped username/password shape PostgresCredentials/ MySQLCredentials already establish, injected as MONGO_INITDB_ROOT_USERNAME/MONGO_INITDB_ROOT_PASSWORD, the official mongo image's own root-credential env vars. Unlike MySQL's image, mongo's does not refuse to start without these, so this controller still requires them (WithMongoDBCredentials unset blocks reconciling just like an unset WithPostgresCredentials/WithMySQLCredentials does), keeping every managed database on the same "never unauthenticated" posture rather than making Mongo the one exception.

type MySQLCredentials

type MySQLCredentials struct {
	Username string
	Password string
}

MySQLCredentials is what MySQL reconciliation needs: the same dbName-scoped username/password shape PostgresCredentials already establishes, injected as MYSQL_USER/MYSQL_PASSWORD/MYSQL_DATABASE (see reconcileMySQLEnv). Password also backs MYSQL_ROOT_PASSWORD: unlike Postgres' image, MySQL's own image refuses to start at all without a root password (or an explicit, deliberately-not-used empty-password opt-out), so there is no unauthenticated-but-running MySQL state this controller could otherwise reach.

type Option

type Option func(*Controller)

Option configures optional Controller behavior.

func WithAPIKeyCredentials

func WithAPIKeyCredentials(creds *APIKeyCredentials) Option

WithAPIKeyCredentials supplies the API key Meilisearch, Qdrant and Typesense reconciliation needs; without one they stay credentials-blocked.

func WithAgentVersion

func WithAgentVersion(fn AgentVersionLookup) Option

WithAgentVersion enables the outdated-agent gate: drift that would replace a container is ignored while the node's agent is older than the minimum.

func WithClickHouseCredentials

func WithClickHouseCredentials(creds *ClickHouseCredentials) Option

WithClickHouseCredentials supplies the credentials ClickHouse reconciliation needs. Without one, every ClickHouse database reports the credentials-blocked condition.

func WithMariaDBCredentials

func WithMariaDBCredentials(creds *MariaDBCredentials) Option

WithMariaDBCredentials supplies the credentials MariaDB reconciliation needs, the same "populate to activate, no restructuring needed" shape the other WithXCredentials options already establish. Without one, every MariaDB database reports the credentials-blocked condition.

func WithMeshDNSAddr

func WithMeshDNSAddr(addr string) Option

WithMeshDNSAddr points every container this controller creates at addr, a bare nameserver IP (never "ip:port": neither Docker's DNS HostConfig field nor a container's own resolv.conf supports a non-standard nameserver port, confirmed live while building this option, see cmd/levelrail/mesh.go's dockerNameserverPort doc comment), as an additional nameserver ahead of whatever Docker's own resolver would otherwise configure (docker.ContainerSpec.DNS). Without one configured (the default, empty string), Reconcile behaves exactly as before this field existed: no DNS override at all. addr is expected to already be a real, container-reachable address (cmd/levelrail/mesh.go's containerDNSAddr resolves Docker's own bridge gateway IP, only once the mesh DNS server is confirmed bound to port 53); this controller does not validate it.

func WithMeshEndpoint added in v0.2.0

func WithMeshEndpoint(r meshpath.EndpointResolver, ports MeshPortLookup) Option

WithMeshEndpoint publishes a database that has a mesh port on its node's mesh address, so apps on other nodes can reach it. Only databases with a cross-node consumer ever get a port, and the bind is never a wildcard.

func WithMongoDBCredentials

func WithMongoDBCredentials(creds *MongoDBCredentials) Option

WithMongoDBCredentials supplies the credentials MongoDB reconciliation needs, the same "populate to activate, no restructuring needed" shape WithPostgresCredentials/WithMySQLCredentials already establish. Without one, every MongoDB database reports the credentials-blocked condition, identical reasoning to Postgres and MySQL.

func WithMySQLCredentials

func WithMySQLCredentials(creds *MySQLCredentials) Option

WithMySQLCredentials supplies the credentials MySQL reconciliation needs, the same "populate to activate, no restructuring needed" shape WithPostgresCredentials already establishes. Without one, every MySQL database reports the credentials-blocked condition, identical reasoning to Postgres: no container starts without real auth.

func WithPostgresCredentials

func WithPostgresCredentials(creds *PostgresCredentials) Option

WithPostgresCredentials supplies the credentials Postgres reconciliation needs. Until envelope-encrypted secrets exist nothing calls this, so every Postgres database reports the credentials-blocked condition instead of starting an unauthenticated container.

func WithProvisionRecorder

func WithProvisionRecorder(rec *dbprovision.Recorder) Option

WithProvisionRecorder makes the controller report each creation step to rec.

func WithReadyProbe

func WithReadyProbe(fn func(ctx context.Context, addr string) error) Option

WithReadyProbe replaces the TCP connect probe, for tests.

func WithSlowQueryThreshold added in v0.2.0

func WithSlowQueryThreshold(ms int) Option

WithSlowQueryThreshold sets the Postgres log_min_duration_statement/ MySQL long_query_time threshold, in milliseconds, above which a statement is captured in the container's own log stream. ms <= 0 leaves the field unset, so Reconcile falls back to defaultSlowQueryThresholdMs; this mirrors every other WithX option's "unset means the documented default" shape rather than requiring every caller to know the default value itself. Only read by the Postgres and MySQL cases in Reconcile.

func WithTLS added in v0.2.0

func WithTLS(material *TLSMaterial) Option

WithTLS supplies the self-signed certificate/key an engine that SupportsTLS should terminate TLS with, generated once and persisted the same way credentials already are (cmd/levelrail's tlsMaterialFor). Only read by the Postgres and Redis cases in Reconcile; every other engine ignores it. Nil (the default) reconciles exactly as before this option existed: plaintext, same as every database created before this feature.

func WithTLSDisabled

func WithTLSDisabled() Option

WithTLSDisabled marks TLS as deliberately off so a container still serving TLS is replaced; a nil WithTLS alone never downgrades.

type PoolSaturation

type PoolSaturation struct {
	ClientsWaiting int
	MaxWaitSeconds float64
}

PoolSaturation is the client queue pgbouncer reports across the pools of one database.

func ParseShowPools

func ParseShowPools(out string) (PoolSaturation, error)

ParseShowPools reads psql -A output of pgbouncer SHOW POOLS (header row, then one row per pool, separated by |). Columns are found by header name because their order differs between pgbouncer releases.

type PostgresCredentials

type PostgresCredentials struct {
	Username string
	Password string
}

PostgresCredentials is what Postgres reconciliation needs once envelope-encrypted secrets land: a username and password to inject as POSTGRES_USER/POSTGRES_PASSWORD. Deliberately a plain struct, not wired to any secret store: Controller takes an optional, currently-always-nil *PostgresCredentials so real Postgres support turns on by populating it, not by rewriting this controller.

type PostgresVariant

type PostgresVariant struct {
	Name     string
	Suffix   string
	Image    string
	MinMajor int
	// contains filtered or unexported fields
}

PostgresVariant describes one allowlisted Postgres extension image.

func PostgresVariants

func PostgresVariants() []PostgresVariant

PostgresVariants lists the allowlisted extension images, in picker order.

type SearchLayout

type SearchLayout struct {
	VolumePath string
	DataDir    string
	Port       int
	// KeyEnv is the container env var holding the API key; in-container
	// scripts read it so the key never travels through exec arguments.
	KeyEnv string
	// ImportFlag loads a pending artifact at boot; empty means the boot
	// script swaps a pre-extracted staging directory into DataDir instead.
	ImportFlag string
	// ArtifactExt is appended to the pending and importing file names.
	ArtifactExt string
	// contains filtered or unexported fields
}

SearchLayout is where one search engine keeps its data and how it is reached from inside its own container.

func SearchEngineLayout

func SearchEngineLayout(engine string) (SearchLayout, bool)

SearchEngineLayout returns engine's layout and whether it is a search engine.

func (SearchLayout) BootScript

func (l SearchLayout) BootScript() string

BootScript is the container's sh -c command: apply a requested rollback, then a pending restore, then exec the engine. Each step is ordered so a crash between any two commands is safe to rerun.

func (SearchLayout) BootsPath

func (l SearchLayout) BootsPath() string

BootsPath gains one line per boot while a restore is unconfirmed, so a single line proves the engine serving now is the one that loaded it.

func (SearchLayout) ConfirmRestoreScript

func (l SearchLayout) ConfirmRestoreScript() string

ConfirmRestoreScript commits a loaded restore. The importing marker goes first, so a crash before the previous data is removed keeps the restore.

func (SearchLayout) ImportingPath

func (l SearchLayout) ImportingPath() string

ImportingPath marks a loaded but unconfirmed restore.

func (SearchLayout) PendingPath

func (l SearchLayout) PendingPath() string

PendingPath is the file a restore uploads before the restart that loads it.

func (SearchLayout) PreviousPath

func (l SearchLayout) PreviousPath() string

PreviousPath holds the pre-restore data until the restore is confirmed.

func (SearchLayout) RequestRollbackScript

func (l SearchLayout) RequestRollbackScript() string

RequestRollbackScript marks the restore failed; the next boot restores the previous data.

func (SearchLayout) RollbackPath

func (l SearchLayout) RollbackPath() string

RollbackPath requests that the next boot put the previous data back.

func (SearchLayout) StagingPath

func (l SearchLayout) StagingPath() string

StagingPath is the extracted snapshot an ImportFlag-less engine swaps in.

type Store

type Store interface {
	GetDesiredDatabase(ctx context.Context, name string) (*store.DesiredDatabase, error)
}

Store is the narrow surface this controller needs from internal/store, so tests can fake it without a real database. *store.DB satisfies this.

type TLSMaterial added in v0.2.0

type TLSMaterial struct {
	CertPEM []byte
	KeyPEM  []byte
}

TLSMaterial is a self-signed certificate and private key pair for one managed database's server-side TLS, generated once at database creation time (cmd/levelrail's tlsMaterialFor) and persisted through internal/secrets the same way PostgresCredentials' password already is (TLSCertEnvKey/TLSKeyEnvKey, address.go). Self-signed, not issued by a shared platform CA: the only clients that ever use it (internal/reconcile/application's resolveDatabaseURL, via sslmode=require for Postgres or rediss:// for Redis) encrypt without verifying the certificate's issuer, so there is no second party that ever needs to trust it.

Jump to

Keyboard shortcuts

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