winvm

package
v0.0.3 Latest Latest
Warning

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

Go to latest
Published: Oct 1, 2026 License: MIT Imports: 27 Imported by: 0

Documentation

Overview

Package winvm provides the Windows-VM (QEMU) runner backend: a golden Server Core image is baked once, ephemeral jobs run on throwaway copy-on-write overlays, and housekeeping keeps the golden image's eval license valid and its tools fresh.

Index

Constants

This section is empty.

Variables

View Source
var DefaultRunnerSHA256 = imageVersionManifest.Minimal.Runner.WindowsX64SHA256
View Source
var DefaultRunnerVersion = imageVersionManifest.Minimal.Runner.Version

Functions

func AutounattendFiles

func AutounattendFiles(runnerVersion, runnerSHA256, adminPassword string, tools []string) (map[string]string, error)

AutounattendFiles returns the answer file + provisioning scripts with bake substitutions applied (for the autounattend ISO).

func Bake

func Bake(ctx context.Context, o BakeOptions, now time.Time) error

Bake builds the golden image: create base disk, boot the unattended installer, wait for it to finish (guest powers off), and write the metadata sidecar.

func BuildISO

func BuildISO(path, label string, files map[string]string) error

BuildISO writes an ISO containing the given files (name -> content). Used for the JIT config disk and the bake-time autounattend disk.

func BuildISOFiles

func BuildISOFiles(path, label string, files map[string]string, fileRefs map[string]string) error

BuildISOFiles writes an ISO with inline string files (name -> content) plus optional on-disk files (name -> host path) streamed from disk instead of held in memory — used to stage large binaries (runner/MinGit) onto the bake ISO so the guest reads them off the virtual CD instead of over the (slow) VM network.

func BuildJITISO

func BuildJITISO(path, jit string, env map[string]string) error

BuildJITISO writes a tiny ISO carrying the JIT config blob and the runner env, attached to the VM as a CD-ROM. Pure Go (no mkisofs/oscdimg), so it works on Linux and Windows.

func DetectAccel

func DetectAccel(goos, goarch string) string

DetectAccel picks the QEMU accelerator for this backend's x86-64 Windows guest. Hardware virtualization requires the host and guest CPU architectures to match, so ARM hosts use QEMU's cross-architecture TCG emulation, and an amd64 host only gets a hardware accelerator when that accelerator is actually usable. WHPX and HVF have no equivalent cheap probe, so they stay optimistic.

func DetectOVMF

func DetectOVMF(qemuBin string) (codeFD, varsTemplate string)

DetectOVMF locates UEFI firmware: the code blob and a vars template to copy. Searches the QEMU install's share dir and common Linux locations.

func GoldenSerialPath

func GoldenSerialPath(golden string) string

GoldenSerialPath is the bake's COM1 capture file. install-golden.ps1 writes progress markers here (MR:...), ending with MR:GOLDEN_OK on success, so the host can verify the bake finished without mounting the guest disk.

func GoldenVarsPath

func GoldenVarsPath(golden string) string

GoldenVarsPath is the UEFI NVRAM file paired with a golden image.

func OverlayCreateArgs

func OverlayCreateArgs(golden, overlay string) []string

OverlayCreateArgs builds the qemu-img args to create a clean copy-on-write overlay backed by the golden image (so each job starts from a pristine disk).

func PlanBake

func PlanBake(o *BakeOptions) (string, error)

PlanBake validates bake inputs and describes its writes without creating disks, images, listeners, downloads, or processes.

func Prepare

func Prepare(ctx context.Context, o *BakeOptions) (autoISO string, args []string, err error)

Prepare creates the base disk + autounattend ISO for a bake and returns the autounattend ISO path and the QEMU install args (without running QEMU).

func QEMUArgs

func QEMUArgs(o LaunchOpts) []string

QEMUArgs builds the qemu-system-x86_64 args for a headless ephemeral runner VM. The guest auto-runs the runner and powers off after its one job; -no-reboot makes that power-off terminate QEMU so the backend's Wait returns.

func QMPBootKeys

func QMPBootKeys(addr string, presses int, every time.Duration) error

QMPBootKeys resets the VM and sends Enter repeatedly to satisfy Windows media's "Press any key to boot from CD or DVD" prompt (which otherwise times out and falls through to PXE/no-boot). Used during bake.

func QMPEjectOnReset

func QMPEjectOnReset(addr, deviceID string, timeout time.Duration) error

QMPEjectOnReset connects to QMP and waits for the guest's first RESET event (Windows Setup rebooting after it applies the image to disk), then ejects the install CD with block-backend id deviceID. This stops later reboots from re-entering the DVD UEFI loader — which triple-faults under WHPX ("Unexpected VP exit code 4") — sending them to the now-bootable HDD instead. Returns after the eject, or when timeout elapses with no reset.

func QMPQuitNamed

func QMPQuitNamed(ctx context.Context, addr, expectedName string) error

