Skip to content

Commit 9a43b8f

Browse files
committed
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 <[email protected]> (github: dmcgowan)
1 parent 76f514b commit 9a43b8f

4 files changed

Lines changed: 169 additions & 104 deletions

File tree

reference/reference.go

Lines changed: 113 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
// Grammar
55
//
66
// reference := name [ ":" tag ] [ "@" digest ]
7-
// name := [hostname '/'] component ['/' component]*
8-
// hostname := hostcomponent ['.' hostcomponent]* [':' port-number]
9-
// hostcomponent := /([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/
7+
// name := [domain '/'] path-component ['/' path-component]*
8+
// domain := domain-component ['.' domain-component]* [':' port-number]
9+
// domain-component := /([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/
1010
// port-number := /[0-9]+/
11-
// component := alpha-numeric [separator alpha-numeric]*
11+
// path-component := alpha-numeric [separator alpha-numeric]*
1212
// alpha-numeric := /[a-z0-9]+/
1313
// separator := /[_.]|__|[-]*/
1414
//
@@ -126,23 +126,56 @@ type Digested interface {
126126
}
127127

128128
// Canonical reference is an object with a fully unique
129-
// name including a name with hostname and digest
129+
// name including a name with domain and digest
130130
type Canonical interface {
131131
Named
132132
Digest() digest.Digest
133133
}
134134

