imagecat: catalog + fetch for banger image bundles
New package mirroring `kernelcat`: catalog + SHA256-verified HTTP fetch of `.tar.zst` bundles that contain rootfs.ext4 + manifest.json. Mounted empty (version:1, entries:[]) so nothing is pullable via the bundle path yet; wiring into `banger image pull` lands in a later phase. - catalog.go: Catalog/CatEntry, LoadEmbedded, ParseCatalog, Lookup, ValidateName. - fetch.go: Fetch(ctx, client, destDir, entry) downloads the bundle, verifies sha256, extracts exactly rootfs.ext4 and manifest.json into destDir, returns the parsed manifest. Rejects unexpected tar entries, unsafe paths, non-regular files, and cleans up partial writes on failure. - Thirteen unit tests (happy path + every failure mode). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
da471b0640
commit
3d9ae624b1
5 changed files with 597 additions and 0 deletions
80
internal/imagecat/catalog_test.go
Normal file
80
internal/imagecat/catalog_test.go
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
package imagecat
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLoadEmbeddedReturnsVersion1(t *testing.T) {
|
||||
cat, err := LoadEmbedded()
|
||||
if err != nil {
|
||||
t.Fatalf("LoadEmbedded: %v", err)
|
||||
}
|
||||
if cat.Version != 1 {
|
||||
t.Fatalf("Version = %d, want 1", cat.Version)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseCatalogAcceptsNilAndEmpty(t *testing.T) {
|
||||
for _, data := range [][]byte{nil, {}} {
|
||||
cat, err := ParseCatalog(data)
|
||||
if err != nil {
|
||||
t.Fatalf("ParseCatalog(%q): %v", data, err)
|
||||
}
|
||||
if cat.Version != 0 || len(cat.Entries) != 0 {
|
||||
t.Fatalf("ParseCatalog returned non-zero catalog: %+v", cat)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseCatalogRejectsMalformed(t *testing.T) {
|
||||
if _, err := ParseCatalog([]byte("not json")); err == nil {
|
||||
t.Fatal("want parse error for malformed catalog")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookupHitAndMiss(t *testing.T) {
|
||||
cat := Catalog{
|
||||
Version: 1,
|
||||
Entries: []CatEntry{
|
||||
{Name: "debian-bookworm", TarballURL: "https://example.com/a.tar.zst", TarballSHA256: "deadbeef"},
|
||||
},
|
||||
}
|
||||
hit, err := cat.Lookup("debian-bookworm")
|
||||
if err != nil {
|
||||
t.Fatalf("Lookup hit: %v", err)
|
||||
}
|
||||
if hit.TarballURL != "https://example.com/a.tar.zst" {
|
||||
t.Fatalf("unexpected entry: %+v", hit)
|
||||
}
|
||||
if _, err := cat.Lookup("nope"); !errors.Is(err, os.ErrNotExist) {
|
||||
t.Fatalf("Lookup miss error = %v, want ErrNotExist", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateName(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
ok bool
|
||||
}{
|
||||
{"debian-bookworm", true},
|
||||
{"alpine-3.20", true},
|
||||
{"generic-6.12", true},
|
||||
{"a", true},
|
||||
{"", false},
|
||||
{" ", false},
|
||||
{"-starts-with-hyphen", false},
|
||||
{"has spaces", false},
|
||||
{"has/slash", false},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
err := ValidateName(tc.name)
|
||||
if tc.ok && err != nil {
|
||||
t.Errorf("ValidateName(%q): unexpected error %v", tc.name, err)
|
||||
}
|
||||
if !tc.ok && err == nil {
|
||||
t.Errorf("ValidateName(%q): expected error", tc.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue