Image Pipeline
5 minute read
Use packaging/image-pipeline/build.sh in the Barn source checkout to validate or offline-normalize a downloaded qcow2 candidate against an expected SHA-256 digest. The builder runs locally without network access, signing keys, or changes to Barn runtime state, and produces a testing candidate bundle ready for smoke testing and release review.
Host requirements:
- Tools: Python 3 and
qemu-imgfor all modes;offlinemode also requires workinglibguestfstools (virt-customizeandvirt-cat). You can override tool binaries with--qemu-img,--virt-customize, and--virt-cator theQEMU_IMG,VIRT_CUSTOMIZE, andVIRT_CATenvironment variables. - Out-of-tree storage: Keep source images, RPM/DEB package caches, and output directories outside the Git working tree (the repository tracks recipes and lockfiles only).
- Architecture:
--archrecords the target metadata architecture; runofflinecustomization on a host wherelibguestfscan execute the guest’s architecture.
Modes and profiles
validate: Copies and re-hashes the source image, verifies a single-fileqcow2structure withqemu-img check, and writes an evidence bundle without modifying guest files or credentials.offline: Runsvirt-customize --no-networkandvirt-caton the staged copy to normalize guest access: it verifies that UID/GID88is free of unrelated accounts, configures the lockeddba/adminidentity, disables SSH password and root login, strips SSH host keys, shell history, machine-id, andcloud-initcaches, restores SELinux labels, and verifies a deterministic guest marker.
build.sh defaults to --profile base. The available profiles are:
| Profile | Normalization steps | Package inputs |
|---|---|---|
base |
Account, sudo, SSH, and cloud-init normalization only | None (package flags rejected) |
el8 |
Base normalization plus Python 3, SSH drop-in include, legacy network cleanup, chronyd, and SELinux relabeling |
Locked RPM dependency set |
el9 |
Base normalization plus legacy network cleanup and SELinux relabeling | None (package flags rejected) |
d12, d13 |
Base normalization plus offline XFS userspace tools and en_US.UTF-8 / C.UTF-8 locale setup |
Locked DEB dependency set |
validate mode accepts only --profile base. In offline mode, el8, d12, and d13 require both --package-lock and --package-cache matching the profile and architecture. For official catalog targets, use build-official.py to drive these inputs automatically.
Official candidate matrix
packaging/image-pipeline/build-official.py builds the eight offline-normalized official targets—Debian 12/13 (d12, d13) and Rocky Linux 8/9 (el8, el9) on amd64 and arm64—using the upstream URLs, package locks, release versions, SHA-256 digests, and SOURCE_DATE_EPOCH pinned in official-v1.json (the catalog’s Ubuntu artifacts come from the same recipe run directly by pipeline.py; Ubuntu cloud images already ship xfsprogs, so they need no offline package lock).
Usage notes:
- Create
--source-cache,--package-cache, and--outputbeforehand and pass canonical absolute paths. With--fetch, the wrapper downloads any missing pinned HTTPS inputs and verifies their SHA-256 digests before runningvirt-customize --no-network; without--fetch, all locked inputs must already be present in the cache directories. - Omit
--targetto build all eight targets, or repeat--targetto build a subset.--listprints the exact releases pinned in your checkout.
Once all eight target bundles have been built, assemble them into a candidate static repository by passing the parent directory (or repeating --assemble-from across multiple parent directories) that contains the bundle directories:
--assemble-from requires exactly one bundle for each of the eight targets and writes to a new --output path that does not yet exist, invoking barn repo build and barn repo verify (using barn on PATH or --barn /absolute/path/to/barn). The assembled repository maps its channels to candidate (for example d13:candidate) with status testing so you can run live VM acceptance checks before promoting images to stable and supported.
Validate a single downloaded image
Path and size constraints:
--sourcemust be a canonical absolute path to a regular file (not a symlink) up to 16 GiB, and--outputmust be a new path inside an existing parent directory.- Barn validates runtime image artifacts against their declared catalog sizes (or actual file size on local import; see Local image import). The contributor pipeline retains its own recipe bounds.
- The builder acquires an adjacent lockfile, stages work in a
0700temporary directory, and atomically renames the completed bundle into--output(cleaning up only its staging directory on failure).
Each completed output bundle contains:
| File | Purpose |
|---|---|
<name>-<release>-<arch>-<digest-prefix>.qcow2 |
Validated read-only candidate qcow2 image |
build-recipe.json |
Builder invocation, tool versions, and input digests |
manifest-candidate.json |
Unsigned candidate metadata (status: "testing") |
provenance.intoto.json |
SLSA build provenance statement |
sbom.spdx.json |
SPDX software bill of materials covering declared pipeline inputs and injected packages |
validation.json |
Before/after qcow2 checks, normalization marker results, and pending release gates |
checksums.txt |
SHA-256 checksums of all files in the bundle |
package-lock.json |
Included when the profile installs locked RPM/DEB packages |
With fixed inputs and tool versions, validate mode is byte-for-byte reproducible; for offline mode, build the candidate twice and compare the bundles when preparing release evidence. Promoting a candidate to a public supported release requires running live VM smoke tests across target host/guest combinations, updating repo.yaml with the new catalog revision and supported status, running barn repo build and barn repo verify, signing catalog.json.minisig, and verifying the published repository endpoints.