bootstrap

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Sep 14, 2026 License: MIT Imports: 27 Imported by: 0

Documentation

Overview

Package bootstrap is the Go port of the framework-level cluster infrastructure addon system (.lok8s/libs/bootstrap, all 1413 lines — bash wins on any divergence).

It applies spec.bootstrap entries after the cluster is provisioned. Works with ALL drivers (Lo, KubeOne, Capi, Kkp) — not driver-specific.

Entries form a DAG and apply CONCURRENTLY (capped) — ordering edges come from `dependsOn: [name, …]` and `wait: true`; semantics on Engine.Apply.

Addon resolution:

"cilium"          → .lok8s/addons/cilium/
"./targets/foo"   → clusters/<domain>/targets/foo/
"/absolute/path"  → /absolute/path/

Provider-aware values:

addons/cilium/values.yaml          — base (always loaded)
addons/cilium/values.lo.yaml       — driver-specific (if exists)
addons/cilium/values.hetzner.yaml  — provider-specific (if exists)

Per-entry overrides (map form): the reserved keys values / valueFiles / env / wait / dependsOn / name, plus the legacy whole-map-is-helm-values shim — full schema at ParseEntry. Effective helm-values stack: base < driver < provider < valueFiles < values:.

Index

Constants

This section is empty.

Variables

View Source
var ErrEntriesFailed = ui.Handled(errors.New("bootstrap: one or more entries failed or were skipped"))

ErrEntriesFailed is Apply's bare non-zero exit (bash: `return 1` at the bottom of bootstrap::apply — the per-entry errors were already printed).

View Source
var PlatformOwned = []string{"cilium", "ccm"}

PlatformOwned lists the addon DIRS that must never bootstrap onto a HOSTED cluster (the platform owns them there). Extend here, nowhere else (bash: BOOTSTRAP_PLATFORM_OWNED="cilium ccm").

Functions

func ApplyHook

func ApplyHook(p *config.Paths, r execx.Runner, stdout, stderr io.Writer) func(ctx context.Context, domain, clusterYAML, kubeconfig string) error

ApplyHook returns the provision dispatch's Hooks.BootstrapApply seam — the function provision.Dispatcher calls as bootstrap::apply on both the full-provision and --bootstrap paths. The engine is built per call so each invocation reads the decision env (LOK8S_FORCE_RECREATE, LOK8S_BOOTSTRAP_ONLY, LOK8S_BOOTSTRAP_PARALLEL) at apply time, exactly like the sourced bash lib did.

func InlineValues

func InlineValues(p *config.Paths, stderr io.Writer, domain, clusterYAML, addon string) (string, error)