QMPQuitNamed stops only the QEMU process that reports the expected VM name. The QMP endpoint disappears with the process, avoiding unsafe PID-only kills during restart reconciliation.

func QMPScreenshot

func QMPScreenshot(addr, outPath string) error

QMPScreenshot connects to a QEMU QMP endpoint and writes a PNG screenshot of the guest display to outPath. Used to observe headless VM installs.

func RearmGolden

func RearmGolden(ctx context.Context, ref GoldenRef, now time.Time) error

RearmGolden boots the golden image in place (writable, not an overlay) with a rearm marker ISO; the guest runs slmgr /rearm and powers off, resetting the eval clock. Must run while no jobs use the golden (drain first).

func RebuildGolden

func RebuildGolden(ctx context.Context, ref GoldenRef, now time.Time) error

RebuildGolden re-bakes the golden from scratch (resets eval + tool fingerprint).

func SaveMeta

func SaveMeta(golden string, m GoldenMeta) error

SaveMeta writes the golden image's metadata sidecar.

func ToolsHash

func ToolsHash(o BakeOptions) (string, error)

ToolsHash fingerprints every input that changes the provisioned golden image. It is non-empty even without optional tools so template and runner updates rebuild existing minimal goldens.

func ValidateToolSelectors

func ValidateToolSelectors(tools []string) error

ValidateToolSelectors reports whether every golden tool selector resolves, so a typo in a pool's qemu.tools fails at config load instead of at bake time (or never, when the pool has no bake_iso).

Types

type Action

type Action int

Action is what housekeeping decides to do with the golden image.

const (
	ActionNone    Action = iota // golden is fine
	ActionRearm                 // reset the eval clock (slmgr /rearm) + re-snapshot
	ActionRebuild               // full reinstall + re-bake (rearms exhausted or tools stale)
)

func (Action) String

func (a Action) String() string

type ActionFuncs

type ActionFuncs struct {
	Rearm   func(ctx context.Context, ref GoldenRef, now time.Time) error
	Rebuild func(ctx context.Context, ref GoldenRef, now time.Time) error
}

ActionFuncs are the housekeeping side effects (injectable for testing).

func DefaultActions

func DefaultActions() ActionFuncs

DefaultActions wires the real rearm/rebuild operations.

type Backend

type Backend struct {
	// contains filtered or unexported fields
}

Backend runs ephemeral Windows runners as QEMU VMs (golden image + per-job copy-on-write overlay + JIT config ISO). It implements backend.Backend, so it plugs into the pool / autoscaler unchanged.

func NewBackend

func NewBackend(opt Options) (*Backend, error)

NewBackend builds the QEMU backend.

func (*Backend) CleanupOrphans

func (b *Backend) CleanupOrphans()

CleanupOrphans clears leftover per-VM artifacts in the work dir at startup. A clean shutdown removes its overlay, JIT ISO, and NVRAM; its serial log stays for diagnosis until this next startup. Safe to delete at startup: no VM from this process is running yet.

func (*Backend) Close

func (b *Backend) Close() error

func (*Backend) EnsureImage

func (b *Backend) EnsureImage(ctx context.Context, _ string) error

EnsureImage is a no-op for VMs (the golden image is produced by `bake`); it only checks the golden exists.

func (*Backend) Launch

Launch creates a clean overlay + JIT ISO and boots the VM. The guest runs its one job then powers off, which terminates QEMU (see -no-reboot).

func (*Backend) ListOwnedRunners

func (b *Backend) ListOwnedRunners(_ context.Context, ownership backend.RunnerOwnership) ([]backend.OwnedRunner, error)

func (*Backend) Name

func (b *Backend) Name() string

func (*Backend) OSType

func (b *Backend) OSType(ctx context.Context) (string, error)

OSType reports the guest OS this backend runs.

func (*Backend) Ping

func (b *Backend) Ping(ctx context.Context) error

Ping verifies qemu + qemu-img are present and the golden image exists.

func (*Backend) RemoveOwnedRunner

func (b *Backend) RemoveOwnedRunner(ctx context.Context, resourceID string) error

type BakeOptions

type BakeOptions struct {
	WindowsISO       string // path to a Windows Server ISO (Server Core selected by the answer file)
	WindowsISOSHA256 string // optional expected SHA256; the computed digest always enters the golden fingerprint
	Golden           string // output golden qcow2
	DiskGB           int
	MemMB            int
	CPUs             int
	Accel            string // "" = auto
	RunnerVersion    string
	RunnerSHA256     string   // required when RunnerVersion is not DefaultRunnerVersion
	Tools            []string // golden selectors: dotnet[:major] | node[:major] | go | buildtools[:line]
	AdminPassword    string
	EvalDays         int  // 180 (server) / 90 (client)
	MaxRearms        int  // ~5
	Licensed         bool // a real key/KMS is configured -> skip eval housekeeping
	WorkflowsHash    string
	QEMUBin          string
	ImgBin           string
	OVMFCode         string        // UEFI firmware code (auto-detected if empty)
	OVMFVarsTemplate string        // UEFI vars template to copy
	VNC              string        // raw VNC host:port; port 0 selects a free port
	VNCWeb           string        // if set (host:port), serve a noVNC viewer to watch the install
	VNCWebSocket     string        // resolved QEMU WebSocket host:port (set by Prepare)
	Timeout          time.Duration // max install wall-clock before the bake kills a hung guest (default: derived from Tools)
}

