Documentation
¶
Overview ¶
Package bootstrap manages shared infrastructure services that auto-deploy when a datacenter is created. Two contribution paths converge into a single reconciled set per datacenter:
Declarative — operators put a slice of BootstrapServiceSpec on the Datacenter row (e.g. "every k8s cluster ships with a fluent-bit DaemonSet"). Static, persisted, edited like any other config.
Programmatic — extensions register Hook implementations via the Registry; each hook self-filters by datacenter shape and contributes BootstrapServiceSpec entries (e.g. the network extension auto-installing cert-manager on every k8s datacenter).
The reconciler unions both sources, dedupes by Spec.Name (declarative wins on conflict), and drives the resulting set toward Running.
Tenant ownership ¶
BootstrapWorkloads are *not* tenant-scoped. They belong to the datacenter, are managed by the platform under synthesized system claims, and are addressable only via system-admin-gated read paths. This is the documented exception to the tenant-scoping rule in CLAUDE.md — the workloads represent platform infrastructure shared across all tenants of a datacenter, not tenant payload.
Lifecycle (eventually consistent) ¶
Datacenter.Service.Create returns synchronously after the row is inserted; bootstrap reconciliation does not block create. The reconciler worker (worker/reconciler.go) walks every datacenter on each tick and calls bootstrap.Service.Reconcile, which:
- inserts pending rows for desired-but-missing services and calls provider.Provision;
- retires rows whose spec is no longer desired (Deprovision + row delete);
- retries failed rows with an attempts counter for backoff.
Idempotent: re-running Reconcile with no spec change is a no-op.
Phase 1 scope ¶
The current implementation persists to the in-memory store only. Phase 2 adds postgres/sqlite/mongo/badger backends and the datacenter-Update diff path. Phase 3 adds the dashboard panel.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func NewService ¶
func NewService(store Store, providers *provider.Registry, hooks *Registry, events event.Bus) *service
NewService wires a bootstrap service. All four dependencies are required:
- store: persists BootstrapWorkload rows (memory-backed in Phase 1; persistent backends land in Phase 2).
- providers: dispatch target for Provision / Deprovision.
- hooks: programmatic-contribution path; Hooks().Services(...) called every reconcile tick.
- events: reconcile lifecycle events surface here for the audit log + dashboard. Reuses event.DeployStarted / DeploySucceeded / DeployFailed with payload.kind = "bootstrap".
Types ¶
type BootstrapServiceSpec ¶
type BootstrapServiceSpec struct {
Name string `db:"name" json:"name"`
Kind provider.WorkloadKind `db:"kind" json:"kind,omitempty"` // default deployment
Replicas int `db:"replicas" json:"replicas,omitempty"` // default 1
Services []provider.ServiceSpec `db:"services" json:"services"`
Labels map[string]string `db:"labels" json:"labels,omitempty"`
}
BootstrapServiceSpec is the declarative shape carried on the datacenter row and returned by hooks. Re-uses the multi-service primitive from provider.ServiceSpec so a bootstrap workload can be a Main + Sidecars + Inits group exactly like a tenant workload.
Name is unique per datacenter and is the identity key the reconciler uses to dedupe declarative + hook contributions and to match desired against current rows.
type BootstrapWorkload ¶
type BootstrapWorkload struct {
ctrlplane.Entity
DatacenterID id.ID `db:"datacenter_id" json:"datacenter_id"`
Name string `db:"name" json:"name"`
Kind provider.WorkloadKind `db:"kind" json:"kind"`
Services []provider.ServiceSpec `db:"services" json:"services"`
State State `db:"state" json:"state"`
ProviderRef string `db:"provider_ref" json:"provider_ref,omitempty"`
ServiceRefs map[string]string `db:"service_refs" json:"service_refs,omitempty"`
LastError string `db:"last_error" json:"last_error,omitempty"`
Attempts int `db:"attempts" json:"attempts"`
Labels map[string]string `db:"labels" json:"labels,omitempty"`
}
BootstrapWorkload is one running bootstrap row. Detached from tenants — owned by a datacenter, managed by the platform, reconciled toward the union of declarative + hook-contributed specs.
func NewBootstrapWorkload ¶
func NewBootstrapWorkload() *BootstrapWorkload
NewBootstrapWorkload mints a fresh bootstrap row with a TypeID and timestamps. State starts as Pending — the reconciler advances it on the next tick.
type DatacenterInfo ¶
type DatacenterInfo struct {
ID id.ID
ProviderName string
Region string
Zone string
Labels map[string]string
}
DatacenterInfo is the slice of datacenter state the bootstrap package needs to decide what to install. Lives here (not in the datacenter package) so bootstrap stays a leaf package — the datacenter package imports bootstrap, never the other way round.
type Hook ¶
type Hook interface {
// Name returns a stable identifier for this hook. Used for
// logging, audit events, and to dedupe re-registrations of
// the same hook (Register replaces, never duplicates).
Name() string
// Services returns the bootstrap specs this hook contributes
// for the given datacenter. Returning nil or empty means the
// hook has nothing to contribute for this datacenter.
//
// An error from Services is logged and the hook is treated as
// having contributed nothing for this tick — a broken hook
// does not block other hooks or wipe the desired set.
Services(ctx context.Context, dc DatacenterInfo) ([]BootstrapServiceSpec, error)
}
Hook is the programmatic contribution path for bootstrap services. Extensions implement Hook to inject shared services into datacenters matching the hook's criteria — the network extension's cert-manager install, the telemetry extension's fluent-bit DaemonSet, and so on.
Hooks self-filter by inspecting DatacenterInfo (provider name, region, labels). A hook that only applies to k8s clusters returns nil for docker / nomad datacenters. The reconciler does not pre- filter — it calls every registered hook on every datacenter every tick.
Idempotency contract ¶
Hooks must return the same Spec.Name across reconcile calls when they want to keep a service installed. Same Name + different Spec body = update; missing Name = retire. The reconciler keys off Name to match desired against current rows.
Hook implementations must be safe for concurrent calls — the reconciler may invoke the same hook against multiple datacenters in parallel in future phases.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry is the set of currently-registered hooks. Thread-safe; callers register at app construction (typically via the app.WithBootstrapHook option) and the reconciler reads on every tick.
type Service ¶
type Service interface {
// Reconcile drives one datacenter's bootstrap state forward by
// one step. Idempotent: safe to call repeatedly. The caller
// supplies:
//
// - dc: the datacenter projection used to address the
// provider and (via DatacenterInfo) feed registered hooks.
// - declared: the operator-declared spec list from the
// datacenter row. May be nil for datacenters that rely
// entirely on hooks.
//
// Reconcile reads the registered hook set, unions hook-
// contributed specs with `declared`, dedupes by Spec.Name
// (declared wins on conflict), then diffs against the current
// row set and walks each delta toward the desired state.
Reconcile(ctx context.Context, dc DatacenterInfo, declared []BootstrapServiceSpec) error
// ListByDatacenter returns the bootstrap workloads attached to
// a datacenter. Read-only.
ListByDatacenter(ctx context.Context, datacenterID id.ID) ([]*BootstrapWorkload, error)
// Get returns a single bootstrap workload by ID. Read-only.
Get(ctx context.Context, bootstrapID id.ID) (*BootstrapWorkload, error)
}
Service drives bootstrap workloads from desired toward observed.
All write paths run under the platform's authority (no tenant claims) — the service is invoked from the reconciler worker, never from a tenant-driven request. Read paths intended for the dashboard panel go through Get / ListByDatacenter and are gated upstream by the API handler on system:admin claims.
type State ¶
type State string
State is the lifecycle state of a single bootstrap workload row.
The progression for a healthy install is:
pending → provisioning → running
On retire (operator removes the spec or a hook stops contributing it):
running → retiring → retired (then row deleted)
On transient provider failure:
provisioning → failed → provisioning (retry) → running
The reconciler is the only writer of this field outside Insert.
const ( // StatePending — row inserted, no provider call has run yet. StatePending State = "pending" // StateProvisioning — provider.Provision is in flight. StateProvisioning State = "provisioning" // StateRunning — provider acknowledged the workload is live; // ProviderRef and ServiceRefs populated. StateRunning State = "running" // StateFailed — last reconcile attempt errored; LastError + Attempts // populated. Next tick retries. StateFailed State = "failed" // StateRetiring — desired set no longer contains this spec; // Deprovision is in flight. StateRetiring State = "retiring" // StateRetired — Deprovision returned successfully. The reconciler // will delete the row immediately on the same tick. StateRetired State = "retired" )
type Store ¶
type Store interface {
// InsertBootstrap persists a new bootstrap workload row.
InsertBootstrap(ctx context.Context, bw *BootstrapWorkload) error
// GetBootstrap returns a bootstrap workload by ID.
GetBootstrap(ctx context.Context, bootstrapID id.ID) (*BootstrapWorkload, error)
// GetBootstrapByName returns the row whose (DatacenterID, Name)
// pair matches. Used by the reconciler to match desired specs
// against current rows.
GetBootstrapByName(ctx context.Context, datacenterID id.ID, name string) (*BootstrapWorkload, error)
// ListBootstraps returns every bootstrap workload attached to
// the given datacenter. Read path for the reconciler and the
// dashboard panel.
ListBootstraps(ctx context.Context, datacenterID id.ID) ([]*BootstrapWorkload, error)
// UpdateBootstrap persists changes to an existing row.
UpdateBootstrap(ctx context.Context, bw *BootstrapWorkload) error
// DeleteBootstrap removes a row. Called by the reconciler after
// Deprovision returns successfully on a retiring workload.
DeleteBootstrap(ctx context.Context, bootstrapID id.ID) error
}
Store is the persistence interface for BootstrapWorkload entities. Unlike the rest of the codebase's stores, methods here do NOT take a tenantID — bootstrap workloads are platform-owned and addressable only by their own ID or by the parent datacenter.