plan

package
v0.0.0-...-b25b90c Latest Latest
Warning

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

Go to latest
Published: Nov 2, 2026 License: BSD-3-Clause Imports: 10 Imported by: 0

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 Facts

type Facts = lldp.Facts

Facts re-exports lldp.Facts so callers needn't import both packages.

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.

func (m *Menu) TimeoutDuration() (time.Duration, error)

TimeoutDuration parses Menu.Timeout. Accepted forms: a Go duration ("5s", "1m30s") or a bare integer interpreted as seconds. An empty string returns 0 (menu disabled).

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

func Decode(data []byte, filename string, ctx *hcl.EvalContext) (*Plan, error)

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

func (p *Plan) EligibleTargets(arch string) []*Target

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.

func (*Plan) Pick

func (p *Plan) Pick(name, arch string) (*Target, error)

Pick returns the matching target by name and current arch. An empty name falls back to DefaultTarget, or — if exactly one target is defined — to that target. Targets carrying an explicit Arch are filtered against the runtime arch (or the supplied arch, if non-empty).

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.

Jump to

Keyboard shortcuts

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