Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

libimage: add save tests #551

Merged
merged 1 commit into from
May 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions libimage/save.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
109 changes: 109 additions & 0 deletions libimage/save_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}