135+
// NamedRepository is a reference to a repository with a name.
136+
// A NamedRepository has both domain and path components.
137+
type NamedRepository interface {
138+
Named
139+
Domain() string
140+
Path() string
141+
}
142+
143+
// Domain returns the domain part of the Named reference
144+
func Domain(named Named) string {
145+
if r, ok := named.(NamedRepository); ok {
146+
return r.Domain()
147+
}
148+
domain, _ := splitDomain(named.Name())
149+
return domain
150+
}
151+
152+
// Path returns the name without the domain part of the Named reference
153+
func Path(named Named) (name string) {
154+
if r, ok := named.(NamedRepository); ok {
155+
return r.Path()
156+
}
157+
_, path := splitDomain(named.Name())
158+
return path
159+
}
160+
161+
func splitDomain(name string) (string, string) {
162+
match := anchoredNameRegexp.FindStringSubmatch(name)
163+
if len(match) != 3 {
164+
return "", name
165+
}
166+
return match[1], match[2]
167+
}
168+
135169
// SplitHostname splits a named reference into a
136170
// hostname and name string. If no valid hostname is
137171
// found, the hostname is empty and the full value
138172
// is returned as name
173+
// DEPRECATED: Use Domain or Path
139174
func SplitHostname(named Named) (string, string) {
140-
name := named.Name()
141-
match := anchoredNameRegexp.FindStringSubmatch(name)
142-
if len(match) != 3 {
143-
return "", name
175+
if r, ok := named.(NamedRepository); ok {
176+
return r.Domain(), r.Path()
144177
}
145-
return match[1], match[2]
178+
return splitDomain(named.Name())
146179
}
147180

148181
// Parse parses s and returns a syntactically valid Reference.
@@ -164,9 +197,20 @@ func Parse(s string) (Reference, error) {
164197
return nil, ErrNameTooLong
165198
}
166199

200+
var repo repository
201+
202+
nameMatch := anchoredNameRegexp.FindStringSubmatch(matches[1])
203+
if nameMatch != nil && len(nameMatch) == 3 {
204+
repo.domain = nameMatch[1]
205+
repo.path = nameMatch[2]
206+
} else {
207+
repo.domain = ""
208+
repo.path = matches[1]
209+
}
210+
167211
ref := reference{
168-
name: matches[1],
169-
tag: matches[2],
212+
repository: repo,
213+
tag: matches[2],
170214
}
171215
if matches[3] != "" {
172216
var err error
@@ -207,10 +251,15 @@ func WithName(name string) (Named, error) {
207251
if len(name) > NameTotalLengthMax {
208252
return nil, ErrNameTooLong
209253
}
210-
if !anchoredNameRegexp.MatchString(name) {
254+
255+
match := anchoredNameRegexp.FindStringSubmatch(name)
256+
if match == nil || len(match) != 3 {
211257
return nil, ErrReferenceInvalidFormat
212258
}
213-
return repository(name), nil
259+
return repository{
260+
domain: match[1],
261+
path: match[2],
262+
}, nil
214263
}
215264

216265
// 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) {
219268
if !anchoredTagRegexp.MatchString(tag) {
220269
return nil, ErrTagInvalidFormat
221270
}
271+
var repo repository
272+
if r, ok := name.(NamedRepository); ok {
273+
repo.domain = r.Domain()
274+
repo.path = r.Path()
275+
} else {
276+
repo.path = name.Name()
277+
}
222278
if canonical, ok := name.(Canonical); ok {
223279
return reference{
224-
name: name.Name(),
280+
repository: repo,
225281
tag: tag,
226282
digest: canonical.Digest(),
227283
}, nil
228284
}
229285
return taggedReference{
230-
name: name.Name(),
231-
tag: tag,
286+
repository: repo,
287+
tag: tag,
232288
}, nil
233289
}
234290

@@ -238,16 +294,23 @@ func WithDigest(name Named, digest digest.Digest) (Canonical, error) {
238294
if !anchoredDigestRegexp.MatchString(digest.String()) {
239295
return nil, ErrDigestInvalidFormat
240296
}
297+
var repo repository
298+
if r, ok := name.(NamedRepository); ok {
299+
repo.domain = r.Domain()
300+
repo.path = r.Path()
301+
} else {
302+
repo.path = name.Name()
303+
}
241304
if tagged, ok := name.(Tagged); ok {
242305
return reference{
243-
name: name.Name(),
306+
repository: repo,
244307
tag: tagged.Tag(),
245308
digest: digest,
246309
}, nil
247310
}
248311
return canonicalReference{
249-
name: name.Name(),
250-
digest: digest,
312+
repository: repo,
313+
digest: digest,
251314
}, nil
252315
}
253316

@@ -267,7 +330,7 @@ func TrimNamed(ref Named) Named {
267330
}
268331

269332
func getBestReferenceType(ref reference) Reference {
270-
if ref.name == "" {
333+
if ref.repository.path == "" {
271334
// Allow digest only references
272335
if ref.digest != "" {
273336
return digestReference(ref.digest)
@@ -277,34 +340,30 @@ func getBestReferenceType(ref reference) Reference {
277340
if ref.tag == "" {
278341
if ref.digest != "" {
279342
return canonicalReference{
280-
name: ref.name,
281-
digest: ref.digest,
343+
repository: ref.repository,
344+
digest: ref.digest,
282345
}
283346
}
284-
return repository(ref.name)
347+
return ref.repository
285348
}
286349
if ref.digest == "" {
287350
return taggedReference{
288-
name: ref.name,
289-
tag: ref.tag,
351+
repository: ref.repository,
352+
tag: ref.tag,
290353
}
291354
}
292355

293356
return ref
294357
}
295358

296359
type reference struct {
297-
name string
360+
repository
298361
tag string
299362
digest digest.Digest
300363
}
301364

302365
func (r reference) String() string {
303-
return r.name + ":" + r.tag + "@" + r.digest.String()
304-
}
305-
306-
func (r reference) Name() string {
307-
return r.name
366+
return r.Name() + ":" + r.tag + "@" + r.digest.String()
308367
}
309368

310369
func (r reference) Tag() string {
@@ -315,14 +374,28 @@ func (r reference) Digest() digest.Digest {
315374
return r.digest
316375
}
317376

318-
type repository string
377+
type repository struct {
378+
domain string
379+
path string
380+
}
319381

320382
func (r repository) String() string {
321-
return string(r)
383+
return r.Name()
322384
}
323385

324386
func (r repository) Name() string {
325-
return string(r)
387+
if r.domain == "" {
388+
return r.path
389+
}
390+
return r.domain + "/" + r.path
391+
}
392+
393+
func (r repository) Domain() string {
394+
return r.domain
395+
}
396+
397+
func (r repository) Path() string {
398+
return r.path
326399
}
327400

328401
type digestReference digest.Digest
@@ -336,33 +409,25 @@ func (d digestReference) Digest() digest.Digest {
336409
}
337410

338411
type taggedReference struct {
339-
name string
340-
tag string
412+
repository
413+
tag string
341414
}
342415

343416
func (t taggedReference) String() string {
344-
return t.name + ":" + t.tag
345-
}
346-
347-
func (t taggedReference) Name() string {
348-
return t.name
417+
return t.Name() + ":" + t.tag
349418
}
350419

351420
func (t taggedReference) Tag() string {
352421
return t.tag
353422
}
354423

355424
type canonicalReference struct {
356-
name string
425+
repository
357426
digest digest.Digest
358427
}
359428

360429
func (c canonicalReference) String() string {
361-
return c.name + "@" + c.digest.String()
362-
}
363-
364-
func (c canonicalReference) Name() string {
365-
return c.name
430+
return c.Name() + "@" + c.digest.String()
366431
}
367432

368433
func (c canonicalReference) Digest() digest.Digest {

0 commit comments

Comments
 (0)