Documentation
¶
Overview ¶
Package plan parses HCL boot plans pulled from an OCI registry.
A plan describes one or more targets; each target points at an OCI primary ref (`index`, typically a multi-arch image index whose manifest bundles kernel + initrd + modules + cmdline layers) and/or role-specific refs (`kernel` / `initrd` / `modules`) that override individual layers. Each referenced ref may itself be a multi-arch OCI index; arch resolution happens at pull time, not at plan parse time.
HCL example:
default_target = "primary"
locals {
registry = "_oci._tcp.registry.example.com/boot"
console = arch == "arm64" ? "ttyAMA0" : "ttyS0" # locals see `arch`
}
target "primary" {
version = "6.6"
label = "Production Linux ${self.version}"
index = "${local.registry}/linux:${self.version}"
cmdline = "console=${local.console} ro root=/dev/vda1"
}
target "rescue" {
arch = "amd64" # optional arch filter
kernel = "${local.registry}/rescue:latest"
cmdline = [
"console=${local.console}",
"single",
"rd.break",
]
}
Top-level expressions evaluate against an EvalContext exposing "arch" (runtime arch) and "lldp" (neighbor facts; .available is false when no frame was observed). Inside a target, a per-target child context additionally exposes "self" with that target's scalars (`self.name`, `self.arch`, `self.version`) so attribute expressions can compose values without DRY violations.
Cmdline accepts either a string or a list of strings (joined with a single space) for readable multi-token cmdlines.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func EvalContext ¶
func EvalContext(arch string, facts *lldp.Facts) *hcl.EvalContext
EvalContext builds an HCL evaluation context exposing runtime facts to plan expressions. arch is the runtime architecture (defaults to runtime.GOARCH). facts is the LLDP neighbor data; pass nil if LLDP is disabled or no frame was observed.
Types ¶
type Disk ¶
type Disk struct {
Device string `hcl:"device"` // e.g. "/dev/vda2" (required)
FS string `hcl:"fs,optional"` // filesystem; defaults to ext4
Kernel string `hcl:"kernel,optional"` // path on the mounted disk; default newest /boot/vmlinuz-*
Initrd string `hcl:"initrd,optional"` // path on the mounted disk; default paired with kernel
}
Disk selects "kexec into the kernel that already lives on a local block device" instead of fetching one from OCI. cloud-boot-init mounts Device read-only, picks Kernel (or the newest /boot/vmlinuz-*) and Initrd (paired with the kernel by version suffix), then kexecs with Target.Cmdline.
type Menu ¶
type Menu struct {
Timeout string `hcl:"timeout,optional"` // "5s" / "10" / "1m30s"; "" disables
Prompt string `hcl:"prompt,optional"` // header text; default supplied by caller
}
Menu configures the interactive boot-time chooser. When nil (block absent from the plan), no menu is shown and DefaultTarget is selected directly. Timeout is parsed by time.ParseDuration ("5s", "1m30s") or, for ergonomics, a bare integer is accepted and interpreted as seconds.
type Plan ¶
type Plan struct {
DefaultTarget string `hcl:"default_target,optional"`
// DNS lists IP addresses to write into /etc/resolv.conf after the
// plan has been parsed, replacing whatever DHCP supplied. Useful
// when the plan's target refs sit behind an SRV name that only
// resolves via a private DNS (typical dev setup: bring up the
// guest with public DNS, fetch the plan, then switch to the dev
// DNS for the target/modloop fetches). `cloudboot.dns=ip,ip,...`
// on the kernel cmdline takes precedence over this field.
DNS []string `hcl:"dns,optional"`
Menu *Menu `hcl:"menu,block"`
Targets []Target `hcl:"target,block"`
}
Plan is the decoded top-level HCL document.
func Decode ¶
Decode parses HCL bytes; filename governs the parser (must end in .hcl or .json). ctx supplies the variables and functions used during expression evaluation.
Plans may declare a single `locals { … }` block at the top level whose attributes are evaluated against ctx (so they can themselves reference `arch`, `lldp.*`, etc.) and re-exposed under a `local` namespace for the rest of the file. This mirrors Terraform's locals semantics and lets multiple targets share a value — typically the SRV-shaped registry host:
locals {
registry = "_oci._tcp.registry.example.com/boot"
}
target "alpine" { index = "${local.registry}/alpine:3.21" … }
target "primary" { index = "${local.registry}/linux:6.6" … }
func (*Plan) EligibleTargets ¶
EligibleTargets returns the subset of targets whose Arch field is empty or matches arch. Used to build the interactive menu, so the user never sees a target they couldn't actually boot.
type Target ¶
type Target struct {
Name string `hcl:"name,label"`
Arch string `hcl:"arch,optional"` // optional filter: amd64|arm64|riscv64
Version string `hcl:"version,optional"` // free-form release tag, exposed as `self.version`
// The label / OCI-ref / cmdline attributes are kept as
// hcl.Expression instead of plain string so they can reference the
// `self` variable (`self.name`, `self.arch`, `self.version`) when
// composing values. Decode evaluates each against a per-target
// EvalContext after the scalar fields (Name / Arch / Version) are
// known. The decoded textual form lands in the matching plain-
// string field below — that's what every other package reads.
//
// CmdlineExpr additionally accepts a list of strings (joined with
// a single space) for readable multi-token cmdlines, e.g.
// cmdline = [
// "console=ttyS0",
// "ip={{.IPSpec}}",
// "alpine_repo=${local.alpine_repo}/${self.version}/main",
// ]
LabelExpr hcl.Expression `hcl:"label,optional"`
IndexExpr hcl.Expression `hcl:"index,optional"`
KernelExpr hcl.Expression `hcl:"kernel,optional"`
InitrdExpr hcl.Expression `hcl:"initrd,optional"`
ModulesExpr hcl.Expression `hcl:"modules,optional"`
CmdlineExpr hcl.Expression `hcl:"cmdline,optional"`
// SubplanExpr makes the target a CHAINED PLAN reference rather
// than something directly bootable. When set (and non-empty
// after eval), init fetches the referenced OCI plan, decodes
// it, and runs target selection on the inner plan — letting
// an embedded boot.iso ship a tiny static menu whose entries
// expand into live plans pulled from a registry. Mutually
// exclusive with the OCI/disk fields.
SubplanExpr hcl.Expression `hcl:"subplan,optional"`
Disk *Disk `hcl:"disk,block"` // when set, boot from a local disk
// Resolved string forms, populated by Decode. No HCL tag —
// gohcl skips them.
Label string
Index string
Kernel string
Initrd string
Modules string
Cmdline string
Subplan string
}
Target describes one bootable selection. Either at least one OCI ref (Index / Kernel / Initrd / Modules) must be set or Disk must be set — the two modes are mutually exclusive. Decode validates that.
The four OCI fields name their role-by-convention:
Index — primary ref. Typically an OCI image index (multi-arch
manifest list) whose target manifest carries the full
bundle of layers: kernel + initrd + (modules) + (cmdline).
This is what `task push:alpine:multi` pushes for the
Alpine smoke target, and what production bundles usually
look like.
Kernel — ref whose manifest carries *only* a kernel layer.
Initrd — ref whose manifest carries *only* an initrd layer.
Modules — ref whose manifest carries *only* a modules cpio.gz layer.
Mechanically, cloud-boot-init pulls every set ref and merges all layers it recognises by media type. Index is processed first so its layers form a baseline; an explicit Kernel / Initrd / Modules ref then overrides the matching role. This lets you, e.g., bundle a stock kernel via Index and swap just the initrd via Initrd.