testutil/manifests.go (232 lines of code) (raw):

package testutil import ( "fmt" "io" "testing" "github.com/docker/distribution" "github.com/docker/distribution/context" "github.com/docker/distribution/manifest" "github.com/docker/distribution/manifest/manifestlist" mlcompat "github.com/docker/distribution/manifest/manifestlist/compat" "github.com/docker/distribution/manifest/schema1" "github.com/docker/distribution/manifest/schema2" "github.com/docker/libtrust" "github.com/opencontainers/go-digest" v1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/stretchr/testify/require" ) type Image struct { manifest distribution.Manifest ManifestDigest digest.Digest Layers map[digest.Digest]io.ReadSeeker } type ImageList struct { manifest distribution.Manifest ManifestDigest digest.Digest Images []Image } const SampleManifestJSON = `{"schemaVersion":2,"mediaType":"application/vnd.docker.distribution.manifest.v2+json","config":{"mediaType":"application/vnd.docker.container.image.v1+json","size":1640,"digest":"sha256:ea8a54fd13889d3649d0a4e45735116474b8a650815a2cda4940f652158579b9"},"layers":[{"mediaType":"application/vnd.docker.image.rootfs.diff.tar.gzip","size":2802957,"digest":"sha256:c9b1b535fdd91a9855fb7f82348177e5f019329a58c53c47272962dd60f71fc9"},{"mediaType":"application/vnd.docker.image.rootfs.diff.tar.gzip","size":108,"digest":"sha256:6b0937e234ce911b75630b744fb12836fe01bda5f7db203927edbb1390bc7e21"}]}` // MakeManifestList constructs a manifest list out of a list of manifest digests func MakeManifestList(blobstatter distribution.BlobStatter, manifestDigests []digest.Digest) (*manifestlist.DeserializedManifestList, error) { ctx := context.Background() var manifestDescriptors []manifestlist.ManifestDescriptor for _, manifestDigest := range manifestDigests { descriptor, err := blobstatter.Stat(ctx, manifestDigest) if err != nil { return nil, err } platformSpec := manifestlist.PlatformSpec{ Architecture: "atari2600", OS: "CP/M", Variant: "ternary", Features: []string{"VLIW", "superscalaroutoforderdevnull"}, } manifestDescriptor := manifestlist.ManifestDescriptor{ Descriptor: descriptor, Platform: platformSpec, } manifestDescriptors = append(manifestDescriptors, manifestDescriptor) } return manifestlist.FromDescriptors(manifestDescriptors) } // MakeSchema1Manifest constructs a schema 1 manifest from a given list of digests and returns // the digest of the manifest func MakeSchema1Manifest(digests []digest.Digest) (distribution.Manifest, error) { m := schema1.Manifest{ Versioned: manifest.Versioned{ SchemaVersion: 1, }, Name: "who", Tag: "cares", } for _, digest := range digests { m.FSLayers = append(m.FSLayers, schema1.FSLayer{BlobSum: digest}) m.History = append(m.History, schema1.History{V1Compatibility: ""}) } pk, err := libtrust.GenerateECP256PrivateKey() if err != nil { return nil, fmt.Errorf("unexpected error generating private key: %v", err) } signedManifest, err := schema1.Sign(&m, pk) if err != nil { return nil, fmt.Errorf("error signing manifest: %v", err) } return signedManifest, nil } // MakeSchema2Manifest constructs a schema 2 manifest from a given list of digests and returns // the digest of the manifest func MakeSchema2Manifest(repository distribution.Repository, digests []digest.Digest) (distribution.Manifest, error) { ctx := context.Background() blobStore := repository.Blobs(ctx) builder := schema2.NewManifestBuilder(blobStore, schema2.MediaTypeImageConfig, make([]byte, 0)) for _, digest := range digests { err := builder.AppendReference(distribution.Descriptor{Digest: digest}) if err != nil { return nil, fmt.Errorf("appending reference: %w", err) } } m, err := builder.Build(ctx) if err != nil { return nil, fmt.Errorf("unexpected error generating manifest: %v", err) } return m, nil } func UploadRandomSchema1Image(repository distribution.Repository) (Image, error) { randomLayers, err := CreateRandomLayers(2) if err != nil { return Image{}, err } digests := make([]digest.Digest, 0) for digest := range randomLayers { digests = append(digests, digest) } m, err := MakeSchema1Manifest(digests) if err != nil { return Image{}, err } manifestDigest, err := UploadImage(repository, Image{manifest: m, Layers: randomLayers}) if err != nil { return Image{}, err } return Image{ manifest: m, ManifestDigest: manifestDigest, Layers: randomLayers, }, nil } func UploadRandomSchema2Image(repository distribution.Repository) (Image, error) { randomLayers, err := CreateRandomLayers(2) if err != nil { return Image{}, err } digests := make([]digest.Digest, 0) for digest := range randomLayers { digests = append(digests, digest) } m, err := MakeSchema2Manifest(repository, digests) if err != nil { return Image{}, err } manifestDigest, err := UploadImage(repository, Image{manifest: m, Layers: randomLayers}) if err != nil { return Image{}, err } return Image{ manifest: m, ManifestDigest: manifestDigest, Layers: randomLayers, }, nil } func UploadRandomImageList(tb testing.TB, registry distribution.Namespace, repository distribution.Repository) ImageList { ctx := context.Background() var manifestDescriptors []manifestlist.ManifestDescriptor images := make([]Image, 4) for i := range images { image, err := UploadRandomSchema2Image(repository) require.NoError(tb, err) images[i] = image blobstatter := registry.BlobStatter() descriptor, err := blobstatter.Stat(ctx, image.ManifestDigest) require.NoError(tb, err) // Set correct mediatype for image descriptor. mt, _, err := image.manifest.Payload() require.NoError(tb, err) descriptor.MediaType = mt platformSpec := manifestlist.PlatformSpec{ Architecture: "atari2600", OS: "CP/M", Variant: "ternary", Features: []string{"VLIW", "superscalaroutoforderdevnull"}, } manifestDescriptor := manifestlist.ManifestDescriptor{ Descriptor: descriptor, Platform: platformSpec, } manifestDescriptors = append(manifestDescriptors, manifestDescriptor) } ml, err := manifestlist.FromDescriptors(manifestDescriptors) require.NoError(tb, err) manifestService, err := repository.Manifests(ctx) require.NoError(tb, err) dgst, err := manifestService.Put(ctx, ml) require.NoError(tb, err) return ImageList{ manifest: ml, ManifestDigest: dgst, Images: images, } } func UploadRandomNonConformantBuildxCache(tb testing.TB, registry distribution.Namespace, repository distribution.Repository) ImageList { ctx := context.Background() var manifestDescriptors []manifestlist.ManifestDescriptor randomLayers, err := CreateRandomLayers(4) require.NoError(tb, err) err = UploadBlobs(repository, randomLayers) require.NoError(tb, err) for layerDigest := range randomLayers { blobstatter := registry.BlobStatter() descriptor, err := blobstatter.Stat(ctx, layerDigest) require.NoError(tb, err) platformSpec := manifestlist.PlatformSpec{ Architecture: "atari2600", OS: "CP/M", Variant: "ternary", Features: []string{"VLIW", "superscalaroutoforderdevnull"}, } manifestDescriptor := manifestlist.ManifestDescriptor{ Descriptor: descriptor, Platform: platformSpec, } manifestDescriptors = append(manifestDescriptors, manifestDescriptor) } // Ensure one reference looks like a buildx cache config for validation. manifestDescriptors[0].Descriptor.MediaType = mlcompat.MediaTypeBuildxCacheConfig ml, err := manifestlist.FromDescriptors(manifestDescriptors) require.NoError(tb, err) ml.MediaType = v1.MediaTypeImageIndex manifestService, err := repository.Manifests(ctx) require.NoError(tb, err) dgst, err := manifestService.Put(ctx, ml) require.NoError(tb, err) return ImageList{ manifest: ml, ManifestDigest: dgst, } } func UploadImage(repository distribution.Repository, im Image) (digest.Digest, error) { // upload layers err := UploadBlobs(repository, im.Layers) if err != nil { return "", fmt.Errorf("layer upload failed: %v", err) } // upload manifest ctx := context.Background() manifestService, err := MakeManifestService(repository) if err != nil { return "", fmt.Errorf("failed to create manifest service: %v", err) } manifestDigest, err := manifestService.Put(ctx, im.manifest) if err != nil { return "", fmt.Errorf("manifest upload failed: %v", err) } return manifestDigest, nil } func MakeManifestService(repository distribution.Repository) (distribution.ManifestService, error) { ctx := context.Background() manifestService, err := repository.Manifests(ctx) if err != nil { return nil, fmt.Errorf("failed to construct manifest store: %v", err) } return manifestService, nil }