Documentation
¶
Overview ¶
Package boot is the first-boot provisioning runner. The host stamps four reserved properties on the VM at create time (weft.boot/source.kind, source.url, source.ref, script — see openweft/weft-webui commit 2c098e7) ; the pkg/properties subscriber mirrors them to /run/weft/properties/weft.boot/* ; THIS package reads from there, pulls the payload, runs the script. One-shot per VM ; idempotent via a sentinel file so reboots don't re-provision.
Why piggyback on properties instead of a dedicated NATS subject : keeps the host-side wire simple (the host already publishes weft.boot/* with the property subscriber's publishes ; a separate subject would duplicate that). Also gives the Properties drawer tab visibility into what was stamped — operator can audit the boot config without a separate panel.
Script execution uses mvdan.cc/sh/v3 (POSIX sh in Go) so the payload doesn't need to ship /bin/sh in its image — works for scratch / distroless / alpine alike. Git clone uses go-git (pure-Go, no /usr/bin/git in the ramdisk) ; OCI pull uses oras-go (pure-Go, anonymous registry pulls). Both are wired via the Cloner / Puller hooks on Runner so tests can stub them.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func RunScript ¶
RunScript parses + executes a POSIX sh body in cwd via mvdan.cc/sh/v3. Stdout + stderr go to logOut ; stdin is a closed empty reader (no interactive script). Errors from the interpreter (including non-zero exit) propagate.
Exposed separately from Runner.Run so tests can exercise the script path without setting up a workdir / sentinel.
Types ¶
type Buffer ¶
Buffer is a small helper so callers can capture script output without depending on bytes.Buffer in tests. Wraps a bytes.Buffer so the returned writer is goroutine-safe enough for the runner's single-writer use.
type Config ¶
type Config struct {
SourceKind string // "" | "git" | "oci"
SourceURL string
SourceRef string // branch / tag / commit / digest
Script string // POSIX sh body
}
Config is the resolved boot request : what to pull + what to run. All fields optional ; an empty Config is a no-op (the runner logs "no provisioning requested" and stamps the sentinel anyway, so a reboot doesn't keep checking).
func ReadFromPropertiesDir ¶
ReadFromPropertiesDir loads a Config from the property tree the pkg/properties subscriber maintains (typically /run/weft/properties). Each weft.boot/* property is a file ; missing files come back as empty strings (not an error — the host may have stamped only a subset).
type Runner ¶
type Runner struct {
WorkDir string
SentinelPath string
LogOut io.Writer
Cloner func(ctx context.Context, url, ref, dst string) error
Puller func(ctx context.Context, url, ref, dst string) error
}
Runner is the apply side : pull + script. WorkDir is the parent directory the payload lands under (.../payload subdir) ; SentinelPath is touched after a successful run so reboots skip. LogOut captures script stdout/stderr ; pass io.Discard if you don't care.
Cloner is the git-clone hook ; nil means "git pull not supported" (the runner returns an error if SourceKind=="git" without a Cloner). Tests inject a stub ; production wires a go-git helper in cmd/weft-microvm-agent/boot.go.
Puller is the symmetric OCI hook ; same nil-means-unsupported rule. Production uses oras-go for anonymous registry pulls.
func (*Runner) Run ¶
Run executes one provisioning pass. Idempotent : if SentinelPath already exists, returns nil immediately without touching anything. On success the sentinel is created ; on failure it isn't, so a re-published config can retry.
Order of effects :
- sentinel check (early return when already provisioned)
- pull (git clone or oras pull into <WorkDir>/payload ; skipped when SourceKind is empty)
- script (mvdan.cc/sh/v3 in payload CWD if it exists, else WorkDir)
- sentinel write (only when everything above succeeded)