banger internal make-bundle: build image bundles from flat rootfs tars
New hidden subcommand that turns a `docker export`-style rootfs tar
into a banger bundle (`rootfs.ext4` + `manifest.json`, tar+zstd):
1. FlattenTar (new in imagepull) extracts the stream into a staging
dir while capturing per-file uid/gid/mode into a Metadata record.
2. imagepull.BuildExt4 produces the ext4 via `mkfs.ext4 -d`.
3. imagepull.ApplyOwnership re-applies the captured metadata with
`debugfs sif` so setuid/root-owned files keep their identity.
4. imagepull.InjectGuestAgents drops the vsock agent + network
bootstrap + first-boot service into the ext4.
5. manifest.json is written with name/distro/arch/kernel_ref.
6. Both files are packaged as .tar.zst with max compression.
Flags: --rootfs-tar (file or '-' for stdin), --name, --distro, --arch,
--kernel-ref, --description, --size, --out. Stdout prints bundle path,
sha256, and size so callers can patch the catalog.
Unit tests cover flag registration, required-arg validation, the
bundle tar round-trip, sha256HexFile, and dirSize. An end-to-end test
runs the full pipeline against a synthesized tiny rootfs tar; skips
gracefully when mkfs.ext4 / debugfs / companion binaries are missing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3d9ae624b1
commit
bb95a0a273
3 changed files with 623 additions and 0 deletions
|
|
@ -41,6 +41,42 @@ func newMetadata() Metadata {
|
|||
return Metadata{Entries: make(map[string]FileMeta)}
|
||||
}
|
||||
|
||||
// FlattenTar reads a single flat tar stream (e.g. the output of
|
||||
// `docker export`) into destDir, returning per-file metadata. Unlike
|
||||
// Flatten this does NOT treat the input as OCI-layered — there are no
|
||||
// whiteouts, no previous layers. Whiteout markers, if they somehow
|
||||
// appear, are still handled by applyEntry but should never be present
|
||||
// in a docker-export stream.
|
||||
//
|
||||
// destDir must exist. Path-traversal members and symlink targets that
|
||||
// escape destDir are rejected.
|
||||
func FlattenTar(ctx context.Context, r io.Reader, destDir string) (Metadata, error) {
|
||||
meta := newMetadata()
|
||||
absDest, err := filepath.Abs(destDir)
|
||||
if err != nil {
|
||||
return meta, err
|
||||
}
|
||||
if err := ctx.Err(); err != nil {
|
||||
return meta, err
|
||||
}
|
||||
tr := tar.NewReader(r)
|
||||
for {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return meta, err
|
||||
}
|
||||
hdr, err := tr.Next()
|
||||
if err == io.EOF {
|
||||
return meta, nil
|
||||
}
|
||||
if err != nil {
|
||||
return meta, fmt.Errorf("read tar entry: %w", err)
|
||||
}
|
||||
if err := applyEntry(tr, hdr, absDest, &meta); err != nil {
|
||||
return meta, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Flatten replays the image's layers in oldest-first order into destDir
|
||||
// and returns a Metadata record of each surviving file's tar-header
|
||||
// ownership/mode. destDir must exist and ideally be empty. Path-traversal
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue