package reference import ( "testing" "github.com/docker/distribution/digest" ) 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", "thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev", } 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", "1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a", "docker.io/1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a", } for _, name := range invalidRepoNames { _, err := ParseNamed(name) if err == nil { t.Fatalf("Expected invalid repo name for %q", name) } } for _, name := range validRepoNames { _, err := ParseNamed(name) 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", //Username doc and image name docker being tested. "doc/docker", // single character names are now allowed. "d/docker", "jess/t", // Consecutive underscores. "dock__er/docker", } for _, repositoryName := range validRepositoryNames { _, err := ParseNamed(repositoryName) 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/", //namespace too long "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 { if _, err := ParseNamed(repositoryName); err == nil { t.Errorf("Repository name should be invalid: %v", repositoryName) } } } func TestParseRepositoryInfo(t *testing.T) { type tcase struct { RemoteName, NormalizedName, FullName, AmbiguousName, Hostname string } tcases := []tcase{ { RemoteName: "fooo/bar", NormalizedName: "fooo/bar", FullName: "docker.io/fooo/bar", AmbiguousName: "index.docker.io/fooo/bar", Hostname: "docker.io", }, { RemoteName: "library/ubuntu", NormalizedName: "ubuntu", FullName: "docker.io/library/ubuntu", AmbiguousName: "library/ubuntu", Hostname: "docker.io", }, { RemoteName: "nonlibrary/ubuntu", NormalizedName: "nonlibrary/ubuntu", FullName: "docker.io/nonlibrary/ubuntu", AmbiguousName: "", Hostname: "docker.io", }, { RemoteName: "other/library", NormalizedName: "other/library", FullName: "docker.io/other/library", AmbiguousName: "", Hostname: "docker.io", }, { RemoteName: "private/moonbase", NormalizedName: "127.0.0.1:8000/private/moonbase", FullName: "127.0.0.1:8000/private/moonbase", AmbiguousName: "", Hostname: "127.0.0.1:8000", }, { RemoteName: "privatebase", NormalizedName: "127.0.0.1:8000/privatebase", FullName: "127.0.0.1:8000/privatebase", AmbiguousName: "", Hostname: "127.0.0.1:8000", }, { RemoteName: "private/moonbase", NormalizedName: "example.com/private/moonbase", FullName: "example.com/private/moonbase", AmbiguousName: "", Hostname: "example.com", }, { RemoteName: "privatebase", NormalizedName: "example.com/privatebase", FullName: "example.com/privatebase", AmbiguousName: "", Hostname: "example.com", }, { RemoteName: "private/moonbase", NormalizedName: "example.com:8000/private/moonbase", FullName: "example.com:8000/private/moonbase", AmbiguousName: "", Hostname: "example.com:8000", }, { RemoteName: "privatebasee", NormalizedName: "example.com:8000/privatebasee", FullName: "example.com:8000/privatebasee", AmbiguousName: "", Hostname: "example.com:8000", }, { RemoteName: "library/ubuntu-12.04-base", NormalizedName: "ubuntu-12.04-base", FullName: "docker.io/library/ubuntu-12.04-base", AmbiguousName: "index.docker.io/library/ubuntu-12.04-base", Hostname: "docker.io", }, } for _, tcase := range tcases { refStrings := []string{tcase.NormalizedName, tcase.FullName} if tcase.AmbiguousName != "" { refStrings = append(refStrings, tcase.AmbiguousName) } var refs []Named for _, r := range refStrings { named, err := ParseNamed(r) if err != nil { t.Fatal(err) } refs = append(refs, named) named, err = WithName(r) if err != nil { t.Fatal(err) } refs = append(refs, named) } for _, r := range refs { if expected, actual := tcase.NormalizedName, r.Name(); expected != actual { t.Fatalf("Invalid normalized reference for %q. Expected %q, got %q", r, expected, actual) } if expected, actual := tcase.FullName, r.FullName(); expected != actual { t.Fatalf("Invalid normalized reference for %q. Expected %q, got %q", r, expected, actual) } if expected, actual := tcase.Hostname, r.Hostname(); expected != actual { t.Fatalf("Invalid hostname for %q. Expected %q, got %q", r, expected, actual) } if expected, actual := tcase.RemoteName, r.RemoteName(); expected != actual { t.Fatalf("Invalid remoteName for %q. Expected %q, got %q", r, expected, actual) } } } } func TestParseReferenceWithTagAndDigest(t *testing.T) { ref, err := ParseNamed("busybox:latest@sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa") if err != nil { t.Fatal(err) } if _, isTagged := ref.(NamedTagged); isTagged { t.Fatalf("Reference from %q should not support tag", ref) } if _, isCanonical := ref.(Canonical); !isCanonical { t.Fatalf("Reference from %q should not support digest", ref) } if expected, actual := "busybox@sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa", ref.String(); actual != expected { t.Fatalf("Invalid parsed reference for %q: expected %q, got %q", ref, expected, actual) } } func TestInvalidReferenceComponents(t *testing.T) { if _, err := WithName("-foo"); err == nil { t.Fatal("Expected WithName to detect invalid name") } ref, err := WithName("busybox") 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 WithName to detect invalid digest") } }