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
- func CheckInPlaceVersionChange(engine, from, to string) error
- func ClickHouseIdent(name string) string
- func ContainerName(dbName string) string
- func ContainerPort(engine string) (int, bool)
- func ConventionalEnvVar(engine, field string) string
- func CreateIsolatedRedisACLCommand(username, password string) []string
- func CreateIsolatedRoleSQL(role, dbName, password string) string
- func DataVolumeName(dbName string) string
- func DropIsolatedRedisACLCommand(username string) []string
- func DropIsolatedRoleSQL(role, dbName string) string
- func GenerateIsolatedRolePassword() (string, error)
- func GenerateSelfSignedCert(commonName string) (certPEM, keyPEM []byte, err error)
- func ImageRef(engine, version string) string
- func ImageTag(engine, version string) string
- func IsLegacyTLSCert(certPEM []byte) bool
- func IsolatedRoleName(previewName, sourceKey string) string
- func LeadingMajor(v string) (int, bool)
- func MemcachedCacheMB(res *store.ServiceResources) int
- func MemcachedStatsCmd() []string
- func ParseMemcachedStats(out string) (map[string]string, error)
- func ParsePgvectorVersion(version string) (major int, isVariant bool, err error)
- func ParsePostgresVariant(version string) (major int, variant string, err error)
- func PasswordSecretKey(engine string) (key string, ok bool)
- func PoolerActive(d *store.DesiredDatabase) bool
- func PoolerContainerName(dbName string) string
- func PoolerImage() string
- func PostgresNeedsPGDATA(version string) bool
- func ScratchSearchSpec(engine, key string, spec *docker.ContainerSpec) bool
- func ServerTLSEngine(engine string) bool
- func SupportsField(engine, field string) bool
- func SupportsPITR(engine string) bool
- func SupportsTLS(engine string) bool
- func TLSContainerPort(engine string) (int, bool)
- func ValidateEngineVersion(engine, version string) error
- func WALArchiveVolumeName(dbName string) string
- type APIKeyCredentials
- type AgentVersionLookup
- type ClickHouseCredentials
- type Controller
- type MariaDBCredentials
- type MeshPortLookup
- type MongoDBCredentials
- type MySQLCredentials
- type Option
- func WithAPIKeyCredentials(creds *APIKeyCredentials) Option
- func WithAgentVersion(fn AgentVersionLookup) Option
- func WithClickHouseCredentials(creds *ClickHouseCredentials) Option
- func WithMariaDBCredentials(creds *MariaDBCredentials) Option
- func WithMeshDNSAddr(addr string) Option
- func WithMeshEndpoint(r meshpath.EndpointResolver, ports MeshPortLookup) Option
- func WithMongoDBCredentials(creds *MongoDBCredentials) Option
- func WithMySQLCredentials(creds *MySQLCredentials) Option
- func WithPostgresCredentials(creds *PostgresCredentials) Option
- func WithProvisionRecorder(rec *dbprovision.Recorder) Option
- func WithReadyProbe(fn func(ctx context.Context, addr string) error) Option
- func WithSlowQueryThreshold(ms int) Option
- func WithTLS(material *TLSMaterial) Option
- func WithTLSDisabled() Option
- type PoolSaturation
- type PostgresCredentials
- type PostgresVariant
- type SearchLayout
- func (l SearchLayout) BootScript() string
- func (l SearchLayout) BootsPath() string
- func (l SearchLayout) ConfirmRestoreScript() string
- func (l SearchLayout) ImportingPath() string
- func (l SearchLayout) PendingPath() string
- func (l SearchLayout) PreviousPath() string
- func (l SearchLayout) RequestRollbackScript() string
- func (l SearchLayout) RollbackPath() string
- func (l SearchLayout) StagingPath() string
- type Store
- type TLSMaterial
Constants ¶
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.
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.
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" )
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.
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.
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.
const ( MeilisearchAPIKeyEnvKey = "meilisearch_master_key" QdrantAPIKeyEnvKey = "qdrant_api_key" TypesenseAPIKeyEnvKey = "typesense_api_key" )
Secrets envKeys each engine's generated API key is stored under.
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.
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.
const DefaultPoolerImage = "edoburu/pgbouncer:v1.26.1-p0"
DefaultPoolerImage is used when EnvPoolerImage is unset. It is pinned to an exact version and speaks the env contract poolerEnv writes.
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.
const EnvCrashLoopRestarts = "APP_DB_CRASH_LOOP_RESTARTS"
EnvCrashLoopRestarts is how many container restarts inside the ready window count as a crash loop.
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.
const MySQLBinlogBasename = "mysql-bin"
MySQLBinlogBasename is the log-bin prefix inside MySQLBinlogPath.
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.
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.
const PoolerConditionType = "PoolerReady"
PoolerConditionType is reported next to Ready so a broken pooler never marks the database itself as down.
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
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
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 ¶
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 ¶
ContainerPort returns the standard container port engine's image listens on, and whether engine is recognized.
func ConventionalEnvVar ¶
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
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
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
DataVolumeName is the Docker volume holding a database's data.
func DropIsolatedRedisACLCommand ¶ added in v0.2.0
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
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
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
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 ImageTag ¶ added in v0.2.0
ImageTag returns the tag part of the image a database of engine and version runs.
func IsLegacyTLSCert ¶ added in v0.2.0
IsLegacyTLSCert reports whether certPEM is one of the ed25519 certificates earlier releases generated, which libpq cannot use.
func IsolatedRoleName ¶ added in v0.2.0
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
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 ¶
ParseMemcachedStats reads "STAT name value" lines up to END.
func ParsePgvectorVersion ¶ added in v0.2.0
ParsePgvectorVersion reports whether version selects the pgvector variant and its Postgres major. Other variants return (0, false, nil).
func ParsePostgresVariant ¶
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 ¶
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 ¶
PoolerContainerName is the pooler child container for dbName.
func PoolerImage ¶
func PoolerImage() string
PoolerImage is the env override, else the pinned default.
func PostgresNeedsPGDATA ¶ added in v0.2.0
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 ¶
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 SupportsField ¶
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 ¶
SupportsPITR reports whether engine can run with point-in-time restore enabled.
func SupportsTLS ¶ added in v0.2.0
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
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
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
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 ¶
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 (*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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
WithReadyProbe replaces the TCP connect probe, for tests.
func WithSlowQueryThreshold ¶ added in v0.2.0
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 ¶
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 ¶
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
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.