diff --git a/reference/normalize.go b/reference/normalize.go index 5714ce89..43170e6f 100644 --- a/reference/normalize.go +++ b/reference/normalize.go @@ -58,14 +58,14 @@ func ParseNormalizedNamed(s string) (Named, error) { return nil, fmt.Errorf("invalid repository name (%s), cannot specify 64-byte hexadecimal strings", s) } domain, remainder := splitDockerDomain(s) - var remoteName string + var remote string if tagSep := strings.IndexRune(remainder, ':'); tagSep > -1 { - remoteName = remainder[:tagSep] + remote = remainder[:tagSep] } else { - remoteName = remainder + remote = remainder } - if strings.ToLower(remoteName) != remoteName { - return nil, fmt.Errorf("invalid reference format: repository name (%s) must be lowercase", remoteName) + if strings.ToLower(remote) != remote { + return nil, fmt.Errorf("invalid reference format: repository name (%s) must be lowercase", remote) } ref, err := Parse(domain + "/" + remainder) @@ -120,7 +120,7 @@ func ParseDockerRef(ref string) (Named, error) { return TagNameOnly(named), nil } -// splitDockerDomain splits a repository name to domain and remotename string. +// splitDockerDomain splits a repository name to domain and remote-name. // 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) { diff --git a/reference/reference.go b/reference/reference.go index e5d889a3..e98c44da 100644 --- a/reference/reference.go +++ b/reference/reference.go @@ -4,13 +4,14 @@ // Grammar // // reference := name [ ":" tag ] [ "@" digest ] -// name := [domain '/'] path-component ['/' path-component]* +// name := [domain '/'] remote-name // domain := host [':' port-number] // host := domain-name | IPv4address | \[ IPv6address \] ; rfc3986 appendix-A // domain-name := domain-component ['.' domain-component]* // domain-component := /([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/ // port-number := /[0-9]+/ // path-component := alpha-numeric [separator alpha-numeric]* +// path (or "remote-name") := path-component ['/' path-component]* // alpha-numeric := /[a-z0-9]+/ // separator := /[_.]|__|[-]*/ // diff --git a/reference/regexp.go b/reference/regexp.go index 9448938b..2974b1eb 100644 --- a/reference/regexp.go +++ b/reference/regexp.go @@ -101,7 +101,13 @@ var ( // character, with following parts able to be separated by a separator // (one period, one or two underscore and multiple dashes). pathComponent = alphanumeric + anyTimes(separator+alphanumeric) - namePat = optional(domainAndPort+`/`) + pathComponent + anyTimes(`/`+pathComponent) + + // remoteName matches the remote-name of a repository. It consists of one + // or more forward slash (/) delimited path-components: + // + // pathComponent[[/pathComponent] ...] // e.g., "library/ubuntu" + remoteName = pathComponent + anyTimes(`/`+pathComponent) + namePat = optional(domainAndPort+`/`) + remoteName // NameRegexp is the format for the name component of references, including // an optional domain and port, but without tag or digest suffix. @@ -109,7 +115,7 @@ var ( // anchoredNameRegexp is used to parse a name value, capturing the // domain and trailing components. - anchoredNameRegexp = regexp.MustCompile(anchored(optional(capture(domainAndPort), `/`), capture(pathComponent, anyTimes(`/`+pathComponent)))) + anchoredNameRegexp = regexp.MustCompile(anchored(optional(capture(domainAndPort), `/`), capture(remoteName))) referencePat = anchored(capture(namePat), optional(`:`, capture(tag)), optional(`@`, capture(digestPat)))