provisioning

package
v1.801.433 Latest Latest
Warning

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

Go to latest
Published: Aug 4, 2026 License: Apache-2.0 Imports: 42 Imported by: 0

Documentation

Overview

Package provisioning is one-click data add-ons: a SQL, key-value, document, vector, search or object store, wired straight into your app.

It turns "create a database" into a real logical resource inside an already-live, shared product backend, per the unified /v1 binary (HIP-0106).

One HTTP surface, seven kinds, two strategies:

DEDICATED-instance — the four on-demand data add-ons. Each org OWNS its instance (an operator Datastore CR in tenant-<org>), so its admin credential is naturally tenant-scoped; the assembled DSN is injected as <KIND>_URL into the app instance's addons Secret, switching it off Base onto the backend:

kv        -> Hanzo KV        Datastore type=valkey       kv://…:6379
sql       -> Hanzo SQL       Datastore type=postgresql   postgres://…:5432
docdb     -> Hanzo DocDB     Datastore type=docdb        mongodb://…:27017
datastore -> Hanzo Datastore Datastore type=datastore    datastore://…:8123

SHARED-logical — a logical resource inside an already-live shared backend:

vector    -> Qdrant      vector.hanzo.svc:6333    PUT /collections/{name}
search    -> Meilisearch search.hanzo.svc:7700    POST /indexes
s3        -> S3/SeaweedFS     s3.hanzo.svc:9000        MakeBucket

Tenancy: every request is scoped to the gateway-minted org (X-Org-Id / c.Org()). Empty org is rejected 403 unless the caller is an admin. The physical resource on the shared backend is namespaced "o"<hash(org)>_<name> with a FIXED-WIDTH org hash, so the org→name boundary is unambiguous and two distinct tenants can never fold onto one backend resource. A global UNIQUE(physical_name) guard makes any residual fold fail closed with 409.

Secrets: generated per-resource passwords are sealed in Hanzo KMS (client-side encrypted) and only a secret_ref is persisted. When KMS is not configured the service degrades safely — it returns the password once in the create response and stores NOTHING in plaintext. See kms.go.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BucketName

func BucketName(org, name string) string

BucketName + BucketPrefix export the tenant→S3-bucket naming so the ONE convention is shared, not re-implemented. clients/s3 (the /v1/s3 file manager) operates on the SAME buckets this control plane allocates for kind "s3", so a bucket provisioned via POST /v1/s3 {name:x} is browsable there as bucket "x". The two subsystems MUST derive the S3 bucket name identically or the tenant boundary drifts between "allocate" and "operate" — and worse, a file manager using the raw physical name would try to create an underscore-containing bucket that S3 rejects. The full derivation is bucketName(physicalName(org,name)): the fixed-width org-hash prefix makes it injective in (org,name); the '_'→'-' fold makes it a DNS-safe S3 name. clients/s3 imports provisioning for exactly these; the dependency is one-directional (provisioning never imports s3), so no cycle.

func BucketPrefix

func BucketPrefix(org string) string

BucketPrefix is the S3-bucket-name prefix ALL of an org's buckets share (== bucketName("o"+orgHash(org))+"-"). clients/s3 lists all buckets and filters to this prefix (the caller only ever sees its own), then strips it to recover friendly names. Derived through bucketName so it matches the real bucket names exactly, INCLUDING the '_'→'-' fold of the org-hash separator.

func Mount

func Mount(app cloud.Router, deps cloud.Deps) error

Mount wires the provisioning surface onto app per HIP-0106. It is the complex flavour of the generic subsystem: it keeps a package global (mounted) for cross-package reach and starts a recurring footprint meter, so it constructs the Service value directly rather than through cloud.Mount.

func Shutdown

func Shutdown(context.Context) error

Shutdown closes the provisioning metadata store. Idempotent. Mirrors the plan Shutdown contract so the serve layer can release subsystem resources uniformly.

Types

type Provisioner

type Provisioner interface {
	Create(ctx context.Context, physicalName, user, password string) (connString, host string, port int, db string, err error)
	Drop(ctx context.Context, physicalName, user string) error
}

