diff --git a/ociref/reference.go b/ociref/reference.go index 3ab3888..d3a68ee 100644 --- a/ociref/reference.go +++ b/ociref/reference.go @@ -164,7 +164,7 @@ func IsValidDigest(d string) bool { // Unlike "docker pull" however, there is no default registry: when // presented with a bare repository name, Parse will return an error. func Parse(refStr string) (Reference, error) { - ref, err := ParseRelative(refStr) + ref, err := parse(refStr) if err != nil { return Reference{}, err } @@ -179,9 +179,28 @@ func Parse(refStr string) (Reference, error) { // // It is represented in string form as [HOST[:PORT]/]NAME[:TAG|@DIGEST] // form: the same syntax accepted by "docker pull". -// Unlike "docker pull" however, there is no default registry: when -// presented with a bare repository name, the Host field will be empty. +// Like "docker pull", references without a host default to Docker Hub, +// and single-component Docker Hub repository names default to the "library" +// namespace. func ParseRelative(refStr string) (Reference, error) { + ref, err := parse(refStr) + if err != nil { + return Reference{}, err + } + // Normalize Docker Hub registry hosts, also default to Docker if none is provided + if _, ok := ocidocker.DockerHubHosts[ref.Host]; ok || ref.Host == "" { + ref.Host = "docker.io" + } + if ref.Host == "docker.io" && !strings.Contains(ref.Repository, "/") { + ref.Repository = "library/" + ref.Repository + } + if len(ref.Repository) > 255 { + return Reference{}, fmt.Errorf("repository name too long") + } + return ref, nil +} + +func parse(refStr string) (Reference, error) { m := referencePat().FindStringSubmatch(refStr) if m == nil { return Reference{}, fmt.Errorf("invalid reference syntax (%q)", refStr) @@ -201,13 +220,6 @@ func ParseRelative(refStr string) (Reference, error) { return Reference{}, fmt.Errorf("invalid digest %q: %v", ref.Digest, err) } } - // Normalize Docker Hub registry hosts, also default to Docker if none is provided - if _, ok := ocidocker.DockerHubHosts[ref.Host]; ok || ref.Host == "" { - ref.Host = "docker.io" - } - if ref.Host == "docker.io" && !strings.Contains(ref.Repository, "/") { - ref.Repository = "library/" + ref.Repository - } if len(ref.Repository) > 255 { return Reference{}, fmt.Errorf("repository name too long") } diff --git a/ociref/reference_test.go b/ociref/reference_test.go index 2c8ec66..f8287b5 100644 --- a/ociref/reference_test.go +++ b/ociref/reference_test.go @@ -400,15 +400,60 @@ func TestParseReference(t *testing.T) { require.NoError(t, err) assert.Equal(t, test.wantRef, ref) //assert.Equal(t, test.input, ref.String()) - if test.wantRef.Host != "" { - ref1, err := Parse(test.input) - require.NoError(t, err) - assert.Equal(t, test.wantRef, ref1) - } else { - _, err := Parse(test.input) + }) + } +} + +func TestParse(t *testing.T) { + tests := []struct { + input string + wantErr string + wantRef Reference + }{{ + input: "test.com/repo:tag", + wantRef: Reference{ + Host: "test.com", + Repository: "repo", + Tag: "tag", + }, + }, { + input: "test:5000/repo", + wantRef: Reference{ + Host: "test:5000", + Repository: "repo", + }, + }, { + input: "docker.io/ubuntu", + wantRef: Reference{ + Host: "docker.io", + Repository: "ubuntu", + }, + }, { + input: "index.docker.io/foo", + wantRef: Reference{ + Host: "index.docker.io", + Repository: "foo", + }, + }, { + input: "test_com", + wantErr: `reference does not contain host name`, + }, { + input: "rocket.chat", + wantErr: `reference does not contain host name`, + }, { + input: "test.com:5000", + wantErr: `reference does not contain host name`, + }} + for _, test := range tests { + t.Run(test.input, func(t *testing.T) { + ref, err := Parse(test.input) + if test.wantErr != "" { require.Error(t, err) - require.Regexp(t, `reference does not contain host name`, err.Error()) + require.Regexp(t, test.wantErr, err.Error()) + return } + require.NoError(t, err) + assert.Equal(t, test.wantRef, ref) }) } }