image pull: dispatch to imagecat bundle path before OCI

PullImage now checks the embedded imagecat catalog first. If the
ref matches a catalog entry, it takes the bundle path:

  1. Fetch the .tar.zst bundle into a staging dir (rootfs.ext4 +
     manifest.json).
  2. Strip manifest.json (staging-only metadata).
  3. Stage kernel/initrd/modules alongside rootfs.ext4.
  4. Publish the staging dir and upsert the image row.

Bundle rootfs is already flattened + ownership-fixed + agent-
injected at build time, so the daemon-side work is strictly I/O —
no flatten, no mkfs, no debugfs.

Kernel resolution in the bundle path: --kernel-ref > entry.kernel_ref
> --kernel/--initrd/--modules.

If the ref doesn't match a catalog entry, PullImage falls through
to the existing OCI path unchanged (extracted into pullFromOCI).

New test seam: d.bundleFetch. Six unit tests cover happy path,
--kernel-ref override, existing-name rejection, kernel-required
error, fetch-failure cleanup, and the catalog → OCI fallthrough.

CLI help updated: image pull now documents both forms and takes
<name-or-oci-ref> instead of requiring an OCI ref.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Thales Maciel 2026-04-17 15:43:33 -03:00
parent d22d05555c
commit 5bdc9985c2
No known key found for this signature in database
GPG key ID: 33112E6833C34679
4 changed files with 391 additions and 19 deletions

View file

@ -1811,16 +1811,30 @@ func newImagePullCommand() *cobra.Command {
sizeRaw string
)
cmd := &cobra.Command{
Use: "pull <oci-ref>",
Short: "Pull an OCI image and register it as a managed banger image",
Long: "Download an OCI image (e.g. docker.io/library/debian:bookworm), " +
"flatten its layers into an ext4 rootfs, and register the result as a " +
"managed image. Kernel info is required (via --kernel-ref or direct paths). " +
"\n\nNote: Phase A primitive — file ownership in the produced ext4 reflects " +
"the runner's uid/gid, not the OCI tar headers, so the resulting image is " +
"suitable as a base for `image build` but is not directly bootable until a " +
"future ownership-fixup pass lands.",
Args: exactArgsUsage(1, "usage: banger image pull <oci-ref> [--name <name>] (--kernel-ref <name> | --kernel <path> [--initrd <path>] [--modules <dir>]) [--size <human>]"),
Use: "pull <name-or-oci-ref>",
Short: "Pull an image bundle (catalog name) or OCI image and register it",
Long: strings.TrimSpace(`
Pull an image into banger. Two paths:
Catalog name (e.g. 'debian-bookworm')
Fetches a pre-built bundle from the embedded imagecat catalog.
Kernel-ref comes from the catalog entry; --kernel-ref still
overrides.
OCI reference (e.g. 'docker.io/library/debian:bookworm')
Pulls the image, flattens its layers, fixes ownership, injects
banger's guest agents. --kernel-ref or direct --kernel/--initrd/
--modules are required.
Use 'banger image catalog' to see available catalog names (once that
subcommand lands).
`),
Example: strings.TrimSpace(`
banger image pull debian-bookworm
banger image pull debian-bookworm --name sandbox
banger image pull docker.io/library/debian:bookworm --kernel-ref generic-6.12
`),
Args: exactArgsUsage(1, "usage: banger image pull <name-or-oci-ref> [--name <name>] [--kernel-ref <name>] [--kernel <path>] [--initrd <path>] [--modules <dir>] [--size <human>]"),
RunE: func(cmd *cobra.Command, args []string) error {
params.Ref = args[0]
if strings.TrimSpace(params.KernelRef) != "" && (params.KernelPath != "" || params.InitrdPath != "" || params.ModulesDir != "") {