Provisioner creates and drops one kind of logical resource inside a shared, already-live backend. Create receives the namespaced physical name plus a per-resource user + password (the handler generates these); it returns a client connection string, the public host/port of the backend service, and the logical database/collection/bucket name. Backends without per-resource auth (Qdrant, Meilisearch, S3) ignore user/password and return an empty username via the handler's kind map.

type Resource

type Resource struct {
	ID           string
	Org          string
	Kind         string
	Name         string
	PhysicalName string
	SecretRef    string
	Host         string
	Port         int
	Username     string
	DBName       string
	Status       string
	CreatedAt    int64
	// Size is the declared storage footprint of a DEDICATED-instance resource
	// (e.g. "10Gi"), empty for the shared-logical kinds. It is the live-size
	// source the recurring footprint meter multiplies into a per-org GB-time
	// charge — so an instance the org runs is billed for what it reserves.
	Size string
	// Instance binds a DEDICATED-instance resource to the app instance whose
	// on-demand add-on it is (e.g. "commerce"). When set, the assembled DSN is
	// injected as <KIND>_URL into the Secret "<instance>-addons" in tenant-<org>,
	// so that instance switches off Base onto this backend; drop removes it and
	// the instance reverts to Base. Empty = not instance-bound (the DSN is only
	// returned once, wired by the caller) — the pre-instance-binding behavior, so
	// every existing provision is unchanged.
	Instance string
}

Resource is one row of provisioned_resources: the control-plane record for a logical resource (database, bucket, collection, …) created inside a shared backend. It never carries the plaintext password — only secret_ref, the KMS key under which the password is sealed.

type Store

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

Store is the provisioning metadata database. ONE SQLite file — the system namespace's "provisioning" — holds every org's records; tenant isolation is by the org column, enforced at the query layer. MaxOpenConns(1) serializes access so multi-step writes never race the SQLite file lock.

func (*Store) Close

func (s *Store) Close() error

Close closes the underlying database.

func (*Store) Delete

func (s *Store) Delete(ctx context.Context, org, kind, name string) (bool, error)

Delete removes the resource row inside a transaction. Reports whether a row was actually deleted.

func (*Store) Get

func (s *Store) Get(ctx context.Context, org, kind, name string) (Resource, error)

Get returns the resource for (org,kind,name) or errNotFound.

func (*Store) Insert

func (s *Store) Insert(ctx context.Context, r Resource) error

Insert writes one resource row inside a transaction. A UNIQUE(org,kind,name) OR UNIQUE(physical_name) violation surfaces as errConflict so the caller can roll back the backend side-effects it already performed.

func (*Store) List

func (s *Store) List(ctx context.Context, org, kind string) ([]Resource, error)

List returns every resource of kind for org, oldest first.

func (*Store) ListAllByStatus

func (s *Store) ListAllByStatus(ctx context.Context, status string) ([]Resource, error)

ListAllByStatus returns every row (across ALL orgs) in the given status. It is the read side of the recurring footprint meter: the sweep lists every "ready" row and charges each dedicated instance's OWN org. Rows carry Org/Kind/Size so the caller attributes and prices without a second lookup.

func (*Store) ListByInstance

func (s *Store) ListByInstance(ctx context.Context, org, instance string) ([]Resource, error)

ListByInstance returns every resource an org has bound to one app instance, oldest first — the set of on-demand add-ons active for that instance (each is one <KIND>_URL projected into the instance's addons Secret). Scoped to (org, instance) so it can never surface another tenant's bindings.

func (*Store) PhysicalExists

func (s *Store) PhysicalExists(ctx context.Context, physical string) (bool, error)

PhysicalExists reports whether ANY org already owns the given physical backend name. This is the global (cross-org) uniqueness pre-check: paired with the UNIQUE(physical_name) index it lets the handler fail closed with 409 BEFORE it touches a backend, so a residual name-fold (or hash collision) can never silently provision over another tenant's physical resource.

func (*Store) UpdateStatus

func (s *Store) UpdateStatus(ctx context.Context, org, kind, name, status string) (bool, error)

UpdateStatus advances one resource's status (e.g. dedicated "provisioning" -> "ready" once the operator reports the instance is up). Scoped to (org,kind, name) so it can never touch another tenant's row. Reports whether a row moved.

Jump to

Keyboard shortcuts

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