provision

package
v0.0.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 16, 2026 License: Apache-2.0 Imports: 7 Imported by: 0

Documentation

Overview

Package provision is the db-per-tenant onboarding control plane (spec 2026-07-03, D4/D7): an idempotent, resumable state machine that creates a tenant's dedicated database, migrates it to head, and flips the catalog entry active — plus the fleet migration roller that upgrades every tenant database in bounded batches.

Provisioning is EXPLICIT (a CLI verb / admin endpoint), never implicit on first write, and fabriq never drops a database: offboarding is Suspend (route-off); the physical drop is a human decision.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ClusterOps

type ClusterOps interface {
	// CreateDatabase ensures the database exists on the cluster.
	CreateDatabase(ctx context.Context, clusterID, database string) error
	// Migrate runs fabriq's migration chain against the database and
	// returns the resulting head version.
	Migrate(ctx context.Context, clusterID, database string) (version string, err error)
}

ClusterOps performs the physical work on a Postgres cluster. Both operations MUST be idempotent — the state machine's resumability (crash between any two steps, re-run converges) rests on it.

type MigrateAllOpts

type MigrateAllOpts struct {
	// Batch is the number of tenant databases migrated concurrently
	// (default 8).
	Batch int
	// MaxFailures stops the roll after this many failed tenants
	// (default 3). The roll is always safe to re-run.
	MaxFailures int
	// TargetVersion skips tenants already recorded at it ("" = always run;
	// the migration chain itself is idempotent).
	TargetVersion string
}

MigrateAllOpts tunes the fleet roller.

type Provisioner

type Provisioner struct {
	// contains filtered or unexported fields
}

Provisioner drives tenant lifecycles against the catalog.

func New

func New(cat catalog.Catalog, ops ClusterOps) *Provisioner

New builds a Provisioner.

func (*Provisioner) MigrateAll

func (p *Provisioner) MigrateAll(ctx context.Context, opts MigrateAllOpts) (Report, error)

MigrateAll walks the catalog and migrates every active tenant database, Batch at a time, stopping once MaxFailures tenants have failed. Each success records the tenant's new version in the catalog.

func (*Provisioner) Provision

func (p *Provisioner) Provision(ctx context.Context, tenantID, clusterID string) (catalog.Entry, error)

Provision creates (or resumes) a tenant's dedicated database and returns the active catalog entry. Idempotent: an already-active tenant returns its entry unchanged; a half-provisioned one resumes from its state (both physical steps are idempotent, so the states are progress markers, not resume points). A CAS conflict means another provisioner holds the tenant — surfaced as CodeVersionConflict for the caller to observe/retry.

func (*Provisioner) Resume

func (p *Provisioner) Resume(ctx context.Context, tenantID string) (catalog.Entry, error)

Resume re-activates a suspended tenant.

func (*Provisioner) Suspend

func (p *Provisioner) Suspend(ctx context.Context, tenantID string) (catalog.Entry, error)

Suspend routes a tenant off (takes effect within the directory TTL). The database is untouched.

type Report

type Report struct {
	Migrated int            `json:"migrated"`
	Skipped  int            `json:"skipped"`
	Failed   int            `json:"failed"`
	Results  []TenantResult `json:"results"`
}

Report summarizes a fleet roll.

type SchemaClusterOps

type SchemaClusterOps interface {
	// EnsureBootstrap prepares a consolidation database ONCE: creates the
	// shared schema and its extensions (pgvector/postgis) so every tenant
	// schema resolves their types via search_path. Idempotent per database.
	EnsureBootstrap(ctx context.Context, clusterID, database, sharedSchema string) error
	// CreateSchema ensures a tenant's schema exists in the consolidation
	// database.
	CreateSchema(ctx context.Context, clusterID, database, schema string) error
	// MigrateSchema runs fabriq's migration chain inside a tenant schema
	// (bare-named DDL + grove_migrations land there via search_path) and
	// returns the resulting head version.
	MigrateSchema(ctx context.Context, clusterID, database, schema, sharedSchema string) (version string, err error)
}

SchemaClusterOps performs the physical work for schema-per-tenant consolidation mode: many tenants share one consolidation database, each isolated by a schema. Every operation MUST be idempotent — the SchemaProvisioner's resumability rests on it.

type SchemaProvisioner

type SchemaProvisioner struct {
	// contains filtered or unexported fields
}

SchemaProvisioner drives tenant lifecycles for schema-per-tenant consolidation mode: many tenants share one consolidation database, each isolated by a schema (spec 2026-07-03 schema design, S6). It mirrors Provisioner's idempotent, resumable state machine (pending → creating → migrating → active) but the physical "creating" step bootstraps the consolidation database once and creates the tenant schema, and "migrating" runs the chain inside that schema.

Placement is explicit: the caller names both the cluster and the consolidation database (S5); the schema is derived deterministically from the tenant id and recorded in Entry.Schema.

func NewSchemaProvisioner

func NewSchemaProvisioner(cat catalog.Catalog, ops SchemaClusterOps, sharedSchema string) *SchemaProvisioner

NewSchemaProvisioner builds a schema-mode provisioner. An empty sharedSchema defaults to "fabriq_shared".

func (*SchemaProvisioner) MigrateAll

func (p *SchemaProvisioner) MigrateAll(ctx context.Context, opts MigrateAllOpts) (Report, error)

MigrateAll upgrades every active tenant's schema in bounded batches, recording each new version — the schema-mode fleet roller.

func (*SchemaProvisioner) Provision

func (p *SchemaProvisioner) Provision(ctx context.Context, tenantID, clusterID, database string) (catalog.Entry, error)

Provision creates (or resumes) a tenant's schema in the named consolidation database and returns the active catalog entry. Idempotent: an already-active tenant returns unchanged; a half-provisioned one resumes (both physical steps are idempotent). A CAS conflict surfaces as CodeVersionConflict.

func (*SchemaProvisioner) Resume

func (p *SchemaProvisioner) Resume(ctx context.Context, tenantID string) (catalog.Entry, error)

Resume re-activates a suspended tenant.

func (*SchemaProvisioner) Suspend

func (p *SchemaProvisioner) Suspend(ctx context.Context, tenantID string) (catalog.Entry, error)

Suspend routes a tenant off (takes effect within the directory TTL).

type TenantResult

type TenantResult struct {
	TenantID string `json:"tenantId"`
	Version  string `json:"version,omitempty"`
	Skipped  bool   `json:"skipped,omitempty"`
	Err      string `json:"error,omitempty"`
}

TenantResult is one tenant's outcome in a fleet roll.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL