Browse code

Merge pull request #30043 from dmcgowan/distribution-reference-update-1

Distribution reference update

Aaron Lehmann authored on 2017/01/25 13:38:20
Showing 25 changed files
... ...
@@ -5,6 +5,7 @@ import (
5 5
 	"io"
6 6
 	"os"
7 7
 
8
+	"github.com/docker/distribution/reference"
8 9
 	"github.com/docker/docker/api/types"
9 10
 	"github.com/docker/docker/api/types/container"
10 11
 	networktypes "github.com/docker/docker/api/types/network"
... ...
@@ -13,8 +14,6 @@ import (
13 13
 	"github.com/docker/docker/cli/command/image"
14 14
 	apiclient "github.com/docker/docker/client"
15 15
 	"github.com/docker/docker/pkg/jsonmessage"
16
-	// FIXME migrate to docker/distribution/reference
17
-	"github.com/docker/docker/reference"
18 16
 	"github.com/docker/docker/registry"
19 17
 	"github.com/spf13/cobra"
20 18
 	"github.com/spf13/pflag"
... ...
@@ -72,7 +71,7 @@ func runCreate(dockerCli *command.DockerCli, flags *pflag.FlagSet, opts *createO
72 72
 }
73 73
 
74 74
 func pullImage(ctx context.Context, dockerCli *command.DockerCli, image string, out io.Writer) error {
75
-	ref, err := reference.ParseNamed(image)
75
+	ref, err := reference.ParseNormalizedNamed(image)
76 76
 	if err != nil {
77 77
 		return err
78 78
 	}
... ...
@@ -150,7 +149,12 @@ func newCIDFile(path string) (*cidFile, error) {
150 150
 func createContainer(ctx context.Context, dockerCli *command.DockerCli, config *container.Config, hostConfig *container.HostConfig, networkingConfig *networktypes.NetworkingConfig, cidfile, name string) (*container.ContainerCreateCreatedBody, error) {
151 151
 	stderr := dockerCli.Err()
152 152
 
153
-	var containerIDFile *cidFile
153
+	var (
154
+		containerIDFile *cidFile
155
+		trustedRef      reference.Canonical
156
+		namedRef        reference.Named
157
+	)
158
+
154 159
 	if cidfile != "" {
155 160
 		var err error
156 161
 		if containerIDFile, err = newCIDFile(cidfile); err != nil {
... ...
@@ -159,21 +163,24 @@ func createContainer(ctx context.Context, dockerCli *command.DockerCli, config *
159 159
 		defer containerIDFile.Close()
160 160
 	}
161 161
 
162
-	var trustedRef reference.Canonical
163
-	_, ref, err := reference.ParseIDOrReference(config.Image)
162
+	ref, err := reference.ParseAnyReference(config.Image)
164 163
 	if err != nil {
165 164
 		return nil, err
166 165
 	}
167
-	if ref != nil {
168
-		ref = reference.WithDefaultTag(ref)
166
+	if named, ok := ref.(reference.Named); ok {
167
+		if reference.IsNameOnly(named) {
168
+			namedRef = reference.EnsureTagged(named)
169
+		} else {
170
+			namedRef = named
171
+		}
169 172
 
170
-		if ref, ok := ref.(reference.NamedTagged); ok && command.IsTrusted() {
173
+		if taggedRef, ok := namedRef.(reference.NamedTagged); ok && command.IsTrusted() {
171 174
 			var err error
172
-			trustedRef, err = image.TrustedReference(ctx, dockerCli, ref, nil)
175
+			trustedRef, err = image.TrustedReference(ctx, dockerCli, taggedRef, nil)
173 176
 			if err != nil {
174 177
 				return nil, err
175 178
 			}
176
-			config.Image = trustedRef.String()
179
+			config.Image = reference.FamiliarString(trustedRef)
177 180
 		}
178 181
 	}
179 182
 
... ...
@@ -182,15 +189,15 @@ func createContainer(ctx context.Context, dockerCli *command.DockerCli, config *
182 182
 
183 183
 	//if image not found try to pull it
184 184
 	if err != nil {
185
-		if apiclient.IsErrImageNotFound(err) && ref != nil {
186
-			fmt.Fprintf(stderr, "Unable to find image '%s' locally\n", ref.String())
185
+		if apiclient.IsErrImageNotFound(err) && namedRef != nil {
186
+			fmt.Fprintf(stderr, "Unable to find image '%s' locally\n", reference.FamiliarString(namedRef))
187 187
 
188 188
 			// we don't want to write to stdout anything apart from container.ID
189 189
 			if err = pullImage(ctx, dockerCli, config.Image, stderr); err != nil {
190 190
 				return nil, err
191 191
 			}
192
-			if ref, ok := ref.(reference.NamedTagged); ok && trustedRef != nil {
193
-				if err := image.TagTrusted(ctx, dockerCli, trustedRef, ref); err != nil {
192
+			if taggedRef, ok := namedRef.(reference.NamedTagged); ok && trustedRef != nil {
193
+				if err := image.TagTrusted(ctx, dockerCli, trustedRef, taggedRef); err != nil {
194 194
 					return nil, err
195 195
 				}
196 196
 			}
... ...
@@ -4,9 +4,9 @@ import (
4 4
 	"fmt"
5 5
 	"time"
6 6
 
7
+	"github.com/docker/distribution/reference"
7 8
 	"github.com/docker/docker/api/types"
8 9
 	"github.com/docker/docker/pkg/stringid"
9
-	"github.com/docker/docker/reference"
10 10
 	units "github.com/docker/go-units"
11 11
 )
12 12
 
... ...
@@ -95,21 +95,23 @@ func imageFormat(ctx ImageContext, images []types.ImageSummary, format func(subC
95 95
 			repoDigests := map[string][]string{}
96 96
 
97 97
 			for _, refString := range append(image.RepoTags) {
98
-				ref, err := reference.ParseNamed(refString)
98
+				ref, err := reference.ParseNormalizedNamed(refString)
99 99
 				if err != nil {
100 100
 					continue
101 101
 				}
102 102
 				if nt, ok := ref.(reference.NamedTagged); ok {
103
-					repoTags[ref.Name()] = append(repoTags[ref.Name()], nt.Tag())
103
+					familiarRef := reference.FamiliarName(ref)
104
+					repoTags[familiarRef] = append(repoTags[familiarRef], nt.Tag())
104 105
 				}
105 106
 			}
106 107
 			for _, refString := range append(image.RepoDigests) {
107
-				ref, err := reference.ParseNamed(refString)
108
+				ref, err := reference.ParseNormalizedNamed(refString)
108 109
 				if err != nil {
109 110
 					continue
110 111
 				}
111 112
 				if c, ok := ref.(reference.Canonical); ok {
112
-					repoDigests[ref.Name()] = append(repoDigests[ref.Name()], c.Digest().String())
113
+					familiarRef := reference.FamiliarName(ref)
114
+					repoDigests[familiarRef] = append(repoDigests[familiarRef], c.Digest().String())
113 115
 				}
114 116
 			}
115 117
 
... ...
@@ -11,6 +11,7 @@ import (
11 11
 	"regexp"
12 12
 	"runtime"
13 13
 
14
+	"github.com/docker/distribution/reference"
14 15
 	"github.com/docker/docker/api"
15 16
 	"github.com/docker/docker/api/types"
16 17
 	"github.com/docker/docker/api/types/container"
... ...
@@ -25,7 +26,6 @@ import (
25 25
 	"github.com/docker/docker/pkg/progress"
26 26
 	"github.com/docker/docker/pkg/streamformatter"
27 27
 	"github.com/docker/docker/pkg/urlutil"
28
-	"github.com/docker/docker/reference"
29 28
 	runconfigopts "github.com/docker/docker/runconfig/opts"
30 29
 	units "github.com/docker/go-units"
31 30
 	"github.com/spf13/cobra"
... ...
@@ -360,7 +360,7 @@ type translatorFunc func(context.Context, reference.NamedTagged) (reference.Cano
360 360
 
361 361
 // validateTag checks if the given image name can be resolved.
362 362
 func validateTag(rawRepo string) (string, error) {
363
-	_, err := reference.ParseNamed(rawRepo)
363
+	_, err := reference.ParseNormalizedNamed(rawRepo)
364 364
 	if err != nil {
365 365
 		return "", err
366 366
 	}
... ...
@@ -392,18 +392,21 @@ func rewriteDockerfileFrom(ctx context.Context, dockerfile io.Reader, translator
392 392
 		matches := dockerfileFromLinePattern.FindStringSubmatch(line)
393 393
 		if matches != nil && matches[1] != api.NoBaseImageSpecifier {
394 394
 			// Replace the line with a resolved "FROM repo@digest"
395
-			ref, err := reference.ParseNamed(matches[1])
395
+			var ref reference.Named
396
+			ref, err = reference.ParseNormalizedNamed(matches[1])
396 397
 			if err != nil {
397 398
 				return nil, nil, err
398 399
 			}
399
-			ref = reference.WithDefaultTag(ref)
400
+			if reference.IsNameOnly(ref) {
401
+				ref = reference.EnsureTagged(ref)
402
+			}
400 403
 			if ref, ok := ref.(reference.NamedTagged); ok && command.IsTrusted() {
401 404
 				trustedRef, err := translator(ctx, ref)
402 405
 				if err != nil {
403 406
 					return nil, nil, err
404 407
 				}
405 408
 
406
-				line = dockerfileFromLinePattern.ReplaceAllLiteralString(line, fmt.Sprintf("FROM %s", trustedRef.String()))
409
+				line = dockerfileFromLinePattern.ReplaceAllLiteralString(line, fmt.Sprintf("FROM %s", reference.FamiliarString(trustedRef)))
407 410
 				resolvedTags = append(resolvedTags, &resolvedTag{
408 411
 					digestRef: trustedRef,
409 412
 					tagRef:    ref,
... ...
@@ -7,9 +7,9 @@ import (
7 7
 
8 8
 	"golang.org/x/net/context"
9 9
 
10
+	"github.com/docker/distribution/reference"
10 11
 	"github.com/docker/docker/cli"
11 12
 	"github.com/docker/docker/cli/command"
12
-	"github.com/docker/docker/reference"
13 13
 	"github.com/docker/docker/registry"
14 14
 	"github.com/spf13/cobra"
15 15
 )
... ...
@@ -42,7 +42,8 @@ func NewPullCommand(dockerCli *command.DockerCli) *cobra.Command {
42 42
 }
43 43
 
44 44
 func runPull(dockerCli *command.DockerCli, opts pullOptions) error {
45
-	distributionRef, err := reference.ParseNamed(opts.remote)
45
+	var distributionRef reference.Named
46
+	distributionRef, err := reference.ParseNormalizedNamed(opts.remote)
46 47
 	if err != nil {
47 48
 		return err
48 49
 	}
... ...
@@ -51,8 +52,9 @@ func runPull(dockerCli *command.DockerCli, opts pullOptions) error {
51 51
 	}
52 52
 
53 53
 	if !opts.all && reference.IsNameOnly(distributionRef) {
54
-		distributionRef = reference.WithDefaultTag(distributionRef)
55
-		fmt.Fprintf(dockerCli.Out(), "Using default tag: %s\n", reference.DefaultTag)
54
+		taggedRef := reference.EnsureTagged(distributionRef)
55
+		fmt.Fprintf(dockerCli.Out(), "Using default tag: %s\n", taggedRef.Tag())
56
+		distributionRef = taggedRef
56 57
 	}
57 58
 
58 59
 	// Resolve the Repository name from fqn to RepositoryInfo
... ...
@@ -71,7 +73,7 @@ func runPull(dockerCli *command.DockerCli, opts pullOptions) error {
71 71
 	if command.IsTrusted() && !isCanonical {
72 72
 		err = trustedPull(ctx, dockerCli, repoInfo, distributionRef, authConfig, requestPrivilege)
73 73
 	} else {
74
-		err = imagePullPrivileged(ctx, dockerCli, authConfig, distributionRef.String(), requestPrivilege, opts.all)
74
+		err = imagePullPrivileged(ctx, dockerCli, authConfig, reference.FamiliarString(distributionRef), requestPrivilege, opts.all)
75 75
 	}
76 76
 	if err != nil {
77 77
 		if strings.Contains(err.Error(), "target is plugin") {
... ...
@@ -3,10 +3,10 @@ package image
3 3
 import (
4 4
 	"golang.org/x/net/context"
5 5
 
6
+	"github.com/docker/distribution/reference"
6 7
 	"github.com/docker/docker/cli"
7 8
 	"github.com/docker/docker/cli/command"
8 9
 	"github.com/docker/docker/pkg/jsonmessage"
9
-	"github.com/docker/docker/reference"
10 10
 	"github.com/docker/docker/registry"
11 11
 	"github.com/spf13/cobra"
12 12
 )
... ...
@@ -30,7 +30,7 @@ func NewPushCommand(dockerCli *command.DockerCli) *cobra.Command {
30 30
 }
31 31
 
32 32
 func runPush(dockerCli *command.DockerCli, remote string) error {
33
-	ref, err := reference.ParseNamed(remote)
33
+	ref, err := reference.ParseNormalizedNamed(remote)
34 34
 	if err != nil {
35 35
 		return err
36 36
 	}
... ...
@@ -51,7 +51,7 @@ func runPush(dockerCli *command.DockerCli, remote string) error {
51 51
 		return trustedPush(ctx, dockerCli, repoInfo, ref, authConfig, requestPrivilege)
52 52
 	}
53 53
 
54
-	responseBody, err := imagePushPrivileged(ctx, dockerCli, authConfig, ref.String(), requestPrivilege)
54
+	responseBody, err := imagePushPrivileged(ctx, dockerCli, authConfig, ref, requestPrivilege)
55 55
 	if err != nil {
56 56
 		return err
57 57
 	}
... ...
@@ -10,11 +10,11 @@ import (
10 10
 	"sort"
11 11
 
12 12
 	"github.com/Sirupsen/logrus"
13
+	"github.com/docker/distribution/reference"
13 14
 	"github.com/docker/docker/api/types"
14 15
 	"github.com/docker/docker/cli/command"
15 16
 	"github.com/docker/docker/cli/trust"
16 17
 	"github.com/docker/docker/pkg/jsonmessage"
17
-	"github.com/docker/docker/reference"
18 18
 	"github.com/docker/docker/registry"
19 19
 	"github.com/docker/notary/client"
20 20
 	"github.com/docker/notary/tuf/data"
... ...
@@ -30,7 +30,7 @@ type target struct {
30 30
 
31 31
 // trustedPush handles content trust pushing of an image
32 32
 func trustedPush(ctx context.Context, cli *command.DockerCli, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error {
33
-	responseBody, err := imagePushPrivileged(ctx, cli, authConfig, ref.String(), requestPrivilege)
33
+	responseBody, err := imagePushPrivileged(ctx, cli, authConfig, ref, requestPrivilege)
34 34
 	if err != nil {
35 35
 		return err
36 36
 	}
... ...
@@ -202,7 +202,7 @@ func addTargetToAllSignableRoles(repo *client.NotaryRepository, target *client.T
202 202
 }
203 203
 
204 204
 // imagePushPrivileged push the image
205
-func imagePushPrivileged(ctx context.Context, cli *command.DockerCli, authConfig types.AuthConfig, ref string, requestPrivilege types.RequestPrivilegeFunc) (io.ReadCloser, error) {
205
+func imagePushPrivileged(ctx context.Context, cli *command.DockerCli, authConfig types.AuthConfig, ref reference.Named, requestPrivilege types.RequestPrivilegeFunc) (io.ReadCloser, error) {
206 206
 	encodedAuth, err := command.EncodeAuthToBase64(authConfig)
207 207
 	if err != nil {
208 208
 		return nil, err
... ...
@@ -212,7 +212,7 @@ func imagePushPrivileged(ctx context.Context, cli *command.DockerCli, authConfig
212 212
 		PrivilegeFunc: requestPrivilege,
213 213
 	}
214 214
 
215
-	return cli.Client().ImagePush(ctx, ref, options)
215
+	return cli.Client().ImagePush(ctx, reference.FamiliarString(ref), options)
216 216
 }
217 217
 
218 218
 // trustedPull handles content trust pulling of an image
... ...
@@ -229,12 +229,12 @@ func trustedPull(ctx context.Context, cli *command.DockerCli, repoInfo *registry
229 229
 		// List all targets
230 230
 		targets, err := notaryRepo.ListTargets(trust.ReleasesRole, data.CanonicalTargetsRole)
231 231
 		if err != nil {
232
-			return trust.NotaryError(repoInfo.FullName(), err)
232
+			return trust.NotaryError(ref.Name(), err)
233 233
 		}
234 234
 		for _, tgt := range targets {
235 235
 			t, err := convertTarget(tgt.Target)
236 236
 			if err != nil {
237
-				fmt.Fprintf(cli.Out(), "Skipping target for %q\n", repoInfo.Name())
237
+				fmt.Fprintf(cli.Out(), "Skipping target for %q\n", reference.FamiliarName(ref))
238 238
 				continue
239 239
 			}
240 240
 			// Only list tags in the top level targets role or the releases delegation role - ignore
... ...
@@ -245,17 +245,17 @@ func trustedPull(ctx context.Context, cli *command.DockerCli, repoInfo *registry
245 245
 			refs = append(refs, t)
246 246
 		}
247 247
 		if len(refs) == 0 {
248
-			return trust.NotaryError(repoInfo.FullName(), fmt.Errorf("No trusted tags for %s", repoInfo.FullName()))
248
+			return trust.NotaryError(ref.Name(), fmt.Errorf("No trusted tags for %s", ref.Name()))
249 249
 		}
250 250
 	} else {
251 251
 		t, err := notaryRepo.GetTargetByName(tagged.Tag(), trust.ReleasesRole, data.CanonicalTargetsRole)
252 252
 		if err != nil {
253
-			return trust.NotaryError(repoInfo.FullName(), err)
253
+			return trust.NotaryError(ref.Name(), err)
254 254
 		}
255 255
 		// Only get the tag if it's in the top level targets role or the releases delegation role
256 256
 		// ignore it if it's in any other delegation roles
257 257
 		if t.Role != trust.ReleasesRole && t.Role != data.CanonicalTargetsRole {
258
-			return trust.NotaryError(repoInfo.FullName(), fmt.Errorf("No trust data for %s", tagged.Tag()))
258
+			return trust.NotaryError(ref.Name(), fmt.Errorf("No trust data for %s", tagged.Tag()))
259 259
 		}
260 260
 
261 261
 		logrus.Debugf("retrieving target for %s role\n", t.Role)
... ...
@@ -272,24 +272,21 @@ func trustedPull(ctx context.Context, cli *command.DockerCli, repoInfo *registry
272 272
 		if displayTag != "" {
273 273
 			displayTag = ":" + displayTag
274 274
 		}
275
-		fmt.Fprintf(cli.Out(), "Pull (%d of %d): %s%s@%s\n", i+1, len(refs), repoInfo.Name(), displayTag, r.digest)
275
+		fmt.Fprintf(cli.Out(), "Pull (%d of %d): %s%s@%s\n", i+1, len(refs), reference.FamiliarName(ref), displayTag, r.digest)
276 276
 
277
-		ref, err := reference.WithDigest(reference.TrimNamed(repoInfo), r.digest)
277
+		trustedRef, err := reference.WithDigest(reference.TrimNamed(ref), r.digest)
278 278
 		if err != nil {
279 279
 			return err
280 280
 		}
281
-		if err := imagePullPrivileged(ctx, cli, authConfig, ref.String(), requestPrivilege, false); err != nil {
281
+		if err := imagePullPrivileged(ctx, cli, authConfig, reference.FamiliarString(trustedRef), requestPrivilege, false); err != nil {
282 282
 			return err
283 283
 		}
284 284
 
285
-		tagged, err := reference.WithTag(repoInfo, r.name)
286
-		if err != nil {
287
-			return err
288
-		}
289
-		trustedRef, err := reference.WithDigest(reference.TrimNamed(repoInfo), r.digest)
285
+		tagged, err := reference.WithTag(reference.TrimNamed(ref), r.name)
290 286
 		if err != nil {
291 287
 			return err
292 288
 		}
289
+
293 290
 		if err := TagTrusted(ctx, cli, trustedRef, tagged); err != nil {
294 291
 			return err
295 292
 		}
... ...
@@ -375,7 +372,11 @@ func convertTarget(t client.Target) (target, error) {
375 375
 
376 376
 // TagTrusted tags a trusted ref
377 377
 func TagTrusted(ctx context.Context, cli *command.DockerCli, trustedRef reference.Canonical, ref reference.NamedTagged) error {
378
-	fmt.Fprintf(cli.Out(), "Tagging %s as %s\n", trustedRef.String(), ref.String())
378
+	// Use familiar references when interacting with client and output
379
+	familiarRef := reference.FamiliarString(ref)
380
+	trustedFamiliarRef := reference.FamiliarString(trustedRef)
381
+
382
+	fmt.Fprintf(cli.Out(), "Tagging %s as %s\n", trustedFamiliarRef, familiarRef)
379 383
 
380
-	return cli.Client().ImageTag(ctx, trustedRef.String(), ref.String())
384
+	return cli.Client().ImageTag(ctx, trustedFamiliarRef, familiarRef)
381 385
 }
... ...
@@ -8,18 +8,18 @@ import (
8 8
 	"path/filepath"
9 9
 
10 10
 	"github.com/Sirupsen/logrus"
11
+	"github.com/docker/distribution/reference"
11 12
 	"github.com/docker/docker/api/types"
12 13
 	"github.com/docker/docker/cli"
13 14
 	"github.com/docker/docker/cli/command"
14 15
 	"github.com/docker/docker/pkg/archive"
15
-	"github.com/docker/docker/reference"
16 16
 	"github.com/spf13/cobra"
17 17
 	"golang.org/x/net/context"
18 18
 )
19 19
 
20 20
 // validateTag checks if the given repoName can be resolved.
21 21
 func validateTag(rawRepo string) error {
22
-	_, err := reference.ParseNamed(rawRepo)
22
+	_, err := reference.ParseNormalizedNamed(rawRepo)
23 23
 
24 24
 	return err
25 25
 }
... ...
@@ -6,14 +6,13 @@ import (
6 6
 	"fmt"
7 7
 	"strings"
8 8
 
9
-	distreference "github.com/docker/distribution/reference"
9
+	"github.com/docker/distribution/reference"
10 10
 	"github.com/docker/docker/api/types"
11 11
 	registrytypes "github.com/docker/docker/api/types/registry"
12 12
 	"github.com/docker/docker/cli"
13 13
 	"github.com/docker/docker/cli/command"
14 14
 	"github.com/docker/docker/cli/command/image"
15 15
 	"github.com/docker/docker/pkg/jsonmessage"
16
-	"github.com/docker/docker/reference"
17 16
 	"github.com/docker/docker/registry"
18 17
 	"github.com/spf13/cobra"
19 18
 	"golang.org/x/net/context"
... ...
@@ -52,8 +51,8 @@ func newInstallCommand(dockerCli *command.DockerCli) *cobra.Command {
52 52
 	return cmd
53 53
 }
54 54
 
55
-func getRepoIndexFromUnnormalizedRef(ref distreference.Named) (*registrytypes.IndexInfo, error) {
56
-	named, err := reference.ParseNamed(ref.Name())
55
+func getRepoIndexFromUnnormalizedRef(ref reference.Named) (*registrytypes.IndexInfo, error) {
56
+	named, err := reference.ParseNormalizedNamed(ref.Name())
57 57
 	if err != nil {
58 58
 		return nil, err
59 59
 	}
... ...
@@ -85,71 +84,60 @@ func newRegistryService() registry.Service {
85 85
 }
86 86
 
87 87
 func runInstall(dockerCli *command.DockerCli, opts pluginOptions) error {
88
-	// Parse name using distribution reference package to support name
89
-	// containing both tag and digest. Names with both tag and digest
90
-	// will be treated by the daemon as a pull by digest with
91
-	// an alias for the tag (if no alias is provided).
92
-	ref, err := distreference.ParseNamed(opts.name)
88
+	// Names with both tag and digest will be treated by the daemon
89
+	// as a pull by digest with an alias for the tag
90
+	// (if no alias is provided).
91
+	ref, err := reference.ParseNormalizedNamed(opts.name)
93 92
 	if err != nil {
94 93
 		return err
95 94
 	}
96 95
 
97 96
 	alias := ""
98 97
 	if opts.alias != "" {
99
-		aref, err := reference.ParseNamed(opts.alias)
98
+		aref, err := reference.ParseNormalizedNamed(opts.alias)
100 99
 		if err != nil {
101 100
 			return err
102 101
 		}
103
-		aref = reference.WithDefaultTag(aref)
104
-		if _, ok := aref.(reference.NamedTagged); !ok {
102
+		if _, ok := aref.(reference.Canonical); ok {
105 103
 			return fmt.Errorf("invalid name: %s", opts.alias)
106 104
 		}
107
-		alias = aref.String()
105
+		alias = reference.FamiliarString(reference.EnsureTagged(aref))
108 106
 	}
109 107
 	ctx := context.Background()
110 108
 
111
-	index, err := getRepoIndexFromUnnormalizedRef(ref)
109
+	repoInfo, err := registry.ParseRepositoryInfo(ref)
112 110
 	if err != nil {
113 111
 		return err
114 112
 	}
115 113
 
116 114
 	remote := ref.String()
117 115
 
118
-	_, isCanonical := ref.(distreference.Canonical)
116
+	_, isCanonical := ref.(reference.Canonical)
119 117
 	if command.IsTrusted() && !isCanonical {
120 118
 		if alias == "" {
121
-			alias = ref.String()
119
+			alias = reference.FamiliarString(ref)
122 120
 		}
123
-		var nt reference.NamedTagged
124
-		named, err := reference.ParseNamed(ref.Name())
125
-		if err != nil {
126
-			return err
127
-		}
128
-		if tagged, ok := ref.(distreference.Tagged); ok {
129
-			nt, err = reference.WithTag(named, tagged.Tag())
130
-			if err != nil {
131
-				return err
132
-			}
133
-		} else {
134
-			named = reference.WithDefaultTag(named)
135
-			nt = named.(reference.NamedTagged)
121
+
122
+		nt, ok := ref.(reference.NamedTagged)
123
+		if !ok {
124
+			nt = reference.EnsureTagged(ref)
136 125
 		}
137 126
 
138 127
 		trusted, err := image.TrustedReference(ctx, dockerCli, nt, newRegistryService())
139 128
 		if err != nil {
140 129
 			return err
141 130
 		}
142
-		remote = trusted.String()
131
+		remote = reference.FamiliarString(trusted)
143 132
 	}
144 133
 
145
-	authConfig := command.ResolveAuthConfig(ctx, dockerCli, index)
134
+	authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index)
146 135
 
147 136
 	encodedAuth, err := command.EncodeAuthToBase64(authConfig)
148 137
 	if err != nil {
149 138
 		return err
150 139
 	}
151 140
 
152
-	registryAuthFunc := command.RegistryAuthenticationPrivilegedFunc(dockerCli, index, "plugin install")
141
+	registryAuthFunc := command.RegistryAuthenticationPrivilegedFunc(dockerCli, repoInfo.Index, "plugin install")
153 142
 
154 143
 	options := types.PluginInstallOptions{
155 144
 		RegistryAuth:          encodedAuth,
... ...
@@ -5,11 +5,11 @@ import (
5 5
 
6 6
 	"golang.org/x/net/context"
7 7
 
8
+	"github.com/docker/distribution/reference"
8 9
 	"github.com/docker/docker/cli"
9 10
 	"github.com/docker/docker/cli/command"
10 11
 	"github.com/docker/docker/cli/command/image"
11 12
 	"github.com/docker/docker/pkg/jsonmessage"
12
-	"github.com/docker/docker/reference"
13 13
 	"github.com/docker/docker/registry"
14 14
 	"github.com/spf13/cobra"
15 15
 )
... ...
@@ -32,16 +32,17 @@ func newPushCommand(dockerCli *command.DockerCli) *cobra.Command {
32 32
 }
33 33
 
34 34
 func runPush(dockerCli *command.DockerCli, name string) error {
35
-	named, err := reference.ParseNamed(name) // FIXME: validate
35
+	named, err := reference.ParseNormalizedNamed(name)
36 36
 	if err != nil {
37 37
 		return err
38 38
 	}
39
-	if reference.IsNameOnly(named) {
40
-		named = reference.WithDefaultTag(named)
39
+	if _, ok := named.(reference.Canonical); ok {
40
+		return fmt.Errorf("invalid name: %s", name)
41 41
 	}
42
-	ref, ok := named.(reference.NamedTagged)
42
+
43
+	taggedRef, ok := named.(reference.NamedTagged)
43 44
 	if !ok {
44
-		return fmt.Errorf("invalid name: %s", named.String())
45
+		taggedRef = reference.EnsureTagged(named)
45 46
 	}
46 47
 
47 48
 	ctx := context.Background()
... ...
@@ -56,7 +57,8 @@ func runPush(dockerCli *command.DockerCli, name string) error {
56 56
 	if err != nil {
57 57
 		return err
58 58
 	}
59
-	responseBody, err := dockerCli.Client().PluginPush(ctx, ref.String(), encodedAuth)
59
+
60
+	responseBody, err := dockerCli.Client().PluginPush(ctx, reference.FamiliarString(taggedRef), encodedAuth)
60 61
 	if err != nil {
61 62
 		return err
62 63
 	}
... ...
@@ -12,10 +12,10 @@ import (
12 12
 
13 13
 	"golang.org/x/net/context"
14 14
 
15
+	"github.com/docker/distribution/reference"
15 16
 	"github.com/docker/docker/api/types"
16 17
 	registrytypes "github.com/docker/docker/api/types/registry"
17 18
 	"github.com/docker/docker/pkg/term"
18
-	"github.com/docker/docker/reference"
19 19
 	"github.com/docker/docker/registry"
20 20
 )
21 21
 
... ...
@@ -174,7 +174,7 @@ func RetrieveAuthTokenFromImage(ctx context.Context, cli *DockerCli, image strin
174 174
 
175 175
 // resolveAuthConfigFromImage retrieves that AuthConfig using the image string
176 176
 func resolveAuthConfigFromImage(ctx context.Context, cli *DockerCli, image string) (types.AuthConfig, error) {
177
-	registryRef, err := reference.ParseNamed(image)
177
+	registryRef, err := reference.ParseNormalizedNamed(image)
178 178
 	if err != nil {
179 179
 		return types.AuthConfig{}, err
180 180
 	}
... ...
@@ -5,11 +5,10 @@ import (
5 5
 	"fmt"
6 6
 
7 7
 	"github.com/Sirupsen/logrus"
8
-	distreference "github.com/docker/distribution/reference"
8
+	"github.com/docker/distribution/reference"
9 9
 	"github.com/docker/docker/api/types/swarm"
10 10
 	"github.com/docker/docker/cli/command"
11 11
 	"github.com/docker/docker/cli/trust"
12
-	"github.com/docker/docker/reference"
13 12
 	"github.com/docker/docker/registry"
14 13
 	"github.com/docker/notary/tuf/data"
15 14
 	"github.com/opencontainers/go-digest"
... ...
@@ -24,41 +23,34 @@ func resolveServiceImageDigest(dockerCli *command.DockerCli, service *swarm.Serv
24 24
 		return nil
25 25
 	}
26 26
 
27
-	image := service.TaskTemplate.ContainerSpec.Image
28
-
29
-	// We only attempt to resolve the digest if the reference
30
-	// could be parsed as a digest reference. Specifying an image ID
31
-	// is valid but not resolvable. There is no warning message for
32
-	// an image ID because it's valid to use one.
33
-	if _, err := digest.Parse(image); err == nil {
34
-		return nil
35
-	}
36
-
37
-	ref, err := reference.ParseNamed(image)
27
+	ref, err := reference.ParseAnyReference(service.TaskTemplate.ContainerSpec.Image)
38 28
 	if err != nil {
39
-		return fmt.Errorf("Could not parse image reference %s", service.TaskTemplate.ContainerSpec.Image)
29
+		return errors.Wrapf(err, "invalid reference %s", service.TaskTemplate.ContainerSpec.Image)
40 30
 	}
41
-	if _, ok := ref.(reference.Canonical); !ok {
42
-		ref = reference.WithDefaultTag(ref)
43 31
 
44
-		taggedRef, ok := ref.(reference.NamedTagged)
32
+	// If reference does not have digest (is not canonical nor image id)
33
+	if _, ok := ref.(reference.Digested); !ok {
34
+		namedRef, ok := ref.(reference.Named)
45 35
 		if !ok {
46
-			// This should never happen because a reference either
47
-			// has a digest, or WithDefaultTag would give it a tag.
48
-			return errors.New("Failed to resolve image digest using content trust: reference is missing a tag")
36
+			return errors.New("failed to resolve image digest using content trust: reference is not named")
37
+
49 38
 		}
50 39
 
40
+		taggedRef := reference.EnsureTagged(namedRef)
41
+
51 42
 		resolvedImage, err := trustedResolveDigest(context.Background(), dockerCli, taggedRef)
52 43
 		if err != nil {
53
-			return fmt.Errorf("Failed to resolve image digest using content trust: %v", err)
44
+			return errors.Wrap(err, "failed to resolve image digest using content trust")
54 45
 		}
55
-		logrus.Debugf("resolved image tag to %s using content trust", resolvedImage.String())
56
-		service.TaskTemplate.ContainerSpec.Image = resolvedImage.String()
46
+		resolvedFamiliar := reference.FamiliarString(resolvedImage)
47
+		logrus.Debugf("resolved image tag to %s using content trust", resolvedFamiliar)
48
+		service.TaskTemplate.ContainerSpec.Image = resolvedFamiliar
57 49
 	}
50
+
58 51
 	return nil
59 52
 }
60 53
 
61
-func trustedResolveDigest(ctx context.Context, cli *command.DockerCli, ref reference.NamedTagged) (distreference.Canonical, error) {
54
+func trustedResolveDigest(ctx context.Context, cli *command.DockerCli, ref reference.NamedTagged) (reference.Canonical, error) {
62 55
 	repoInfo, err := registry.ParseRepositoryInfo(ref)
63 56
 	if err != nil {
64 57
 		return nil, err
... ...
@@ -78,7 +70,7 @@ func trustedResolveDigest(ctx context.Context, cli *command.DockerCli, ref refer
78 78
 	// Only get the tag if it's in the top level targets role or the releases delegation role
79 79
 	// ignore it if it's in any other delegation roles
80 80
 	if t.Role != trust.ReleasesRole && t.Role != data.CanonicalTargetsRole {
81
-		return nil, trust.NotaryError(repoInfo.FullName(), fmt.Errorf("No trust data for %s", ref.String()))
81
+		return nil, trust.NotaryError(repoInfo.FullName(), fmt.Errorf("No trust data for %s", reference.FamiliarString(ref)))
82 82
 	}
83 83
 
84 84
 	logrus.Debugf("retrieving target for %s role\n", t.Role)
... ...
@@ -89,8 +81,6 @@ func trustedResolveDigest(ctx context.Context, cli *command.DockerCli, ref refer
89 89
 
90 90
 	dgst := digest.NewDigestFromHex("sha256", hex.EncodeToString(h))
91 91
 
92
-	// Using distribution reference package to make sure that adding a
93
-	// digest does not erase the tag. When the two reference packages
94
-	// are unified, this will no longer be an issue.
95
-	return distreference.WithDigest(ref, dgst)
92
+	// Allow returning canonical reference with tag
93
+	return reference.WithDigest(ref, dgst)
96 94
 }
... ...
@@ -202,7 +202,7 @@ func TestLayerAlreadyExists(t *testing.T) {
202 202
 			checkOtherRepositories: true,
203 203
 			metadata: []metadata.V2Metadata{
204 204
 				{Digest: digest.Digest("apple"), SourceRepository: "docker.io/library/hello-world"},
205
-				{Digest: digest.Digest("orange"), SourceRepository: "docker.io/library/busybox/subapp"},
205
+				{Digest: digest.Digest("orange"), SourceRepository: "docker.io/busybox/subapp"},
206 206
 				{Digest: digest.Digest("pear"), SourceRepository: "docker.io/busybox"},
207 207
 				{Digest: digest.Digest("plum"), SourceRepository: "busybox"},
208 208
 				{Digest: digest.Digest("banana"), SourceRepository: "127.0.0.1/busybox"},
... ...
@@ -3082,7 +3082,7 @@ func (s *DockerSuite) TestBuildInvalidTag(c *check.C) {
3082 3082
 	name := "abcd:" + stringutils.GenerateRandomAlphaOnlyString(200)
3083 3083
 	buildImage(name, withDockerfile("FROM "+minimalBaseImage()+"\nMAINTAINER quux\n")).Assert(c, icmd.Expected{
3084 3084
 		ExitCode: 125,
3085
-		Err:      "Error parsing reference",
3085
+		Err:      "invalid reference format",
3086 3086
 	})
3087 3087
 }
3088 3088
 
... ...
@@ -274,7 +274,7 @@ func (s *DockerSuite) TestCreateByImageID(c *check.C) {
274 274
 		c.Fatalf("expected non-zero exit code; received %d", exit)
275 275
 	}
276 276
 
277
-	if expected := "Error parsing reference"; !strings.Contains(out, expected) {
277
+	if expected := "invalid reference format"; !strings.Contains(out, expected) {
278 278
 		c.Fatalf(`Expected %q in output; got: %s`, expected, out)
279 279
 	}
280 280
 
... ...
@@ -3862,8 +3862,8 @@ func (s *DockerSuite) TestRunInvalidReference(c *check.C) {
3862 3862
 		c.Fatalf("expected non-zero exist code; received %d", exit)
3863 3863
 	}
3864 3864
 
3865
-	if !strings.Contains(out, "Error parsing reference") {
3866
-		c.Fatalf(`Expected "Error parsing reference" in output; got: %s`, out)
3865
+	if !strings.Contains(out, "invalid reference format") {
3866
+		c.Fatalf(`Expected "invalid reference format" in output; got: %s`, out)
3867 3867
 	}
3868 3868
 }
3869 3869
 
... ...
@@ -12,6 +12,7 @@ import (
12 12
 	"sync"
13 13
 
14 14
 	"github.com/Sirupsen/logrus"
15
+	"github.com/docker/distribution/reference"
15 16
 	"github.com/docker/docker/api/types"
16 17
 	"github.com/docker/docker/image"
17 18
 	"github.com/docker/docker/layer"
... ...
@@ -19,7 +20,6 @@ import (
19 19
 	"github.com/docker/docker/pkg/ioutils"
20 20
 	"github.com/docker/docker/pkg/mount"
21 21
 	"github.com/docker/docker/plugin/v2"
22
-	"github.com/docker/docker/reference"
23 22
 	"github.com/docker/docker/registry"
24 23
 	"github.com/opencontainers/go-digest"
25 24
 	"github.com/pkg/errors"
... ...
@@ -1,13 +1,12 @@
1 1
 package reference
2 2
 
3 3
 import (
4
-	"errors"
5 4
 	"fmt"
6
-	"strings"
7 5
 
8 6
 	distreference "github.com/docker/distribution/reference"
9 7
 	"github.com/docker/docker/pkg/stringid"
10 8
 	"github.com/opencontainers/go-digest"
9
+	"github.com/pkg/errors"
11 10
 )
12 11
 
13 12
 const (
... ...
@@ -53,21 +52,31 @@ type Canonical interface {
53 53
 // returned.
54 54
 // If an error was encountered it is returned, along with a nil Reference.
55 55
 func ParseNamed(s string) (Named, error) {
56
-	named, err := distreference.ParseNamed(s)
56
+	named, err := distreference.ParseNormalizedNamed(s)
57 57
 	if err != nil {
58
-		return nil, fmt.Errorf("Error parsing reference: %q is not a valid repository/tag: %s", s, err)
58
+		return nil, errors.Wrapf(err, "failed to parse reference %q", s)
59 59
 	}
60
-	r, err := WithName(named.Name())
61
-	if err != nil {
60
+	if err := validateName(distreference.FamiliarName(named)); err != nil {
62 61
 		return nil, err
63 62
 	}
63
+
64
+	// Ensure returned reference cannot have tag and digest
64 65
 	if canonical, isCanonical := named.(distreference.Canonical); isCanonical {
65
-		return WithDigest(r, canonical.Digest())
66
+		r, err := distreference.WithDigest(distreference.TrimNamed(named), canonical.Digest())
67
+		if err != nil {
68
+			return nil, err
69
+		}
70
+		return &canonicalRef{namedRef{r}}, nil
66 71
 	}
67 72
 	if tagged, isTagged := named.(distreference.NamedTagged); isTagged {
68
-		return WithTag(r, tagged.Tag())
73
+		r, err := distreference.WithTag(distreference.TrimNamed(named), tagged.Tag())
74
+		if err != nil {
75
+			return nil, err
76
+		}
77
+		return &taggedRef{namedRef{r}}, nil
69 78
 	}
70
-	return r, nil
79
+
80
+	return &namedRef{named}, nil
71 81
 }
72 82
 
73 83
 // TrimNamed removes any tag or digest from the named reference
... ...
@@ -78,16 +87,15 @@ func TrimNamed(ref Named) Named {
78 78
 // WithName returns a named object representing the given string. If the input
79 79
 // is invalid ErrReferenceInvalidFormat will be returned.
80 80
 func WithName(name string) (Named, error) {
81
-	name, err := normalize(name)
81
+	r, err := distreference.ParseNormalizedNamed(name)
82 82
 	if err != nil {
83 83
 		return nil, err
84 84
 	}
85
-	if err := validateName(name); err != nil {
85
+	if err := validateName(distreference.FamiliarName(r)); err != nil {
86 86
 		return nil, err
87 87
 	}
88
-	r, err := distreference.WithName(name)
89
-	if err != nil {
90
-		return nil, err
88
+	if !distreference.IsNameOnly(r) {
89
+		return nil, distreference.ErrReferenceInvalidFormat
91 90
 	}
92 91
 	return &namedRef{r}, nil
93 92
 }
... ...
@@ -122,17 +130,22 @@ type canonicalRef struct {
122 122
 	namedRef
123 123
 }
124 124
 
125
+func (r *namedRef) Name() string {
126
+	return distreference.FamiliarName(r.Named)
127
+}
128
+
129
+func (r *namedRef) String() string {
130
+	return distreference.FamiliarString(r.Named)
131
+}
132
+
125 133
 func (r *namedRef) FullName() string {
126
-	hostname, remoteName := splitHostname(r.Name())
127
-	return hostname + "/" + remoteName
134
+	return r.Named.Name()
128 135
 }
129 136
 func (r *namedRef) Hostname() string {
130
-	hostname, _ := splitHostname(r.Name())
131
-	return hostname
137
+	return distreference.Domain(r.Named)
132 138
 }
133 139
 func (r *namedRef) RemoteName() string {
134
-	_, remoteName := splitHostname(r.Name())
135
-	return remoteName
140
+	return distreference.Path(r.Named)
136 141
 }
137 142
 func (r *taggedRef) Tag() string {
138 143
 	return r.namedRef.Named.(distreference.NamedTagged).Tag()
... ...
@@ -173,41 +186,6 @@ func ParseIDOrReference(idOrRef string) (digest.Digest, Named, error) {
173 173
 	return "", ref, err
174 174
 }
175 175
 
176
-// splitHostname splits a repository name to hostname and remotename string.
177
-// If no valid hostname is found, the default hostname is used. Repository name
178
-// needs to be already validated before.
179
-func splitHostname(name string) (hostname, remoteName string) {
180
-	i := strings.IndexRune(name, '/')
181
-	if i == -1 || (!strings.ContainsAny(name[:i], ".:") && name[:i] != "localhost") {
182
-		hostname, remoteName = DefaultHostname, name
183
-	} else {
184
-		hostname, remoteName = name[:i], name[i+1:]
185
-	}
186
-	if hostname == LegacyDefaultHostname {
187
-		hostname = DefaultHostname
188
-	}
189
-	if hostname == DefaultHostname && !strings.ContainsRune(remoteName, '/') {
190
-		remoteName = DefaultRepoPrefix + remoteName
191
-	}
192
-	return
193
-}
194
-
195
-// normalize returns a repository name in its normalized form, meaning it
196
-// will not contain default hostname nor library/ prefix for official images.
197
-func normalize(name string) (string, error) {
198
-	host, remoteName := splitHostname(name)
199
-	if strings.ToLower(remoteName) != remoteName {
200
-		return "", errors.New("invalid reference format: repository name must be lowercase")
201
-	}
202
-	if host == DefaultHostname {
203
-		if strings.HasPrefix(remoteName, DefaultRepoPrefix) {
204
-			return strings.TrimPrefix(remoteName, DefaultRepoPrefix), nil
205
-		}
206
-		return remoteName, nil
207
-	}
208
-	return name, nil
209
-}
210
-
211 176
 func validateName(name string) error {
212 177
 	if err := stringid.ValidateID(name); err == nil {
213 178
 		return fmt.Errorf("Invalid repository name (%s), cannot specify 64-byte hexadecimal strings", name)
... ...
@@ -1,16 +1,17 @@
1 1
 package registry
2 2
 
3 3
 import (
4
-	"errors"
5 4
 	"fmt"
6 5
 	"net"
7 6
 	"net/url"
8 7
 	"strings"
9 8
 
10 9
 	"github.com/Sirupsen/logrus"
10
+	"github.com/docker/distribution/reference"
11 11
 	registrytypes "github.com/docker/docker/api/types/registry"
12 12
 	"github.com/docker/docker/opts"
13
-	"github.com/docker/docker/reference"
13
+	forkedref "github.com/docker/docker/reference"
14
+	"github.com/pkg/errors"
14 15
 	"github.com/spf13/pflag"
15 16
 )
16 17
 
... ...
@@ -270,8 +271,8 @@ func ValidateMirror(val string) (string, error) {
270 270
 
271 271
 // ValidateIndexName validates an index name.
272 272
 func ValidateIndexName(val string) (string, error) {
273
-	if val == reference.LegacyDefaultHostname {
274
-		val = reference.DefaultHostname
273
+	if val == forkedref.LegacyDefaultHostname {
274
+		val = forkedref.DefaultHostname
275 275
 	}
276 276
 	if strings.HasPrefix(val, "-") || strings.HasSuffix(val, "-") {
277 277
 		return "", fmt.Errorf("Invalid index name (%s). Cannot begin or end with a hyphen.", val)
... ...
@@ -321,13 +322,19 @@ func GetAuthConfigKey(index *registrytypes.IndexInfo) string {
321 321
 
322 322
 // newRepositoryInfo validates and breaks down a repository name into a RepositoryInfo
323 323
 func newRepositoryInfo(config *serviceConfig, name reference.Named) (*RepositoryInfo, error) {
324
-	index, err := newIndexInfo(config, name.Hostname())
324
+	index, err := newIndexInfo(config, reference.Domain(name))
325
+	if err != nil {
326
+		return nil, err
327
+	}
328
+	official := !strings.ContainsRune(reference.FamiliarName(name), '/')
329
+
330
+	// TODO: remove used of forked reference package
331
+	nameref, err := forkedref.ParseNamed(name.String())
325 332
 	if err != nil {
326 333
 		return nil, err
327 334
 	}
328
-	official := !strings.ContainsRune(name.Name(), '/')
329 335
 	return &RepositoryInfo{
330
-		Named:    name,
336
+		Named:    nameref,
331 337
 		Index:    index,
332 338
 		Official: official,
333 339
 	}, nil
... ...
@@ -10,10 +10,11 @@ import (
10 10
 	"strings"
11 11
 	"testing"
12 12
 
13
+	"github.com/docker/distribution/reference"
13 14
 	"github.com/docker/distribution/registry/client/transport"
14 15
 	"github.com/docker/docker/api/types"
15 16
 	registrytypes "github.com/docker/docker/api/types/registry"
16
-	"github.com/docker/docker/reference"
17
+	forkedref "github.com/docker/docker/reference"
17 18
 )
18 19
 
19 20
 var (
... ...
@@ -201,7 +202,7 @@ func TestGetRemoteImageLayer(t *testing.T) {
201 201
 
202 202
 func TestGetRemoteTag(t *testing.T) {
203 203
 	r := spawnTestRegistrySession(t)
204
-	repoRef, err := reference.ParseNamed(REPO)
204
+	repoRef, err := forkedref.ParseNamed(REPO)
205 205
 	if err != nil {
206 206
 		t.Fatal(err)
207 207
 	}
... ...
@@ -211,7 +212,7 @@ func TestGetRemoteTag(t *testing.T) {
211 211
 	}
212 212
 	assertEqual(t, tag, imageID, "Expected tag test to map to "+imageID)
213 213
 
214
-	bazRef, err := reference.ParseNamed("foo42/baz")
214
+	bazRef, err := forkedref.ParseNamed("foo42/baz")
215 215
 	if err != nil {
216 216
 		t.Fatal(err)
217 217
 	}
... ...
@@ -223,7 +224,7 @@ func TestGetRemoteTag(t *testing.T) {
223 223
 
224 224
 func TestGetRemoteTags(t *testing.T) {
225 225
 	r := spawnTestRegistrySession(t)
226
-	repoRef, err := reference.ParseNamed(REPO)
226
+	repoRef, err := forkedref.ParseNamed(REPO)
227 227
 	if err != nil {
228 228
 		t.Fatal(err)
229 229
 	}
... ...
@@ -235,7 +236,7 @@ func TestGetRemoteTags(t *testing.T) {
235 235
 	assertEqual(t, tags["latest"], imageID, "Expected tag latest to map to "+imageID)
236 236
 	assertEqual(t, tags["test"], imageID, "Expected tag test to map to "+imageID)
237 237
 
238
-	bazRef, err := reference.ParseNamed("foo42/baz")
238
+	bazRef, err := forkedref.ParseNamed("foo42/baz")
239 239
 	if err != nil {
240 240
 		t.Fatal(err)
241 241
 	}
... ...
@@ -252,7 +253,7 @@ func TestGetRepositoryData(t *testing.T) {
252 252
 		t.Fatal(err)
253 253
 	}
254 254
 	host := "http://" + parsedURL.Host + "/v1/"
255
-	repoRef, err := reference.ParseNamed(REPO)
255
+	repoRef, err := forkedref.ParseNamed(REPO)
256 256
 	if err != nil {
257 257
 		t.Fatal(err)
258 258
 	}
... ...
@@ -505,7 +506,7 @@ func TestParseRepositoryInfo(t *testing.T) {
505 505
 	}
506 506
 
507 507
 	for reposName, expectedRepoInfo := range expectedRepoInfos {
508
-		named, err := reference.WithName(reposName)
508
+		named, err := reference.ParseNormalizedNamed(reposName)
509 509
 		if err != nil {
510 510
 			t.Error(err)
511 511
 		}
... ...
@@ -669,7 +670,7 @@ func TestMirrorEndpointLookup(t *testing.T) {
669 669
 	if err != nil {
670 670
 		t.Error(err)
671 671
 	}
672
-	pushAPIEndpoints, err := s.LookupPushEndpoints(imageName.Hostname())
672
+	pushAPIEndpoints, err := s.LookupPushEndpoints(reference.Domain(imageName))
673 673
 	if err != nil {
674 674
 		t.Fatal(err)
675 675
 	}
... ...
@@ -677,7 +678,7 @@ func TestMirrorEndpointLookup(t *testing.T) {
677 677
 		t.Fatal("Push endpoint should not contain mirror")
678 678
 	}
679 679
 
680
-	pullAPIEndpoints, err := s.LookupPullEndpoints(imageName.Hostname())
680
+	pullAPIEndpoints, err := s.LookupPullEndpoints(reference.Domain(imageName))
681 681
 	if err != nil {
682 682
 		t.Fatal(err)
683 683
 	}
... ...
@@ -688,7 +689,7 @@ func TestMirrorEndpointLookup(t *testing.T) {
688 688
 
689 689
 func TestPushRegistryTag(t *testing.T) {
690 690
 	r := spawnTestRegistrySession(t)
691
-	repoRef, err := reference.ParseNamed(REPO)
691
+	repoRef, err := forkedref.ParseNamed(REPO)
692 692
 	if err != nil {
693 693
 		t.Fatal(err)
694 694
 	}
... ...
@@ -710,7 +711,7 @@ func TestPushImageJSONIndex(t *testing.T) {
710 710
 			Checksum: "sha256:bea7bf2e4bacd479344b737328db47b18880d09096e6674165533aa994f5e9f2",
711 711
 		},
712 712
 	}
713
-	repoRef, err := reference.ParseNamed(REPO)
713
+	repoRef, err := forkedref.ParseNamed(REPO)
714 714
 	if err != nil {
715 715
 		t.Fatal(err)
716 716
 	}
... ...
@@ -11,10 +11,10 @@ import (
11 11
 	"golang.org/x/net/context"
12 12
 
13 13
 	"github.com/Sirupsen/logrus"
14
+	"github.com/docker/distribution/reference"
14 15
 	"github.com/docker/distribution/registry/client/auth"
15 16
 	"github.com/docker/docker/api/types"
16 17
 	registrytypes "github.com/docker/docker/api/types/registry"
17
-	"github.com/docker/docker/reference"
18 18
 )
19 19
 
20 20
 const (
... ...
@@ -43,7 +43,7 @@ github.com/boltdb/bolt fff57c100f4dea1905678da7e90d92429dff2904
43 43
 github.com/miekg/dns 75e6e86cc601825c5dbcd4e0c209eab180997cd7
44 44
 
45 45
 # get graph and distribution packages
46
-github.com/docker/distribution 7dba427612198a11b161a27f9d40bb2dca1ccd20
46
+github.com/docker/distribution 129ad8ea0c3760d878b34cffdb9c3be874a7b2f7
47 47
 github.com/vbatts/tar-split v0.10.1
48 48
 github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb
49 49
 
... ...
@@ -10,3 +10,21 @@ func IsNameOnly(ref Named) bool {
10 10
 	}
11 11
 	return true
12 12
 }
13
+
14
+// FamiliarName returns the familiar name string
15
+// for the given named, familiarizing if needed.
16
+func FamiliarName(ref Named) string {
17
+	if nn, ok := ref.(NormalizedNamed); ok {
18
+		return nn.Familiar().Name()
19
+	}
20
+	return ref.Name()
21
+}
22
+
23
+// FamiliarString returns the familiar string representation
24
+// for the given reference, familiarizing if needed.
25
+func FamiliarString(ref Reference) string {
26
+	if nn, ok := ref.(NormalizedNamed); ok {
27
+		return nn.Familiar().String()
28
+	}
29
+	return ref.String()
30
+}
... ...
@@ -1,9 +1,125 @@
1 1
 package reference
2 2
 
3
+import (
4
+	"errors"
5
+	"fmt"
6
+	"strings"
7
+
8
+	"github.com/docker/distribution/digestset"
9
+	"github.com/opencontainers/go-digest"
10
+)
11
+
3 12
 var (
4
-	defaultTag = "latest"
13
+	legacyDefaultDomain = "index.docker.io"
14
+	defaultDomain       = "docker.io"
15
+	defaultRepoPrefix   = "library/"
16
+	defaultTag          = "latest"
5 17
 )
6 18
 
19
+// NormalizedNamed represents a name which has been
20
+// normalized and has a familiar form. A familiar name
21
+// is what is used in Docker UI. An example normalized
22
+// name is "docker.io/library/ubuntu" and corresponding
23
+// familiar name of "ubuntu".
24
+type NormalizedNamed interface {
25
+	Named
26
+	Familiar() Named
27
+}
28
+
29
+// ParseNormalizedNamed parses a string into a named reference
30
+// transforming a familiar name from Docker UI to a fully
31
+// qualified reference. If the value may be an identifier
32
+// use ParseAnyReference.
33
+func ParseNormalizedNamed(s string) (NormalizedNamed, error) {
34
+	if ok := anchoredIdentifierRegexp.MatchString(s); ok {
35
+		return nil, fmt.Errorf("invalid repository name (%s), cannot specify 64-byte hexadecimal strings", s)
36
+	}
37
+	domain, remainder := splitDockerDomain(s)
38
+	var remoteName string
39
+	if tagSep := strings.IndexRune(remainder, ':'); tagSep > -1 {
40
+		remoteName = remainder[:tagSep]
41
+	} else {
42
+		remoteName = remainder
43
+	}
44
+	if strings.ToLower(remoteName) != remoteName {
45
+		return nil, errors.New("invalid reference format: repository name must be lowercase")
46
+	}
47
+
48
+	ref, err := Parse(domain + "/" + remainder)
49
+	if err != nil {
50
+		return nil, err
51
+	}
52
+	named, isNamed := ref.(NormalizedNamed)
53
+	if !isNamed {
54
+		return nil, fmt.Errorf("reference %s has no name", ref.String())
55
+	}
56
+	return named, nil
57
+}
58
+
59
+// splitDockerDomain splits a repository name to domain and remotename string.
60
+// If no valid domain is found, the default domain is used. Repository name
61
+// needs to be already validated before.
62
+func splitDockerDomain(name string) (domain, remainder string) {
63
+	i := strings.IndexRune(name, '/')
64
+	if i == -1 || (!strings.ContainsAny(name[:i], ".:") && name[:i] != "localhost") {
65
+		domain, remainder = defaultDomain, name
66
+	} else {
67
+		domain, remainder = name[:i], name[i+1:]
68
+	}
69
+	if domain == legacyDefaultDomain {
70
+		domain = defaultDomain
71
+	}
72
+	if domain == defaultDomain && !strings.ContainsRune(remainder, '/') {
73
+		remainder = defaultRepoPrefix + remainder
74
+	}
75
+	return
76
+}
77
+
78
+// familiarizeName returns a shortened version of the name familiar
79
+// to to the Docker UI. Familiar names have the default domain
80
+// "docker.io" and "library/" repository prefix removed.
81
+// For example, "docker.io/library/redis" will have the familiar
82
+// name "redis" and "docker.io/dmcgowan/myapp" will be "dmcgowan/myapp".
83
+// Returns a familiarized named only reference.
84
+func familiarizeName(named namedRepository) repository {
85
+	repo := repository{
86
+		domain: named.Domain(),
87
+		path:   named.Path(),
88
+	}
89
+
90
+	if repo.domain == defaultDomain {
91
+		repo.domain = ""
92
+		repo.path = strings.TrimPrefix(repo.path, defaultRepoPrefix)
93
+	}
94
+	return repo
95
+}
96
+
97
+func (r reference) Familiar() Named {
98
+	return reference{
99
+		namedRepository: familiarizeName(r.namedRepository),
100
+		tag:             r.tag,
101
+		digest:          r.digest,
102
+	}
103
+}
104
+
105
+func (r repository) Familiar() Named {
106
+	return familiarizeName(r)
107
+}
108
+
109
+func (t taggedReference) Familiar() Named {
110
+	return taggedReference{
111
+		namedRepository: familiarizeName(t.namedRepository),
112
+		tag:             t.tag,
113
+	}
114
+}
115
+
116
+func (c canonicalReference) Familiar() Named {
117
+	return canonicalReference{
118
+		namedRepository: familiarizeName(c.namedRepository),
119
+		digest:          c.digest,
120
+	}
121
+}
122
+
7 123
 // EnsureTagged adds the default tag "latest" to a reference if it only has
8 124
 // a repo name.
9 125
 func EnsureTagged(ref Named) NamedTagged {
... ...
@@ -20,3 +136,33 @@ func EnsureTagged(ref Named) NamedTagged {
20 20
 	}
21 21
 	return namedTagged
22 22
 }
23
+
24
+// ParseAnyReference parses a reference string as a possible identifier,
25
+// full digest, or familiar name.
26
+func ParseAnyReference(ref string) (Reference, error) {
27
+	if ok := anchoredIdentifierRegexp.MatchString(ref); ok {
28
+		return digestReference("sha256:" + ref), nil
29
+	}
30
+	if dgst, err := digest.Parse(ref); err == nil {
31
+		return digestReference(dgst), nil
32
+	}
33
+
34
+	return ParseNormalizedNamed(ref)
35
+}
36
+
37
+// ParseAnyReferenceWithSet parses a reference string as a possible short
38
+// identifier to be matched in a digest set, a full digest, or familiar name.
39
+func ParseAnyReferenceWithSet(ref string, ds *digestset.Set) (Reference, error) {
40
+	if ok := anchoredShortIdentifierRegexp.MatchString(ref); ok {
41
+		dgst, err := ds.Lookup(ref)
42
+		if err == nil {
43
+			return digestReference(dgst), nil
44
+		}
45
+	} else {
46
+		if dgst, err := digest.Parse(ref); err == nil {
47
+			return digestReference(dgst), nil
48
+		}
49
+	}
50
+
51
+	return ParseNormalizedNamed(ref)
52
+}
... ...
@@ -4,11 +4,11 @@
4 4
 // Grammar
5 5
 //
6 6
 // 	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])/
10 10
 //	port-number                     := /[0-9]+/
11
-//	component                       := alpha-numeric [separator alpha-numeric]*
11
+//	path-component                  := alpha-numeric [separator alpha-numeric]*
12 12
 // 	alpha-numeric                   := /[a-z0-9]+/
13 13
 //	separator                       := /[_.]|__|[-]*/
14 14
 //
... ...
@@ -19,6 +19,9 @@
19 19
 //	digest-algorithm-separator      := /[+.-_]/
20 20
 //	digest-algorithm-component      := /[A-Za-z][A-Za-z0-9]*/
21 21
 //	digest-hex                      := /[0-9a-fA-F]{32,}/ ; At least 128 bit digest value
22
+//
23
+//      identifier                      := /[a-f0-9]{64}/
24
+//      short-identifier                := /[a-f0-9]{6,64}/
22 25
 package reference
23 26
 
24 27
 import (
... ...
@@ -126,23 +129,56 @@ type Digested interface {
126 126
 }
127 127
 
128 128
 // 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
130 130
 type Canonical interface {
131 131
 	Named
132 132
 	Digest() digest.Digest
133 133
 }
134 134
 
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
+
135 169
 // SplitHostname splits a named reference into a
136 170
 // hostname and name string. If no valid hostname is
137 171
 // found, the hostname is empty and the full value
138 172
 // is returned as name
173
+// DEPRECATED: Use Domain or Path
139 174
 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()
144 177
 	}
145
-	return match[1], match[2]
178
+	return splitDomain(named.Name())
146 179
 }
147 180
 
148 181
 // Parse parses s and returns a syntactically valid Reference.
... ...
@@ -164,9 +200,20 @@ func Parse(s string) (Reference, error) {
164 164
 		return nil, ErrNameTooLong
165 165
 	}
166 166
 
167
+	var repo repository
168
+
169
+	nameMatch := anchoredNameRegexp.FindStringSubmatch(matches[1])
170
+	if nameMatch != nil && len(nameMatch) == 3 {
171
+		repo.domain = nameMatch[1]
172
+		repo.path = nameMatch[2]
173
+	} else {
174
+		repo.domain = ""
175
+		repo.path = matches[1]
176
+	}
177
+
167 178
 	ref := reference{
168
-		name: matches[1],
169
-		tag:  matches[2],
179
+		namedRepository: repo,
180
+		tag:             matches[2],
170 181
 	}
171 182
 	if matches[3] != "" {
172 183
 		var err error
... ...
@@ -207,10 +254,15 @@ func WithName(name string) (Named, error) {
207 207
 	if len(name) > NameTotalLengthMax {
208 208
 		return nil, ErrNameTooLong
209 209
 	}
210
-	if !anchoredNameRegexp.MatchString(name) {
210
+
211
+	match := anchoredNameRegexp.FindStringSubmatch(name)
212
+	if match == nil || len(match) != 3 {
211 213
 		return nil, ErrReferenceInvalidFormat
212 214
 	}
213
-	return repository(name), nil
215
+	return repository{
216
+		domain: match[1],
217
+		path:   match[2],
218
+	}, nil
214 219
 }
215 220
 
216 221
 // WithTag combines the name from "name" and the tag from "tag" to form a
... ...
@@ -219,16 +271,23 @@ func WithTag(name Named, tag string) (NamedTagged, error) {
219 219
 	if !anchoredTagRegexp.MatchString(tag) {
220 220
 		return nil, ErrTagInvalidFormat
221 221
 	}
222
+	var repo repository
223
+	if r, ok := name.(namedRepository); ok {
224
+		repo.domain = r.Domain()
225
+		repo.path = r.Path()
226
+	} else {
227
+		repo.path = name.Name()
228
+	}
222 229
 	if canonical, ok := name.(Canonical); ok {
223 230
 		return reference{
224
-			name:   name.Name(),
225
-			tag:    tag,
226
-			digest: canonical.Digest(),
231
+			namedRepository: repo,
232
+			tag:             tag,
233
+			digest:          canonical.Digest(),
227 234
 		}, nil
228 235
 	}
229 236
 	return taggedReference{
230
-		name: name.Name(),
231
-		tag:  tag,
237
+		namedRepository: repo,
238
+		tag:             tag,
232 239
 	}, nil
233 240
 }
234 241
 
... ...
@@ -238,16 +297,23 @@ func WithDigest(name Named, digest digest.Digest) (Canonical, error) {
238 238
 	if !anchoredDigestRegexp.MatchString(digest.String()) {
239 239
 		return nil, ErrDigestInvalidFormat
240 240
 	}
241
+	var repo repository
242
+	if r, ok := name.(namedRepository); ok {
243
+		repo.domain = r.Domain()
244
+		repo.path = r.Path()
245
+	} else {
246
+		repo.path = name.Name()
247
+	}
241 248
 	if tagged, ok := name.(Tagged); ok {
242 249
 		return reference{
243
-			name:   name.Name(),
244
-			tag:    tagged.Tag(),
245
-			digest: digest,
250
+			namedRepository: repo,
251
+			tag:             tagged.Tag(),
252
+			digest:          digest,
246 253
 		}, nil
247 254
 	}
248 255
 	return canonicalReference{
249
-		name:   name.Name(),
250
-		digest: digest,
256
+		namedRepository: repo,
257
+		digest:          digest,
251 258
 	}, nil
252 259
 }
253 260
 
... ...
@@ -263,11 +329,15 @@ func Match(pattern string, ref Reference) (bool, error) {
263 263
 
264 264
 // TrimNamed removes any tag or digest from the named reference.
265 265
 func TrimNamed(ref Named) Named {
266
-	return repository(ref.Name())
266
+	domain, path := SplitHostname(ref)
267
+	return repository{
268
+		domain: domain,
269
+		path:   path,
270
+	}
267 271
 }
268 272
 
269 273
 func getBestReferenceType(ref reference) Reference {
270
-	if ref.name == "" {
274
+	if ref.Name() == "" {
271 275
 		// Allow digest only references
272 276
 		if ref.digest != "" {
273 277
 			return digestReference(ref.digest)
... ...
@@ -277,16 +347,16 @@ func getBestReferenceType(ref reference) Reference {
277 277
 	if ref.tag == "" {
278 278
 		if ref.digest != "" {
279 279
 			return canonicalReference{
280
-				name:   ref.name,
281
-				digest: ref.digest,
280
+				namedRepository: ref.namedRepository,
281
+				digest:          ref.digest,
282 282
 			}
283 283
 		}
284
-		return repository(ref.name)
284
+		return ref.namedRepository
285 285
 	}
286 286
 	if ref.digest == "" {
287 287
 		return taggedReference{
288
-			name: ref.name,
289
-			tag:  ref.tag,
288
+			namedRepository: ref.namedRepository,
289
+			tag:             ref.tag,
290 290
 		}
291 291
 	}
292 292
 
... ...
@@ -294,17 +364,13 @@ func getBestReferenceType(ref reference) Reference {
294 294
 }
295 295
 
296 296
 type reference struct {
297
-	name   string
297
+	namedRepository
298 298
 	tag    string
299 299
 	digest digest.Digest
300 300
 }
301 301
 
302 302
 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
303
+	return r.Name() + ":" + r.tag + "@" + r.digest.String()
308 304
 }
309 305
 
310 306
 func (r reference) Tag() string {
... ...
@@ -315,14 +381,28 @@ func (r reference) Digest() digest.Digest {
315 315
 	return r.digest
316 316
 }
317 317
 
318
-type repository string
318
+type repository struct {
319
+	domain string
320
+	path   string
321
+}
319 322
 
320 323
 func (r repository) String() string {
321
-	return string(r)
324
+	return r.Name()
322 325
 }
323 326
 
324 327
 func (r repository) Name() string {
325
-	return string(r)
328
+	if r.domain == "" {
329
+		return r.path
330
+	}
331
+	return r.domain + "/" + r.path
332
+}
333
+
334
+func (r repository) Domain() string {
335
+	return r.domain
336
+}
337
+
338
+func (r repository) Path() string {
339
+	return r.path
326 340
 }
327 341
 
328 342
 type digestReference digest.Digest
... ...
@@ -336,16 +416,12 @@ func (d digestReference) Digest() digest.Digest {
336 336
 }
337 337
 
338 338
 type taggedReference struct {
339
-	name string
340
-	tag  string
339
+	namedRepository
340
+	tag string
341 341
 }
342 342
 
343 343
 func (t taggedReference) String() string {
344
-	return t.name + ":" + t.tag
345
-}
346
-
347
-func (t taggedReference) Name() string {
348
-	return t.name
344
+	return t.Name() + ":" + t.tag
349 345
 }
350 346
 
351 347
 func (t taggedReference) Tag() string {
... ...
@@ -353,16 +429,12 @@ func (t taggedReference) Tag() string {
353 353
 }
354 354
 
355 355
 type canonicalReference struct {
356
-	name   string
356
+	namedRepository
357 357
 	digest digest.Digest
358 358
 }
359 359
 
360 360
 func (c canonicalReference) String() string {
361
-	return c.name + "@" + c.digest.String()
362
-}
363
-
364
-func (c canonicalReference) Name() string {
365
-	return c.name
361
+	return c.Name() + "@" + c.digest.String()
366 362
 }
367 363
 
368 364
 func (c canonicalReference) Digest() digest.Digest {
... ...
@@ -19,18 +19,18 @@ var (
19 19
 		alphaNumericRegexp,
20 20
 		optional(repeated(separatorRegexp, alphaNumericRegexp)))
21 21
 
22
-	// hostnameComponentRegexp restricts the registry hostname component of a
23
-	// repository name to start with a component as defined by hostnameRegexp
22
+	// domainComponentRegexp restricts the registry domain component of a
23
+	// repository name to start with a component as defined by domainRegexp
24 24
 	// and followed by an optional port.
25
-	hostnameComponentRegexp = match(`(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`)
25
+	domainComponentRegexp = match(`(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`)
26 26
 
27
-	// hostnameRegexp defines the structure of potential hostname components
27
+	// domainRegexp defines the structure of potential domain components
28 28
 	// that may be part of image names. This is purposely a subset of what is
29 29
 	// allowed by DNS to ensure backwards compatibility with Docker image
30 30
 	// names.
31
-	hostnameRegexp = expression(
32
-		hostnameComponentRegexp,
33
-		optional(repeated(literal(`.`), hostnameComponentRegexp)),
31
+	domainRegexp = expression(
32
+		domainComponentRegexp,
33
+		optional(repeated(literal(`.`), domainComponentRegexp)),
34 34
 		optional(literal(`:`), match(`[0-9]+`)))
35 35
 
36 36
 	// TagRegexp matches valid tag names. From docker/docker:graph/tags.go.
... ...
@@ -48,17 +48,17 @@ var (
48 48
 	anchoredDigestRegexp = anchored(DigestRegexp)
49 49
 
50 50
 	// NameRegexp is the format for the name component of references. The
51
-	// regexp has capturing groups for the hostname and name part omitting
51
+	// regexp has capturing groups for the domain and name part omitting
52 52
 	// the separating forward slash from either.
53 53
 	NameRegexp = expression(
54
-		optional(hostnameRegexp, literal(`/`)),
54
+		optional(domainRegexp, literal(`/`)),
55 55
 		nameComponentRegexp,
56 56
 		optional(repeated(literal(`/`), nameComponentRegexp)))
57 57
 
58 58
 	// anchoredNameRegexp is used to parse a name value, capturing the
59
-	// hostname and trailing components.
59
+	// domain and trailing components.
60 60
 	anchoredNameRegexp = anchored(
61
-		optional(capture(hostnameRegexp), literal(`/`)),
61
+		optional(capture(domainRegexp), literal(`/`)),
62 62
 		capture(nameComponentRegexp,
63 63
 			optional(repeated(literal(`/`), nameComponentRegexp))))
64 64
 
... ...
@@ -68,6 +68,25 @@ var (
68 68
 	ReferenceRegexp = anchored(capture(NameRegexp),
69 69
 		optional(literal(":"), capture(TagRegexp)),
70 70
 		optional(literal("@"), capture(DigestRegexp)))
71
+
72
+	// IdentifierRegexp is the format for string identifier used as a
73
+	// content addressable identifier using sha256. These identifiers
74
+	// are like digests without the algorithm, since sha256 is used.
75
+	IdentifierRegexp = match(`([a-f0-9]{64})`)
76
+
77
+	// ShortIdentifierRegexp is the format used to represent a prefix
78
+	// of an identifier. A prefix may be used to match a sha256 identifier
79
+	// within a list of trusted identifiers.
80
+	ShortIdentifierRegexp = match(`([a-f0-9]{6,64})`)
81
+
82
+	// anchoredIdentifierRegexp is used to check or match an
83
+	// identifier value, anchored at start and end of string.
84
+	anchoredIdentifierRegexp = anchored(IdentifierRegexp)
85
+
86
+	// anchoredShortIdentifierRegexp is used to check if a value
87
+	// is a possible identifier prefix, anchored at start and end
88
+	// of string.
89
+	anchoredShortIdentifierRegexp = anchored(ShortIdentifierRegexp)
71 90
 )
72 91
 
73 92
 // match compiles the string to a regular expression.