373d21a744
Postgres-backed External Node Classifier for Puppet, replacing Cobbler. - encapi HTTP server (chi + pgx): read/write API + two ENC document shapes (reshaped for the exec terminus; cobbler-wire for enc_direct_facts.rb) - encapi-cli: classify/node/role/status CRUD + import-cobbler seeder - pkg/client Go SDK; unit tests across all packages (DB via testcontainers) - Dockerfile (distroless), Makefile, nfpm RPM (encapi-cli + encapi-enc wrapper), Woodpecker CI, docs/cutover.md
72 lines
2.0 KiB
Go
72 lines
2.0 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"
|
|
"fmt"
|
|
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
)
|
|
|
|
// DB wraps a pgx connection pool.
|
|
type DB struct {
|
|
Pool *pgxpool.Pool
|
|
}
|
|
|
|
// New connects to Postgres, verifies the connection, and runs migrations.
|
|
func New(dsn string) (*DB, error) {
|
|
pool, err := pgxpool.New(context.Background(), dsn)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("connect to postgres: %w", err)
|
|
}
|
|
if err := pool.Ping(context.Background()); err != nil {
|
|
pool.Close()
|
|
return nil, fmt.Errorf("ping postgres: %w", err)
|
|
}
|
|
|
|
db := &DB{Pool: pool}
|
|
if err := db.migrate(); err != nil {
|
|
pool.Close()
|
|
return nil, fmt.Errorf("run migrations: %w", err)
|
|
}
|
|
return db, nil
|
|
}
|
|
|
|
// Close releases the pool.
|
|
func (db *DB) Close() { db.Pool.Close() }
|
|
|
|
func (db *DB) migrate() error {
|
|
_, err := db.Pool.Exec(context.Background(), `
|
|
CREATE TABLE IF NOT EXISTS statuses (
|
|
name TEXT PRIMARY KEY,
|
|
description TEXT NOT NULL DEFAULT '',
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS roles (
|
|
name TEXT PRIMARY KEY,
|
|
description TEXT NOT NULL DEFAULT '',
|
|
default_params JSONB NOT NULL DEFAULT '{}',
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS nodes (
|
|
certname TEXT PRIMARY KEY,
|
|
role TEXT NOT NULL REFERENCES roles(name) ON UPDATE CASCADE,
|
|
environment TEXT NOT NULL REFERENCES statuses(name) ON UPDATE CASCADE,
|
|
params JSONB NOT NULL DEFAULT '{}',
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
`)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|