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.
66 lines
1.8 KiB
Go
66 lines
1.8 KiB
Go
// Package config loads encapi server configuration from the environment.
|
|
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
|
|
"git.unkin.net/unkin/golib/pg"
|
|
)
|
|
|
|
// Config is the fully-resolved server configuration.
|
|
type Config struct {
|
|
ListenAddr string
|
|
|
|
DBHost string
|
|
DBPort int
|
|
DBUser string
|
|
DBPass string
|
|
DBName string
|
|
DBSSL string
|
|
|
|
// WriteToken guards all mutating endpoints. Reads are always open.
|
|
// When empty, writes are refused entirely (fail-closed).
|
|
WriteToken string
|
|
|
|
// DistroAPIURL, when set, points encapi at an external kickstart/distro
|
|
// API that resolves per-host provisioning params (epel, os release, ...).
|
|
// Left empty, no distro params are injected into ENC output.
|
|
DistroAPIURL string
|
|
}
|
|
|
|
// DatabaseDSN renders a libpq/pgx connection string. The fields come from this
|
|
// package rather than golib's pg.DSNFromEnv because encapi defaults DBUSER and
|
|
// DBNAME to "encapi", where DSNFromEnv treats both as required.
|
|
func (c *Config) DatabaseDSN() string {
|
|
return pg.DSN(c.DBHost, c.DBPort, c.DBUser, c.DBPass, c.DBName, c.DBSSL)
|
|
}
|
|
|
|
// Load reads configuration from the environment, applying defaults.
|
|
func Load() (*Config, error) {
|
|
dbPort, err := strconv.Atoi(getenv("DBPORT", "5432"))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid DBPORT: %w", err)
|
|
}
|
|
|
|
return &Config{
|
|
ListenAddr: getenv("LISTEN_ADDR", ":8000"),
|
|
DBHost: getenv("DBHOST", "localhost"),
|
|
DBPort: dbPort,
|
|
DBUser: getenv("DBUSER", "encapi"),
|
|
DBPass: getenv("DBPASS", "encapi"),
|
|
DBName: getenv("DBNAME", "encapi"),
|
|
DBSSL: getenv("DBSSL", "disable"),
|
|
WriteToken: os.Getenv("ENCAPI_WRITE_TOKEN"),
|
|
DistroAPIURL: os.Getenv("ENCAPI_DISTRO_API_URL"),
|
|
}, nil
|
|
}
|
|
|
|
func getenv(key, def string) string {
|
|
if v := os.Getenv(key); v != "" {
|
|
return v
|
|
}
|
|
return def
|
|
}
|