mirror of
https://github.com/podman-container-tools/podman.git
synced 2026-08-25 09:47:56 +00:00
Again, rely on the official API, instead of the suprising "suspiciousTagValueForSearch" value (set to :latest on untagged images, and :none on digested ones!) CHANGES BEHAVIOR, but the previous output of normalization of digested values was not even syntatically valid, so this can't really be worse. Still, maybe we should refuse to tag with digested references in the first place. Signed-off-by: Miloslav Trmač <mitr@redhat.com>
281 lines
8.6 KiB
Go
281 lines
8.6 KiB
Go
package image
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/containers/storage"
|
|
"github.com/opencontainers/go-digest"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
var (
|
|
bbNames = []string{"docker.io/library/busybox:latest", "docker.io/library/busybox", "docker.io/busybox:latest", "docker.io/busybox", "busybox:latest", "busybox"}
|
|
bbGlibcNames = []string{"docker.io/library/busybox:glibc", "docker.io/busybox:glibc", "busybox:glibc"}
|
|
fedoraNames = []string{"registry.fedoraproject.org/fedora-minimal:latest", "registry.fedoraproject.org/fedora-minimal", "fedora-minimal:latest", "fedora-minimal"}
|
|
)
|
|
|
|
type localImageTest struct {
|
|
fqname, taggedName string
|
|
img *Image
|
|
names []string
|
|
}
|
|
|
|
// make a temporary directory for the runtime
|
|
func mkWorkDir() (string, error) {
|
|
return ioutil.TempDir("", "podman-test")
|
|
}
|
|
|
|
// shutdown the runtime and clean behind it
|
|
func cleanup(workdir string, ir *Runtime) {
|
|
if err := ir.Shutdown(false); err != nil {
|
|
fmt.Println(err)
|
|
os.Exit(1)
|
|
}
|
|
err := os.RemoveAll(workdir)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func makeLocalMatrix(b, bg *Image) ([]localImageTest, error) {
|
|
var l []localImageTest
|
|
// busybox
|
|
busybox := localImageTest{
|
|
fqname: "docker.io/library/busybox:latest",
|
|
taggedName: "bb:latest",
|
|
}
|
|
busybox.img = b
|
|
busybox.names = b.Names()
|
|
busybox.names = append(busybox.names, []string{"bb:latest", "bb", b.ID(), b.ID()[0:7], fmt.Sprintf("busybox@%s", b.Digest())}...)
|
|
|
|
// busybox-glibc
|
|
busyboxGlibc := localImageTest{
|
|
fqname: "docker.io/library/busybox:glibc",
|
|
taggedName: "bb:glibc",
|
|
}
|
|
|
|
busyboxGlibc.img = bg
|
|
busyboxGlibc.names = bbGlibcNames
|
|
|
|
l = append(l, busybox, busyboxGlibc)
|
|
return l, nil
|
|
|
|
}
|
|
|
|
// TestImage_NewFromLocal tests finding the image locally by various names,
|
|
// tags, and aliases
|
|
func TestImage_NewFromLocal(t *testing.T) {
|
|
if os.Geteuid() != 0 { // containers/storage requires root access
|
|
t.Skipf("Test not running as root")
|
|
}
|
|
|
|
workdir, err := mkWorkDir()
|
|
assert.NoError(t, err)
|
|
so := storage.StoreOptions{
|
|
RunRoot: workdir,
|
|
GraphRoot: workdir,
|
|
}
|
|
var writer io.Writer
|
|
writer = os.Stdout
|
|
|
|
// Need images to be present for this test
|
|
ir, err := NewImageRuntimeFromOptions(so)
|
|
assert.NoError(t, err)
|
|
bb, err := ir.New(context.Background(), "docker.io/library/busybox:latest", "", "", writer, nil, SigningOptions{}, false)
|
|
assert.NoError(t, err)
|
|
bbglibc, err := ir.New(context.Background(), "docker.io/library/busybox:glibc", "", "", writer, nil, SigningOptions{}, false)
|
|
assert.NoError(t, err)
|
|
|
|
tm, err := makeLocalMatrix(bb, bbglibc)
|
|
assert.NoError(t, err)
|
|
|
|
for _, image := range tm {
|
|
// tag our images
|
|
image.img.TagImage(image.taggedName)
|
|
assert.NoError(t, err)
|
|
for _, name := range image.names {
|
|
newImage, err := ir.NewFromLocal(name)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, newImage.ID(), image.img.ID())
|
|
}
|
|
}
|
|
|
|
// Shutdown the runtime and remove the temporary storage
|
|
cleanup(workdir, ir)
|
|
}
|
|
|
|
// TestImage_New tests pulling the image by various names, tags, and from
|
|
// different registries
|
|
func TestImage_New(t *testing.T) {
|
|
if os.Geteuid() != 0 { // containers/storage requires root access
|
|
t.Skipf("Test not running as root")
|
|
}
|
|
|
|
var names []string
|
|
workdir, err := mkWorkDir()
|
|
assert.NoError(t, err)
|
|
|
|
so := storage.StoreOptions{
|
|
RunRoot: workdir,
|
|
GraphRoot: workdir,
|
|
}
|
|
ir, err := NewImageRuntimeFromOptions(so)
|
|
assert.NoError(t, err)
|
|
// Build the list of pull names
|
|
names = append(names, bbNames...)
|
|
names = append(names, fedoraNames...)
|
|
var writer io.Writer
|
|
writer = os.Stdout
|
|
|
|
// Iterate over the names and delete the image
|
|
// after the pull
|
|
for _, img := range names {
|
|
newImage, err := ir.New(context.Background(), img, "", "", writer, nil, SigningOptions{}, false)
|
|
assert.NoError(t, err)
|
|
assert.NotEqual(t, newImage.ID(), "")
|
|
err = newImage.Remove(false)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
// Shutdown the runtime and remove the temporary storage
|
|
cleanup(workdir, ir)
|
|
}
|
|
|
|
// TestImage_MatchRepoTag tests the various inputs we need to match
|
|
// against an image's reponames
|
|
func TestImage_MatchRepoTag(t *testing.T) {
|
|
if os.Geteuid() != 0 { // containers/storage requires root access
|
|
t.Skipf("Test not running as root")
|
|
}
|
|
|
|
//Set up
|
|
workdir, err := mkWorkDir()
|
|
assert.NoError(t, err)
|
|
|
|
so := storage.StoreOptions{
|
|
RunRoot: workdir,
|
|
GraphRoot: workdir,
|
|
}
|
|
ir, err := NewImageRuntimeFromOptions(so)
|
|
assert.NoError(t, err)
|
|
newImage, err := ir.New(context.Background(), "busybox", "", "", os.Stdout, nil, SigningOptions{}, false)
|
|
assert.NoError(t, err)
|
|
err = newImage.TagImage("foo:latest")
|
|
assert.NoError(t, err)
|
|
err = newImage.TagImage("foo:bar")
|
|
assert.NoError(t, err)
|
|
|
|
// Tests start here.
|
|
for _, name := range bbNames {
|
|
repoTag, err := newImage.MatchRepoTag(name)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "docker.io/library/busybox:latest", repoTag)
|
|
}
|
|
|
|
// Test against tagged images of busybox
|
|
|
|
// foo should resolve to foo:latest
|
|
repoTag, err := newImage.MatchRepoTag("foo")
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "localhost/foo:latest", repoTag)
|
|
|
|
// foo:bar should resolve to foo:bar
|
|
repoTag, err = newImage.MatchRepoTag("foo:bar")
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "localhost/foo:bar", repoTag)
|
|
// Shutdown the runtime and remove the temporary storage
|
|
cleanup(workdir, ir)
|
|
}
|
|
|
|
// TestImage_RepoDigests tests RepoDigest generation.
|
|
func TestImage_RepoDigests(t *testing.T) {
|
|
dgst, err := digest.Parse("sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
for _, test := range []struct {
|
|
name string
|
|
names []string
|
|
expected []string
|
|
}{
|
|
{
|
|
name: "empty",
|
|
names: []string{},
|
|
expected: nil,
|
|
},
|
|
{
|
|
name: "tagged",
|
|
names: []string{"docker.io/library/busybox:latest"},
|
|
expected: []string{"docker.io/library/busybox@sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc"},
|
|
},
|
|
{
|
|
name: "digest",
|
|
names: []string{"docker.io/library/busybox@sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc"},
|
|
expected: []string{"docker.io/library/busybox@sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc"},
|
|
},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
image := &Image{
|
|
image: &storage.Image{
|
|
Names: test.names,
|
|
Digest: dgst,
|
|
},
|
|
}
|
|
actual, err := image.RepoDigests()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
assert.Equal(t, test.expected, actual)
|
|
})
|
|
}
|
|
}
|
|
|
|
// Test_splitString tests the splitString function in image that
|
|
// takes input and splits on / and returns the last array item
|
|
func Test_splitString(t *testing.T) {
|
|
assert.Equal(t, splitString("foo/bar"), "bar")
|
|
assert.Equal(t, splitString("a/foo/bar"), "bar")
|
|
assert.Equal(t, splitString("bar"), "bar")
|
|
}
|
|
|
|
// Test_stripSha256 tests test the stripSha256 function which removes
|
|
// the prefix "sha256:" from a string if it is present
|
|
func Test_stripSha256(t *testing.T) {
|
|
assert.Equal(t, stripSha256(""), "")
|
|
assert.Equal(t, stripSha256("test1"), "test1")
|
|
assert.Equal(t, stripSha256("sha256:9110ae7f579f35ee0c3938696f23fe0f5fbe641738ea52eb83c2df7e9995fa17"), "9110ae7f579f35ee0c3938696f23fe0f5fbe641738ea52eb83c2df7e9995fa17")
|
|
assert.Equal(t, stripSha256("sha256:9110ae7f"), "9110ae7f")
|
|
assert.Equal(t, stripSha256("sha256:"), "sha256:")
|
|
assert.Equal(t, stripSha256("sha256:a"), "a")
|
|
}
|
|
|
|
func TestNormalizeTag(t *testing.T) {
|
|
const digestSuffix = "@sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
|
|
|
for _, c := range []struct{ input, expected string }{
|
|
{"#", ""}, // Clearly invalid
|
|
{"example.com/busybox", "example.com/busybox:latest"}, // Qualified name-only
|
|
{"example.com/busybox:notlatest", "example.com/busybox:notlatest"}, // Qualified name:tag
|
|
{"example.com/busybox" + digestSuffix, "example.com/busybox" + digestSuffix}, // Qualified name@digest; FIXME? Should we allow tagging with a digest at all?
|
|
{"example.com/busybox:notlatest" + digestSuffix, "example.com/busybox:notlatest" + digestSuffix}, // Qualified name:tag@digest
|
|
{"busybox:latest", "localhost/busybox:latest"}, // Unqualified name-only
|
|
{"ns/busybox:latest", "localhost/ns/busybox:latest"}, // Unqualified with a dot-less namespace
|
|
{"docker.io/busybox:latest", "docker.io/library/busybox:latest"}, // docker.io without /library/
|
|
} {
|
|
res, err := normalizeTag(c.input)
|
|
if c.expected == "" {
|
|
assert.Error(t, err, c.input)
|
|
} else {
|
|
assert.NoError(t, err, c.input)
|
|
assert.Equal(t, c.expected, res)
|
|
}
|
|
}
|
|
}
|