2016-06-15 22:40:15 +00:00
|
|
|
package reference
|
|
|
|
|
|
|
|
import (
|
2017-01-10 00:52:05 +00:00
|
|
|
"strconv"
|
2016-06-15 22:40:15 +00:00
|
|
|
"testing"
|
|
|
|
|
2020-08-24 11:18:39 +00:00
|
|
|
"github.com/distribution/distribution/v3/digestset"
|
2017-01-09 23:44:13 +00:00
|
|
|
"github.com/opencontainers/go-digest"
|
2016-06-15 22:40:15 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestValidateReferenceName(t *testing.T) {
|
|
|
|
validRepoNames := []string{
|
|
|
|
"docker/docker",
|
|
|
|
"library/debian",
|
|
|
|
"debian",
|
|
|
|
"docker.io/docker/docker",
|
|
|
|
"docker.io/library/debian",
|
|
|
|
"docker.io/debian",
|
|
|
|
"index.docker.io/docker/docker",
|
|
|
|
"index.docker.io/library/debian",
|
|
|
|
"index.docker.io/debian",
|
|
|
|
"127.0.0.1:5000/docker/docker",
|
|
|
|
"127.0.0.1:5000/library/debian",
|
|
|
|
"127.0.0.1:5000/debian",
|
2022-06-25 10:23:38 +00:00
|
|
|
"192.168.0.1",
|
|
|
|
"192.168.0.1:80",
|
|
|
|
"192.168.0.1:8/debian",
|
|
|
|
"192.168.0.2:25000/debian",
|
2016-06-15 22:40:15 +00:00
|
|
|
"thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev",
|
2022-06-25 10:23:38 +00:00
|
|
|
"[fc00::1]:5000/docker",
|
|
|
|
"[fc00::1]:5000/docker/docker",
|
|
|
|
"[fc00:1:2:3:4:5:6:7]:5000/library/debian",
|
2016-06-15 22:40:15 +00:00
|
|
|
|
|
|
|
// This test case was moved from invalid to valid since it is valid input
|
|
|
|
// when specified with a hostname, it removes the ambiguity from about
|
|
|
|
// whether the value is an identifier or repository name
|
|
|
|
"docker.io/1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
|
2019-08-14 22:27:59 +00:00
|
|
|
"Docker/docker",
|
|
|
|
"DOCKER/docker",
|
2016-06-15 22:40:15 +00:00
|
|
|
}
|
|
|
|
invalidRepoNames := []string{
|
|
|
|
"https://github.com/docker/docker",
|
|
|
|
"docker/Docker",
|
|
|
|
"-docker",
|
|
|
|
"-docker/docker",
|
|
|
|
"-docker.io/docker/docker",
|
|
|
|
"docker///docker",
|
|
|
|
"docker.io/docker/Docker",
|
|
|
|
"docker.io/docker///docker",
|
2022-06-25 10:23:38 +00:00
|
|
|
"[fc00::1]",
|
|
|
|
"[fc00::1]:5000",
|
|
|
|
"fc00::1:5000/debian",
|
|
|
|
"[fe80::1%eth0]:5000/debian",
|
|
|
|
"[2001:db8:3:4::192.0.2.33]:5000/debian",
|
2016-06-15 22:40:15 +00:00
|
|
|
"1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, name := range invalidRepoNames {
|
2016-06-27 23:41:28 +00:00
|
|
|
_, err := ParseNormalizedNamed(name)
|
2016-06-15 22:40:15 +00:00
|
|
|
if err == nil {
|
|
|
|
t.Fatalf("Expected invalid repo name for %q", name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, name := range validRepoNames {
|
2016-06-27 23:41:28 +00:00
|
|
|
_, err := ParseNormalizedNamed(name)
|
2016-06-15 22:40:15 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Error parsing repo name %s, got: %q", name, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestValidateRemoteName(t *testing.T) {
|
|
|
|
validRepositoryNames := []string{
|
|
|
|
// Sanity check.
|
|
|
|
"docker/docker",
|
|
|
|
|
|
|
|
// Allow 64-character non-hexadecimal names (hexadecimal names are forbidden).
|
|
|
|
"thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev",
|
|
|
|
|
|
|
|
// Allow embedded hyphens.
|
|
|
|
"docker-rules/docker",
|
|
|
|
|
|
|
|
// Allow multiple hyphens as well.
|
|
|
|
"docker---rules/docker",
|
|
|
|
|
2022-11-02 21:05:45 +00:00
|
|
|
// Username doc and image name docker being tested.
|
2016-06-15 22:40:15 +00:00
|
|
|
"doc/docker",
|
|
|
|
|
|
|
|
// single character names are now allowed.
|
|
|
|
"d/docker",
|
|
|
|
"jess/t",
|
|
|
|
|
|
|
|
// Consecutive underscores.
|
|
|
|
"dock__er/docker",
|
|
|
|
}
|
|
|
|
for _, repositoryName := range validRepositoryNames {
|
2016-06-27 23:41:28 +00:00
|
|
|
_, err := ParseNormalizedNamed(repositoryName)
|
2016-06-15 22:40:15 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Repository name should be valid: %v. Error: %v", repositoryName, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
invalidRepositoryNames := []string{
|
|
|
|
// Disallow capital letters.
|
|
|
|
"docker/Docker",
|
|
|
|
|
|
|
|
// Only allow one slash.
|
|
|
|
"docker///docker",
|
|
|
|
|
|
|
|
// Disallow 64-character hexadecimal.
|
|
|
|
"1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
|
|
|
|
|
|
|
|
// Disallow leading and trailing hyphens in namespace.
|
|
|
|
"-docker/docker",
|
|
|
|
"docker-/docker",
|
|
|
|
"-docker-/docker",
|
|
|
|
|
|
|
|
// Don't allow underscores everywhere (as opposed to hyphens).
|
|
|
|
"____/____",
|
|
|
|
|
|
|
|
"_docker/_docker",
|
|
|
|
|
|
|
|
// Disallow consecutive periods.
|
|
|
|
"dock..er/docker",
|
|
|
|
"dock_.er/docker",
|
|
|
|
"dock-.er/docker",
|
|
|
|
|
|
|
|
// No repository.
|
|
|
|
"docker/",
|
|
|
|
|
2022-11-02 21:05:45 +00:00
|
|
|
// namespace too long
|
2016-06-15 22:40:15 +00:00
|
|
|
"this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255/docker",
|
|
|
|
}
|
|
|
|
for _, repositoryName := range invalidRepositoryNames {
|
2016-06-27 23:41:28 +00:00
|
|
|
if _, err := ParseNormalizedNamed(repositoryName); err == nil {
|
2016-06-15 22:40:15 +00:00
|
|
|
t.Errorf("Repository name should be invalid: %v", repositoryName)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestParseRepositoryInfo(t *testing.T) {
|
|
|
|
type tcase struct {
|
|
|
|
RemoteName, FamiliarName, FullName, AmbiguousName, Domain string
|
|
|
|
}
|
|
|
|
|
|
|
|
tcases := []tcase{
|
|
|
|
{
|
|
|
|
RemoteName: "fooo/bar",
|
|
|
|
FamiliarName: "fooo/bar",
|
|
|
|
FullName: "docker.io/fooo/bar",
|
|
|
|
AmbiguousName: "index.docker.io/fooo/bar",
|
|
|
|
Domain: "docker.io",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
RemoteName: "library/ubuntu",
|
|
|
|
FamiliarName: "ubuntu",
|
|
|
|
FullName: "docker.io/library/ubuntu",
|
|
|
|
AmbiguousName: "library/ubuntu",
|
|
|
|
Domain: "docker.io",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
RemoteName: "nonlibrary/ubuntu",
|
|
|
|
FamiliarName: "nonlibrary/ubuntu",
|
|
|
|
FullName: "docker.io/nonlibrary/ubuntu",
|
|
|
|
AmbiguousName: "",
|
|
|
|
Domain: "docker.io",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
RemoteName: "other/library",
|
|
|
|
FamiliarName: "other/library",
|
|
|
|
FullName: "docker.io/other/library",
|
|
|
|
AmbiguousName: "",
|
|
|
|
Domain: "docker.io",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
RemoteName: "private/moonbase",
|
|
|
|
FamiliarName: "127.0.0.1:8000/private/moonbase",
|
|
|
|
FullName: "127.0.0.1:8000/private/moonbase",
|
|
|
|
AmbiguousName: "",
|
|
|
|
Domain: "127.0.0.1:8000",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
RemoteName: "privatebase",
|
|
|
|
FamiliarName: "127.0.0.1:8000/privatebase",
|
|
|
|
FullName: "127.0.0.1:8000/privatebase",
|
|
|
|
AmbiguousName: "",
|
|
|
|
Domain: "127.0.0.1:8000",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
RemoteName: "private/moonbase",
|
|
|
|
FamiliarName: "example.com/private/moonbase",
|
|
|
|
FullName: "example.com/private/moonbase",
|
|
|
|
AmbiguousName: "",
|
|
|
|
Domain: "example.com",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
RemoteName: "privatebase",
|
|
|
|
FamiliarName: "example.com/privatebase",
|
|
|
|
FullName: "example.com/privatebase",
|
|
|
|
AmbiguousName: "",
|
|
|
|
Domain: "example.com",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
RemoteName: "private/moonbase",
|
|
|
|
FamiliarName: "example.com:8000/private/moonbase",
|
|
|
|
FullName: "example.com:8000/private/moonbase",
|
|
|
|
AmbiguousName: "",
|
|
|
|
Domain: "example.com:8000",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
RemoteName: "privatebasee",
|
|
|
|
FamiliarName: "example.com:8000/privatebasee",
|
|
|
|
FullName: "example.com:8000/privatebasee",
|
|
|
|
AmbiguousName: "",
|
|
|
|
Domain: "example.com:8000",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
RemoteName: "library/ubuntu-12.04-base",
|
|
|
|
FamiliarName: "ubuntu-12.04-base",
|
|
|
|
FullName: "docker.io/library/ubuntu-12.04-base",
|
|
|
|
AmbiguousName: "index.docker.io/library/ubuntu-12.04-base",
|
|
|
|
Domain: "docker.io",
|
|
|
|
},
|
2017-01-14 00:19:24 +00:00
|
|
|
{
|
|
|
|
RemoteName: "library/foo",
|
|
|
|
FamiliarName: "foo",
|
|
|
|
FullName: "docker.io/library/foo",
|
|
|
|
AmbiguousName: "docker.io/foo",
|
|
|
|
Domain: "docker.io",
|
|
|
|
},
|
2017-01-11 18:46:48 +00:00
|
|
|
{
|
|
|
|
RemoteName: "library/foo/bar",
|
|
|
|
FamiliarName: "library/foo/bar",
|
|
|
|
FullName: "docker.io/library/foo/bar",
|
|
|
|
AmbiguousName: "",
|
|
|
|
Domain: "docker.io",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
RemoteName: "store/foo/bar",
|
|
|
|
FamiliarName: "store/foo/bar",
|
|
|
|
FullName: "docker.io/store/foo/bar",
|
|
|
|
AmbiguousName: "",
|
|
|
|
Domain: "docker.io",
|
|
|
|
},
|
2019-08-14 22:27:59 +00:00
|
|
|
{
|
|
|
|
RemoteName: "bar",
|
|
|
|
FamiliarName: "Foo/bar",
|
|
|
|
FullName: "Foo/bar",
|
|
|
|
AmbiguousName: "",
|
|
|
|
Domain: "Foo",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
RemoteName: "bar",
|
|
|
|
FamiliarName: "FOO/bar",
|
|
|
|
FullName: "FOO/bar",
|
|
|
|
AmbiguousName: "",
|
|
|
|
Domain: "FOO",
|
|
|
|
},
|
2016-06-15 22:40:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, tcase := range tcases {
|
|
|
|
refStrings := []string{tcase.FamiliarName, tcase.FullName}
|
|
|
|
if tcase.AmbiguousName != "" {
|
|
|
|
refStrings = append(refStrings, tcase.AmbiguousName)
|
|
|
|
}
|
|
|
|
|
2017-01-11 20:58:00 +00:00
|
|
|
var refs []Named
|
2016-06-15 22:40:15 +00:00
|
|
|
for _, r := range refStrings {
|
2016-06-27 23:41:28 +00:00
|
|
|
named, err := ParseNormalizedNamed(r)
|
2016-06-15 22:40:15 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
refs = append(refs, named)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, r := range refs {
|
2017-01-11 20:58:00 +00:00
|
|
|
if expected, actual := tcase.FamiliarName, FamiliarName(r); expected != actual {
|
2016-06-15 22:40:15 +00:00
|
|
|
t.Fatalf("Invalid normalized reference for %q. Expected %q, got %q", r, expected, actual)
|
|
|
|
}
|
|
|
|
if expected, actual := tcase.FullName, r.String(); expected != actual {
|
|
|
|
t.Fatalf("Invalid canonical reference for %q. Expected %q, got %q", r, expected, actual)
|
|
|
|
}
|
|
|
|
if expected, actual := tcase.Domain, Domain(r); expected != actual {
|
|
|
|
t.Fatalf("Invalid domain for %q. Expected %q, got %q", r, expected, actual)
|
|
|
|
}
|
|
|
|
if expected, actual := tcase.RemoteName, Path(r); expected != actual {
|
|
|
|
t.Fatalf("Invalid remoteName for %q. Expected %q, got %q", r, expected, actual)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestParseReferenceWithTagAndDigest(t *testing.T) {
|
2016-06-27 23:41:28 +00:00
|
|
|
shortRef := "busybox:latest@sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa"
|
2017-01-11 20:58:00 +00:00
|
|
|
ref, err := ParseNormalizedNamed(shortRef)
|
2016-06-15 22:40:15 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2017-01-11 20:58:00 +00:00
|
|
|
if expected, actual := "docker.io/library/"+shortRef, ref.String(); actual != expected {
|
|
|
|
t.Fatalf("Invalid parsed reference for %q: expected %q, got %q", ref, expected, actual)
|
2016-06-15 22:40:15 +00:00
|
|
|
}
|
|
|
|
|
2016-06-27 23:41:28 +00:00
|
|
|
if _, isTagged := ref.(NamedTagged); !isTagged {
|
|
|
|
t.Fatalf("Reference from %q should support tag", ref)
|
2016-06-15 22:40:15 +00:00
|
|
|
}
|
|
|
|
if _, isCanonical := ref.(Canonical); !isCanonical {
|
2016-06-27 23:41:28 +00:00
|
|
|
t.Fatalf("Reference from %q should support digest", ref)
|
2016-06-15 22:40:15 +00:00
|
|
|
}
|
2017-01-11 20:58:00 +00:00
|
|
|
if expected, actual := shortRef, FamiliarString(ref); actual != expected {
|
2016-06-15 22:40:15 +00:00
|
|
|
t.Fatalf("Invalid parsed reference for %q: expected %q, got %q", ref, expected, actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestInvalidReferenceComponents(t *testing.T) {
|
2016-06-27 23:41:28 +00:00
|
|
|
if _, err := ParseNormalizedNamed("-foo"); err == nil {
|
2016-06-15 22:40:15 +00:00
|
|
|
t.Fatal("Expected WithName to detect invalid name")
|
|
|
|
}
|
2016-06-27 23:41:28 +00:00
|
|
|
ref, err := ParseNormalizedNamed("busybox")
|
2016-06-15 22:40:15 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if _, err := WithTag(ref, "-foo"); err == nil {
|
|
|
|
t.Fatal("Expected WithName to detect invalid tag")
|
|
|
|
}
|
|
|
|
if _, err := WithDigest(ref, digest.Digest("foo")); err == nil {
|
|
|
|
t.Fatal("Expected WithDigest to detect invalid digest")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func equalReference(r1, r2 Reference) bool {
|
|
|
|
switch v1 := r1.(type) {
|
|
|
|
case digestReference:
|
|
|
|
if v2, ok := r2.(digestReference); ok {
|
|
|
|
return v1 == v2
|
|
|
|
}
|
|
|
|
case repository:
|
|
|
|
if v2, ok := r2.(repository); ok {
|
|
|
|
return v1 == v2
|
|
|
|
}
|
|
|
|
case taggedReference:
|
|
|
|
if v2, ok := r2.(taggedReference); ok {
|
|
|
|
return v1 == v2
|
|
|
|
}
|
|
|
|
case canonicalReference:
|
|
|
|
if v2, ok := r2.(canonicalReference); ok {
|
|
|
|
return v1 == v2
|
|
|
|
}
|
|
|
|
case reference:
|
|
|
|
if v2, ok := r2.(reference); ok {
|
|
|
|
return v1 == v2
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestParseAnyReference(t *testing.T) {
|
|
|
|
tcases := []struct {
|
|
|
|
Reference string
|
|
|
|
Equivalent string
|
|
|
|
Expected Reference
|
|
|
|
Digests []digest.Digest
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
Reference: "redis",
|
|
|
|
Equivalent: "docker.io/library/redis",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Reference: "redis:latest",
|
|
|
|
Equivalent: "docker.io/library/redis:latest",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Reference: "docker.io/library/redis:latest",
|
|
|
|
Equivalent: "docker.io/library/redis:latest",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Reference: "redis@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
|
|
|
|
Equivalent: "docker.io/library/redis@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Reference: "docker.io/library/redis@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
|
|
|
|
Equivalent: "docker.io/library/redis@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Reference: "dmcgowan/myapp",
|
|
|
|
Equivalent: "docker.io/dmcgowan/myapp",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Reference: "dmcgowan/myapp:latest",
|
|
|
|
Equivalent: "docker.io/dmcgowan/myapp:latest",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Reference: "docker.io/mcgowan/myapp:latest",
|
|
|
|
Equivalent: "docker.io/mcgowan/myapp:latest",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Reference: "dmcgowan/myapp@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
|
|
|
|
Equivalent: "docker.io/dmcgowan/myapp@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Reference: "docker.io/dmcgowan/myapp@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
|
|
|
|
Equivalent: "docker.io/dmcgowan/myapp@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
|
|
|
|
},
|
|
|
|
{
|
2017-01-20 19:38:07 +00:00
|
|
|
Reference: "dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
|
|
|
|
Expected: digestReference("sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"),
|
|
|
|
Equivalent: "sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
|
2016-06-15 22:40:15 +00:00
|
|
|
},
|
|
|
|
{
|
2017-01-20 19:38:07 +00:00
|
|
|
Reference: "sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
|
|
|
|
Expected: digestReference("sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"),
|
|
|
|
Equivalent: "sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
|
2016-06-15 22:40:15 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Reference: "dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9",
|
|
|
|
Equivalent: "docker.io/library/dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9",
|
|
|
|
},
|
|
|
|
{
|
2017-01-20 19:38:07 +00:00
|
|
|
Reference: "dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9",
|
|
|
|
Expected: digestReference("sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"),
|
|
|
|
Equivalent: "sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
|
2016-06-15 22:40:15 +00:00
|
|
|
Digests: []digest.Digest{
|
|
|
|
digest.Digest("sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"),
|
|
|
|
digest.Digest("sha256:abcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Reference: "dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9",
|
|
|
|
Equivalent: "docker.io/library/dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9",
|
|
|
|
Digests: []digest.Digest{
|
|
|
|
digest.Digest("sha256:abcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2017-01-20 19:38:07 +00:00
|
|
|
Reference: "dbcc1c",
|
|
|
|
Expected: digestReference("sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"),
|
|
|
|
Equivalent: "sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
|
2016-06-15 22:40:15 +00:00
|
|
|
Digests: []digest.Digest{
|
|
|
|
digest.Digest("sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"),
|
|
|
|
digest.Digest("sha256:abcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Reference: "dbcc1",
|
|
|
|
Equivalent: "docker.io/library/dbcc1",
|
|
|
|
Digests: []digest.Digest{
|
|
|
|
digest.Digest("sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"),
|
|
|
|
digest.Digest("sha256:abcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Reference: "dbcc1c",
|
|
|
|
Equivalent: "docker.io/library/dbcc1c",
|
|
|
|
Digests: []digest.Digest{
|
|
|
|
digest.Digest("sha256:abcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tcase := range tcases {
|
|
|
|
var ref Reference
|
|
|
|
var err error
|
|
|
|
if len(tcase.Digests) == 0 {
|
|
|
|
ref, err = ParseAnyReference(tcase.Reference)
|
|
|
|
} else {
|
2017-01-09 23:44:13 +00:00
|
|
|
ds := digestset.NewSet()
|
2016-06-15 22:40:15 +00:00
|
|
|
for _, dgst := range tcase.Digests {
|
|
|
|
if err := ds.Add(dgst); err != nil {
|
|
|
|
t.Fatalf("Error adding digest %s: %v", dgst.String(), err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ref, err = ParseAnyReferenceWithSet(tcase.Reference, ds)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Error parsing reference %s: %v", tcase.Reference, err)
|
|
|
|
}
|
2017-01-20 19:38:07 +00:00
|
|
|
if ref.String() != tcase.Equivalent {
|
|
|
|
t.Fatalf("Unexpected string: %s, expected %s", ref.String(), tcase.Equivalent)
|
|
|
|
}
|
2016-06-15 22:40:15 +00:00
|
|
|
|
|
|
|
expected := tcase.Expected
|
|
|
|
if expected == nil {
|
|
|
|
expected, err = Parse(tcase.Equivalent)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Error parsing reference %s: %v", tcase.Equivalent, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !equalReference(ref, expected) {
|
|
|
|
t.Errorf("Unexpected reference %#v, expected %#v", ref, expected)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-01-10 00:52:05 +00:00
|
|
|
|
|
|
|
func TestNormalizedSplitHostname(t *testing.T) {
|
|
|
|
testcases := []struct {
|
|
|
|
input string
|
|
|
|
domain string
|
|
|
|
name string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
input: "test.com/foo",
|
|
|
|
domain: "test.com",
|
|
|
|
name: "foo",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
input: "test_com/foo",
|
|
|
|
domain: "docker.io",
|
|
|
|
name: "test_com/foo",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
input: "docker/migrator",
|
|
|
|
domain: "docker.io",
|
|
|
|
name: "docker/migrator",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
input: "test.com:8080/foo",
|
|
|
|
domain: "test.com:8080",
|
|
|
|
name: "foo",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
input: "test-com:8080/foo",
|
|
|
|
domain: "test-com:8080",
|
|
|
|
name: "foo",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
input: "foo",
|
|
|
|
domain: "docker.io",
|
|
|
|
name: "library/foo",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
input: "xn--n3h.com/foo",
|
|
|
|
domain: "xn--n3h.com",
|
|
|
|
name: "foo",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
input: "xn--n3h.com:18080/foo",
|
|
|
|
domain: "xn--n3h.com:18080",
|
|
|
|
name: "foo",
|
|
|
|
},
|
2017-01-11 18:46:48 +00:00
|
|
|
{
|
|
|
|
input: "docker.io/foo",
|
|
|
|
domain: "docker.io",
|
|
|
|
name: "library/foo",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
input: "docker.io/library/foo",
|
|
|
|
domain: "docker.io",
|
|
|
|
name: "library/foo",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
input: "docker.io/library/foo/bar",
|
|
|
|
domain: "docker.io",
|
|
|
|
name: "library/foo/bar",
|
|
|
|
},
|
2017-01-10 00:52:05 +00:00
|
|
|
}
|
|
|
|
for _, testcase := range testcases {
|
|
|
|
failf := func(format string, v ...interface{}) {
|
|
|
|
t.Logf(strconv.Quote(testcase.input)+": "+format, v...)
|
|
|
|
t.Fail()
|
|
|
|
}
|
|
|
|
|
|
|
|
named, err := ParseNormalizedNamed(testcase.input)
|
|
|
|
if err != nil {
|
|
|
|
failf("error parsing name: %s", err)
|
|
|
|
}
|
|
|
|
domain, name := SplitHostname(named)
|
|
|
|
if domain != testcase.domain {
|
|
|
|
failf("unexpected domain: got %q, expected %q", domain, testcase.domain)
|
|
|
|
}
|
|
|
|
if name != testcase.name {
|
|
|
|
failf("unexpected name: got %q, expected %q", name, testcase.name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-01-14 01:08:46 +00:00
|
|
|
|
|
|
|
func TestMatchError(t *testing.T) {
|
|
|
|
named, err := ParseAnyReference("foo")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
_, err = FamiliarMatch("[-x]", named)
|
|
|
|
if err == nil {
|
|
|
|
t.Fatalf("expected an error, got nothing")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMatch(t *testing.T) {
|
|
|
|
matchCases := []struct {
|
|
|
|
reference string
|
|
|
|
pattern string
|
|
|
|
expected bool
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
reference: "foo",
|
|
|
|
pattern: "foo/**/ba[rz]",
|
|
|
|
expected: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
reference: "foo/any/bat",
|
|
|
|
pattern: "foo/**/ba[rz]",
|
|
|
|
expected: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
reference: "foo/a/bar",
|
|
|
|
pattern: "foo/**/ba[rz]",
|
|
|
|
expected: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
reference: "foo/b/baz",
|
|
|
|
pattern: "foo/**/ba[rz]",
|
|
|
|
expected: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
reference: "foo/c/baz:tag",
|
|
|
|
pattern: "foo/**/ba[rz]",
|
|
|
|
expected: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
reference: "foo/c/baz:tag",
|
|
|
|
pattern: "foo/*/baz:tag",
|
|
|
|
expected: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
reference: "foo/c/baz:tag",
|
|
|
|
pattern: "foo/c/baz:tag",
|
|
|
|
expected: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
reference: "example.com/foo/c/baz:tag",
|
|
|
|
pattern: "*/foo/c/baz",
|
|
|
|
expected: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
reference: "example.com/foo/c/baz:tag",
|
|
|
|
pattern: "example.com/foo/c/baz",
|
|
|
|
expected: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, c := range matchCases {
|
|
|
|
named, err := ParseAnyReference(c.reference)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
actual, err := FamiliarMatch(c.pattern, named)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if actual != c.expected {
|
|
|
|
t.Fatalf("expected %s match %s to be %v, was %v", c.reference, c.pattern, c.expected, actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-12-10 22:51:58 +00:00
|
|
|
|
|
|
|
func TestParseDockerRef(t *testing.T) {
|
|
|
|
testcases := []struct {
|
|
|
|
name string
|
|
|
|
input string
|
|
|
|
expected string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "nothing",
|
|
|
|
input: "busybox",
|
|
|
|
expected: "docker.io/library/busybox:latest",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "tag only",
|
|
|
|
input: "busybox:latest",
|
|
|
|
expected: "docker.io/library/busybox:latest",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "digest only",
|
|
|
|
input: "busybox@sha256:e6693c20186f837fc393390135d8a598a96a833917917789d63766cab6c59582",
|
|
|
|
expected: "docker.io/library/busybox@sha256:e6693c20186f837fc393390135d8a598a96a833917917789d63766cab6c59582",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "path only",
|
|
|
|
input: "library/busybox",
|
|
|
|
expected: "docker.io/library/busybox:latest",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "hostname only",
|
|
|
|
input: "docker.io/busybox",
|
|
|
|
expected: "docker.io/library/busybox:latest",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "no tag",
|
|
|
|
input: "docker.io/library/busybox",
|
|
|
|
expected: "docker.io/library/busybox:latest",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "no path",
|
|
|
|
input: "docker.io/busybox:latest",
|
|
|
|
expected: "docker.io/library/busybox:latest",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "no hostname",
|
|
|
|
input: "library/busybox:latest",
|
|
|
|
expected: "docker.io/library/busybox:latest",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "full reference with tag",
|
|
|
|
input: "docker.io/library/busybox:latest",
|
|
|
|
expected: "docker.io/library/busybox:latest",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "gcr reference without tag",
|
|
|
|
input: "gcr.io/library/busybox",
|
|
|
|
expected: "gcr.io/library/busybox:latest",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "both tag and digest",
|
|
|
|
input: "gcr.io/library/busybox:latest@sha256:e6693c20186f837fc393390135d8a598a96a833917917789d63766cab6c59582",
|
|
|
|
expected: "gcr.io/library/busybox@sha256:e6693c20186f837fc393390135d8a598a96a833917917789d63766cab6c59582",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, test := range testcases {
|
|
|
|
t.Run(test.name, func(t *testing.T) {
|
|
|
|
normalized, err := ParseDockerRef(test.input)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
output := normalized.String()
|
|
|
|
if output != test.expected {
|
|
|
|
t.Fatalf("expected %q to be parsed as %v, got %v", test.input, test.expected, output)
|
|
|
|
}
|
|
|
|
_, err = Parse(output)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("%q should be a valid reference, but got an error: %v", output, err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|