mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-05-23 16:31:31 +00:00
122 lines
3.8 KiB
Go
122 lines
3.8 KiB
Go
//go:build integration
|
|
|
|
// Multi-arch image index coverage. Buildx, ko, kaniko, and most modern build
|
|
// tooling push an OCI image index referencing per-platform children — exercising
|
|
// the manifest-list validation path in pkg/appview/storage/manifest_store.go
|
|
// (isManifestList check and the per-child s.Exists() loop). Without this test,
|
|
// any regression in that path goes unnoticed by the rest of the suite, which
|
|
// only pushes single-arch v1.Image.
|
|
|
|
package integration
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/google/go-containerregistry/pkg/authn"
|
|
"github.com/google/go-containerregistry/pkg/name"
|
|
v1 "github.com/google/go-containerregistry/pkg/v1"
|
|
"github.com/google/go-containerregistry/pkg/v1/empty"
|
|
"github.com/google/go-containerregistry/pkg/v1/mutate"
|
|
"github.com/google/go-containerregistry/pkg/v1/random"
|
|
"github.com/google/go-containerregistry/pkg/v1/remote"
|
|
|
|
"atcr.io/internal/testharness"
|
|
|
|
_ "github.com/distribution/distribution/v3/registry/auth/token"
|
|
_ "github.com/distribution/distribution/v3/registry/storage/driver/inmemory"
|
|
)
|
|
|
|
func TestMultiArchIndexPushPull(t *testing.T) {
|
|
h := testharness.New(t)
|
|
alice := h.AddSailor("alice.test")
|
|
|
|
amdImg, err := random.Image(1<<18, 2)
|
|
if err != nil {
|
|
t.Fatalf("amd image: %v", err)
|
|
}
|
|
armImg, err := random.Image(1<<18, 2)
|
|
if err != nil {
|
|
t.Fatalf("arm image: %v", err)
|
|
}
|
|
|
|
idx := mutate.AppendManifests(empty.Index,
|
|
mutate.IndexAddendum{Add: amdImg, Descriptor: v1.Descriptor{
|
|
Platform: &v1.Platform{Architecture: "amd64", OS: "linux"},
|
|
}},
|
|
mutate.IndexAddendum{Add: armImg, Descriptor: v1.Descriptor{
|
|
Platform: &v1.Platform{Architecture: "arm64", OS: "linux"},
|
|
}},
|
|
)
|
|
pushedDigest, err := idx.Digest()
|
|
if err != nil {
|
|
t.Fatalf("idx digest: %v", err)
|
|
}
|
|
|
|
for _, c := range Clients {
|
|
t.Run(c.Name(), func(t *testing.T) {
|
|
// Per-client repo so concurrent / matrixed runs don't collide on
|
|
// shared blob digests at the registry (same pattern as
|
|
// TestPushPullHappyPath).
|
|
ref, err := name.ParseReference(
|
|
fmt.Sprintf("%s/%s/multi-%s:latest", h.AppViewHostPort(), alice.Handle(), c.Name()),
|
|
name.Insecure,
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("parse ref: %v", err)
|
|
}
|
|
|
|
creds := h.RegistryCreds(alice)
|
|
if err := c.PushIndex(t.Context(), t, ref.String(), idx, creds); err != nil {
|
|
t.Fatalf("push index: %v", err)
|
|
}
|
|
|
|
pulledDigest, err := c.PullIndex(t.Context(), ref.String(), creds)
|
|
if err != nil {
|
|
t.Fatalf("pull index: %v", err)
|
|
}
|
|
if pushedDigest != pulledDigest {
|
|
t.Fatalf("digest mismatch: pushed=%s pulled=%s", pushedDigest, pulledDigest)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// Asserts that the appview rejects an index whose child manifest hasn't been
|
|
// pushed. manifest_store.go's per-child s.Exists() loop is the only guard
|
|
// against dangling-reference indexes; this test pins it open.
|
|
func TestMultiArchIndex_RejectsMissingChild(t *testing.T) {
|
|
h := testharness.New(t)
|
|
alice := h.AddSailor("alice.test")
|
|
|
|
orphan, err := random.Image(1<<17, 1)
|
|
if err != nil {
|
|
t.Fatalf("orphan image: %v", err)
|
|
}
|
|
idx := mutate.AppendManifests(empty.Index,
|
|
mutate.IndexAddendum{Add: orphan, Descriptor: v1.Descriptor{
|
|
Platform: &v1.Platform{Architecture: "amd64", OS: "linux"},
|
|
}},
|
|
)
|
|
|
|
// Client libraries upload child manifests + blobs alongside the index,
|
|
// so a naive PushIndex would succeed. remote.Put writes only the index
|
|
// manifest body — no children — which is exactly the dangling-reference
|
|
// scenario manifest_store.go guards against.
|
|
ref, err := name.ParseReference(
|
|
fmt.Sprintf("%s/%s/orphan-idx:latest", h.AppViewHostPort(), alice.Handle()),
|
|
name.Insecure,
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("parse ref: %v", err)
|
|
}
|
|
|
|
creds := h.RegistryCreds(alice)
|
|
err = remote.Put(ref, idx,
|
|
remote.WithAuth(&authn.Basic{Username: creds.Username, Password: creds.Password}),
|
|
)
|
|
if err == nil {
|
|
t.Fatal("expected index PUT to be rejected (orphan child manifest)")
|
|
}
|
|
}
|