Files
mesh-catalog/modules/postgres/cmd/postgres-provider/provisioner.go
T
jschoubben 35ff67bda0
mesh/delivery-group group feat/774-l3-reland rejected: composed together: the checker's verdict at the members' heads (b48c90fea31f): integration: mesh-self-ch…
mesh/delivery closed: a newer head of the same pull request
mesh/check failed: integration: mesh-self-check/cmd/mesh-self-check.TestTheSelfCheckWholeRunsOnTheBus: the run answered {Run:doctor-1793351737-1 At:20…
Design 47's last milestones: the keeper, self-check, healers, hand-acts, provisioner, vault, artifact sweep, disk readings, credential forms (issues 774, 1952, 1953, 1954, 1955)
One commit with exactly the tree of the reviewed head 35b4509c0a, on main 12c2574c9d, so two compiled programs added and removed on the branch leave no trace in history (issue 1577).
2026-10-30 02:21:51 +01:00

86 lines
3.6 KiB
Go

package main
// postgres's provisioner — the adapter that makes postgres a provider of the mesh
// `postgres-database` interface (novox/hq ADR 0039/0040/0048). A consumer connects to a database it
// alone owns, as `as` with the password the mesh minted.
//
// **The role name and password are the mesh's, not the provisioner's (ADR 0048).** postgres creates
// a role and a same-named database under exactly that login — a name the consumer cannot learn is a
// database it cannot reach.
//
// **Extensions are the provider's to install.** A contribution may name extensions
// (`"extensions": ["vector"]`); most are not trusted, so only the superuser this module holds can
// create them, in the consumer's database, on every pass, and never drops one.
import (
"context"
"git.novox.be/novox/mesh-sdk/go/provider/harness"
"time"
)
// provisioner is the adapter over the client; announce emits a lifecycle event.
type provisioner struct {
pg *Client
announce func(event string, body map[string]string)
}
func (a provisioner) Create(ctx context.Context, p harness.Provision) error {
// Read before anything runs: a malformed list is refused without touching the server.
extensions, err := Extensions(p.Values)
if err != nil {
return err
}
// Database and owning role share the consumer's login, so the consumer owns exactly its own.
database := p.As
if err := a.pg.CreateDatabaseAndRole(ctx, database, p.As, p.Password); err != nil {
return err
}
// A login retired and asked for again takes back what its keeper kept (retire_pg.go).
if err := a.pg.Reclaim(ctx, database, p.As); err != nil {
return err
}
if err := a.pg.EnsureExtensions(ctx, database, extensions); err != nil {
return err
}
// The active mark: a login locked before ADR 0570 and asked for again loses its mark to delete here, its LOGIN
// already set again by the role statement above (novox/hq ADR 0230).
if err := a.pg.MarkActive(ctx, p.As, p.Consumer); err != nil {
return err
}
a.announce("database.provisioned", map[string]string{"consumer": p.Consumer, "database": database, "user": p.As})
return nil
}
// Retire removes the login on the operator's yes (novox/hq ADR 0570 clause 5, ADR 0114 §2): what it owns is handed
// to its keeper, its sessions end and the login is dropped; the database and its data are kept (retire_pg.go).
// Deleting is `cleanup delete`, a person's act; taking a database out of service by hand is postgres_retire_database,
// which renames rather than drops.
func (a provisioner) Retire(ctx context.Context, as string, _ map[string]any, why string, at time.Time) error {
if err := a.pg.RetireRole(ctx, as, why, at); err != nil {
return err
}
a.announce("database.deprovisioned", map[string]string{"database": as, "kept": "true", "retired": "true"})
return nil
}
// Inventory is what this server holds that the mesh made (retire_pg.go).
func (a provisioner) Inventory(ctx context.Context) (harness.Inventory, error) {
return a.pg.Inventory(ctx)
}
// Delete drops a retired consumer's database and role, or a database set aside — a person's act.
func (a provisioner) Delete(ctx context.Context, r harness.Retired) (int64, error) {
return a.pg.DeleteRetired(ctx, r)
}
// Holds is asked every minute: whether the consumer can still log in as the mesh gave it, and finds
// the extensions it asked for, so a login or extension lost behind the provisioner's back is made
// again (novox/hq issue 120).
func (a provisioner) Holds(ctx context.Context, p harness.Provision) (bool, error) {
extensions, err := Extensions(p.Values)
if err != nil {
return false, err
}
return a.pg.Holds(ctx, p.As, p.As, p.Password, extensions)
}