oci

package
v0.7.21 Latest Latest
Warning

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

Go to latest
Published: Jul 21, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package oci builds an ext4 root filesystem image from an OCI container reference. It is the input to the Firecracker driver's cold-boot path: given "docker://python:3.11", produce a single file rootfs.ext4 that a Firecracker drive entry can point at.

The pipeline is three subprocesses chained on the host:

  1. skopeo copy docker://<ref> oci:<staging-dir>:<tag> Pull the image manifest + layer blobs into a local OCI-layout dir. Stateless on success; failure leaves the staging dir partial and the caller is expected to clean up via the Result's CleanupDir method.

  2. umoci unpack --image <staging-dir>:<tag> <bundle-dir> Materialize the layers into a runc-style bundle. The rootfs lives at <bundle-dir>/rootfs/. config.json is written too but ignored — we read the OCI config from the staging dir directly so layer ordering is unambiguous.

  3. mkfs.ext4 -d <bundle-dir>/rootfs/ -F <out.ext4> <size> Build a single-file ext4 filesystem from the materialized rootfs. The size is ALWAYS passed explicitly (mke2fs refuses to size a new file from -d alone); it is derived from the unpacked rootfs plus metadata/journal + guest headroom, floored by MinSizeMiB. See ext4SizeMiB / runMkfs.

What does NOT live here: the per-sandbox overlay file (that's the runtime driver's job; the overlay is a sparse file plus a writeback drive entry pointing at it). What lives here is only the base read-only rootfs.ext4 that overlay sits on top of.

External-tool dependencies are intentional — reimplementing OCI unpack and ext4 in Go would be a project on its own. The tools are well-known, scriptable, and the test wraps them with a fake-binary shim so the package's own tests don't require them on the host. Production hosts install skopeo + umoci + e2fsprogs via Ansible.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type BuildRequest

type BuildRequest struct {
	ImageRef   string
	OutPath    string
	MinSizeMiB int
	// Tag is the OCI tag to apply inside the staging layout. Almost
	// always "latest" — kept as a knob because skopeo's docker://
	// transport sometimes needs the tag explicitly when the source ref
	// doesn't carry one.
	Tag string
	// InjectFiles are host-provided files written into the unpacked rootfs
	// AFTER umoci and BEFORE mkfs, so they become part of the ext4 image.
	// The Firecracker cold-boot path uses this to bake the in-guest agent
	// (toolboxd), its init shim, and the per-sandbox token into an
	// otherwise-stock OCI image — a plain image has no agent, so without
	// this the guest boots with nothing listening on vsock (see
	// internal/runtime/firecracker cold-boot agent injection). Empty for
	// template builds, which expect the operator's image to already carry
	// an init that brings the agent up.
	InjectFiles []InjectFile
}

BuildRequest is one image build's inputs. ImageRef is a skopeo-style reference ("docker://python:3.11", "oci-archive:/path/foo.tar", etc.) — skopeo handles the transport prefix, so the daemon passes it through verbatim. OutPath is the destination rootfs.ext4 file; existing files are overwritten (mkfs.ext4 -F). MinSizeMiB rounds the image up so the guest has writable headroom on a sparse-but-fixed-size ext4; zero means "minimum size as computed by mkfs.ext4 -d".

type Builder

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

Builder is a Config-bound handle for kicking off image builds. The zero value is not usable; construct via New.

func New

func New(cfg Config) (*Builder, error)

New validates the config and returns a Builder. All three tool paths are required: a missing path at boot is preferable to a confusing "exec: <empty>: no such file or directory" at the first sandbox create.

func (*Builder) Build

func (b *Builder) Build(ctx context.Context, req BuildRequest) (*Result, error)

Build runs the three-stage pipeline. Each stage's stderr tail is surfaced in the returned error on failure so the operator sees the real cause (most commonly "401 unauthorized" from skopeo or "no space left" from mkfs.ext4). On success the temp tree is preserved at Result.StagingDir until the caller cleans it up.

type Config

type Config struct {
	// SkopeoBin: path to `skopeo`. Pulls the OCI image. Apt/yum/brew
	// install ship skopeo as a static binary; the daemon does NOT
	// expect a Skopeo daemon.
	SkopeoBin string
	// UmociBin: path to `umoci`. Unpacks OCI layout into a bundle.
	UmociBin string
	// Mkfs4Bin: path to `mkfs.ext4` (from e2fsprogs). Builds the
	// single-file ext4 image from the unpacked rootfs.
	Mkfs4Bin string
	// WorkDir: the parent directory under which per-build staging trees
	// live. Each Builder.Build call creates a fresh subdirectory here.
	// Should be on a filesystem with at least 2x the largest image size
	// in free space (skopeo + umoci both materialize layers).
	WorkDir string
}

Config carries the absolute paths to the external tools, so the daemon's config can override them and tests can substitute fakes without an env-var dance. All three are required; New rejects an empty path rather than silently picking up something on $PATH (which would be a security smell on a daemon that pulls remote images).

type InjectFile

type InjectFile struct {
	HostPath  string
	Content   []byte
	GuestPath string
	Mode      os.FileMode
}

InjectFile is one file to write into the rootfs before mkfs. Exactly one of HostPath or Content supplies the bytes: HostPath copies an existing host file (e.g. the toolboxd binary), Content writes inline bytes (e.g. a generated init script or a per-sandbox env file). GuestPath is the absolute path inside the guest ("/usr/local/bin/toolboxd"); parent dirs are created. Mode is the file mode applied to the written file.

type Result

type Result struct {
	RootfsPath string
	StagingDir string
	SizeBytes  int64
}

Result reports a finished build. RootfsPath is OutPath echoed back for the caller's convenience. StagingDir is the per-build temp tree; callers should CleanupDir after they have the rootfs in a permanent location. SizeBytes is the rootfs.ext4 file size after mkfs (NOT the unpacked-rootfs size — guests see this as the disk size).

func (Result) CleanupDir

func (r Result) CleanupDir() error

CleanupDir removes the staging directory. Best-effort: a stale tree only costs disk, not correctness. The runtime driver calls this once it has hard-linked the rootfs into the per-template directory.

Jump to

Keyboard shortcuts

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