Documentation
¶
Overview ¶
Package firecracker is the native Firecracker runtime driver. It is the second implementation of internal/runtime.Runtime (the first being pkg/docker.Client) and runs sandboxes as Firecracker microVMs with no Docker daemon in the path.
This file lands the package as a skeleton: the Driver type implements every Runtime method but returns models.ErrRuntimeNotImplemented from each one until the actual lifecycle code arrives. The point of landing the skeleton first is to:
- prove the runtime.Runtime interface holds with a second implementation (the "visible property at end of Phase 1" from plans/snapshot-clone-fast-boot.md),
- give cmd/sandboxd/main.go a real type to wire alongside the Docker driver when SB_ENABLE_FIRECRACKER is true, and
- give the service layer a non-nil firecracker driver to dispatch to, so the rejection lives in this package's methods (where the future real implementation will replace them) rather than as a special-case branch in CreateSandbox.
Phase 1 (per the plan): the methods below get filled in one at a time: Ping first (cheapest), then Create / Start / Destroy on a single cold boot, then Stop / Inspect / ListManaged, then snapshot support. Each step is independently mergeable because the surface stays stable.
Index ¶
- type Config
- type Driver
- func (d *Driver) ApplyEgressPolicy(_ string, _, _ []string) error
- func (d *Driver) ApplyNetworkBlockAll(_ string) error
- func (d *Driver) ApplyNetworkBlockIngress(_ string) error
- func (d *Driver) ClearEgressPolicy(_ string, _, _ []string) error
- func (d *Driver) ClearNetworkBlockEgress(_ string) error
- func (d *Driver) ClearNetworkBlockIngress(_ string) error
- func (d *Driver) ClearNetworkRules(_ string) error
- func (d *Driver) Create(ctx context.Context, req models.CreateSandboxRequest, ...) (*models.SandboxRuntimeState, error)
- func (d *Driver) CreateSnapshot(_ context.Context, _, _ string) (string, error)
- func (d *Driver) Destroy(ctx context.Context, sandbox *models.Sandbox) error
- func (d *Driver) Inspect(ctx context.Context, sandboxID string) (*models.SandboxRuntimeState, error)
- func (d *Driver) ListManaged(ctx context.Context) (map[string]*models.SandboxRuntimeState, error)
- func (d *Driver) Ping(_ context.Context) error
- func (d *Driver) PushAllowedPorts(_ context.Context, _, _ string, _ []int) error
- func (d *Driver) RemoveImage(_ context.Context, _ string) error
- func (d *Driver) Resize(_ context.Context, _ string, _ models.ResizeSandboxRequest) error
- func (d *Driver) RuntimeHealth(ctx context.Context) string
- func (d *Driver) SetClientFactory(f vmmClientFactory)
- func (d *Driver) SetPool(p TapPool)
- func (d *Driver) SetRSSSampler(s *RSSSampler)
- func (d *Driver) SetRootfsBuilder(b RootfsBuilder)
- func (d *Driver) SetSpawner(s vmmSpawner)
- func (d *Driver) SetTapHost(h TapHost)
- func (d *Driver) SetTemplateHealthNotifier(n TemplateHealthNotifier)
- func (d *Driver) SetTemplateResolver(t TemplateResolver)
- func (d *Driver) SetVsockDialer(v VsockDialer)
- func (d *Driver) SetWarmPool(p WarmPool)
- func (d *Driver) SnapshotTemplate(ctx context.Context, req TemplateSnapshotRequest) (*TemplateSnapshotResult, error)
- func (d *Driver) Start(ctx context.Context, sandboxID string) (*models.SandboxRuntimeState, error)
- func (d *Driver) Stop(ctx context.Context, sandboxID string) error
- func (d *Driver) WarmSpawn(ctx context.Context, req WarmSpawnRequest) (WarmHandle, error)
- type InjectFile
- type PoolSpawner
- type RSSSampler
- type RootfsBuildRequest
- type RootfsBuilder
- type RootfsResult
- type TapHost
- type TapPool
- type TapSlot
- type TemplateHealthNotifier
- type TemplateResolution
- type TemplateResolver
- type TemplateSnapshotRequest
- type TemplateSnapshotResult
- type VMMClient
- type VMMHandle
- type VsockDialer
- type WarmHandle
- type WarmPool
- type WarmSpawnRequest
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
// FirecrackerBinary is the absolute path to the firecracker VMM. The
// driver checks it on Ping (does the file exist and is it executable),
// not on construction — the daemon can start with a misconfigured
// path; /healthz surfaces the failure.
FirecrackerBinary string
// JailerBinary is the absolute path to the jailer helper. Same
// existence-check policy as FirecrackerBinary.
JailerBinary string
// KernelImage is the host path to the guest kernel image used for
// template builds (and, until per-template kernels arrive, for every
// cold-boot VMM).
KernelImage string
// RunDir is the per-sandbox runtime state root. Each sandbox gets a
// subdirectory <RunDir>/<sandbox-id>/ holding the API socket, the
// jailer chroot, and per-sandbox vsock UDS files. tmpfs strongly
// recommended; the daemon does not enforce it.
RunDir string
// TemplatesDir is the persistent root for template artifacts (kernel,
// rootfs.ext4, snapshot.memory, snapshot.state, manifest.json). Lives
// across daemon restarts.
TemplatesDir string
// UseJailer flips the spawn from `firecracker` directly to `jailer`,
// which chroots+cgroups+drops-priv into JailerUID/JailerGID. Production
// hosts always set this; dev/CI without root leave it false. When
// true, vmm.go re-roots a sandbox's runDir under JailerChrootBase
// rather than RunDir; see jailer.go for the path math.
UseJailer bool
// JailerChrootBase is the parent directory under which jailer creates
// each sandbox's chroot. Canonical layout is
// <JailerChrootBase>/firecracker/<sandbox-id>/root/.
JailerChrootBase string
// JailerUID / JailerGID are the UID/GID the firecracker process drops
// into inside the jail. Must already exist on the host.
JailerUID int
JailerGID int
// SnapshotVerifyOnLoad gates SHA256 verification of snapshot.memory
// and snapshot.state immediately before each LoadSnapshot. Default
// true on production daemons; bypass only for benchmarking the raw
// load-cost of a known-good snapshot. Mirrors
// internal/config.Config.FirecrackerSnapshotVerifyOnLoad.
SnapshotVerifyOnLoad bool
// SnapshotVerifyMode controls whether a verified snapshot is re-hashed
// on every LoadSnapshot ("always") or once per daemon boot and file
// identity ("once", default). Only consulted when SnapshotVerifyOnLoad
// is true and the template has a persisted checksum.
SnapshotVerifyMode string
// OverlayEnabled is the daemon-wide opt-out for the per-sandbox
// writable overlay drive. Mirrors
// internal/config.Config.FirecrackerOverlayEnabled. When false,
// Create rejects any request with OverlaySizeGB > 0.
OverlayEnabled bool
// OverlayMkfs makes the driver run mkfs.ext4 -F on each per-
// sandbox overlay.ext4 right after sparse allocation. Mirrors
// internal/config.Config.FirecrackerOverlayMkfs. Off by default —
// the guest is normally expected to mkfs /dev/vdb itself.
OverlayMkfs bool
// Mkfs4Bin is the host path to the mkfs.ext4 binary, reused from
// the OCI builder for the optional host-side overlay format step.
// Only consulted when OverlayMkfs is true.
Mkfs4Bin string
// PostResumeTimeout bounds the best-effort post_resume vsock send
// (carries host wall clock to the guest for clock+RNG resync).
// Mirrors internal/config.Config.FirecrackerSnapshotPostResumeTimeout.
PostResumeTimeout time.Duration
// ToolboxBinaryPath is the host path to the toolboxd agent binary,
// baked into every cold-boot rootfs so the guest has something
// listening on vsock (the Create readiness handshake) and HTTP (exec /
// files / sessions). Mirrors internal/config.Config.ToolboxBinaryPath.
// When empty, cold-boot still works but the guest comes up with no
// agent — Create's vsock handshake will then time out, so production
// daemons must set it. The template builder adapter also uses this
// binary so templates built from stock OCI images can be snapshotted
// after the same toolbox readiness handshake.
ToolboxBinaryPath string
}
Config is the subset of internal/config.Config the driver actually uses. Lifted to its own type so unit tests can construct a driver without reaching into the full daemon config, and so the package has no import cycle with internal/config (the public config package depends on pkg/ types via models — this package depends on neither for its core surface).
func FromDaemonConfig ¶
FromDaemonConfig copies the Firecracker-relevant fields out of the full daemon config. Defined as a free helper rather than a method so the driver package doesn't import internal/config in its hot path; main.go is the only caller.
type Driver ¶
type Driver struct {
// contains filtered or unexported fields
}
Driver implements runtime.Runtime against Firecracker. The zero value is not usable; callers must go through New.
Concurrency model mirrors pkg/docker.Client: methods are safe for concurrent use across different sandboxes; per-sandbox serialization is the service layer's responsibility. The driver keeps a small registry of per-sandbox API clients (one Firecracker process => one Unix socket => one *firecracker.Client) so callers don't re-resolve the socket path on every method call.
func New ¶
New returns a Driver. The constructor does not stat the binaries or create RunDir — those checks land in Ping so a daemon with a transient misconfiguration can still boot and surface the problem through /healthz, the same model the Docker driver uses for unreachable dockerd.
func (*Driver) ApplyEgressPolicy ¶
func (*Driver) ApplyNetworkBlockAll ¶
func (*Driver) ApplyNetworkBlockIngress ¶
func (*Driver) ClearEgressPolicy ¶
func (*Driver) ClearNetworkBlockEgress ¶
func (*Driver) ClearNetworkBlockIngress ¶
func (*Driver) ClearNetworkRules ¶
ClearNetworkRules releases per-IP host-side rules attached to a sandbox's guest IP. The Firecracker analogue of the Docker iptables rules: a routed L3 setup with per-TAP egress allow/deny lists. Phase 1 uses a bridge with the same iptables shape as Docker for parity; later phases may move to eBPF (see CubeVS reference in the plan).
func (*Driver) Create ¶
func (d *Driver) Create(ctx context.Context, req models.CreateSandboxRequest, sandboxID, toolboxToken string, binds []mounts.ContainerBind) (*models.SandboxRuntimeState, error)
Create provisions a sandbox on Firecracker. Phase 1 path (no snapshots): spawn a jailed firecracker VMM, write machine-config + boot-source + rootfs drive + TAP iface + vsock, issue InstanceStart, wait for the in-VM toolbox to handshake over vsock, return the runtime state.
Phase 3+ path (snapshot clone): pull a paused VMM from the warm pool, PATCH the per-sandbox overlay and TAP onto it, PATCH /vm state=Resumed, return.
Cleanup contract — pr-review.md §4: every failure path between an acquired resource and the function return must release that resource. The implementation uses deferred-with-flag closures so every step's cleanup is in scope for every later step's failure. The order — pool slot → rootfs staging → host-side TAP → VMM process → registry entries — matches the order in which each is acquired, so cleanup unwinds in LIFO order.
Boot-path latency — pr-review.md §2: under normal load this issues 3 SQLite writes (pool allocation), 1 OCI subprocess pipeline (the dominant cost — skopeo+umoci+mkfs.ext4 can be tens of seconds on a large image), 3 `ip` shell-outs, 1 firecracker subprocess spawn, 6 HTTP-over-UDS calls, and 1 Firecracker vsock proxy dial. The OCI pipeline is the only stage with multi-second worst-case latency; per the plan, Phase 2 caches the rootfs and skips this stage when a template hit lands.
func (*Driver) CreateSnapshot ¶
CreateSnapshot is the runtime-level "commit" primitive. On the Firecracker side this is more involved than on Docker: we need to pause the VMM, write snapshot.memory + snapshot.state, copy the read-only rootfs reference, and write a manifest — the template-builder pipeline in internal/templates uses this driver method as one of its building blocks rather than calling Firecracker directly.
func (*Driver) Destroy ¶
Destroy tears down the VMM, releases the per-sandbox TAP/IP/vsock-CID back to their pools, removes the per-sandbox overlay file, and removes the host-side TAP device. Idempotent: calling Destroy on an already-destroyed sandbox is a no-op (the maps and the pool will all be empty).
Cleanup order — inverse of Create:
- shutdown the VMM (kills the process supervising the guest).
- remove the host-side TAP device.
- release the pool slot back to SQLite.
- drop the per-sandbox runDir.
- unregister from the in-memory maps.
Each step's error is logged and we continue — a partial cleanup is better than aborting halfway and leaking resources on a daemon restart. The first error encountered is returned at the end so the caller can surface it; subsequent errors are logged.
func (*Driver) Inspect ¶
func (d *Driver) Inspect(ctx context.Context, sandboxID string) (*models.SandboxRuntimeState, error)
Inspect returns the runtime state of a single VMM. Cheap: one GET / against the API socket. Returns nil/nil when the sandbox isn't in the driver's registry — mirrors the Docker driver's behavior for a recently-destroyed sandbox.
func (*Driver) ListManaged ¶
ListManaged enumerates every Firecracker VMM the driver knows about. Walks the in-memory registry (no SQLite hit) — reconcile uses this to compare driver state against the persisted row set.
func (*Driver) Ping ¶
Ping confirms the daemon has working Firecracker tooling on this host. We check the binaries exist and that the run directory is creatable. Phase 1 enhancement: also try to spawn-and-immediately-kill a VMM as a liveness probe; for now the binary check is enough to make /healthz useful.
func (*Driver) PushAllowedPorts ¶
PushAllowedPorts forwards the toolbox allowlist update. On the Firecracker path the toolbox channel is vsock, not the TAP-side TCP that Docker uses, so the call path differs from the Docker driver's HTTP dial against the container IP.
func (*Driver) RemoveImage ¶
RemoveImage is a Docker concept; on the Firecracker path images are flattened into per-template rootfs files and GCed by the template service. Calls here are unexpected but harmless — surfacing ErrRuntimeNotImplemented makes the misuse observable.
func (*Driver) Resize ¶
Resize updates VCPU and memory caps. Firecracker only supports PATCH /machine-config for memory hot-add via the balloon device today; CPU resize requires a snapshot+restore cycle. The driver may reject CPU-resize on a running VMM with a clearer error than Docker's.
func (*Driver) RuntimeHealth ¶
RuntimeHealth reports whether this node can prove the vmgenid prerequisites needed to close the pre-userspace snapshot-clone entropy window. "ok" means the daemon validated its local Firecracker binary and found a neighboring kernel config artifact with CONFIG_VMGENID=y; any other string is an operator-facing degradation reason suitable for /health.
func (*Driver) SetClientFactory ¶
func (d *Driver) SetClientFactory(f vmmClientFactory)
SetClientFactory overrides the default REST-client constructor. Tests use this to swap in a fake client recording every REST call.
func (*Driver) SetPool ¶
SetPool injects the network-allocation pool. Called once by main.go after both the store and the pool have been constructed, before the driver is registered with the service. Passing nil clears the dependency (used by tests that exercise non-Create methods).
func (*Driver) SetRSSSampler ¶
func (d *Driver) SetRSSSampler(s *RSSSampler)
SetRSSSampler injects the Phase 5 per-VMM RSS sampler. When non-nil the driver Registers each spawned VMM's PID (cold sandboxes by sandbox ID, warm pool slots by slot ID, then re-keyed to sandbox ID on warm acquire) and Unregisters on Destroy / warm Shutdown. nil leaves admission on nominal accounting — same as a daemon built without Phase 5. Tests that exercise lifecycle ordering can use this to inspect Register/Unregister calls via the sampler's TotalRSSMB.
func (*Driver) SetRootfsBuilder ¶
func (d *Driver) SetRootfsBuilder(b RootfsBuilder)
SetRootfsBuilder injects the OCI-image-to-rootfs.ext4 builder. Production impl is a thin adapter around *pkg/oci.Builder; tests inject a fake that touches a file at the requested OutPath.
func (*Driver) SetSpawner ¶
func (d *Driver) SetSpawner(s vmmSpawner)
SetSpawner overrides the default VMM spawn function. Tests use this to swap in a fake handle that records Start/WaitSocket/ Shutdown calls without exec'ing firecracker.
func (*Driver) SetTapHost ¶
SetTapHost injects the host-side TAP-device manager. Production impl is *internal/network/tap.Host; tests inject a recording fake.
func (*Driver) SetTemplateHealthNotifier ¶
func (d *Driver) SetTemplateHealthNotifier(n TemplateHealthNotifier)
SetTemplateHealthNotifier wires the notifier. Called once from cmd/sandboxd/main.go after Service is constructed. Nil disables the notifier — the cold-load path's service-side intercept still works (it goes through service.createFirecrackerSandbox, which has direct access to Service methods), but the warm-spawn path becomes silent on corruption. Production deployments always wire it; the nil-safety exists for unit tests that exercise warmspawn lifecycle without pulling in service.
func (*Driver) SetTemplateResolver ¶
func (d *Driver) SetTemplateResolver(t TemplateResolver)
SetTemplateResolver injects the Phase 2 template→rootfs lookup. When non-nil, Create with req.TemplateID != "" hard-links the resolved rootfs into the per-sandbox runDir and skips the OCI pipeline. Nil leaves the driver in Phase 1 mode (every Create runs skopeo+umoci+ mkfs).
func (*Driver) SetVsockDialer ¶
func (d *Driver) SetVsockDialer(v VsockDialer)
SetVsockDialer injects the host-side Firecracker vsock proxy dialer. Production impl is the Linux-only NewLinuxVsockDialer(); tests inject a fake that returns an in-memory io.ReadWriteCloser pair.
func (*Driver) SetWarmPool ¶
SetWarmPool injects the warm-VMM pool. Called once by main.go after both the pool and the spawner adapter are constructed. Nil leaves the driver in pre-pool mode: every snapshot-load Create runs the existing cold-spawn + LoadSnapshot path.
func (*Driver) SnapshotTemplate ¶
func (d *Driver) SnapshotTemplate(ctx context.Context, req TemplateSnapshotRequest) (*TemplateSnapshotResult, error)
SnapshotTemplate captures a Firecracker full snapshot of the template VMM. Two artifact files (req.OutStatePath, req.OutMemoryPath) are written and SHA256'd before return; on success the rootfs.ext4 the snapshot booted from is untouched, so callers can promote both files to the per-template directory in one step.
Cleanup discipline: every allocation (TAP slot, host TAP device, VMM process, partial output files) is wrapped in a flag-and-defer release so a mid-pipeline error tears everything down. Output files are removed on failure — a half-written snapshot.memory is worse than no snapshot at all because the integrity check at load time would rightly reject it but the row would be marked has_snapshot=true.
func (*Driver) Start ¶
Start boots a previously-stopped Firecracker sandbox by restoring the per-sandbox snapshot Stop wrote under the persistent artifact directory. The VMM process itself does not survive Stop; the stable contract is snapshot-on-stop, destroy host resources, then LoadSnapshot+PATCH+Resume on Start.
func (*Driver) Stop ¶
Stop captures a full per-sandbox snapshot, persists the root/overlay disk files that snapshot expects, then tears down the VMM and host TAP. The TAP pool allocation is intentionally kept so Start can restore the same guest IP and vsock identity; Destroy releases that allocation and deletes the snapshot.
func (*Driver) WarmSpawn ¶
func (d *Driver) WarmSpawn(ctx context.Context, req WarmSpawnRequest) (WarmHandle, error)
WarmSpawn produces a paused, snapshot-loaded firecracker process ready for the pool to hand to the Acquire-side code. The flow mirrors Driver.Create's snapshot-load steps up to (but not including) Resume:
- Allocate a slot rundir via the same spawn seam Create uses (creates the dir, returns a non-started handle).
- Allocate and realize a TAP slot under the warm slot id.
- Start the VMM process and wait for its API socket.
- Optionally verify the snapshot checksums (SnapshotVerifyOnLoad).
- LoadSnapshot with EnableDiffSnapshots=true, ResumeVM=false, rebinding eth0 to the warm TAP via network_overrides.
- Return the handle without resuming — the Acquire code transfers the TAP owner and resumes.
On any failure between Start and the final return, the spawned firecracker process is torn down and the rundir cleaned up so the pool never sees a leaked handle. The contract in spawner.go promises this and the GC sweep depends on it.
Acquire-side responsibilities (NOT done here):
Transferring the warm slot's TAP owner from slot id to sandbox id.
Allocating the per-sandbox overlay file. The snapshot state references the 1 MiB placeholder used at capture time; the Acquire path PATCHes the drive's path_on_host before Resume.
Issuing PATCH /vm state=Resumed and the vsock handshake.
type InjectFile ¶
InjectFile mirrors pkg/oci.InjectFile. Exactly one of HostPath/Content supplies the bytes; GuestPath is the absolute in-guest destination.
func ColdBootInjectFiles ¶
func ColdBootInjectFiles(toolboxBinaryPath, toolboxToken string, slot *TapSlot) []InjectFile
ColdBootInjectFiles returns the files to bake into a Firecracker rootfs so the guest comes up running the agent. It is exported for the daemon's template builder adapter: templates built from stock OCI images need the same injected agent/init as one-off cold boots before SnapshotTemplate can capture a ready guest.
type PoolSpawner ¶
type PoolSpawner struct {
// contains filtered or unexported fields
}
PoolSpawner adapts a *Driver to vmmpool.Spawner. Construction is a thin wrapper around a driver pointer — no state, no locks; every Spawn call delegates to Driver.WarmSpawn and returns the resulting WarmHandle directly (WarmHandle's method set is the structural superset of vmmpool.SpawnedHandle, so no conversion shim is needed).
The import is aliased to vmmpool because the runtime package already has an internal *vmm type (the firecracker process supervisor) and the unaliased import would shadow it.
func NewPoolSpawner ¶
func NewPoolSpawner(driver *Driver) *PoolSpawner
NewPoolSpawner wires a driver into the pool's Spawner contract. main.go calls this once at boot, alongside the vmmpool.Pool construction.
func (*PoolSpawner) Spawn ¶
func (s *PoolSpawner) Spawn(ctx context.Context, slotID string, inputs vmmpool.SnapshotInputs) (vmmpool.SpawnedHandle, error)
Spawn satisfies vmmpool.Spawner. The refill goroutine has already chosen a slot ID and resolved the template's snapshot artifacts; this method only forwards them into the runtime's WarmSpawn primitive. The returned handle's Shutdown is what the pool calls when the slot ages out or the daemon stops.
type RSSSampler ¶
type RSSSampler struct {
// contains filtered or unexported fields
}
RSSSampler tracks per-VMM resident-set-size and exposes the aggregate via TotalRSSMB. The zero value is not usable; construct with NewRSSSampler.
Goroutine safety: Register / Unregister take mu; TotalRSSMB does an atomic load with no lock so admission (PR 5-B) can call it under Admitter.mu without nested-lock concerns. sampleOnce takes mu only long enough to snapshot the pid map, releases it for the readRSSPagesFn calls, then takes mu again to commit updates — that keeps a slow /proc read on one bad pid from blocking Register on a new spawn.
func NewRSSSampler ¶
func NewRSSSampler(logger *slog.Logger) *RSSSampler
NewRSSSampler returns a sampler with empty registries and zero total. Logger may be nil — discarded internally if so.
func (*RSSSampler) Ready ¶
func (s *RSSSampler) Ready() bool
Ready reports whether sampleOnce has run at least once. Admission uses this to gate the RSS check: pre-tick, capacity falls back to nominal accounting because TotalRSSMB() returning 0 would otherwise look indistinguishable from "host is empty" and break overcommit safety the moment the operator sets a non-zero watermark ratio.
func (*RSSSampler) Register ¶
func (s *RSSSampler) Register(id string, pid int)
Register sets the pid for id. Idempotent on the same id: a second call overwrites the prior pid silently. The next sampleOnce reads the new pid; until then, the prior sample lingers in the aggregate (sub-second staleness is acceptable per the design — see file header).
Empty id or non-positive pid are rejected silently — admission counters would be wrong either way, and the caller (PR 5-C's Driver.Create) is expected to gate on these before calling.
func (*RSSSampler) Run ¶
func (s *RSSSampler) Run(ctx context.Context, interval time.Duration)
Run drives sampleOnce on a fixed interval until ctx is done. Blocks; callers start it as `go sampler.Run(ctx, interval)` from main.go (PR 5-B). Interval ≤0 disables the loop (Run returns immediately) — useful for tests that drive sampleOnce manually.
func (*RSSSampler) TotalRSSMB ¶
func (s *RSSSampler) TotalRSSMB() int
TotalRSSMB returns the most recent aggregate. Reads the atomic once. Returns 0 before the first sampleOnce; pair with Ready() to distinguish "no data yet" from "genuinely 0 MB resident".
func (*RSSSampler) Unregister ¶
func (s *RSSSampler) Unregister(id string)
Unregister drops id from the registry, removes any cached sample, and decrements the aggregate by the cached MB so admission sees the VMM go away immediately (rather than waiting for the next tick to notice the dead pid). No-op on unknown id.
type RootfsBuildRequest ¶
type RootfsBuildRequest struct {
ImageRef string
OutPath string
MinSizeMiB int
Tag string
// InjectFiles mirrors pkg/oci.BuildRequest.InjectFiles. The cold-boot
// path uses it to bake the in-guest agent + init shim + per-sandbox
// token into a stock OCI image.
InjectFiles []InjectFile
}
RootfsBuildRequest mirrors pkg/oci.BuildRequest. Re-declared here so the runtime package's seam can be satisfied without importing pkg/oci. main.go's adapter is the single conversion point. Field semantics match pkg/oci.BuildRequest exactly — see that doc comment for the wire-shape contract.
type RootfsBuilder ¶
type RootfsBuilder interface {
// Build produces an ext4 image at req.OutPath. Returns a *RootfsResult
// describing where the staging directory is so the caller can clean
// it up after the rootfs has been promoted to the per-template
// directory.
Build(ctx context.Context, req RootfsBuildRequest) (*RootfsResult, error)
}
RootfsBuilder is the seam for turning an OCI image reference into a rootfs.ext4 file the VMM can boot. Production impl is a thin adapter around *pkg/oci.Builder; tests use a fake that touches a file at OutPath.
type RootfsResult ¶
type RootfsResult struct {
RootfsPath string
StagingDir string
SizeBytes int64
// contains filtered or unexported fields
}
RootfsResult is the seam-side mirror of pkg/oci.Result. The driver cares about three things: where the rootfs landed, where to clean up, and how to perform the cleanup (so the seam owns the temp tree).
func NewRootfsResult ¶
func NewRootfsResult(rootfsPath, stagingDir string, sizeBytes int64, cleanup func() error) *RootfsResult
NewRootfsResult is the constructor adapters use to attach their own cleanup hook. The unexported field stays out of the public API so fake builders can't accidentally bypass the seam's idempotency guarantee.
func (*RootfsResult) Cleanup ¶
func (r *RootfsResult) Cleanup() error
Cleanup removes the staging tree. Idempotent (a second call is a no-op) and best-effort (a stale tree costs disk, not correctness).
type TapHost ¶
type TapHost interface {
Ensure(ctx context.Context, slot TapSlot) error
Remove(ctx context.Context, tapName string) error
}
TapHost is the seam for putting a TAP slot onto the host's network surface. Production impl is *internal/network/tap.Host; tests use a fake that records calls. Uses TapSlot (declared in driver.go) so the seam stays inside the runtime package's type domain.
type TapPool ¶
type TapPool interface {
Allocate(ctx context.Context, sandboxID string, now time.Time) (*TapSlot, error)
Transfer(ctx context.Context, fromID, toID string, now time.Time) (*TapSlot, error)
Release(ctx context.Context, sandboxID string) error
Get(ctx context.Context, sandboxID string) (*TapSlot, error)
}
TapPool is the interface Driver depends on for network allocation. Defined here rather than referencing *tap.Pool directly so the runtime package has no import dependency on internal/network/tap (which would drag the store into the runtime's test binary). main.go injects the real *tap.Pool, which satisfies the interface structurally.
type TapSlot ¶
type TapSlot struct {
TapName string
CIDR string
HostIP string
GuestIP string
VsockCID uint32
GuestMAC string
}
TapSlot mirrors internal/network/tap.Slot. Re-declared here to keep the import-cycle wall up; main.go's adapter converts between the two shapes. GuestMAC is optional and is set only when snapshot restore needs the host neighbor entry to match a MAC frozen in snapshot state.
type TemplateHealthNotifier ¶
type TemplateHealthNotifier interface {
MarkSnapshotCorrupt(ctx context.Context, templateID, reason string) error
}
TemplateHealthNotifier is the runtime → service seam for "I just observed a corrupt snapshot for this template". The service-layer implementation transitions the template to models.TemplateStatusUnhealthy (idempotent UPDATE WHERE status='ready') and kicks an async snapshot rebuild on the first observer; later observers see no-op transitions and skip the rebuild kick.
reason is a human-readable string for operator-facing log lines and (later) the LastError column — typically the wrapped error message produced at the verifySnapshotChecksum call site.
type TemplateResolution ¶
type TemplateResolution struct {
TemplateID string
RootfsPath string
SnapshotMemoryPath string
SnapshotStatePath string
SnapshotChecksum string
SnapshotVsockCID uint32
HasSnapshot bool
// HasOverlay reports whether the template was built with the
// per-sandbox overlay drive placeholder baked into its snapshot
// state (Phase 3 PR-B). False for PR-A templates. The driver
// rejects a snapshot-load request with OverlaySizeGB > 0 against
// a HasOverlay=false template with a clear "rebuild template"
// error rather than failing mid-PATCH — Firecracker cannot add a
// virtio-blk device post-load, only PATCH an existing one's path.
HasOverlay bool
}
TemplateResolution is the richer return shape the driver needs to pick between the cold-boot (link rootfs + boot) and snapshot-load (LoadSnapshot + Resume) paths in Create. HasSnapshot=false signals the driver to fall back to cold-boot — the rootfs is usable on its own, snapshot artifacts are not present. When HasSnapshot=true, SnapshotMemoryPath / SnapshotStatePath / SnapshotVsockCID are required; SnapshotChecksum is optional (used for integrity verification when SnapshotVerifyOnLoad is on).
type TemplateResolver ¶
type TemplateResolver interface {
Resolve(ctx context.Context, templateID string) (*TemplateResolution, error)
}
TemplateResolver maps a template ID to a *TemplateResolution describing where the prepared artifacts live on disk. Implementations are responsible for asserting that the template is in a usable state (status=ready or ready_no_snapshot) — a non-nil path returned with status=pending/failed would crash the VMM at boot. The interface lives in this package (rather than re-exporting the service-layer one) so the runtime package has no service import.
type TemplateSnapshotRequest ¶
type TemplateSnapshotRequest struct {
TemplateID string
RootfsPath string
OutMemoryPath string
OutStatePath string
GuestCID uint32
MemoryMB int
VCPU int
}
TemplateSnapshotRequest is the driver-facing snapshot input. Mirrors service.TemplateSnapshotRequest exactly so the adapter in cmd/sandboxd/main.go is a one-field cast — keeping the shapes identical keeps the seam cheap and the mental model uniform.
type TemplateSnapshotResult ¶
TemplateSnapshotResult is the driver-facing snapshot result. Mirrors service.TemplateSnapshotResult. Checksum is the joined "sha256:<hex>|sha256:<hex>" (memory|state) computed before return so the service layer can persist it as a single column.
type VMMClient ¶
type VMMClient interface {
PutMachineConfig(ctx context.Context, cfg firecracker.MachineConfig) error
PutBootSource(ctx context.Context, src firecracker.BootSource) error
PutDrive(ctx context.Context, driveID string, drv firecracker.Drive) error
PutNetworkInterface(ctx context.Context, ifaceID string, iface firecracker.NetworkInterface) error
PutVsock(ctx context.Context, v firecracker.Vsock) error
Action(ctx context.Context, a firecracker.Action) error
PatchVM(ctx context.Context, vm firecracker.VM) error
InstanceInfo(ctx context.Context) (*firecracker.InstanceInfo, error)
CreateSnapshot(ctx context.Context, req firecracker.SnapshotCreate) error
LoadSnapshot(ctx context.Context, req firecracker.SnapshotLoad) error
// PatchDrive is the snapshot-load + overlay seam (Phase 3 PR-B).
// Firecracker accepts a PATCH of `path_on_host` between LoadSnapshot
// and PATCH /vm state=Resumed; the driver uses it to swap the template's
// placeholder overlay (1 MiB scratch file from snapshot capture
// time) for the clone's own per-sandbox overlay.ext4 before the VM
// resumes. Drive geometry (read-only flag, cache type) is
// inherited from the snapshot state — only the backing path is
// mutable.
PatchDrive(ctx context.Context, driveID string, patch firecracker.DrivePatch) error
PatchNetworkInterface(ctx context.Context, ifaceID string, patch firecracker.NetworkInterfacePatch) error
}
VMMClient is the subset of *pkg/firecracker.Client Driver.Create and Driver.SnapshotTemplate use. Adding a method here is the discipline gate for "yes, this orchestration step is part of the driver" — keep it tight. CreateSnapshot / LoadSnapshot are the Phase 3 additions for the template snapshot capture and the per-sandbox snapshot-load fast boot paths respectively.
type VMMHandle ¶
type VMMHandle interface {
APISocket() string
RunDir() string
Start(ctx context.Context) error
WaitSocket(ctx context.Context, timeout time.Duration) error
Shutdown(ctx context.Context, grace time.Duration) error
Kill() error
Cleanup() error
StderrTail() string
// Pid returns the firecracker process PID, or 0 if the process has
// not started or has already exited. Phase 5 (PR 5-C) needs this so
// Driver.Create can register the running VMM with the RSS sampler
// for the effective-memory admission axis. 0 is a safe sentinel:
// the sampler rejects non-positive PIDs in Register, so a
// not-yet-started handle (or a test fake that doesn't model
// processes) silently skips registration.
Pid() int
}
VMMHandle is the subset of *vmm the driver uses. Interface form lets Create's orchestration be exercised in tests without spawning the real firecracker binary — vmm_fake.go in the test binary satisfies it and the tests inspect Start/WaitSocket/Shutdown calls directly.
type VsockDialer ¶
type VsockDialer interface {
Dial(ctx context.Context, socketPath string, cid, port uint32) (io.ReadWriteCloser, error)
}
VsockDialer is the seam for opening Firecracker's host-side vsock proxy into the guest. The driver's post-InstanceStart readiness check connects to the VMM's configured host UDS, asks Firecracker to forward to (CID=guest-cid, port=defaultVsockPort), and Pings the toolbox. The dial deadline is enforced by ctx.
Production impl is Linux-only in daemon builds, but it uses Firecracker's AF_UNIX proxy protocol rather than opening a host AF_VSOCK socket directly.
func NewLinuxVsockDialer ¶
func NewLinuxVsockDialer() VsockDialer
NewLinuxVsockDialer returns the production VsockDialer for use from cmd/sandboxd/main.go.
type WarmHandle ¶
type WarmHandle interface {
APISocket() string
RunDir() string
Shutdown(ctx context.Context, grace time.Duration) error
// Pid is the host PID of the paused firecracker process. PR 5-C
// needs this so PoolSpawner can hand it to the RSS sampler (warm
// slots count toward host memory pressure even before they're
// claimed by a sandbox). See VMMHandle.Pid for the 0-sentinel
// contract.
Pid() int
}
WarmHandle is the runtime-side handle the pool stores after a successful WarmSpawn. APISocket lets the Acquire-side code (in Driver.Create's pool-hit path) construct a fresh REST client against the paused VMM; RunDir gives the Acquire code a place to drop the per-sandbox overlay file. Shutdown is what the pool calls when it ages the slot out.
This intentionally mirrors the existing VMMHandle interface in seams.go — the production type satisfies both via embedding. The separate name keeps the Spawner contract narrow (no Start / WaitSocket / Kill exposure into the pool layer).
type WarmPool ¶
type WarmPool interface {
AcquireWithHandle(ctx context.Context, templateID, sandboxID string, now time.Time) (*vmmpool.Slot, vmmpool.SpawnedHandle, error)
Release(ctx context.Context, sandboxID string, now time.Time) error
}
WarmPool is the seam Driver.Create uses to consult the warm-VMM pool. Production impl is *internal/pool/vmm.Pool; tests inject a fake that records Acquire/Release calls without touching SQLite.
Declared as an interface so the runtime package keeps the import-cycle wall up the same way the TAP pool's TapPool seam does: internal/pool/vmm imports internal/store; the runtime package needs the SpawnedHandle/SnapshotInputs shapes (already imported via poolspawner.go) but should not depend on the Pool's full surface.
type WarmSpawnRequest ¶
type WarmSpawnRequest struct {
SlotID string
TemplateID string
SnapshotMemoryPath string
SnapshotStatePath string
SnapshotChecksum string
VsockCID uint32
HasOverlay bool
}
WarmSpawnRequest is the per-slot input the pool's Spawner hands to the runtime. Mirrors vmm.SnapshotInputs from internal/pool/vmm but is declared here so the runtime package keeps its zero-imports-from- pool-vmm property (the pool imports nothing above store, and the runtime adapter — which lives in this package — translates between the two shapes).