InlineValues returns the cluster spec's merged inline values for ONE addon, by the SAME semantics the bootstrap applies (bash: bootstrap::inline_values — both entry shapes, `values:` + `valueFiles:` pre-merge, cluster-dir resolution). Returns "" when the spec has no entry for the addon or the entry carries no values. The kubeone driver's render_addons reads this so an addon it renders for `kubeone apply` carries the SAME values the bootstrap would overlay (issue #157: an inline-only value silently reverted at upgrade time). A parse error is a hard error, never a silent empty.

func RecreatePrompt

func RecreatePrompt(count int, list string, stuckNS []string) string

RecreatePrompt composes the batched recreate prompt (bash: bootstrap::_recreate_prompt). Split out because this text IS the safety control — the single consent gate for every heal in the batch — so it has to be assertable rather than buried in a tty write.

The prompt REPLACES kapply's pointed per-object confirms for accepted entries (they re-apply under force, which returns 0 from both the sealed-Secret and the ns-finalize confirm without asking). Everything those confirms would have said must therefore be said here, or it is not said at all.

func ResolveEntries

func ResolveEntries(clusterYAML, kind string) ([]string, error)

ResolveEntries resolves which bootstrap addon entries to apply, one compact-JSON string per element (bash: bootstrap::_resolve_entries — the exact `yq -o=json -I=0 '.spec.bootstrap[]?'` stream shape, so map entries stay one element and YAML comments are gone). Pure (no cluster access). The cases and the per-driver default are bootstrapspec.Resolve's; the lo default is the bare word `cilium`, matching the bash `echo "cilium"`.

Types

type Dispatcher

type Dispatcher struct {
	Engine *Engine

	// Drivers overrides the driver lookup (nil → the package registry,
	// driver.Get) — same seam as provision.Dispatcher.
	Drivers func(name string) (driver.Factory, bool)

	// InventoryPublish is inventory::publish, refreshed after a standalone
	// `lo bootstrap` too (same tail as provision::dispatch — fail-soft,
	// probed: bash only calls it when the inventory lib is loaded, so a
	// nil hook is SKIPPED).
	InventoryPublish func(ctx context.Context, domain, clusterYAML, kubeconfig string)
}

Dispatcher drives the standalone `lo bootstrap` core (bash: bootstrap::dispatch — factored out of main::bootstrap so it is testable without the argsh :args builtin, mirroring provision::dispatch).

func (*Dispatcher) Dispatch

func (d *Dispatcher) Dispatch(ctx context.Context, domainName string) error

Dispatch runs the standalone `lo bootstrap <domain>` flow.

CRITICAL ordering (bash comment preserved): the domain's driver export runs BEFORE Apply — it populates the spec-derived env (LOK8S_SPEC_*) that spec.bootstrap addons reference via envsubst; without it they render those values empty. Same order provision::dispatch uses.

type Engine

type Engine struct {
	Paths  *config.Paths
	Runner execx.Runner
	Stdout io.Writer
	Stderr io.Writer

	// ApplyOne overrides the per-entry apply (tests stub it exactly like
	// the bats redefine bootstrap::_apply_one). nil → the real applyOne.
	// Returns the entry's exit code (0 ok, non-zero failed).
	ApplyOne func(ctx context.Context, job Job, stdout, stderr io.Writer) int

	// Interactive/Ask are the batched-recreate prompt's tty seams (bash:
	// bootstrap::_interactive / bootstrap::_ask). One place touches
	// /dev/tty, which is what makes the prompt TEXT assertable in a test.
	Interactive func() bool
	Ask         func(prompt string) bool

	// Sleep is the retry/backoff seam (tests install clock.NoSleep). nil =
	// clock.Sleep, which ends with the context.
	Sleep clock.SleepFunc

	// SopsDecrypt decrypts one restore.d/*.sops.yaml in memory (nil → the
	// secrets package's sops library decrypt — NEVER a sops|kubectl pipe).
	SopsDecrypt func(path string) ([]byte, error)
}

Engine drives the bootstrap addon system. Zero-value fields fall back to the same env/tty defaults the bash implementation read.

func (*Engine) Apply

func (e *Engine) Apply(ctx context.Context, domain, clusterYAML, kubeconfig string) error

Apply reads spec.bootstrap from the cluster YAML and runs the entries as a topological-parallel DAG capped at LOK8S_BOOTSTRAP_PARALLEL (default 8) — bash: bootstrap::apply. Edge semantics:

  • `dependsOn: [name, …]` — an explicit edge: wait for those entries' READINESS.
  • `wait: true` — a GLOBAL gate: it depends on ALL entries before it, and every entry after it depends on it.

An entry waits for its own workloads to be Ready IFF something depends on it OR it is a wait-gate; a pure leaf just applies and is done.

FAILURE is isolated to the failed entry's SUBTREE — not the whole run: only its TRANSITIVE dependents are skipped; every unrelated entry still applies. A failed wait-gate skips everything after it automatically. This is deliberate bootstrap policy: apply as much as possible, then report non-zero if anything failed or was skipped (so the caller re-runs `lo up` to converge).

type Entry

type Entry struct {
	// Raw is the compact-JSON entry as resolved (used verbatim in the
	// "addon not found" error, like bash's ${entry}).
	Raw string
	// Name is the entry identity: basename / map-key, or the explicit
	// `name:` override.
	Name string
	// Dir is the resolved addon directory (never changed by `name:`).
	Dir string
	// Inline is the merged inline helm values as YAML ("" when none;
	// "null" for an explicit `values: null`, matching yq -r).
	Inline string
	// EnvLines is the newline-separated KEY=value envsubst overrides
	// ("" when none) — the exact shape bash hands _apply_one.
	EnvLines string
	// Wait marks a global barrier gate (`wait: true`).
	Wait bool
	// Deps are the dependsOn entry names, in order.
	Deps []string
	// Explicit reports whether Name came from an explicit `name:` override
	// (a name collision on it is a hard error, not a tolerated clash).
	Explicit bool
	// Builtin reports a bare framework-addon entry (Dir resolved through
	// internal/assets: the project's .lok8s/addons/<name> when present, else
	// the copy embedded in the binary) as opposed to a cluster-local target
	// or an absolute path.
	Builtin bool
}

Entry is one parsed spec.bootstrap entry (bash: the out-params of bootstrap::_parse_entry).

func ParseEntry

func ParseEntry(p *config.Paths, stderr io.Writer, domain, entry string) (*Entry, error)

ParseEntry parses ONE spec.bootstrap entry — the compact JSON from ResolveEntries — into the fields the apply path needs (bash: bootstrap::_parse_entry; the shared reader is internal/bootstrapspec, the schema doc lives there). Returns an error (after printing it) on a malformed entry or on `values:` set against a non-chart target. The valueFiles pre-merge (files in list order, inline `values:` on top) uses the SAME deep-merge idiom addons.Render stacks values with (maps deep-merge, lists REPLACE); the result rides render's existing inline-values arg.

type Job

type Job struct {
	Name     string
	Dir      string
	Kind     string
	Provider string
	// Kubeconfig is the --kubeconfig path threaded through every kubectl.
	Kubeconfig string
	// Inline is the merged inline helm values ("" when none).
	Inline string
	// Wait is set when the scheduler wants the post-apply readiness wait:
	// a dep-target or a wait-gate. A pure leaf runs without it.
	Wait bool
	// Hosted marks a hosted cluster: the platform owns the CNI and the
	// cloud integration, so those addons are skipped.
	Hosted bool
	// BootstrapOnly is LOK8S_BOOTSTRAP_ONLY=1: the KubeOne driver did not
	// apply cilium/ccm on this run, so the engine reconciles them.
	BootstrapOnly bool
	// EnvLines is the newline-separated KEY=value envsubst overrides.
	EnvLines string
	// Force re-applies under LOK8S_FORCE_RECREATE=1 semantics (the
	// resolve-parked foreground heal).
	Force bool
	// NonInteractive marks a backgrounded apply (bash: the exported
	// LOK8S_NONINTERACTIVE=1 on every concurrent subshell — no shared
	// /dev/tty, so kapply never prompts and never draws).
	NonInteractive bool
}

Job is one scheduled bootstrap entry as handed to the per-entry apply (bash: the argv of bootstrap::_apply_one).

Jump to

Keyboard shortcuts

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