@@ -123,20 +123,51 @@ func ParseDockerRef(ref string) (Named, error) {
123123// splitDockerDomain splits a repository name to domain and remote-name.
124124// If no valid domain is found, the default domain is used. Repository name
125125// needs to be already validated before.
126- func splitDockerDomain (name string ) (domain , remainder string ) {
127- i := strings .IndexRune (name , '/' )
128- if i == - 1 || (! strings .ContainsAny (name [:i ], ".:" ) && name [:i ] != localhost && strings .ToLower (name [:i ]) == name [:i ]) {
129- domain , remainder = defaultDomain , name
130- } else {
131- domain , remainder = name [:i ], name [i + 1 :]
132- }
133- if domain == legacyDefaultDomain {
134- domain = defaultDomain
135- }
136- if domain == defaultDomain && ! strings .ContainsRune (remainder , '/' ) {
137- remainder = officialRepoPrefix + remainder
138- }
139- return
126+ func splitDockerDomain (name string ) (domain , remoteName string ) {
127+ maybeDomain , maybeRemoteName , ok := strings .Cut (name , "/" )
128+ if ! ok {
129+ // Fast-path for single element ("familiar" names), such as "ubuntu"
130+ // or "ubuntu:latest". Familiar names must be handled separately, to
131+ // prevent them from being handled as "hostname:port".
132+ //
133+ // Canonicalize them as "docker.io/library/name[:tag]"
134+
135+ // FIXME(thaJeztah): account for bare "localhost" or "example.com" names, which SHOULD be considered a domain.
136+ return defaultDomain , officialRepoPrefix + name
137+ }
138+
139+ switch {
140+ case maybeDomain == localhost :
141+ // localhost is a reserved namespace and always considered a domain.
142+ domain , remoteName = maybeDomain , maybeRemoteName
143+ case maybeDomain == legacyDefaultDomain :
144+ // canonicalize the Docker Hub and legacy "Docker Index" domains.
145+ domain , remoteName = defaultDomain , maybeRemoteName
146+ case strings .ContainsAny (maybeDomain , ".:" ):
147+ // Likely a domain or IP-address:
148+ //
149+ // - contains a "." (e.g., "example.com" or "127.0.0.1")
150+ // - contains a ":" (e.g., "example:5000", "::1", or "[::1]:5000")
151+ domain , remoteName = maybeDomain , maybeRemoteName
152+ case strings .ToLower (maybeDomain ) != maybeDomain :
153+ // Uppercase namespaces are not allowed, so if the first element
154+ // is not lowercase, we assume it to be a domain-name.
155+ domain , remoteName = maybeDomain , maybeRemoteName
156+ default :
157+ // None of the above: it's not a domain, so use the default, and
158+ // use the name input the remote-name.
159+ domain , remoteName = defaultDomain , name
160+ }
161+
162+ if domain == defaultDomain && ! strings .ContainsRune (remoteName , '/' ) {
163+ // Canonicalize "familiar" names, but only on Docker Hub, not
164+ // on other domains:
165+ //
166+ // "docker.io/ubuntu[:tag]" => "docker.io/library/ubuntu[:tag]"
167+ remoteName = officialRepoPrefix + remoteName
168+ }
169+
170+ return domain , remoteName
140171}
141172
142173// familiarizeName returns a shortened version of the name familiar
0 commit comments