c8d/save: Add tests

Test saving a shallow/partial image

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
This commit is contained in:
Paweł Gronowski
2024-10-22 11:50:03 +02:00
parent 5f0f32ba98
commit db68a019fd
2 changed files with 159 additions and 0 deletions

View File

@@ -0,0 +1,103 @@
package containerd
import (
"context"
"io"
"path/filepath"
"testing"
"github.com/containerd/containerd/namespaces"
"github.com/containerd/platforms"
"github.com/docker/docker/errdefs"
"github.com/docker/docker/internal/testutils/specialimage"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
func TestImageMultiplatformSaveShallowWithNative(t *testing.T) {
ctx := namespaces.WithNamespace(context.TODO(), "testing-"+t.Name())
contentDir := t.TempDir()
store := &blobsDirContentStore{blobs: filepath.Join(contentDir, "blobs/sha256")}
native := platforms.Platform{
OS: "linux",
Architecture: "amd64",
}
arm64 := platforms.Platform{
OS: "linux",
Architecture: "arm64",
}
imgSvc := fakeImageService(t, ctx, store)
// Mock the native platform.
imgSvc.defaultPlatformOverride = platforms.Only(native)
idx, _, err := specialimage.PartialMultiPlatform(contentDir, "partial-with-native:latest", specialimage.PartialOpts{
Stored: []ocispec.Platform{native},
Missing: []ocispec.Platform{arm64},
})
assert.NilError(t, err)
img, err := imgSvc.images.Create(ctx, imagesFromIndex(idx)[0])
assert.NilError(t, err)
t.Run("export without specific platform", func(t *testing.T) {
err = imgSvc.ExportImage(ctx, []string{img.Name}, nil, io.Discard)
assert.NilError(t, err)
})
t.Run("export native", func(t *testing.T) {
err = imgSvc.ExportImage(ctx, []string{img.Name}, &native, io.Discard)
assert.NilError(t, err)
})
t.Run("export missing", func(t *testing.T) {
err = imgSvc.ExportImage(ctx, []string{img.Name}, &arm64, io.Discard)
assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
})
}
func TestImageMultiplatformSaveShallowWithoutNative(t *testing.T) {
ctx := namespaces.WithNamespace(context.TODO(), "testing-"+t.Name())
contentDir := t.TempDir()
store := &blobsDirContentStore{blobs: filepath.Join(contentDir, "blobs/sha256")}
native := platforms.Platform{
OS: "linux",
Architecture: "amd64",
}
arm64 := platforms.Platform{
OS: "linux",
Architecture: "arm64",
}
imgSvc := fakeImageService(t, ctx, store)
// Mock the native platform.
imgSvc.defaultPlatformOverride = platforms.Only(native)
idx, _, err := specialimage.PartialMultiPlatform(contentDir, "partial-without-native:latest", specialimage.PartialOpts{
Stored: []ocispec.Platform{arm64},
Missing: []ocispec.Platform{native},
})
assert.NilError(t, err)
img, err := imgSvc.images.Create(ctx, imagesFromIndex(idx)[0])
assert.NilError(t, err)
t.Run("export without specific platform", func(t *testing.T) {
t.Skip("TODO(vvoland): https://github.com/docker/cli/issues/5476")
err = imgSvc.ExportImage(ctx, []string{img.Name}, nil, io.Discard)
assert.NilError(t, err)
})
t.Run("export native", func(t *testing.T) {
err = imgSvc.ExportImage(ctx, []string{img.Name}, &native, io.Discard)
assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
})
t.Run("export arm64", func(t *testing.T) {
err = imgSvc.ExportImage(ctx, []string{img.Name}, &arm64, io.Discard)
assert.NilError(t, err)
})
}

View File

@@ -0,0 +1,56 @@
package specialimage
import (
"github.com/containerd/platforms"
"github.com/distribution/reference"
"github.com/opencontainers/go-digest"
"github.com/opencontainers/image-spec/specs-go"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
type PartialOpts struct {
Stored []ocispec.Platform
Missing []ocispec.Platform
}
// PartialMultiPlatform creates an index with all platforms in storedPlatforms
// and missingPlatforms. However, only the blobs of the storedPlatforms are
// created and stored, while the missingPlatforms are only referenced in the
// index.
func PartialMultiPlatform(dir string, imageRef string, opts PartialOpts) (*ocispec.Index, []ocispec.Descriptor, error) {
ref, err := reference.ParseNormalizedNamed(imageRef)
if err != nil {
return nil, nil, err
}
var descs []ocispec.Descriptor
for _, platform := range opts.Stored {
ps := platforms.Format(platform)
manifestDesc, err := oneLayerPlatformManifest(dir, platform, FileInLayer{Path: "bash", Content: []byte("layer-" + ps)})
if err != nil {
return nil, nil, err
}
descs = append(descs, manifestDesc)
}
for _, platform := range opts.Missing {
platformStr := platforms.FormatAll(platform)
dgst := digest.FromBytes([]byte(platformStr))
platform := platform
descs = append(descs, ocispec.Descriptor{
MediaType: ocispec.MediaTypeImageManifest,
Size: 128,
Platform: &platform,
Digest: dgst,
})
}
idx, err := multiPlatformImage(dir, ref, ocispec.Index{
Versioned: specs.Versioned{SchemaVersion: 2},
MediaType: ocispec.MediaTypeImageIndex,
Manifests: descs,
})
return idx, descs, err
}