Documentation
¶
Overview ¶
Package operator is the Go port of the shell-operator hooks (operator/hooks/*.sh, frozen at .archive/legacy/operator/hooks/): the three reconcilers behind `lo operator <hook>`, which the hook shims exec.
The shell-operator hook contract, unchanged: `--config` prints the binding configuration (byte-identical to the bash heredocs — pinned against testdata/*.config.yaml, which were generated ONCE from the bash hooks); otherwise the hook reads $BINDING_CONTEXT_PATH, a JSON array of events, and reconciles. Every kubectl/clusterctl call runs through the execx.Runner seam with the exact argv the bash built, so the recorded call log in a test IS the bash KLOG assertion.
Bash wins: where the bash body did something odd (patch bodies, log prefixes, fall-through on a failed sub-step), the port does the same and the comment says so. The one structural difference is how the hook bodies reach the framework: the bash sourced the driver + libs into its shell and called driver::provision / bootstrap::apply / gitops::bootstrap / deploy::apply; the Go calls the ported packages (internal/driver/lo, internal/bootstrap, internal/gitops, internal/deploy) through the same injectable seams the dispatch layer uses.
Index ¶
Constants ¶
const ( // DefaultHookDir is where the image mounts the hook tree; the framework // pieces the bash sourced live under it (lib/, drivers/, addons/, // capi-templates/ …), so it doubles as PATH_LOK8S. DefaultHookDir = "/hooks" // DefaultStateDir is LOK8S_STATE_DIR's default: the writable volume laid // out like a lok8s project (clusters/<domain>/cluster.lok8s.yaml, // .kubeconfig/<cluster>.yaml). DefaultStateDir = "/var/lib/lok8s" // DefaultKustomizePluginHome is the image's khelm plugin root. DefaultKustomizePluginHome = "/usr/local/kustomize-plugins" )
Defaults of the operator runtime (runtime.sh + operator/Dockerfile).
const CapiFinalizer = "lok8s.dev/capi-teardown"
CapiFinalizer intercepts deletion so the workload cluster is torn down before the Capi CR vanishes (otherwise the applied CAPI Cluster + machines + infra are orphaned and keep the cloud cluster — and its bill — alive).
const LoFinalizer = "lok8s.dev/lo-teardown"
LoFinalizer guards Lo deletion until the driver tore the cluster down.
Variables ¶
var ErrHandled = ui.ErrHandled // one sentinel for every package; see internal/ui
ErrHandled marks a failure whose message was already printed on stderr (the CLI exits 1 without printing more).
Functions ¶
func BuildStatusPatch ¶
BuildStatusPatch renders the Capi status patch for one CAPI status (the jq construction in hook::trigger), pretty-printed as jq prints it.
Types ¶
type CapiHook ¶
type CapiHook struct {
Paths *config.Paths
Runner execx.Runner
Stdout io.Writer
Stderr io.Writer
// TemplateDir is the CAPI template tree (bash: ${HOOK_DIR}/capi-templates;
// Env.CapiTemplateDir in the CLI).
TemplateDir string
// contains filtered or unexported fields
}
CapiHook reconciles Capi CRs.
type CapiStatusSyncHook ¶
type CapiStatusSyncHook struct {
Paths *config.Paths
Runner execx.Runner
Stdout io.Writer
Stderr io.Writer
// GitopsBootstrap is gitops::bootstrap (gitops.BootstrapHook in the
// CLI). nil = the lib is not loaded: the bash `declare -f` probe then
// skips BOTH the bootstrap and the gitops status patch.
GitopsBootstrap func(ctx context.Context, domain, provider string) error
// DeployApply is deploy::apply (deploy.Deployer.Apply in the CLI). nil =
// not loaded → probed away, like the bash.
DeployApply func(ctx context.Context, domain string) error
}
CapiStatusSyncHook bridges CAPI Cluster status → Capi CR status.
func (*CapiStatusSyncHook) Config ¶
func (h *CapiStatusSyncHook) Config() string
Config implements Hook.
type Env ¶
type Env struct {
// HookDir is the hook tree (bash: RUNTIME_DIR / HOOK_DIR → PATH_LOK8S).
HookDir string
// StateDir is the reconcile state volume (bash: LOK8S_STATE_DIR →
// PATH_BASE).
StateDir string
// KustomizePluginHome is KUSTOMIZE_PLUGIN_HOME (defaulted, never
// overridden when set).
KustomizePluginHome string
}
Env is the operator runtime layout (runtime.sh's framework-env block).
func ResolveEnv ¶
func ResolveEnv() *Env
ResolveEnv reads the runtime layout from the process environment: PATH_LOK8S (the hook tree; the bash derived it from the hook file's own directory, which the Go binary — installed under /usr/local/bin — cannot), LOK8S_STATE_DIR and KUSTOMIZE_PLUGIN_HOME, each with the runtime.sh / Dockerfile default.
func (*Env) CapiTemplateDir ¶
CapiTemplateDir is where the image copies the CAPI templates for the capi-reconcile hook (Dockerfile: `COPY .lok8s/drivers/capi/cluster/ /hooks/capi-templates/`; bash: `${HOOK_DIR}/capi-templates`).
func (*Env) Export ¶
Export mirrors runtime.sh's exports + mkdirs: the framework env every subprocess (kubectl, kustomize plugins, the lo driver's tools) and every ported lib reading os.Getenv sees. PATH_SECRETS is the runtime's flat store under the state volume (runtime.sh: `${PATH_BASE}/.secrets`).
func (*Env) Paths ¶
Paths is the project layout the framework packages read, over the state volume (runtime.sh: PATH_BASE=state, PATH_CLUSTERS=state/clusters, PATH_LOK8S=hook dir). Bin points below the state dir — the image has no b-managed toolchain; execx falls back to PATH, where the image's tools live.
type Event ¶
type Event struct {
// Type is the event type: "Synchronization", "Schedule", "Event" — the
// bash read `.type // "Event"`, so a missing type is an Event.
Type string `json:"type"`
// Binding is the binding name (schedule bindings carry it).
Binding string `json:"binding"`
// Object is the watched object (after the jqFilter, for kubernetes
// bindings).
Object json.RawMessage `json:"object"`
// FilterResult is the jqFilter's output (capi-status-sync reads it).
FilterResult json.RawMessage `json:"filterResult"`
}
Event is one entry of the shell-operator binding context. Only the fields the hooks read are decoded; the rest of the entry is ignored, as the jq paths ignored it.
func ReadBindingContext ¶
ReadBindingContext reads the events file shell-operator hands the hook. An unset BINDING_CONTEXT_PATH is the bash `set -u` abort (unbound variable, exit 1); an unreadable file is jq's "Could not open file" (exit 2); a non-JSON file is jq's parse error (exit 5). The message goes to stderr here and the error carries the status (ExitError).
type ExitError ¶
type ExitError struct{ Code int }
ExitError carries the process exit status a bash hook would have ended with (its message is already on stderr): the `set -u` abort is 1, a jq failure is jq's own status — 2 for a usage/system error (file not found, a non-JSON --argjson), 5 for invalid JSON input.
type Hook ¶
type Hook interface {
// Config is the `--config` output (hook::config).
Config() string
// Trigger handles one binding-context batch (hook::trigger).
Trigger(ctx context.Context, events []Event) error
}
Hook is one shell-operator hook.
type LoHook ¶
type LoHook struct {
Paths *config.Paths
Runner execx.Runner
Stdout io.Writer
Stderr io.Writer
// Drivers overrides the driver lookup (nil → driver.Get). The bash
// sourced drivers/lo/main unconditionally — the hook is the Lo
// reconciler, so the kind is fixed: "lo".
Drivers func(name string) (driver.Factory, bool)
// BootstrapApply is bootstrap::apply (bootstrap.ApplyHook in the CLI).
// nil = the lib is not loaded: the bash `driver::provision &&
// bootstrap::apply` then fails on "command not found" and the CR lands
// in ProvisionFailed — same here.
BootstrapApply func(ctx context.Context, domain, clusterYAML, kubeconfig string) error
// contains filtered or unexported fields
}
LoHook reconciles Lo CRs.