BakeOptions configures building (or rebuilding) a golden Windows image.

type Firmware

type Firmware struct {
	CodeFD string // read-only firmware code (edk2-x86_64-code.fd / OVMF_CODE.fd)
	VarsFD string // writable per-VM NVRAM vars copy
}

Firmware holds UEFI (OVMF/edk2) firmware paths. CodeFD empty => legacy BIOS. UEFI is used for Windows because it skips the "Press any key to boot from CD" prompt that hangs unattended BIOS installs.

type GoldenMeta

type GoldenMeta struct {
	// CreatedAt is when the eval clock last reset (initial build or last rearm).
	CreatedAt time.Time `json:"created_at"`
	// EvalDays is the eval window length (180 for Server, 90 for client).
	EvalDays int `json:"eval_days"`
	// RearmsUsed / MaxRearms track remaining slmgr rearms.
	RearmsUsed int `json:"rearms_used"`
	MaxRearms  int `json:"max_rearms"`
	// Licensed is true when a real key/KMS is configured (eval housekeeping off).
	Licensed bool `json:"licensed"`
	// WorkflowsHash fingerprints the baked toolset so changes trigger a rebuild.
	WorkflowsHash string `json:"workflows_hash"`
}

GoldenMeta is the sidecar metadata stored next to the golden image.

func LoadMeta

func LoadMeta(golden string) (GoldenMeta, error)

LoadMeta reads the golden image's metadata sidecar (or a zero value if absent).

func (GoldenMeta) ApplyRearm

func (m GoldenMeta) ApplyRearm(now time.Time) GoldenMeta

ApplyRearm records a rearm: reset the clock and consume one rearm.

func (GoldenMeta) ApplyRebuild

func (m GoldenMeta) ApplyRebuild(now time.Time, wantHash string) GoldenMeta

ApplyRebuild records a fresh build: reset clock, rearms, and tool fingerprint.

func (GoldenMeta) DaysLeft

func (m GoldenMeta) DaysLeft(now time.Time) int

DaysLeft returns the eval days remaining for the golden image at time now.

type GoldenRef

type GoldenRef struct {
	Bake BakeOptions
}

GoldenRef ties a golden image to the bake options needed to rebuild it.

type Housekeeper

type Housekeeper struct {
	// contains filtered or unexported fields
}

Housekeeper periodically keeps golden images' eval licenses valid and tools fresh.

func NewHousekeeper

func NewHousekeeper(refs []GoldenRef, policy Policy, every time.Duration, logger *slog.Logger) *Housekeeper

NewHousekeeper builds a Housekeeper (every<=0 disables periodic checks).

func (*Housekeeper) CheckOnce

func (h *Housekeeper) CheckOnce(ctx context.Context)

CheckOnce evaluates every golden once and applies the chosen action.

func (*Housekeeper) Run

func (h *Housekeeper) Run(ctx context.Context)

Run checks once immediately, then on each interval until ctx is cancelled.

type LaunchOpts

type LaunchOpts struct {
	Golden     string // base (golden) qcow2, read-only backing file
	Overlay    string // per-job copy-on-write overlay qcow2
	JITISOPath string // config ISO carrying the JIT blob
	Name       string // process identity returned by QMP
	QMPAddr    string // loopback QMP endpoint used for restart cleanup
	PIDFile    string // QEMU-owned process ID record
	MemMB      int
	CPUs       int
	Accel      string // "" = tcg; callers normally resolve host-compatible auto mode
	Firmware   Firmware
}

LaunchOpts describes one ephemeral Windows VM launch.

type Options

type Options struct {
	Golden  string // path to the golden qcow2 (built by `multirunner bake`)
	WorkDir string // where per-job overlays/ISOs are written
	MemMB   int
	CPUs    int
	Accel   string // "" = auto-detect (kvm/whpx/hvf on x86-64, tcg on ARM)
	QEMUBin string // qemu-system-x86_64 (default looked up on PATH)
	ImgBin  string // qemu-img (default looked up on PATH)
}

Options configures the QEMU Windows-VM backend.

type Policy

type Policy struct {
	// ThresholdDays: act when fewer than this many eval days remain.
	ThresholdDays int
}

Policy parameterizes housekeeping decisions.

func DefaultPolicy

func DefaultPolicy() Policy

DefaultPolicy returns sensible defaults (act 14 days before expiry).

func (Policy) Decide

func (p Policy) Decide(m GoldenMeta, wantHash string, now time.Time) Action

Decide chooses the housekeeping action for the golden image. wantHash is the current desired workflows/tools fingerprint; a mismatch forces a rebuild.

Jump to

Keyboard shortcuts

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