libimage: add save tests

Add save tests.  Also fix a bug when saving a single image but with
multiple tags.

Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
This commit is contained in:
Valentin Rothberg 2021-05-14 12:21:05 +02:00
parent 73305281fd
commit 9c6c0eab43
2 changed files with 126 additions and 0 deletions

View File

@ -57,9 +57,13 @@ func (r *Runtime) Save(ctx context.Context, names []string, format, path string,
// Dispatch the save operations.
switch format {
case "oci-archive", "oci-dir", "docker-dir":
if len(names) > 1 {
return errors.Errorf("%q does not support saving multiple images (%v)", format, names)
}
return r.saveSingleImage(ctx, names[0], format, path, options)
case "docker-archive":
options.ManifestMIMEType = manifest.DockerV2Schema2MediaType
return r.saveDockerArchive(ctx, names, path, options)
}
@ -134,6 +138,18 @@ func (r *Runtime) saveDockerArchive(ctx context.Context, names []string, path st
tags []reference.NamedTagged
}
additionalTags := []reference.NamedTagged{}
for _, tag := range options.AdditionalTags {
named, err := NormalizeName(tag)
if err == nil {
tagged, withTag := named.(reference.NamedTagged)
if !withTag {
return errors.Errorf("invalid additional tag %q: normalized to untagged %q", tag, named.String())
}
additionalTags = append(additionalTags, tagged)
}
}
orderedIDs := []string{} // to preserve the relative order
localImages := make(map[string]*localImage) // to assemble tags
visitedNames := make(map[string]bool) // filters duplicate names
@ -153,6 +169,7 @@ func (r *Runtime) saveDockerArchive(ctx context.Context, names []string, path st
local, exists := localImages[image.ID()]
if !exists {
local = &localImage{image: image}
local.tags = additionalTags
orderedIDs = append(orderedIDs, image.ID())
}
// Add the tag if the locally resolved name is properly tagged

View File

@ -0,0 +1,109 @@
package libimage
import (
"context"
"io/ioutil"
"os"
"strings"
"testing"
"github.com/containers/common/pkg/config"
"github.com/stretchr/testify/require"
)
func TestSave(t *testing.T) {
runtime, cleanup := testNewRuntime(t)
defer cleanup()
ctx := context.Background()
// Prefetch alpine, busybox.
pullOptions := &PullOptions{}
pullOptions.Writer = os.Stdout
_, err := runtime.Pull(ctx, "docker.io/library/alpine:latest", config.PullPolicyAlways, pullOptions)
require.NoError(t, err)
_, err = runtime.Pull(ctx, "docker.io/library/busybox:latest", config.PullPolicyAlways, pullOptions)
require.NoError(t, err)
// Save the two images into a multi-image archive. This way, we can
// reload the images for each test.
saveOptions := &SaveOptions{}
saveOptions.Writer = os.Stdout
imageCache, err := ioutil.TempFile("", "saveimagecache")
require.NoError(t, err)
imageCache.Close()
defer os.Remove(imageCache.Name())
err = runtime.Save(ctx, []string{"alpine", "busybox"}, "docker-archive", imageCache.Name(), saveOptions)
require.NoError(t, err)
loadOptions := &LoadOptions{}
loadOptions.Writer = os.Stdout
// The table tests are smoke tests to exercise the different code
// paths. More detailed tests follow below.
for _, test := range []struct {
names []string
tags []string
format string
isDir bool
expectError bool
}{
// No `names`
{nil, nil, "", false, true},
{[]string{}, nil, "", false, true},
// Invalid/unsupported format
{[]string{"something"}, nil, "", false, true},
{[]string{"something"}, nil, "else", false, true},
// oci
{[]string{"busybox"}, nil, "oci-dir", true, false},
{[]string{"busybox"}, nil, "oci-archive", false, false},
// oci-archive doesn't support multi-image archives
{[]string{"busybox", "alpine"}, nil, "oci-archive", false, true},
// docker
{[]string{"busybox"}, nil, "docker-archive", false, false},
{[]string{"busybox"}, []string{"localhost/tag:1", "quay.io/repo/image:tag"}, "docker-archive", false, false},
{[]string{"busybox"}, nil, "docker-dir", true, false},
{[]string{"busybox", "alpine"}, nil, "docker-archive", false, false},
// additional tags and multi-images conflict
{[]string{"busybox", "alpine"}, []string{"tag"}, "docker-archive", false, true},
} {
// First clean up all images and load the cache.
_, rmErrors := runtime.RemoveImages(ctx, nil, nil)
require.Nil(t, rmErrors)
_, err = runtime.Load(ctx, imageCache.Name(), loadOptions)
require.NoError(t, err)
tmp, err := ioutil.TempDir("", "libimagesavetest")
require.NoError(t, err)
defer os.RemoveAll(tmp)
if !test.isDir {
tmp += "/archive.tar"
}
saveOptions.AdditionalTags = test.tags
err = runtime.Save(ctx, test.names, test.format, tmp, saveOptions)
if test.expectError {
require.Error(t, err, "%v", test)
continue
}
require.NoError(t, err, "%v", test)
// Now remove all images again and attempt to load the
// previously saved ones.
_, rmErrors = runtime.RemoveImages(ctx, nil, nil)
require.Nil(t, rmErrors)
namesAndTags := append(test.names, test.tags...)
loadedImages, err := runtime.Load(ctx, tmp, loadOptions)
require.NoError(t, err)
require.Len(t, loadedImages, len(namesAndTags))
// Now make sure that all specified names (and tags) resolve to
// an image the local containers storage. Note that names are
// only preserved in archives.
if strings.HasSuffix(test.format, "-dir") {
continue
}
_, err = runtime.ListImages(ctx, namesAndTags, nil)
require.NoError(t, err, "%v", test)
}
}