Merge pull request #8213 from jedevc/export-skip-docker-manifest

archive: consistently respect value of WithSkipDockerManifest
This commit is contained in:
Kazuyoshi Kato 2023-03-14 15:01:08 -07:00 committed by GitHub
commit a570c8184a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 123 additions and 23 deletions

View File

@ -188,7 +188,7 @@ func Export(ctx context.Context, store content.Provider, writer io.Writer, opts
} }
name := desc.Annotations[images.AnnotationImageName] name := desc.Annotations[images.AnnotationImageName]
if name != "" && !eo.skipDockerManifest { if name != "" {
mt.names = append(mt.names, name) mt.names = append(mt.names, name)
} }
case images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex: case images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex:
@ -227,26 +227,24 @@ func Export(ctx context.Context, store content.Provider, writer io.Writer, opts
records = append(records, r...) records = append(records, r...)
} }
if !eo.skipDockerManifest { if len(manifests) >= 1 {
if len(manifests) >= 1 { if len(manifests) > 1 {
if len(manifests) > 1 { sort.SliceStable(manifests, func(i, j int) bool {
sort.SliceStable(manifests, func(i, j int) bool { if manifests[i].Platform == nil {
if manifests[i].Platform == nil { return false
return false }
} if manifests[j].Platform == nil {
if manifests[j].Platform == nil { return true
return true }
} return eo.platform.Less(*manifests[i].Platform, *manifests[j].Platform)
return eo.platform.Less(*manifests[i].Platform, *manifests[j].Platform) })
})
}
d = manifests[0].Digest
dManifests[d] = &exportManifest{
manifest: manifests[0],
}
} else if eo.platform != nil {
return fmt.Errorf("no manifest found for platform: %w", errdefs.ErrNotFound)
} }
d = manifests[0].Digest
dManifests[d] = &exportManifest{
manifest: manifests[0],
}
} else if eo.platform != nil {
return fmt.Errorf("no manifest found for platform: %w", errdefs.ErrNotFound)
} }
resolvedIndex[desc.Digest] = d resolvedIndex[desc.Digest] = d
} }
@ -262,7 +260,7 @@ func Export(ctx context.Context, store content.Provider, writer io.Writer, opts
} }
} }
if len(dManifests) > 0 { if !eo.skipDockerManifest && len(dManifests) > 0 {
tr, err := manifestsRecord(ctx, store, dManifests) tr, err := manifestsRecord(ctx, store, dManifests)
if err != nil { if err != nil {
return fmt.Errorf("unable to create manifests file: %w", err) return fmt.Errorf("unable to create manifests file: %w", err)

View File

@ -18,13 +18,18 @@ package client
import ( import (
"archive/tar" "archive/tar"
"context"
"encoding/json"
"io" "io"
"os" "os"
"testing" "testing"
. "github.com/containerd/containerd" . "github.com/containerd/containerd"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/images/archive" "github.com/containerd/containerd/images/archive"
"github.com/containerd/containerd/platforms" "github.com/containerd/containerd/platforms"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
) )
// TestExport exports testImage as a tar stream // TestExport exports testImage as a tar stream
@ -61,14 +66,103 @@ func TestExport(t *testing.T) {
// Seek to beginning of file before passing it to assertOCITar() // Seek to beginning of file before passing it to assertOCITar()
dstFile.Seek(0, 0) dstFile.Seek(0, 0)
assertOCITar(t, dstFile) assertOCITar(t, dstFile, true)
} }
func assertOCITar(t *testing.T, r io.Reader) { // TestExportDockerManifest exports testImage as a tar stream, using the
// WithSkipDockerManifest option
func TestExportDockerManifest(t *testing.T) {
if testing.Short() {
t.Skip()
}
ctx, cancel := testContext(t)
defer cancel()
client, err := New(address)
if err != nil {
t.Fatal(err)
}
defer client.Close()
_, err = client.Fetch(ctx, testImage)
if err != nil {
t.Fatal(err)
}
dstFile, err := os.CreateTemp("", "export-import-test")
if err != nil {
t.Fatal(err)
}
defer func() {
dstFile.Close()
os.Remove(dstFile.Name())
}()
img, err := client.ImageService().Get(ctx, testImage)
if err != nil {
t.Fatal(err)
}
// test multi-platform export
err = client.Export(ctx, dstFile, archive.WithManifest(img.Target), archive.WithSkipDockerManifest())
if err != nil {
t.Fatal(err)
}
dstFile.Seek(0, 0)
assertOCITar(t, dstFile, false)
// reset to beginning
dstFile.Seek(0, 0)
// test single-platform export
var result ocispec.Descriptor
err = images.Walk(ctx, images.HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
switch desc.MediaType {
case images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest:
p, err := content.ReadBlob(ctx, client.ContentStore(), desc)
if err != nil {
return nil, err
}
var manifest ocispec.Manifest
if err := json.Unmarshal(p, &manifest); err != nil {
return nil, err
}
if desc.Platform == nil || platforms.Default().Match(platforms.Normalize(*desc.Platform)) {
result = desc
}
return nil, nil
case images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex:
p, err := content.ReadBlob(ctx, client.ContentStore(), desc)
if err != nil {
return nil, err
}
var idx ocispec.Index
if err := json.Unmarshal(p, &idx); err != nil {
return nil, err
}
return idx.Manifests, nil
}
return nil, nil
}), img.Target)
if err != nil {
t.Fatal(err)
}
err = client.Export(ctx, dstFile, archive.WithManifest(result), archive.WithSkipDockerManifest())
if err != nil {
t.Fatal(err)
}
dstFile.Seek(0, 0)
assertOCITar(t, dstFile, false)
}
func assertOCITar(t *testing.T, r io.Reader, docker bool) {
// TODO: add more assertion // TODO: add more assertion
tr := tar.NewReader(r) tr := tar.NewReader(r)
foundOCILayout := false foundOCILayout := false
foundIndexJSON := false foundIndexJSON := false
foundManifestJSON := false
for { for {
h, err := tr.Next() h, err := tr.Next()
if err == io.EOF { if err == io.EOF {
@ -84,6 +178,9 @@ func assertOCITar(t *testing.T, r io.Reader) {
if h.Name == "index.json" { if h.Name == "index.json" {
foundIndexJSON = true foundIndexJSON = true
} }
if h.Name == "manifest.json" {
foundManifestJSON = true
}
} }
if !foundOCILayout { if !foundOCILayout {
t.Error("oci-layout not found") t.Error("oci-layout not found")
@ -91,4 +188,9 @@ func assertOCITar(t *testing.T, r io.Reader) {
if !foundIndexJSON { if !foundIndexJSON {
t.Error("index.json not found") t.Error("index.json not found")
} }
if docker && !foundManifestJSON {
t.Error("manifest.json not found")
} else if !docker && foundManifestJSON {
t.Error("manifest.json found")
}
} }