42307f285f
encapi's schema was a cumulative IF NOT EXISTS blob re-executed inline on every start: it grows forever, records nothing, and cannot express a change that is not a fresh CREATE. golib owns that mechanism now, so encapi keeps the SQL and drops the runner. - Move the DDL verbatim into migrations/0001_init.sql, embedded via migrations.FS. It stays IF NOT EXISTS-guarded, so the first start against the live database re-runs it as a no-op and only lands the schema_migrations row. - Build the pool with pg.NewMigrated under the advisory lock named encapi-migrations, and delete the inline migrate(). database.New now takes a context and a logger; main.go hands it the signal context so a start blocked on the migration lock still dies on SIGTERM. - Render the DSN with pg.DSN. The env var contract is untouched — the fields are still resolved by internal/config, because encapi defaults DBUSER and DBNAME to "encapi" where pg.DSNFromEnv treats both as required. - Guard the set: embedded files must match migrations/, every CREATE must be idempotent, the derived lock key is pinned, and a container test proves the adoption path over a database that predates schema_migrations. - Plumb GOPRIVATE=git.unkin.net for the first cross-repo Go dependency: exported by the Makefile, set in the Dockerfile and the woodpecker Go steps, documented in the README.
39 lines
1.1 KiB
Go
39 lines
1.1 KiB
Go
// Package database is the Postgres persistence layer for encapi. It stores
|
|
// three entities — statuses (Puppet environments), roles (class assignment
|
|
// targets with inheritable default params), and nodes (host-to-role
|
|
// assignments) — and enforces referential integrity between them.
|
|
package database
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
|
|
"git.unkin.net/unkin/golib/pg"
|
|
|
|
"git.unkin.net/unkin/encapi/migrations"
|
|
)
|
|
|
|
// DB wraps a pgx connection pool.
|
|
type DB struct {
|
|
Pool *pgxpool.Pool
|
|
}
|
|
|
|
// New connects to Postgres, verifies the connection, and brings the schema up
|
|
// to date from the embedded migrations before returning, so the server never
|
|
// serves against a half-migrated database. log may be nil.
|
|
func New(ctx context.Context, dsn string, log *slog.Logger) (*DB, error) {
|
|
pool, err := pg.NewMigrated(ctx, dsn, migrations.FS, pg.MigrateOptions{
|
|
LockName: migrations.LockName,
|
|
Logger: log,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &DB{Pool: pool}, nil
|
|
}
|
|
|
|
// Close releases the pool.
|
|
func (db *DB) Close() { db.Pool.Close() }
|