bootstrap

package
v1.5.2 Latest Latest
Warning

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

Go to latest
Published: Jun 11, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

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:

  1. 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.

  2. 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.

func NewRegistry

func NewRegistry() *Registry

NewRegistry constructs an empty Registry.

func (*Registry) Hooks

func (r *Registry) Hooks() []Hook

Hooks returns a snapshot of the currently-registered hooks. Safe for the caller to iterate without holding any lock.

func (*Registry) Register

func (r *Registry) Register(h Hook)

Register adds a hook to the registry, keyed by Name. Re-registering a hook with the same Name replaces the previous entry — used during hot-reload of an extension.

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.

Jump to

Keyboard shortcuts

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