forked from TrueCloudLab/distribution
Move docker reference functionality to reference package
Add normalization functions and Docker specific domain splitting to reference package. Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
This commit is contained in:
parent
21db8e8597
commit
042fe9bf46
2 changed files with 547 additions and 2 deletions
|
@ -1,9 +1,89 @@
|
||||||
package reference
|
package reference
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/distribution/digest"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
legacyDefaultDomain = "index.docker.io"
|
||||||
|
defaultDomain = "docker.io"
|
||||||
|
defaultRepoPrefix = "library/"
|
||||||
defaultTag = "latest"
|
defaultTag = "latest"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NormalizedName parses a string into a named reference
|
||||||
|
// transforming a familiar name from Docker UI to a fully
|
||||||
|
// qualified reference. If the value may be an identifier
|
||||||
|
// use ParseAnyReference.
|
||||||
|
func NormalizedName(s string) (Named, error) {
|
||||||
|
if ok := anchoredIdentifierRegexp.MatchString(s); ok {
|
||||||
|
return nil, fmt.Errorf("invalid repository name (%s), cannot specify 64-byte hexadecimal strings", s)
|
||||||
|
}
|
||||||
|
domain, remainder := splitDockerDomain(s)
|
||||||
|
var remoteName string
|
||||||
|
if tagSep := strings.IndexRune(remainder, ':'); tagSep > -1 {
|
||||||
|
remoteName = remainder[:tagSep]
|
||||||
|
} else {
|
||||||
|
remoteName = remainder
|
||||||
|
}
|
||||||
|
if strings.ToLower(remoteName) != remoteName {
|
||||||
|
return nil, errors.New("invalid reference format: repository name must be lowercase")
|
||||||
|
}
|
||||||
|
|
||||||
|
return ParseNamed(domain + "/" + remainder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// splitDockerDomain splits a repository name to domain and remotename string.
|
||||||
|
// If no valid domain is found, the default domain is used. Repository name
|
||||||
|
// needs to be already validated before.
|
||||||
|
func splitDockerDomain(name string) (domain, remainder string) {
|
||||||
|
i := strings.IndexRune(name, '/')
|
||||||
|
if i == -1 || (!strings.ContainsAny(name[:i], ".:") && name[:i] != "localhost") {
|
||||||
|
domain, remainder = defaultDomain, name
|
||||||
|
} else {
|
||||||
|
domain, remainder = name[:i], name[i+1:]
|
||||||
|
}
|
||||||
|
if domain == legacyDefaultDomain {
|
||||||
|
domain = defaultDomain
|
||||||
|
}
|
||||||
|
if domain == defaultDomain && !strings.ContainsRune(remainder, '/') {
|
||||||
|
remainder = defaultRepoPrefix + remainder
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FamiliarName returns a shortened version of the name familiar
|
||||||
|
// to to the Docker UI. Familiar names have the default domain
|
||||||
|
// "docker.io" and "library/" repository prefix removed.
|
||||||
|
// For example, "docker.io/library/redis" will have the familiar
|
||||||
|
// name "redis" and "docker.io/dmcgowan/myapp" will be "dmcgowan/myapp".
|
||||||
|
func FamiliarName(named Named) Named {
|
||||||
|
var repo repository
|
||||||
|
repo.domain, repo.path = splitDomain(named.Name())
|
||||||
|
|
||||||
|
if repo.domain == defaultDomain {
|
||||||
|
repo.domain = ""
|
||||||
|
repo.path = strings.TrimPrefix(repo.path, defaultRepoPrefix)
|
||||||
|
}
|
||||||
|
if digested, ok := named.(Digested); ok {
|
||||||
|
return canonicalReference{
|
||||||
|
repository: repo,
|
||||||
|
digest: digested.Digest(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if tagged, ok := named.(Tagged); ok {
|
||||||
|
return taggedReference{
|
||||||
|
repository: repo,
|
||||||
|
tag: tagged.Tag(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return repo
|
||||||
|
}
|
||||||
|
|
||||||
// EnsureTagged adds the default tag "latest" to a reference if it only has
|
// EnsureTagged adds the default tag "latest" to a reference if it only has
|
||||||
// a repo name.
|
// a repo name.
|
||||||
func EnsureTagged(ref Named) NamedTagged {
|
func EnsureTagged(ref Named) NamedTagged {
|
||||||
|
@ -20,3 +100,33 @@ func EnsureTagged(ref Named) NamedTagged {
|
||||||
}
|
}
|
||||||
return namedTagged
|
return namedTagged
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseAnyReference parses a reference string as a possible identifier,
|
||||||
|
// full digest, or familiar name.
|
||||||
|
func ParseAnyReference(ref string) (Reference, error) {
|
||||||
|
if ok := anchoredIdentifierRegexp.MatchString(ref); ok {
|
||||||
|
return digestReference("sha256:" + ref), nil
|
||||||
|
}
|
||||||
|
if dgst, err := digest.ParseDigest(ref); err == nil {
|
||||||
|
return digestReference(dgst), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return NormalizedName(ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseAnyReferenceWithSet parses a reference string as a possible short
|
||||||
|
// identifier to be matched in a digest set, a full digest, or familiar name.
|
||||||
|
func ParseAnyReferenceWithSet(ref string, ds *digest.Set) (Reference, error) {
|
||||||
|
if ok := anchoredShortIdentifierRegexp.MatchString(ref); ok {
|
||||||
|
dgst, err := ds.Lookup(ref)
|
||||||
|
if err == nil {
|
||||||
|
return digestReference(dgst), nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if dgst, err := digest.ParseDigest(ref); err == nil {
|
||||||
|
return digestReference(dgst), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NormalizedName(ref)
|
||||||
|
}
|
||||||
|
|
435
reference/normalize_test.go
Normal file
435
reference/normalize_test.go
Normal file
|
@ -0,0 +1,435 @@
|
||||||
|
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",
|
||||||
|
|
||||||
|
// 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",
|
||||||
|
}
|
||||||
|
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",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, name := range invalidRepoNames {
|
||||||
|
_, err := NormalizedName(name)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected invalid repo name for %q", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, name := range validRepoNames {
|
||||||
|
_, err := NormalizedName(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 := NormalizedName(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 := NormalizedName(repositoryName); err == nil {
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tcase := range tcases {
|
||||||
|
refStrings := []string{tcase.FamiliarName, tcase.FullName}
|
||||||
|
if tcase.AmbiguousName != "" {
|
||||||
|
refStrings = append(refStrings, tcase.AmbiguousName)
|
||||||
|
}
|
||||||
|
|
||||||
|
var refs []Named
|
||||||
|
for _, r := range refStrings {
|
||||||
|
named, err := NormalizedName(r)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
refs = append(refs, named)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, r := range refs {
|
||||||
|
if expected, actual := tcase.FamiliarName, FamiliarName(r).Name(); expected != actual {
|
||||||
|
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) {
|
||||||
|
ref, err := NormalizedName("busybox:latest@sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if expected, actual := "docker.io/library/busybox:latest@sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa", ref.String(); actual != expected {
|
||||||
|
t.Fatalf("Invalid parsed reference for %q: expected %q, got %q", ref, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
ref = FamiliarName(ref)
|
||||||
|
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", FamiliarName(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 := NormalizedName("-foo"); err == nil {
|
||||||
|
t.Fatal("Expected WithName to detect invalid name")
|
||||||
|
}
|
||||||
|
ref, err := NormalizedName("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 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",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Reference: "dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
|
||||||
|
Expected: digestReference("sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Reference: "sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
|
||||||
|
Expected: digestReference("sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Reference: "dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9",
|
||||||
|
Equivalent: "docker.io/library/dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Reference: "dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9",
|
||||||
|
Expected: digestReference("sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"),
|
||||||
|
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"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Reference: "dbcc1c",
|
||||||
|
Expected: digestReference("sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"),
|
||||||
|
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 {
|
||||||
|
ds := digest.NewSet()
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue