Split apart repository reference into domain and path
Allows having other parsers which are capable of unambiguously keeping domain and path separated in a Reference type. Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
This commit is contained in:
parent
76f514b618
commit
9a43b8f696
@ -4,11 +4,11 @@
|
|||||||
// Grammar
|
// Grammar
|
||||||
//
|
//
|
||||||
// reference := name [ ":" tag ] [ "@" digest ]
|
// reference := name [ ":" tag ] [ "@" digest ]
|
||||||
// name := [hostname '/'] component ['/' component]*
|
// name := [domain '/'] path-component ['/' path-component]*
|
||||||
// hostname := hostcomponent ['.' hostcomponent]* [':' port-number]
|
// domain := domain-component ['.' domain-component]* [':' port-number]
|
||||||
// hostcomponent := /([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/
|
// domain-component := /([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/
|
||||||
// port-number := /[0-9]+/
|
// port-number := /[0-9]+/
|
||||||
// component := alpha-numeric [separator alpha-numeric]*
|
// path-component := alpha-numeric [separator alpha-numeric]*
|
||||||
// alpha-numeric := /[a-z0-9]+/
|
// alpha-numeric := /[a-z0-9]+/
|
||||||
// separator := /[_.]|__|[-]*/
|
// separator := /[_.]|__|[-]*/
|
||||||
//
|
//
|
||||||
@ -126,23 +126,56 @@ type Digested interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Canonical reference is an object with a fully unique
|
// Canonical reference is an object with a fully unique
|
||||||
// name including a name with hostname and digest
|
// name including a name with domain and digest
|
||||||
type Canonical interface {
|
type Canonical interface {
|
||||||
Named
|
Named
|
||||||
Digest() digest.Digest
|
Digest() digest.Digest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NamedRepository is a reference to a repository with a name.
|
||||||
|
// A NamedRepository has both domain and path components.
|
||||||
|
type NamedRepository interface {
|
||||||
|
Named
|
||||||
|
Domain() string
|
||||||
|
Path() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Domain returns the domain part of the Named reference
|
||||||
|
func Domain(named Named) string {
|
||||||
|
if r, ok := named.(NamedRepository); ok {
|
||||||
|
return r.Domain()
|
||||||
|
}
|
||||||
|
domain, _ := splitDomain(named.Name())
|
||||||
|
return domain
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path returns the name without the domain part of the Named reference
|
||||||
|
func Path(named Named) (name string) {
|
||||||
|
if r, ok := named.(NamedRepository); ok {
|
||||||
|
return r.Path()
|
||||||
|
}
|
||||||
|
_, path := splitDomain(named.Name())
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitDomain(name string) (string, string) {
|
||||||
|
match := anchoredNameRegexp.FindStringSubmatch(name)
|
||||||
|
if len(match) != 3 {
|
||||||
|
return "", name
|
||||||
|
}
|
||||||
|
return match[1], match[2]
|
||||||
|
}
|
||||||
|
|
||||||
// SplitHostname splits a named reference into a
|
// SplitHostname splits a named reference into a
|
||||||
// hostname and name string. If no valid hostname is
|
// hostname and name string. If no valid hostname is
|
||||||
// found, the hostname is empty and the full value
|
// found, the hostname is empty and the full value
|
||||||
// is returned as name
|
// is returned as name
|
||||||
|
// DEPRECATED: Use Domain or Path
|
||||||
func SplitHostname(named Named) (string, string) {
|
func SplitHostname(named Named) (string, string) {
|
||||||
name := named.Name()
|
if r, ok := named.(NamedRepository); ok {
|
||||||
match := anchoredNameRegexp.FindStringSubmatch(name)
|
return r.Domain(), r.Path()
|
||||||
if len(match) != 3 {
|
|
||||||
return "", name
|
|
||||||
}
|
}
|
||||||
return match[1], match[2]
|
return splitDomain(named.Name())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse parses s and returns a syntactically valid Reference.
|
// Parse parses s and returns a syntactically valid Reference.
|
||||||
@ -164,9 +197,20 @@ func Parse(s string) (Reference, error) {
|
|||||||
return nil, ErrNameTooLong
|
return nil, ErrNameTooLong
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var repo repository
|
||||||
|
|
||||||
|
nameMatch := anchoredNameRegexp.FindStringSubmatch(matches[1])
|
||||||
|
if nameMatch != nil && len(nameMatch) == 3 {
|
||||||
|
repo.domain = nameMatch[1]
|
||||||
|
repo.path = nameMatch[2]
|
||||||
|
} else {
|
||||||
|
repo.domain = ""
|
||||||
|
repo.path = matches[1]
|
||||||
|
}
|
||||||
|
|
||||||
ref := reference{
|
ref := reference{
|
||||||
name: matches[1],
|
repository: repo,
|
||||||
tag: matches[2],
|
tag: matches[2],
|
||||||
}
|
}
|
||||||
if matches[3] != "" {
|
if matches[3] != "" {
|
||||||
var err error
|
var err error
|
||||||
@ -207,10 +251,15 @@ func WithName(name string) (Named, error) {
|
|||||||
if len(name) > NameTotalLengthMax {
|
if len(name) > NameTotalLengthMax {
|
||||||
return nil, ErrNameTooLong
|
return nil, ErrNameTooLong
|
||||||
}
|
}
|
||||||
if !anchoredNameRegexp.MatchString(name) {
|
|
||||||
|
match := anchoredNameRegexp.FindStringSubmatch(name)
|
||||||
|
if match == nil || len(match) != 3 {
|
||||||
return nil, ErrReferenceInvalidFormat
|
return nil, ErrReferenceInvalidFormat
|
||||||
}
|
}
|
||||||
return repository(name), nil
|
return repository{
|
||||||
|
domain: match[1],
|
||||||
|
path: match[2],
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithTag combines the name from "name" and the tag from "tag" to form a
|
// WithTag combines the name from "name" and the tag from "tag" to form a
|
||||||
@ -219,16 +268,23 @@ func WithTag(name Named, tag string) (NamedTagged, error) {
|
|||||||
if !anchoredTagRegexp.MatchString(tag) {
|
if !anchoredTagRegexp.MatchString(tag) {
|
||||||
return nil, ErrTagInvalidFormat
|
return nil, ErrTagInvalidFormat
|
||||||
}
|
}
|
||||||
|
var repo repository
|
||||||
|
if r, ok := name.(NamedRepository); ok {
|
||||||
|
repo.domain = r.Domain()
|
||||||
|
repo.path = r.Path()
|
||||||
|
} else {
|
||||||
|
repo.path = name.Name()
|
||||||
|
}
|
||||||
if canonical, ok := name.(Canonical); ok {
|
if canonical, ok := name.(Canonical); ok {
|
||||||
return reference{
|
return reference{
|
||||||
name: name.Name(),
|
repository: repo,
|
||||||
tag: tag,
|
tag: tag,
|
||||||
digest: canonical.Digest(),
|
digest: canonical.Digest(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
return taggedReference{
|
return taggedReference{
|
||||||
name: name.Name(),
|
repository: repo,
|
||||||
tag: tag,
|
tag: tag,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,16 +294,23 @@ func WithDigest(name Named, digest digest.Digest) (Canonical, error) {
|
|||||||
if !anchoredDigestRegexp.MatchString(digest.String()) {
|
if !anchoredDigestRegexp.MatchString(digest.String()) {
|
||||||
return nil, ErrDigestInvalidFormat
|
return nil, ErrDigestInvalidFormat
|
||||||
}
|
}
|
||||||
|
var repo repository
|
||||||
|
if r, ok := name.(NamedRepository); ok {
|
||||||
|
repo.domain = r.Domain()
|
||||||
|
repo.path = r.Path()
|
||||||
|
} else {
|
||||||
|
repo.path = name.Name()
|
||||||
|
}
|
||||||
if tagged, ok := name.(Tagged); ok {
|
if tagged, ok := name.(Tagged); ok {
|
||||||
return reference{
|
return reference{
|
||||||
name: name.Name(),
|
repository: repo,
|
||||||
tag: tagged.Tag(),
|
tag: tagged.Tag(),
|
||||||
digest: digest,
|
digest: digest,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
return canonicalReference{
|
return canonicalReference{
|
||||||
name: name.Name(),
|
repository: repo,
|
||||||
digest: digest,
|
digest: digest,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,7 +330,7 @@ func TrimNamed(ref Named) Named {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getBestReferenceType(ref reference) Reference {
|
func getBestReferenceType(ref reference) Reference {
|
||||||
if ref.name == "" {
|
if ref.repository.path == "" {
|
||||||
// Allow digest only references
|
// Allow digest only references
|
||||||
if ref.digest != "" {
|
if ref.digest != "" {
|
||||||
return digestReference(ref.digest)
|
return digestReference(ref.digest)
|
||||||
@ -277,16 +340,16 @@ func getBestReferenceType(ref reference) Reference {
|
|||||||
if ref.tag == "" {
|
if ref.tag == "" {
|
||||||
if ref.digest != "" {
|
if ref.digest != "" {
|
||||||
return canonicalReference{
|
return canonicalReference{
|
||||||
name: ref.name,
|
repository: ref.repository,
|
||||||
digest: ref.digest,
|
digest: ref.digest,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return repository(ref.name)
|
return ref.repository
|
||||||
}
|
}
|
||||||
if ref.digest == "" {
|
if ref.digest == "" {
|
||||||
return taggedReference{
|
return taggedReference{
|
||||||
name: ref.name,
|
repository: ref.repository,
|
||||||
tag: ref.tag,
|
tag: ref.tag,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,17 +357,13 @@ func getBestReferenceType(ref reference) Reference {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type reference struct {
|
type reference struct {
|
||||||
name string
|
repository
|
||||||
tag string
|
tag string
|
||||||
digest digest.Digest
|
digest digest.Digest
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r reference) String() string {
|
func (r reference) String() string {
|
||||||
return r.name + ":" + r.tag + "@" + r.digest.String()
|
return r.Name() + ":" + r.tag + "@" + r.digest.String()
|
||||||
}
|
|
||||||
|
|
||||||
func (r reference) Name() string {
|
|
||||||
return r.name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r reference) Tag() string {
|
func (r reference) Tag() string {
|
||||||
@ -315,14 +374,28 @@ func (r reference) Digest() digest.Digest {
|
|||||||
return r.digest
|
return r.digest
|
||||||
}
|
}
|
||||||
|
|
||||||
type repository string
|
type repository struct {
|
||||||
|
domain string
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
func (r repository) String() string {
|
func (r repository) String() string {
|
||||||
return string(r)
|
return r.Name()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r repository) Name() string {
|
func (r repository) Name() string {
|
||||||
return string(r)
|
if r.domain == "" {
|
||||||
|
return r.path
|
||||||
|
}
|
||||||
|
return r.domain + "/" + r.path
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r repository) Domain() string {
|
||||||
|
return r.domain
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r repository) Path() string {
|
||||||
|
return r.path
|
||||||
}
|
}
|
||||||
|
|
||||||
type digestReference digest.Digest
|
type digestReference digest.Digest
|
||||||
@ -336,16 +409,12 @@ func (d digestReference) Digest() digest.Digest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type taggedReference struct {
|
type taggedReference struct {
|
||||||
name string
|
repository
|
||||||
tag string
|
tag string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t taggedReference) String() string {
|
func (t taggedReference) String() string {
|
||||||
return t.name + ":" + t.tag
|
return t.Name() + ":" + t.tag
|
||||||
}
|
|
||||||
|
|
||||||
func (t taggedReference) Name() string {
|
|
||||||
return t.name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t taggedReference) Tag() string {
|
func (t taggedReference) Tag() string {
|
||||||
@ -353,16 +422,12 @@ func (t taggedReference) Tag() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type canonicalReference struct {
|
type canonicalReference struct {
|
||||||
name string
|
repository
|
||||||
digest digest.Digest
|
digest digest.Digest
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c canonicalReference) String() string {
|
func (c canonicalReference) String() string {
|
||||||
return c.name + "@" + c.digest.String()
|
return c.Name() + "@" + c.digest.String()
|
||||||
}
|
|
||||||
|
|
||||||
func (c canonicalReference) Name() string {
|
|
||||||
return c.name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c canonicalReference) Digest() digest.Digest {
|
func (c canonicalReference) Digest() digest.Digest {
|
||||||
|
@ -21,8 +21,8 @@ func TestReferenceParse(t *testing.T) {
|
|||||||
err error
|
err error
|
||||||
// repository is the string representation for the reference
|
// repository is the string representation for the reference
|
||||||
repository string
|
repository string
|
||||||
// hostname is the hostname expected in the reference
|
// domain is the domain expected in the reference
|
||||||
hostname string
|
domain string
|
||||||
// tag is the tag for the reference
|
// tag is the tag for the reference
|
||||||
tag string
|
tag string
|
||||||
// digest is the digest for the reference (enforces digest reference)
|
// digest is the digest for the reference (enforces digest reference)
|
||||||
@ -44,37 +44,37 @@ func TestReferenceParse(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "test.com/repo:tag",
|
input: "test.com/repo:tag",
|
||||||
hostname: "test.com",
|
domain: "test.com",
|
||||||
repository: "test.com/repo",
|
repository: "test.com/repo",
|
||||||
tag: "tag",
|
tag: "tag",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "test:5000/repo",
|
input: "test:5000/repo",
|
||||||
hostname: "test:5000",
|
domain: "test:5000",
|
||||||
repository: "test:5000/repo",
|
repository: "test:5000/repo",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "test:5000/repo:tag",
|
input: "test:5000/repo:tag",
|
||||||
hostname: "test:5000",
|
domain: "test:5000",
|
||||||
repository: "test:5000/repo",
|
repository: "test:5000/repo",
|
||||||
tag: "tag",
|
tag: "tag",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "test:5000/repo@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
input: "test:5000/repo@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
hostname: "test:5000",
|
domain: "test:5000",
|
||||||
repository: "test:5000/repo",
|
repository: "test:5000/repo",
|
||||||
digest: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
digest: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "test:5000/repo:tag@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
input: "test:5000/repo:tag@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
hostname: "test:5000",
|
domain: "test:5000",
|
||||||
repository: "test:5000/repo",
|
repository: "test:5000/repo",
|
||||||
tag: "tag",
|
tag: "tag",
|
||||||
digest: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
digest: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "test:5000/repo",
|
input: "test:5000/repo",
|
||||||
hostname: "test:5000",
|
domain: "test:5000",
|
||||||
repository: "test:5000/repo",
|
repository: "test:5000/repo",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -122,7 +122,7 @@ func TestReferenceParse(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: strings.Repeat("a/", 127) + "a:tag-puts-this-over-max",
|
input: strings.Repeat("a/", 127) + "a:tag-puts-this-over-max",
|
||||||
hostname: "a",
|
domain: "a",
|
||||||
repository: strings.Repeat("a/", 127) + "a",
|
repository: strings.Repeat("a/", 127) + "a",
|
||||||
tag: "tag-puts-this-over-max",
|
tag: "tag-puts-this-over-max",
|
||||||
},
|
},
|
||||||
@ -132,30 +132,30 @@ func TestReferenceParse(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "sub-dom1.foo.com/bar/baz/quux",
|
input: "sub-dom1.foo.com/bar/baz/quux",
|
||||||
hostname: "sub-dom1.foo.com",
|
domain: "sub-dom1.foo.com",
|
||||||
repository: "sub-dom1.foo.com/bar/baz/quux",
|
repository: "sub-dom1.foo.com/bar/baz/quux",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "sub-dom1.foo.com/bar/baz/quux:some-long-tag",
|
input: "sub-dom1.foo.com/bar/baz/quux:some-long-tag",
|
||||||
hostname: "sub-dom1.foo.com",
|
domain: "sub-dom1.foo.com",
|
||||||
repository: "sub-dom1.foo.com/bar/baz/quux",
|
repository: "sub-dom1.foo.com/bar/baz/quux",
|
||||||
tag: "some-long-tag",
|
tag: "some-long-tag",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "b.gcr.io/test.example.com/my-app:test.example.com",
|
input: "b.gcr.io/test.example.com/my-app:test.example.com",
|
||||||
hostname: "b.gcr.io",
|
domain: "b.gcr.io",
|
||||||
repository: "b.gcr.io/test.example.com/my-app",
|
repository: "b.gcr.io/test.example.com/my-app",
|
||||||
tag: "test.example.com",
|
tag: "test.example.com",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "xn--n3h.com/myimage:xn--n3h.com", // ☃.com in punycode
|
input: "xn--n3h.com/myimage:xn--n3h.com", // ☃.com in punycode
|
||||||
hostname: "xn--n3h.com",
|
domain: "xn--n3h.com",
|
||||||
repository: "xn--n3h.com/myimage",
|
repository: "xn--n3h.com/myimage",
|
||||||
tag: "xn--n3h.com",
|
tag: "xn--n3h.com",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "xn--7o8h.com/myimage:xn--7o8h.com@sha512:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", // 🐳.com in punycode
|
input: "xn--7o8h.com/myimage:xn--7o8h.com@sha512:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", // 🐳.com in punycode
|
||||||
hostname: "xn--7o8h.com",
|
domain: "xn--7o8h.com",
|
||||||
repository: "xn--7o8h.com/myimage",
|
repository: "xn--7o8h.com/myimage",
|
||||||
tag: "xn--7o8h.com",
|
tag: "xn--7o8h.com",
|
||||||
digest: "sha512:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
digest: "sha512:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
@ -167,7 +167,7 @@ func TestReferenceParse(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "foo/foo_bar.com:8080",
|
input: "foo/foo_bar.com:8080",
|
||||||
hostname: "foo",
|
domain: "foo",
|
||||||
repository: "foo/foo_bar.com",
|
repository: "foo/foo_bar.com",
|
||||||
tag: "8080",
|
tag: "8080",
|
||||||
},
|
},
|
||||||
@ -198,11 +198,11 @@ func TestReferenceParse(t *testing.T) {
|
|||||||
if named.Name() != testcase.repository {
|
if named.Name() != testcase.repository {
|
||||||
failf("unexpected repository: got %q, expected %q", named.Name(), testcase.repository)
|
failf("unexpected repository: got %q, expected %q", named.Name(), testcase.repository)
|
||||||
}
|
}
|
||||||
hostname, _ := SplitHostname(named)
|
domain, _ := SplitHostname(named)
|
||||||
if hostname != testcase.hostname {
|
if domain != testcase.domain {
|
||||||
failf("unexpected hostname: got %q, expected %q", hostname, testcase.hostname)
|
failf("unexpected domain: got %q, expected %q", domain, testcase.domain)
|
||||||
}
|
}
|
||||||
} else if testcase.repository != "" || testcase.hostname != "" {
|
} else if testcase.repository != "" || testcase.domain != "" {
|
||||||
failf("expected named type, got %T", repo)
|
failf("expected named type, got %T", repo)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,39 +282,39 @@ func TestWithNameFailure(t *testing.T) {
|
|||||||
|
|
||||||
func TestSplitHostname(t *testing.T) {
|
func TestSplitHostname(t *testing.T) {
|
||||||
testcases := []struct {
|
testcases := []struct {
|
||||||
input string
|
input string
|
||||||
hostname string
|
domain string
|
||||||
name string
|
name string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
input: "test.com/foo",
|
input: "test.com/foo",
|
||||||
hostname: "test.com",
|
domain: "test.com",
|
||||||
name: "foo",
|
name: "foo",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "test_com/foo",
|
input: "test_com/foo",
|
||||||
hostname: "",
|
domain: "",
|
||||||
name: "test_com/foo",
|
name: "test_com/foo",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "test:8080/foo",
|
input: "test:8080/foo",
|
||||||
hostname: "test:8080",
|
domain: "test:8080",
|
||||||
name: "foo",
|
name: "foo",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "test.com:8080/foo",
|
input: "test.com:8080/foo",
|
||||||
hostname: "test.com:8080",
|
domain: "test.com:8080",
|
||||||
name: "foo",
|
name: "foo",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "test-com:8080/foo",
|
input: "test-com:8080/foo",
|
||||||
hostname: "test-com:8080",
|
domain: "test-com:8080",
|
||||||
name: "foo",
|
name: "foo",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "xn--n3h.com:18080/foo",
|
input: "xn--n3h.com:18080/foo",
|
||||||
hostname: "xn--n3h.com:18080",
|
domain: "xn--n3h.com:18080",
|
||||||
name: "foo",
|
name: "foo",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, testcase := range testcases {
|
for _, testcase := range testcases {
|
||||||
@ -327,9 +327,9 @@ func TestSplitHostname(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
failf("error parsing name: %s", err)
|
failf("error parsing name: %s", err)
|
||||||
}
|
}
|
||||||
hostname, name := SplitHostname(named)
|
domain, name := SplitHostname(named)
|
||||||
if hostname != testcase.hostname {
|
if domain != testcase.domain {
|
||||||
failf("unexpected hostname: got %q, expected %q", hostname, testcase.hostname)
|
failf("unexpected domain: got %q, expected %q", domain, testcase.domain)
|
||||||
}
|
}
|
||||||
if name != testcase.name {
|
if name != testcase.name {
|
||||||
failf("unexpected name: got %q, expected %q", name, testcase.name)
|
failf("unexpected name: got %q, expected %q", name, testcase.name)
|
||||||
|
@ -19,18 +19,18 @@ var (
|
|||||||
alphaNumericRegexp,
|
alphaNumericRegexp,
|
||||||
optional(repeated(separatorRegexp, alphaNumericRegexp)))
|
optional(repeated(separatorRegexp, alphaNumericRegexp)))
|
||||||
|
|
||||||
// hostnameComponentRegexp restricts the registry hostname component of a
|
// domainComponentRegexp restricts the registry domain component of a
|
||||||
// repository name to start with a component as defined by hostnameRegexp
|
// repository name to start with a component as defined by domainRegexp
|
||||||
// and followed by an optional port.
|
// and followed by an optional port.
|
||||||
hostnameComponentRegexp = match(`(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`)
|
domainComponentRegexp = match(`(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`)
|
||||||
|
|
||||||
// hostnameRegexp defines the structure of potential hostname components
|
// domainRegexp defines the structure of potential domain components
|
||||||
// that may be part of image names. This is purposely a subset of what is
|
// that may be part of image names. This is purposely a subset of what is
|
||||||
// allowed by DNS to ensure backwards compatibility with Docker image
|
// allowed by DNS to ensure backwards compatibility with Docker image
|
||||||
// names.
|
// names.
|
||||||
hostnameRegexp = expression(
|
domainRegexp = expression(
|
||||||
hostnameComponentRegexp,
|
domainComponentRegexp,
|
||||||
optional(repeated(literal(`.`), hostnameComponentRegexp)),
|
optional(repeated(literal(`.`), domainComponentRegexp)),
|
||||||
optional(literal(`:`), match(`[0-9]+`)))
|
optional(literal(`:`), match(`[0-9]+`)))
|
||||||
|
|
||||||
// TagRegexp matches valid tag names. From docker/docker:graph/tags.go.
|
// TagRegexp matches valid tag names. From docker/docker:graph/tags.go.
|
||||||
@ -48,17 +48,17 @@ var (
|
|||||||
anchoredDigestRegexp = anchored(DigestRegexp)
|
anchoredDigestRegexp = anchored(DigestRegexp)
|
||||||
|
|
||||||
// NameRegexp is the format for the name component of references. The
|
// NameRegexp is the format for the name component of references. The
|
||||||
// regexp has capturing groups for the hostname and name part omitting
|
// regexp has capturing groups for the domain and name part omitting
|
||||||
// the separating forward slash from either.
|
// the separating forward slash from either.
|
||||||
NameRegexp = expression(
|
NameRegexp = expression(
|
||||||
optional(hostnameRegexp, literal(`/`)),
|
optional(domainRegexp, literal(`/`)),
|
||||||
nameComponentRegexp,
|
nameComponentRegexp,
|
||||||
optional(repeated(literal(`/`), nameComponentRegexp)))
|
optional(repeated(literal(`/`), nameComponentRegexp)))
|
||||||
|
|
||||||
// anchoredNameRegexp is used to parse a name value, capturing the
|
// anchoredNameRegexp is used to parse a name value, capturing the
|
||||||
// hostname and trailing components.
|
// domain and trailing components.
|
||||||
anchoredNameRegexp = anchored(
|
anchoredNameRegexp = anchored(
|
||||||
optional(capture(hostnameRegexp), literal(`/`)),
|
optional(capture(domainRegexp), literal(`/`)),
|
||||||
capture(nameComponentRegexp,
|
capture(nameComponentRegexp,
|
||||||
optional(repeated(literal(`/`), nameComponentRegexp))))
|
optional(repeated(literal(`/`), nameComponentRegexp))))
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ func checkRegexp(t *testing.T, r *regexp.Regexp, m regexpMatch) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHostRegexp(t *testing.T) {
|
func TestDomainRegexp(t *testing.T) {
|
||||||
hostcases := []regexpMatch{
|
hostcases := []regexpMatch{
|
||||||
{
|
{
|
||||||
input: "test.com",
|
input: "test.com",
|
||||||
@ -116,7 +116,7 @@ func TestHostRegexp(t *testing.T) {
|
|||||||
match: true,
|
match: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
r := regexp.MustCompile(`^` + hostnameRegexp.String() + `$`)
|
r := regexp.MustCompile(`^` + domainRegexp.String() + `$`)
|
||||||
for i := range hostcases {
|
for i := range hostcases {
|
||||||
checkRegexp(t, r, hostcases[i])
|
checkRegexp(t, r, hostcases[i])
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user