Update regexp to support repeated dash and double underscore

In order to support valid hostnames as name components, supporting repeated dash was added.
Additionally double underscore is now allowed as a separator to loosen the restriction for previously supported names.

Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
This commit is contained in:
Derek McGowan 2015-10-09 16:01:01 -07:00
parent bcda04d6cd
commit 6bd5b8c24e
3 changed files with 85 additions and 10 deletions

View file

@ -8,12 +8,13 @@
// // repository.go // // repository.go
// repository := hostname ['/' component]+ // repository := hostname ['/' component]+
// hostname := hostcomponent [':' port-number] // hostname := hostcomponent [':' port-number]
// component := alpha-numeric [separator alpha-numeric]* // component := subcomponent [separator subcomponent]*
// subcomponent := alpha-numeric ['-'* alpha-numeric]*
// hostcomponent := [hostpart '.']* hostpart // hostcomponent := [hostpart '.']* hostpart
// alpha-numeric := /[a-zA-Z0-9]+/ // alpha-numeric := /[a-z0-9]+/
// separator := /[_-]/ // separator := /([_.]|__)/
// port-number := /[0-9]+/ // port-number := /[0-9]+/
// hostpart := /([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/ // hostpart := /([a-z0-9]|[a-z0-9][a-z0-9-]*[a-z0-9])/
// //
// // tag.go // // tag.go
// tag := /[\w][\w.-]{0,127}/ // tag := /[\w][\w.-]{0,127}/

View file

@ -3,14 +3,19 @@ package reference
import "regexp" import "regexp"
var ( var (
// nameSubComponentRegexp defines the part of the name which must be
// begin and end with an alphanumeric character. These characters can
// be separated by any number of dashes.
nameSubComponentRegexp = regexp.MustCompile(`[a-z0-9]+(?:[-]+[a-z0-9]+)*`)
// nameComponentRegexp restricts registry path component names to // nameComponentRegexp restricts registry path component names to
// start with at least one letter or number, with following parts able to // start with at least one letter or number, with following parts able to
// be separated by one period, dash or underscore. // be separated by one period, underscore or double underscore.
nameComponentRegexp = regexp.MustCompile(`[a-zA-Z0-9]+(?:[._-][a-z0-9]+)*`) nameComponentRegexp = regexp.MustCompile(nameSubComponentRegexp.String() + `(?:(?:[._]|__)` + nameSubComponentRegexp.String() + `)*`)
nameRegexp = regexp.MustCompile(`(?:` + nameComponentRegexp.String() + `/)*` + nameComponentRegexp.String()) nameRegexp = regexp.MustCompile(`(?:` + nameComponentRegexp.String() + `/)*` + nameComponentRegexp.String())
hostnameComponentRegexp = regexp.MustCompile(`(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`) hostnameComponentRegexp = regexp.MustCompile(`(?:[a-z0-9]|[a-z0-9][a-z0-9-]*[a-z0-9])`)
// hostnameComponentRegexp restricts the registry hostname component of a repository name to // hostnameComponentRegexp restricts the registry hostname component of a repository name to
// start with a component as defined by hostnameRegexp and followed by an optional port. // start with a component as defined by hostnameRegexp and followed by an optional port.

View file

@ -16,14 +16,14 @@ func checkRegexp(t *testing.T, r *regexp.Regexp, m regexpMatch) {
matches := r.FindStringSubmatch(m.input) matches := r.FindStringSubmatch(m.input)
if m.match && matches != nil { if m.match && matches != nil {
if len(matches) != (r.NumSubexp()+1) || matches[0] != m.input { if len(matches) != (r.NumSubexp()+1) || matches[0] != m.input {
t.Fatalf("Bad match result: %#v", matches) t.Fatalf("Bad match result %#v for %q", matches, m.input)
} }
if len(matches) < (len(m.subs) + 1) { if len(matches) < (len(m.subs) + 1) {
t.Errorf("Expected %d sub matches, only have %d", len(m.subs), len(matches)-1) t.Errorf("Expected %d sub matches, only have %d for %q", len(m.subs), len(matches)-1, m.input)
} }
for i := range m.subs { for i := range m.subs {
if m.subs[i] != matches[i+1] { if m.subs[i] != matches[i+1] {
t.Errorf("Unexpected submatch %d: %q, expected %q", i+1, matches[i+1], m.subs[i]) t.Errorf("Unexpected submatch %d: %q, expected %q for %q", i+1, matches[i+1], m.subs[i], m.input)
} }
} }
} else if m.match { } else if m.match {
@ -325,6 +325,75 @@ func TestFullNameRegexp(t *testing.T) {
match: true, match: true,
subs: []string{"xn--7o8h.com", "myimage"}, subs: []string{"xn--7o8h.com", "myimage"},
}, },
{
input: "example.com/xn--7o8h.com/myimage", // 🐳.com in punycode
match: true,
subs: []string{"example.com", "xn--7o8h.com/myimage"},
},
{
input: "example.com/some_separator__underscore/myimage",
match: true,
subs: []string{"example.com", "some_separator__underscore/myimage"},
},
{
input: "example.com/__underscore/myimage",
match: false,
},
{
input: "example.com/..dots/myimage",
match: false,
},
{
input: "example.com/.dots/myimage",
match: false,
},
{
input: "example.com/nodouble..dots/myimage",
match: false,
},
{
input: "example.com/nodouble..dots/myimage",
match: false,
},
{
input: "docker./docker",
match: false,
},
{
input: ".docker/docker",
match: false,
},
{
input: "docker-/docker",
match: false,
},
{
input: "-docker/docker",
match: false,
},
{
input: "do..cker/docker",
match: false,
},
{
input: "do__cker:8080/docker",
match: false,
},
{
input: "do__cker/docker",
match: true,
subs: []string{"", "do__cker/docker"},
},
{
input: "b.gcr.io/test.example.com/my-app",
match: true,
subs: []string{"b.gcr.io", "test.example.com/my-app"},
},
{
input: "registry.io/foo/project--id.module--name.ver---sion--name",
match: true,
subs: []string{"registry.io", "foo/project--id.module--name.ver---sion--name"},
},
} }
for i := range testcases { for i := range testcases {
checkRegexp(t, anchoredNameRegexp, testcases[i]) checkRegexp(t, anchoredNameRegexp, testcases[i])