package image import ( "io/ioutil" "os" "testing" "github.com/containers/image/transports" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) const registriesConfWithSearch = `[registries.search] registries = ['example.com', 'docker.io'] ` func TestRefNamesFromPossiblyUnqualifiedName(t *testing.T) { const digestSuffix = "@sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" type pullRefStrings struct{ image, srcRef, dstName string } // pullRefName with string data only registriesConf, err := ioutil.TempFile("", "TestRefNamesFromPossiblyUnqualifiedName") require.NoError(t, err) defer registriesConf.Close() defer os.Remove(registriesConf.Name()) err = ioutil.WriteFile(registriesConf.Name(), []byte(registriesConfWithSearch), 0600) require.NoError(t, err) // Environment is per-process, so this looks very unsafe; actually it seems fine because tests are not // run in parallel unless they opt in by calling t.Parallel(). So don’t do that. oldRCP, hasRCP := os.LookupEnv("REGISTRIES_CONFIG_PATH") defer func() { if hasRCP { os.Setenv("REGISTRIES_CONFIG_PATH", oldRCP) } else { os.Unsetenv("REGISTRIES_CONFIG_PATH") } }() os.Setenv("REGISTRIES_CONFIG_PATH", registriesConf.Name()) for _, c := range []struct { input string expected []pullRefStrings }{ {"#", nil}, // Clearly invalid. { // Fully-explicit docker.io, name-only. "docker.io/library/busybox", // (The docker:// representation is shortened by c/image/docker.Reference but it refers to "docker.io/library".) []pullRefStrings{{"docker.io/library/busybox", "docker://busybox:latest", "docker.io/library/busybox"}}, }, { // docker.io with implied /library/, name-only. "docker.io/busybox", // (The docker:// representation is shortened by c/image/docker.Reference but it refers to "docker.io/library".) // The .dstName fields differ for the explicit/implicit /library/ cases, but StorageTransport.ParseStoreReference normalizes that. []pullRefStrings{{"docker.io/busybox", "docker://busybox:latest", "docker.io/busybox"}}, }, { // Qualified example.com, name-only. "example.com/ns/busybox", []pullRefStrings{{"example.com/ns/busybox", "docker://example.com/ns/busybox:latest", "example.com/ns/busybox"}}, }, { // Qualified example.com, name:tag. "example.com/ns/busybox:notlatest", []pullRefStrings{{"example.com/ns/busybox:notlatest", "docker://example.com/ns/busybox:notlatest", "example.com/ns/busybox:notlatest"}}, }, { // Qualified example.com, name@digest. "example.com/ns/busybox" + digestSuffix, []pullRefStrings{{"example.com/ns/busybox" + digestSuffix, "docker://example.com/ns/busybox" + digestSuffix, // FIXME?! Why is .dstName dropping the digest, and adding :none?! "example.com/ns/busybox:none"}}, }, // Qualified example.com, name:tag@digest. This code is happy to try, but .srcRef parsing currently rejects such input. {"example.com/ns/busybox:notlatest" + digestSuffix, nil}, { // Unqualified, single-name, name-only "busybox", []pullRefStrings{ {"example.com/busybox:latest", "docker://example.com/busybox:latest", "example.com/busybox:latest"}, // (The docker:// representation is shortened by c/image/docker.Reference but it refers to "docker.io/library".) {"docker.io/busybox:latest", "docker://busybox:latest", "docker.io/busybox:latest"}, }, }, { // Unqualified, namespaced, name-only "ns/busybox", // FIXME: This is interpreted as "registry == ns", and actual pull happens from docker.io/ns/busybox:latest; // example.com should be first in the list but isn't used at all. []pullRefStrings{ {"ns/busybox", "docker://ns/busybox:latest", "ns/busybox"}, }, }, { // Unqualified, name:tag "busybox:notlatest", []pullRefStrings{ {"example.com/busybox:notlatest", "docker://example.com/busybox:notlatest", "example.com/busybox:notlatest"}, // (The docker:// representation is shortened by c/image/docker.Reference but it refers to "docker.io/library".) {"docker.io/busybox:notlatest", "docker://busybox:notlatest", "docker.io/busybox:notlatest"}, }, }, { // Unqualified, name@digest "busybox" + digestSuffix, []pullRefStrings{ // FIXME?! Why is .input and .dstName dropping the digest, and adding :none?! {"example.com/busybox:none", "docker://example.com/busybox" + digestSuffix, "example.com/busybox:none"}, // (The docker:// representation is shortened by c/image/docker.Reference but it refers to "docker.io/library".) {"docker.io/busybox:none", "docker://busybox" + digestSuffix, "docker.io/busybox:none"}, }, }, // Unqualified, name:tag@digest. This code is happy to try, but .srcRef parsing currently rejects such input. {"busybox:notlatest" + digestSuffix, nil}, } { res, err := refNamesFromPossiblyUnqualifiedName(c.input) if len(c.expected) == 0 { assert.Error(t, err, c.input) } else { assert.NoError(t, err, c.input) strings := make([]pullRefStrings, len(res)) for i, rn := range res { strings[i] = pullRefStrings{ image: rn.image, srcRef: transports.ImageName(rn.srcRef), dstName: rn.dstName, } } assert.Equal(t, c.expected, strings, c.input) } } }