Browse code

Update Named reference with validation of conversions

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>

Tonis Tiigi authored on 2015/12/12 04:00:13
Showing 25 changed files
... ...
@@ -260,15 +260,11 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
260 260
 
261 261
 // validateTag checks if the given image name can be resolved.
262 262
 func validateTag(rawRepo string) (string, error) {
263
-	ref, err := reference.ParseNamed(rawRepo)
263
+	_, err := reference.ParseNamed(rawRepo)
264 264
 	if err != nil {
265 265
 		return "", err
266 266
 	}
267 267
 
268
-	if err := registry.ValidateRepositoryName(ref); err != nil {
269
-		return "", err
270
-	}
271
-
272 268
 	return rawRepo, nil
273 269
 }
274 270
 
... ...
@@ -10,7 +10,6 @@ import (
10 10
 	"github.com/docker/docker/opts"
11 11
 	flag "github.com/docker/docker/pkg/mflag"
12 12
 	"github.com/docker/docker/reference"
13
-	"github.com/docker/docker/registry"
14 13
 	"github.com/docker/docker/runconfig"
15 14
 )
16 15
 
... ...
@@ -44,9 +43,6 @@ func (cli *DockerCli) CmdCommit(args ...string) error {
44 44
 		if err != nil {
45 45
 			return err
46 46
 		}
47
-		if err := registry.ValidateRepositoryName(ref); err != nil {
48
-			return err
49
-		}
50 47
 
51 48
 		repositoryName = ref.Name()
52 49
 
... ...
@@ -12,7 +12,6 @@ import (
12 12
 	flag "github.com/docker/docker/pkg/mflag"
13 13
 	"github.com/docker/docker/pkg/urlutil"
14 14
 	"github.com/docker/docker/reference"
15
-	"github.com/docker/docker/registry"
16 15
 )
17 16
 
18 17
 // CmdImport creates an empty filesystem image, imports the contents of the tarball into the image, and optionally tags the image.
... ...
@@ -45,11 +44,7 @@ func (cli *DockerCli) CmdImport(args ...string) error {
45 45
 
46 46
 	if repository != "" {
47 47
 		//Check if the given image name can be resolved
48
-		ref, err := reference.ParseNamed(repository)
49
-		if err != nil {
50
-			return err
51
-		}
52
-		if err := registry.ValidateRepositoryName(ref); err != nil {
48
+		if _, err := reference.ParseNamed(repository); err != nil {
53 49
 			return err
54 50
 		}
55 51
 	}
... ...
@@ -7,7 +7,6 @@ import (
7 7
 	Cli "github.com/docker/docker/cli"
8 8
 	flag "github.com/docker/docker/pkg/mflag"
9 9
 	"github.com/docker/docker/reference"
10
-	"github.com/docker/docker/registry"
11 10
 )
12 11
 
13 12
 // CmdTag tags an image into a repository.
... ...
@@ -35,11 +34,6 @@ func (cli *DockerCli) CmdTag(args ...string) error {
35 35
 		tag = tagged.Tag()
36 36
 	}
37 37
 
38
-	//Check if the given image name can be resolved
39
-	if err := registry.ValidateRepositoryName(ref); err != nil {
40
-		return err
41
-	}
42
-
43 38
 	options := types.ImageTagOptions{
44 39
 		ImageID:        cmd.Arg(0),
45 40
 		RepositoryName: ref.Name(),
... ...
@@ -167,12 +167,12 @@ func (cli *DockerCli) getNotaryRepository(repoInfo *registry.RepositoryInfo, aut
167 167
 	}
168 168
 
169 169
 	creds := simpleCredentialStore{auth: authConfig}
170
-	tokenHandler := auth.NewTokenHandler(authTransport, creds, repoInfo.CanonicalName.Name(), "push", "pull")
170
+	tokenHandler := auth.NewTokenHandler(authTransport, creds, repoInfo.FullName(), "push", "pull")
171 171
 	basicHandler := auth.NewBasicHandler(creds)
172 172
 	modifiers = append(modifiers, transport.RequestModifier(auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler)))
173 173
 	tr := transport.NewTransport(base, modifiers...)
174 174
 
175
-	return client.NewNotaryRepository(cli.trustDirectory(), repoInfo.CanonicalName.Name(), server, tr, cli.getPassphraseRetriever())
175
+	return client.NewNotaryRepository(cli.trustDirectory(), repoInfo.FullName(), server, tr, cli.getPassphraseRetriever())
176 176
 }
177 177
 
178 178
 func convertTarget(t client.Target) (target, error) {
... ...
@@ -298,7 +298,7 @@ func (cli *DockerCli) trustedPull(repoInfo *registry.RepositoryInfo, ref registr
298 298
 		for _, tgt := range targets {
299 299
 			t, err := convertTarget(*tgt)
300 300
 			if err != nil {
301
-				fmt.Fprintf(cli.out, "Skipping target for %q\n", repoInfo.LocalName)
301
+				fmt.Fprintf(cli.out, "Skipping target for %q\n", repoInfo.Name())
302 302
 				continue
303 303
 			}
304 304
 			refs = append(refs, t)
... ...
@@ -321,19 +321,19 @@ func (cli *DockerCli) trustedPull(repoInfo *registry.RepositoryInfo, ref registr
321 321
 		if displayTag != "" {
322 322
 			displayTag = ":" + displayTag
323 323
 		}
324
-		fmt.Fprintf(cli.out, "Pull (%d of %d): %s%s@%s\n", i+1, len(refs), repoInfo.LocalName, displayTag, r.digest)
324
+		fmt.Fprintf(cli.out, "Pull (%d of %d): %s%s@%s\n", i+1, len(refs), repoInfo.Name(), displayTag, r.digest)
325 325
 
326
-		if err := cli.imagePullPrivileged(authConfig, repoInfo.LocalName.Name(), r.digest.String(), requestPrivilege); err != nil {
326
+		if err := cli.imagePullPrivileged(authConfig, repoInfo.Name(), r.digest.String(), requestPrivilege); err != nil {
327 327
 			return err
328 328
 		}
329 329
 
330 330
 		// If reference is not trusted, tag by trusted reference
331 331
 		if !r.reference.HasDigest() {
332
-			tagged, err := reference.WithTag(repoInfo.LocalName, r.reference.String())
332
+			tagged, err := reference.WithTag(repoInfo, r.reference.String())
333 333
 			if err != nil {
334 334
 				return err
335 335
 			}
336
-			trustedRef, err := reference.WithDigest(repoInfo.LocalName, r.digest)
336
+			trustedRef, err := reference.WithDigest(repoInfo, r.digest)
337 337
 			if err := cli.tagTrusted(trustedRef, tagged); err != nil {
338 338
 				return err
339 339
 
... ...
@@ -384,7 +384,7 @@ func targetStream(in io.Writer) (io.WriteCloser, <-chan []target) {
384 384
 func (cli *DockerCli) trustedPush(repoInfo *registry.RepositoryInfo, tag string, authConfig types.AuthConfig, requestPrivilege lib.RequestPrivilegeFunc) error {
385 385
 	streamOut, targetChan := targetStream(cli.out)
386 386
 
387
-	reqError := cli.imagePushPrivileged(authConfig, repoInfo.LocalName.Name(), tag, streamOut, requestPrivilege)
387
+	reqError := cli.imagePushPrivileged(authConfig, repoInfo.Name(), tag, streamOut, requestPrivilege)
388 388
 
389 389
 	// Close stream channel to finish target parsing
390 390
 	if err := streamOut.Close(); err != nil {
... ...
@@ -455,7 +455,7 @@ func (cli *DockerCli) trustedPush(repoInfo *registry.RepositoryInfo, tag string,
455 455
 	if err := repo.Initialize(rootKeyID); err != nil {
456 456
 		return notaryError(err)
457 457
 	}
458
-	fmt.Fprintf(cli.out, "Finished initializing %q\n", repoInfo.CanonicalName)
458
+	fmt.Fprintf(cli.out, "Finished initializing %q\n", repoInfo.FullName())
459 459
 
460 460
 	return notaryError(repo.Publish())
461 461
 }
... ...
@@ -1043,7 +1043,6 @@ func (daemon *Daemon) TagImage(newTag reference.Named, imageName string) error {
1043 1043
 	if err != nil {
1044 1044
 		return err
1045 1045
 	}
1046
-	newTag = registry.NormalizeLocalReference(newTag)
1047 1046
 	if err := daemon.referenceStore.AddTag(newTag, imageID, true); err != nil {
1048 1047
 		return err
1049 1048
 	}
... ...
@@ -1301,7 +1300,6 @@ func (daemon *Daemon) GetImageID(refOrID string) (image.ID, error) {
1301 1301
 
1302 1302
 	// Treat it as a possible tag or digest reference
1303 1303
 	if ref, err := reference.ParseNamed(refOrID); err == nil {
1304
-		ref = registry.NormalizeLocalReference(ref)
1305 1304
 		if id, err := daemon.referenceStore.Get(ref); err == nil {
1306 1305
 			return id, nil
1307 1306
 		}
... ...
@@ -87,17 +87,15 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo
87 87
 	}
88 88
 
89 89
 	// makes sure name is not empty or `scratch`
90
-	if err := validateRepoName(repoInfo.LocalName.Name()); err != nil {
90
+	if err := validateRepoName(repoInfo.Name()); err != nil {
91 91
 		return err
92 92
 	}
93 93
 
94
-	endpoints, err := imagePullConfig.RegistryService.LookupPullEndpoints(repoInfo.CanonicalName)
94
+	endpoints, err := imagePullConfig.RegistryService.LookupPullEndpoints(repoInfo)
95 95
 	if err != nil {
96 96
 		return err
97 97
 	}
98 98
 
99
-	localName := registry.NormalizeLocalReference(ref)
100
-
101 99
 	var (
102 100
 		// use a slice to append the error strings and return a joined string to caller
103 101
 		errors []string
... ...
@@ -112,7 +110,7 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo
112 112
 		discardNoSupportErrors bool
113 113
 	)
114 114
 	for _, endpoint := range endpoints {
115
-		logrus.Debugf("Trying to pull %s from %s %s", repoInfo.LocalName, endpoint.URL, endpoint.Version)
115
+		logrus.Debugf("Trying to pull %s from %s %s", repoInfo.Name(), endpoint.URL, endpoint.Version)
116 116
 
117 117
 		puller, err := newPuller(endpoint, repoInfo, imagePullConfig)
118 118
 		if err != nil {
... ...
@@ -148,7 +146,7 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo
148 148
 			}
149 149
 		}
150 150
 
151
-		imagePullConfig.EventsService.Log("pull", localName.String(), "")
151
+		imagePullConfig.EventsService.Log("pull", ref.String(), "")
152 152
 		return nil
153 153
 	}
154 154
 
... ...
@@ -65,18 +65,18 @@ func (p *v1Puller) Pull(ctx context.Context, ref reference.Named) (fallback bool
65 65
 		// TODO(dmcgowan): Check if should fallback
66 66
 		return false, err
67 67
 	}
68
-	progress.Message(p.config.ProgressOutput, "", p.repoInfo.CanonicalName.Name()+": this image was pulled from a legacy registry.  Important: This registry version will not be supported in future versions of docker.")
68
+	progress.Message(p.config.ProgressOutput, "", p.repoInfo.FullName()+": this image was pulled from a legacy registry.  Important: This registry version will not be supported in future versions of docker.")
69 69
 
70 70
 	return false, nil
71 71
 }
72 72
 
73 73
 func (p *v1Puller) pullRepository(ctx context.Context, ref reference.Named) error {
74
-	progress.Message(p.config.ProgressOutput, "", "Pulling repository "+p.repoInfo.CanonicalName.Name())
74
+	progress.Message(p.config.ProgressOutput, "", "Pulling repository "+p.repoInfo.FullName())
75 75
 
76
-	repoData, err := p.session.GetRepositoryData(p.repoInfo.RemoteName)
76
+	repoData, err := p.session.GetRepositoryData(p.repoInfo)
77 77
 	if err != nil {
78 78
 		if strings.Contains(err.Error(), "HTTP code: 404") {
79
-			return fmt.Errorf("Error: image %s not found", p.repoInfo.RemoteName.Name())
79
+			return fmt.Errorf("Error: image %s not found", p.repoInfo.RemoteName())
80 80
 		}
81 81
 		// Unexpected HTTP error
82 82
 		return err
... ...
@@ -86,13 +86,13 @@ func (p *v1Puller) pullRepository(ctx context.Context, ref reference.Named) erro
86 86
 	var tagsList map[string]string
87 87
 	tagged, isTagged := ref.(reference.NamedTagged)
88 88
 	if !isTagged {
89
-		tagsList, err = p.session.GetRemoteTags(repoData.Endpoints, p.repoInfo.RemoteName)
89
+		tagsList, err = p.session.GetRemoteTags(repoData.Endpoints, p.repoInfo)
90 90
 	} else {
91 91
 		var tagID string
92 92
 		tagsList = make(map[string]string)
93
-		tagID, err = p.session.GetRemoteTag(repoData.Endpoints, p.repoInfo.RemoteName, tagged.Tag())
93
+		tagID, err = p.session.GetRemoteTag(repoData.Endpoints, p.repoInfo, tagged.Tag())
94 94
 		if err == registry.ErrRepoNotFound {
95
-			return fmt.Errorf("Tag %s not found in repository %s", tagged.Tag(), p.repoInfo.CanonicalName.Name())
95
+			return fmt.Errorf("Tag %s not found in repository %s", tagged.Tag(), p.repoInfo.FullName())
96 96
 		}
97 97
 		tagsList[tagged.Tag()] = tagID
98 98
 	}
... ...
@@ -121,14 +121,7 @@ func (p *v1Puller) pullRepository(ctx context.Context, ref reference.Named) erro
121 121
 		}
122 122
 	}
123 123
 
124
-	localNameRef := p.repoInfo.LocalName
125
-	if isTagged {
126
-		localNameRef, err = reference.WithTag(localNameRef, tagged.Tag())
127
-		if err != nil {
128
-			localNameRef = p.repoInfo.LocalName
129
-		}
130
-	}
131
-	writeStatus(localNameRef.String(), p.config.ProgressOutput, layersDownloaded)
124
+	writeStatus(ref.String(), p.config.ProgressOutput, layersDownloaded)
132 125
 	return nil
133 126
 }
134 127
 
... ...
@@ -138,7 +131,7 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit
138 138
 		return nil
139 139
 	}
140 140
 
141
-	localNameRef, err := reference.WithTag(p.repoInfo.LocalName, img.Tag)
141
+	localNameRef, err := reference.WithTag(p.repoInfo, img.Tag)
142 142
 	if err != nil {
143 143
 		retErr := fmt.Errorf("Image (id: %s) has invalid tag: %s", img.ID, img.Tag)
144 144
 		logrus.Debug(retErr.Error())
... ...
@@ -149,15 +142,15 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit
149 149
 		return err
150 150
 	}
151 151
 
152
-	progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s", img.Tag, p.repoInfo.CanonicalName.Name())
152
+	progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s", img.Tag, p.repoInfo.FullName())
153 153
 	success := false
154 154
 	var lastErr error
155 155
 	for _, ep := range p.repoInfo.Index.Mirrors {
156 156
 		ep += "v1/"
157
-		progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), fmt.Sprintf("Pulling image (%s) from %s, mirror: %s", img.Tag, p.repoInfo.CanonicalName.Name(), ep))
157
+		progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), fmt.Sprintf("Pulling image (%s) from %s, mirror: %s", img.Tag, p.repoInfo.FullName(), ep))
158 158
 		if err = p.pullImage(ctx, img.ID, ep, localNameRef, layersDownloaded); err != nil {
159 159
 			// Don't report errors when pulling from mirrors.
160
-			logrus.Debugf("Error pulling image (%s) from %s, mirror: %s, %s", img.Tag, p.repoInfo.CanonicalName.Name(), ep, err)
160
+			logrus.Debugf("Error pulling image (%s) from %s, mirror: %s, %s", img.Tag, p.repoInfo.FullName(), ep, err)
161 161
 			continue
162 162
 		}
163 163
 		success = true
... ...
@@ -165,12 +158,12 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit
165 165
 	}
166 166
 	if !success {
167 167
 		for _, ep := range repoData.Endpoints {
168
-			progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s, endpoint: %s", img.Tag, p.repoInfo.CanonicalName.Name(), ep)
168
+			progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s, endpoint: %s", img.Tag, p.repoInfo.FullName(), ep)
169 169
 			if err = p.pullImage(ctx, img.ID, ep, localNameRef, layersDownloaded); err != nil {
170 170
 				// It's not ideal that only the last error is returned, it would be better to concatenate the errors.
171 171
 				// As the error is also given to the output stream the user will see the error.
172 172
 				lastErr = err
173
-				progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Error pulling image (%s) from %s, endpoint: %s, %s", img.Tag, p.repoInfo.CanonicalName.Name(), ep, err)
173
+				progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Error pulling image (%s) from %s, endpoint: %s, %s", img.Tag, p.repoInfo.FullName(), ep, err)
174 174
 				continue
175 175
 			}
176 176
 			success = true
... ...
@@ -178,7 +171,7 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit
178 178
 		}
179 179
 	}
180 180
 	if !success {
181
-		err := fmt.Errorf("Error pulling image (%s) from %s, %v", img.Tag, p.repoInfo.CanonicalName.Name(), lastErr)
181
+		err := fmt.Errorf("Error pulling image (%s) from %s, %v", img.Tag, p.repoInfo.FullName(), lastErr)
182 182
 		progress.Update(p.config.ProgressOutput, stringid.TruncateID(img.ID), err.Error())
183 183
 		return err
184 184
 	}
... ...
@@ -54,19 +54,11 @@ func (p *v2Puller) Pull(ctx context.Context, ref reference.Named) (fallback bool
54 54
 
55 55
 func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named) (err error) {
56 56
 	var refs []reference.Named
57
-	taggedName := p.repoInfo.LocalName
58
-	if tagged, isTagged := ref.(reference.NamedTagged); isTagged {
59
-		taggedName, err = reference.WithTag(p.repoInfo.LocalName, tagged.Tag())
60
-		if err != nil {
61
-			return err
62
-		}
63
-		refs = []reference.Named{taggedName}
64
-	} else if digested, isCanonical := ref.(reference.Canonical); isCanonical {
65
-		taggedName, err = reference.WithDigest(p.repoInfo.LocalName, digested.Digest())
66
-		if err != nil {
67
-			return err
68
-		}
69
-		refs = []reference.Named{taggedName}
57
+	taggedName := ref
58
+	if _, isTagged := ref.(reference.NamedTagged); isTagged {
59
+		refs = []reference.Named{ref}
60
+	} else if _, isCanonical := ref.(reference.Canonical); isCanonical {
61
+		refs = []reference.Named{ref}
70 62
 	} else {
71 63
 		manSvc, err := p.repo.Manifests(ctx)
72 64
 		if err != nil {
... ...
@@ -81,7 +73,7 @@ func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named) (e
81 81
 		// This probably becomes a lot nicer after the manifest
82 82
 		// refactor...
83 83
 		for _, tag := range tags {
84
-			tagRef, err := reference.WithTag(p.repoInfo.LocalName, tag)
84
+			tagRef, err := reference.WithTag(ref, tag)
85 85
 			if err != nil {
86 86
 				return err
87 87
 			}
... ...
@@ -291,7 +283,7 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdat
291 291
 		return false, err
292 292
 	}
293 293
 
294
-	manifestDigest, _, err := digestFromManifest(unverifiedManifest, p.repoInfo.LocalName.Name())
294
+	manifestDigest, _, err := digestFromManifest(unverifiedManifest, p.repoInfo)
295 295
 	if err != nil {
296 296
 		return false, err
297 297
 	}
... ...
@@ -104,21 +104,21 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo
104 104
 		return err
105 105
 	}
106 106
 
107
-	endpoints, err := imagePushConfig.RegistryService.LookupPushEndpoints(repoInfo.CanonicalName)
107
+	endpoints, err := imagePushConfig.RegistryService.LookupPushEndpoints(repoInfo)
108 108
 	if err != nil {
109 109
 		return err
110 110
 	}
111 111
 
112
-	progress.Messagef(imagePushConfig.ProgressOutput, "", "The push refers to a repository [%s]", repoInfo.CanonicalName.String())
112
+	progress.Messagef(imagePushConfig.ProgressOutput, "", "The push refers to a repository [%s]", repoInfo.FullName())
113 113
 
114
-	associations := imagePushConfig.ReferenceStore.ReferencesByName(repoInfo.LocalName)
114
+	associations := imagePushConfig.ReferenceStore.ReferencesByName(repoInfo)
115 115
 	if len(associations) == 0 {
116
-		return fmt.Errorf("Repository does not exist: %s", repoInfo.LocalName)
116
+		return fmt.Errorf("Repository does not exist: %s", repoInfo.Name())
117 117
 	}
118 118
 
119 119
 	var lastErr error
120 120
 	for _, endpoint := range endpoints {
121
-		logrus.Debugf("Trying to push %s to %s %s", repoInfo.CanonicalName, endpoint.URL, endpoint.Version)
121
+		logrus.Debugf("Trying to push %s to %s %s", repoInfo.FullName(), endpoint.URL, endpoint.Version)
122 122
 
123 123
 		pusher, err := NewPusher(ref, endpoint, repoInfo, imagePushConfig)
124 124
 		if err != nil {
... ...
@@ -143,12 +143,12 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo
143 143
 
144 144
 		}
145 145
 
146
-		imagePushConfig.EventsService.Log("push", repoInfo.LocalName.Name(), "")
146
+		imagePushConfig.EventsService.Log("push", repoInfo.Name(), "")
147 147
 		return nil
148 148
 	}
149 149
 
150 150
 	if lastErr == nil {
151
-		lastErr = fmt.Errorf("no endpoints found for %s", repoInfo.CanonicalName)
151
+		lastErr = fmt.Errorf("no endpoints found for %s", repoInfo.FullName())
152 152
 	}
153 153
 	return lastErr
154 154
 }
... ...
@@ -351,8 +351,8 @@ func (p *v1Pusher) pushImageToEndpoint(ctx context.Context, endpoint string, ima
351 351
 		}
352 352
 		if topImage, isTopImage := img.(*v1TopImage); isTopImage {
353 353
 			for _, tag := range tags[topImage.imageID] {
354
-				progress.Messagef(p.config.ProgressOutput, "", "Pushing tag for rev [%s] on {%s}", stringid.TruncateID(v1ID), endpoint+"repositories/"+p.repoInfo.RemoteName.Name()+"/tags/"+tag)
355
-				if err := p.session.PushRegistryTag(p.repoInfo.RemoteName, v1ID, tag, endpoint); err != nil {
354
+				progress.Messagef(p.config.ProgressOutput, "", "Pushing tag for rev [%s] on {%s}", stringid.TruncateID(v1ID), endpoint+"repositories/"+p.repoInfo.RemoteName()+"/tags/"+tag)
355
+				if err := p.session.PushRegistryTag(p.repoInfo, v1ID, tag, endpoint); err != nil {
356 356
 					return err
357 357
 				}
358 358
 			}
... ...
@@ -381,18 +381,18 @@ func (p *v1Pusher) pushRepository(ctx context.Context) error {
381 381
 
382 382
 	// Register all the images in a repository with the registry
383 383
 	// If an image is not in this list it will not be associated with the repository
384
-	repoData, err := p.session.PushImageJSONIndex(p.repoInfo.RemoteName, imageIndex, false, nil)
384
+	repoData, err := p.session.PushImageJSONIndex(p.repoInfo, imageIndex, false, nil)
385 385
 	if err != nil {
386 386
 		return err
387 387
 	}
388
-	progress.Message(p.config.ProgressOutput, "", "Pushing repository "+p.repoInfo.CanonicalName.String())
388
+	progress.Message(p.config.ProgressOutput, "", "Pushing repository "+p.repoInfo.FullName())
389 389
 	// push the repository to each of the endpoints only if it does not exist.
390 390
 	for _, endpoint := range repoData.Endpoints {
391 391
 		if err := p.pushImageToEndpoint(ctx, endpoint, imgList, tags, repoData); err != nil {
392 392
 			return err
393 393
 		}
394 394
 	}
395
-	_, err = p.session.PushImageJSONIndex(p.repoInfo.RemoteName, imageIndex, true, repoData.Endpoints)
395
+	_, err = p.session.PushImageJSONIndex(p.repoInfo, imageIndex, true, repoData.Endpoints)
396 396
 	return err
397 397
 }
398 398
 
... ...
@@ -52,8 +52,6 @@ func (p *v2Pusher) Push(ctx context.Context) (fallback bool, err error) {
52 52
 		return true, err
53 53
 	}
54 54
 
55
-	localName := p.repoInfo.LocalName.Name()
56
-
57 55
 	var associations []reference.Association
58 56
 	if _, isTagged := p.ref.(reference.NamedTagged); isTagged {
59 57
 		imageID, err := p.config.ReferenceStore.Get(p.ref)
... ...
@@ -72,10 +70,10 @@ func (p *v2Pusher) Push(ctx context.Context) (fallback bool, err error) {
72 72
 		associations = p.config.ReferenceStore.ReferencesByName(p.ref)
73 73
 	}
74 74
 	if err != nil {
75
-		return false, fmt.Errorf("error getting tags for %s: %s", localName, err)
75
+		return false, fmt.Errorf("error getting tags for %s: %s", p.repoInfo.Name(), err)
76 76
 	}
77 77
 	if len(associations) == 0 {
78
-		return false, fmt.Errorf("no tags to push for %s", localName)
78
+		return false, fmt.Errorf("no tags to push for %s", p.repoInfo.Name())
79 79
 	}
80 80
 
81 81
 	for _, association := range associations {
... ...
@@ -159,7 +157,7 @@ func (p *v2Pusher) pushV2Tag(ctx context.Context, association reference.Associat
159 159
 		return err
160 160
 	}
161 161
 
162
-	manifestDigest, manifestSize, err := digestFromManifest(signed, p.repo.Name())
162
+	manifestDigest, manifestSize, err := digestFromManifest(signed, ref)
163 163
 	if err != nil {
164 164
 		return err
165 165
 	}
... ...
@@ -19,6 +19,7 @@ import (
19 19
 	"github.com/docker/distribution/registry/client/transport"
20 20
 	"github.com/docker/docker/api/types"
21 21
 	"github.com/docker/docker/distribution/xfer"
22
+	"github.com/docker/docker/reference"
22 23
 	"github.com/docker/docker/registry"
23 24
 	"golang.org/x/net/context"
24 25
 )
... ...
@@ -37,10 +38,10 @@ func (dcs dumbCredentialStore) Basic(*url.URL) (string, string) {
37 37
 func NewV2Repository(repoInfo *registry.RepositoryInfo, endpoint registry.APIEndpoint, metaHeaders http.Header, authConfig *types.AuthConfig, actions ...string) (distribution.Repository, error) {
38 38
 	ctx := context.Background()
39 39
 
40
-	repoName := repoInfo.CanonicalName
40
+	repoName := repoInfo.FullName()
41 41
 	// If endpoint does not support CanonicalName, use the RemoteName instead
42 42
 	if endpoint.TrimHostname {
43
-		repoName = repoInfo.RemoteName
43
+		repoName = repoInfo.RemoteName()
44 44
 	}
45 45
 
46 46
 	// TODO(dmcgowan): Call close idle connections when complete, use keep alive
... ...
@@ -99,16 +100,16 @@ func NewV2Repository(repoInfo *registry.RepositoryInfo, endpoint registry.APIEnd
99 99
 		modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, passThruTokenHandler))
100 100
 	} else {
101 101
 		creds := dumbCredentialStore{auth: authConfig}
102
-		tokenHandler := auth.NewTokenHandler(authTransport, creds, repoName.Name(), actions...)
102
+		tokenHandler := auth.NewTokenHandler(authTransport, creds, repoName, actions...)
103 103
 		basicHandler := auth.NewBasicHandler(creds)
104 104
 		modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler))
105 105
 	}
106 106
 	tr := transport.NewTransport(base, modifiers...)
107 107
 
108
-	return client.NewRepository(ctx, repoName.Name(), endpoint.URL, tr)
108
+	return client.NewRepository(ctx, repoName, endpoint.URL, tr)
109 109
 }
110 110
 
111
-func digestFromManifest(m *schema1.SignedManifest, localName string) (digest.Digest, int, error) {
111
+func digestFromManifest(m *schema1.SignedManifest, name reference.Named) (digest.Digest, int, error) {
112 112
 	payload, err := m.Payload()
113 113
 	if err != nil {
114 114
 		// If this failed, the signatures section was corrupted
... ...
@@ -117,7 +118,7 @@ func digestFromManifest(m *schema1.SignedManifest, localName string) (digest.Dig
117 117
 	}
118 118
 	manifestDigest, err := digest.FromBytes(payload)
119 119
 	if err != nil {
120
-		logrus.Infof("Could not compute manifest digest for %s:%s : %v", localName, m.Tag, err)
120
+		logrus.Infof("Could not compute manifest digest for %s:%s : %v", name.Name(), m.Tag, err)
121 121
 	}
122 122
 	return manifestDigest, len(payload), nil
123 123
 }
... ...
@@ -58,16 +58,14 @@ func TestTokenPassThru(t *testing.T) {
58 58
 	}
59 59
 	n, _ := reference.ParseNamed("testremotename")
60 60
 	repoInfo := &registry.RepositoryInfo{
61
+		Named: n,
61 62
 		Index: &registrytypes.IndexInfo{
62 63
 			Name:     "testrepo",
63 64
 			Mirrors:  nil,
64 65
 			Secure:   false,
65 66
 			Official: false,
66 67
 		},
67
-		RemoteName:    n,
68
-		LocalName:     n,
69
-		CanonicalName: n,
70
-		Official:      false,
68
+		Official: false,
71 69
 	}
72 70
 	imagePullConfig := &ImagePullConfig{
73 71
 		MetaHeaders: http.Header{},
... ...
@@ -15,7 +15,6 @@ import (
15 15
 	"github.com/docker/docker/layer"
16 16
 	"github.com/docker/docker/pkg/archive"
17 17
 	"github.com/docker/docker/reference"
18
-	"github.com/docker/docker/registry"
19 18
 )
20 19
 
21 20
 type imageDescriptor struct {
... ...
@@ -74,7 +73,6 @@ func (l *tarexporter) parseNames(names []string) (map[image.ID]*imageDescriptor,
74 74
 		if err != nil {
75 75
 			return nil, err
76 76
 		}
77
-		ref = registry.NormalizeLocalReference(ref)
78 77
 		if ref.Name() == string(digest.Canonical) {
79 78
 			imgID, err := l.is.Search(name)
80 79
 			if err != nil {
... ...
@@ -1,21 +1,37 @@
1 1
 package reference
2 2
 
3 3
 import (
4
+	"fmt"
5
+	"strings"
6
+
4 7
 	"github.com/docker/distribution/digest"
5 8
 	distreference "github.com/docker/distribution/reference"
9
+	"github.com/docker/docker/image/v1"
6 10
 )
7 11
 
8
-// Reference is an opaque object reference identifier that may include
9
-// modifiers such as a hostname, name, tag, and digest.
10
-type Reference interface {
11
-	// String returns the full reference
12
-	String() string
13
-}
12
+const (
13
+	// DefaultTag defines the default tag used when performing images related actions and no tag or digest is specified
14
+	DefaultTag = "latest"
15
+	// DefaultHostname is the default built-in hostname
16
+	DefaultHostname = "docker.io"
17
+	// LegacyDefaultHostname is automatically converted to DefaultHostname
18
+	LegacyDefaultHostname = "index.docker.io"
19
+	// DefaultRepoPrefix is the prefix used for default repositories in default host
20
+	DefaultRepoPrefix = "library/"
21
+)
14 22
 
15 23
 // Named is an object with a full name
16 24
 type Named interface {
17
-	Reference
25
+	// Name returns normalized repository name, like "ubuntu".
18 26
 	Name() string
27
+	// String returns full reference, like "ubuntu@sha256:abcdef..."
28
+	String() string
29
+	// FullName returns full repository name with hostname, like "docker.io/library/ubuntu"
30
+	FullName() string
31
+	// Hostname returns hostname for the reference, like "docker.io"
32
+	Hostname() string
33
+	// RemoteName returns the repository component of the full name, like "library/ubuntu"
34
+	RemoteName() string
19 35
 }
20 36
 
21 37
 // NamedTagged is an object including a name and tag.
... ...
@@ -35,26 +51,122 @@ type Canonical interface {
35 35
 // the Named interface. The reference must have a name, otherwise an error is
36 36
 // returned.
37 37
 // If an error was encountered it is returned, along with a nil Reference.
38
-// NOTE: ParseNamed will not handle short digests.
39 38
 func ParseNamed(s string) (Named, error) {
40
-	// todo: docker specific validation
41
-	return distreference.ParseNamed(s)
39
+	named, err := distreference.ParseNamed(s)
40
+	if err != nil {
41
+		return nil, err
42
+	}
43
+	r, err := WithName(named.Name())
44
+	if err != nil {
45
+		return nil, err
46
+	}
47
+	if canonical, isCanonical := named.(distreference.Canonical); isCanonical {
48
+		return WithDigest(r, canonical.Digest())
49
+	}
50
+	if tagged, isTagged := named.(distreference.NamedTagged); isTagged {
51
+		return WithTag(r, tagged.Tag())
52
+	}
53
+	return r, nil
42 54
 }
43 55
 
44 56
 // WithName returns a named object representing the given string. If the input
45 57
 // is invalid ErrReferenceInvalidFormat will be returned.
46 58
 func WithName(name string) (Named, error) {
47
-	return distreference.WithName(name)
59
+	name = normalize(name)
60
+	if err := validateName(name); err != nil {
61
+		return nil, err
62
+	}
63
+	r, err := distreference.WithName(name)
64
+	if err != nil {
65
+		return nil, err
66
+	}
67
+	return &namedRef{r}, nil
48 68
 }
49 69
 
50 70
 // WithTag combines the name from "name" and the tag from "tag" to form a
51 71
 // reference incorporating both the name and the tag.
52 72
 func WithTag(name Named, tag string) (NamedTagged, error) {
53
-	return distreference.WithTag(name, tag)
73
+	r, err := distreference.WithTag(name, tag)
74
+	if err != nil {
75
+		return nil, err
76
+	}
77
+	return &taggedRef{namedRef{r}}, nil
54 78
 }
55 79
 
56 80
 // WithDigest combines the name from "name" and the digest from "digest" to form
57 81
 // a reference incorporating both the name and the digest.
58 82
 func WithDigest(name Named, digest digest.Digest) (Canonical, error) {
59
-	return distreference.WithDigest(name, digest)
83
+	r, err := distreference.WithDigest(name, digest)
84
+	if err != nil {
85
+		return nil, err
86
+	}
87
+	return &canonicalRef{namedRef{r}}, nil
88
+}
89
+
90
+type namedRef struct {
91
+	distreference.Named
92
+}
93
+type taggedRef struct {
94
+	namedRef
95
+}
96
+type canonicalRef struct {
97
+	namedRef
98
+}
99
+
100
+func (r *namedRef) FullName() string {
101
+	hostname, remoteName := splitHostname(r.Name())
102
+	return hostname + "/" + remoteName
103
+}
104
+func (r *namedRef) Hostname() string {
105
+	hostname, _ := splitHostname(r.Name())
106
+	return hostname
107
+}
108
+func (r *namedRef) RemoteName() string {
109
+	_, remoteName := splitHostname(r.Name())
110
+	return remoteName
111
+}
112
+func (r *taggedRef) Tag() string {
113
+	return r.namedRef.Named.(distreference.NamedTagged).Tag()
114
+}
115
+func (r *canonicalRef) Digest() digest.Digest {
116
+	return r.namedRef.Named.(distreference.Canonical).Digest()
117
+}
118
+
119
+// splitHostname splits a repository name to hostname and remotename string.
120
+// If no valid hostname is found, the default hostname is used. Repository name
121
+// needs to be already validated before.
122
+func splitHostname(name string) (hostname, remoteName string) {
123
+	i := strings.IndexRune(name, '/')
124
+	if i == -1 || (!strings.ContainsAny(name[:i], ".:") && name[:i] != "localhost") {
125
+		hostname, remoteName = DefaultHostname, name
126
+	} else {
127
+		hostname, remoteName = name[:i], name[i+1:]
128
+	}
129
+	if hostname == LegacyDefaultHostname {
130
+		hostname = DefaultHostname
131
+	}
132
+	if hostname == DefaultHostname && !strings.ContainsRune(remoteName, '/') {
133
+		remoteName = DefaultRepoPrefix + remoteName
134
+	}
135
+	return
136
+}
137
+
138
+// normalize returns a repository name in its normalized form, meaning it
139
+// will not contain default hostname nor library/ prefix for official images.
140
+func normalize(name string) string {
141
+	host, remoteName := splitHostname(name)
142
+	if host == DefaultHostname {
143
+		if strings.HasPrefix(remoteName, DefaultRepoPrefix) {
144
+			return strings.TrimPrefix(remoteName, DefaultRepoPrefix)
145
+		}
146
+		return remoteName
147
+	}
148
+	return name
149
+}
150
+
151
+func validateName(name string) error {
152
+	if err := v1.ValidateID(name); err == nil {
153
+		return fmt.Errorf("Invalid repository name (%s), cannot specify 64-byte hexadecimal strings", name)
154
+	}
155
+	return nil
60 156
 }
61 157
new file mode 100644
... ...
@@ -0,0 +1,275 @@
0
+package reference
1
+
2
+import (
3
+	"testing"
4
+
5
+	"github.com/docker/distribution/digest"
6
+)
7
+
8
+func TestValidateReferenceName(t *testing.T) {
9
+	validRepoNames := []string{
10
+		"docker/docker",
11
+		"library/debian",
12
+		"debian",
13
+		"docker.io/docker/docker",
14
+		"docker.io/library/debian",
15
+		"docker.io/debian",
16
+		"index.docker.io/docker/docker",
17
+		"index.docker.io/library/debian",
18
+		"index.docker.io/debian",
19
+		"127.0.0.1:5000/docker/docker",
20
+		"127.0.0.1:5000/library/debian",
21
+		"127.0.0.1:5000/debian",
22
+		"thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev",
23
+	}
24
+	invalidRepoNames := []string{
25
+		"https://github.com/docker/docker",
26
+		"docker/Docker",
27
+		"-docker",
28
+		"-docker/docker",
29
+		"-docker.io/docker/docker",
30
+		"docker///docker",
31
+		"docker.io/docker/Docker",
32
+		"docker.io/docker///docker",
33
+		"1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
34
+		"docker.io/1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
35
+	}
36
+
37
+	for _, name := range invalidRepoNames {
38
+		_, err := ParseNamed(name)
39
+		if err == nil {
40
+			t.Fatalf("Expected invalid repo name for %q", name)
41
+		}
42
+	}
43
+
44
+	for _, name := range validRepoNames {
45
+		_, err := ParseNamed(name)
46
+		if err != nil {
47
+			t.Fatalf("Error parsing repo name %s, got: %q", name, err)
48
+		}
49
+	}
50
+}
51
+
52
+func TestValidateRemoteName(t *testing.T) {
53
+	validRepositoryNames := []string{
54
+		// Sanity check.
55
+		"docker/docker",
56
+
57
+		// Allow 64-character non-hexadecimal names (hexadecimal names are forbidden).
58
+		"thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev",
59
+
60
+		// Allow embedded hyphens.
61
+		"docker-rules/docker",
62
+
63
+		// Allow multiple hyphens as well.
64
+		"docker---rules/docker",
65
+
66
+		//Username doc and image name docker being tested.
67
+		"doc/docker",
68
+
69
+		// single character names are now allowed.
70
+		"d/docker",
71
+		"jess/t",
72
+
73
+		// Consecutive underscores.
74
+		"dock__er/docker",
75
+	}
76
+	for _, repositoryName := range validRepositoryNames {
77
+		_, err := ParseNamed(repositoryName)
78
+		if err != nil {
79
+			t.Errorf("Repository name should be valid: %v. Error: %v", repositoryName, err)
80
+		}
81
+	}
82
+
83
+	invalidRepositoryNames := []string{
84
+		// Disallow capital letters.
85
+		"docker/Docker",
86
+
87
+		// Only allow one slash.
88
+		"docker///docker",
89
+
90
+		// Disallow 64-character hexadecimal.
91
+		"1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
92
+
93
+		// Disallow leading and trailing hyphens in namespace.
94
+		"-docker/docker",
95
+		"docker-/docker",
96
+		"-docker-/docker",
97
+
98
+		// Don't allow underscores everywhere (as opposed to hyphens).
99
+		"____/____",
100
+
101
+		"_docker/_docker",
102
+
103
+		// Disallow consecutive periods.
104
+		"dock..er/docker",
105
+		"dock_.er/docker",
106
+		"dock-.er/docker",
107
+
108
+		// No repository.
109
+		"docker/",
110
+
111
+		//namespace too long
112
+		"this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255/docker",
113
+	}
114
+	for _, repositoryName := range invalidRepositoryNames {
115
+		if _, err := ParseNamed(repositoryName); err == nil {
116
+			t.Errorf("Repository name should be invalid: %v", repositoryName)
117
+		}
118
+	}
119
+}
120
+
121
+func TestParseRepositoryInfo(t *testing.T) {
122
+	type tcase struct {
123
+		RemoteName, NormalizedName, FullName, AmbiguousName, Hostname string
124
+	}
125
+
126
+	tcases := []tcase{
127
+		{
128
+			RemoteName:     "fooo/bar",
129
+			NormalizedName: "fooo/bar",
130
+			FullName:       "docker.io/fooo/bar",
131
+			AmbiguousName:  "index.docker.io/fooo/bar",
132
+			Hostname:       "docker.io",
133
+		},
134
+		{
135
+			RemoteName:     "library/ubuntu",
136
+			NormalizedName: "ubuntu",
137
+			FullName:       "docker.io/library/ubuntu",
138
+			AmbiguousName:  "library/ubuntu",
139
+			Hostname:       "docker.io",
140
+		},
141
+		{
142
+			RemoteName:     "nonlibrary/ubuntu",
143
+			NormalizedName: "nonlibrary/ubuntu",
144
+			FullName:       "docker.io/nonlibrary/ubuntu",
145
+			AmbiguousName:  "",
146
+			Hostname:       "docker.io",
147
+		},
148
+		{
149
+			RemoteName:     "other/library",
150
+			NormalizedName: "other/library",
151
+			FullName:       "docker.io/other/library",
152
+			AmbiguousName:  "",
153
+			Hostname:       "docker.io",
154
+		},
155
+		{
156
+			RemoteName:     "private/moonbase",
157
+			NormalizedName: "127.0.0.1:8000/private/moonbase",
158
+			FullName:       "127.0.0.1:8000/private/moonbase",
159
+			AmbiguousName:  "",
160
+			Hostname:       "127.0.0.1:8000",
161
+		},
162
+		{
163
+			RemoteName:     "privatebase",
164
+			NormalizedName: "127.0.0.1:8000/privatebase",
165
+			FullName:       "127.0.0.1:8000/privatebase",
166
+			AmbiguousName:  "",
167
+			Hostname:       "127.0.0.1:8000",
168
+		},
169
+		{
170
+			RemoteName:     "private/moonbase",
171
+			NormalizedName: "example.com/private/moonbase",
172
+			FullName:       "example.com/private/moonbase",
173
+			AmbiguousName:  "",
174
+			Hostname:       "example.com",
175
+		},
176
+		{
177
+			RemoteName:     "privatebase",
178
+			NormalizedName: "example.com/privatebase",
179
+			FullName:       "example.com/privatebase",
180
+			AmbiguousName:  "",
181
+			Hostname:       "example.com",
182
+		},
183
+		{
184
+			RemoteName:     "private/moonbase",
185
+			NormalizedName: "example.com:8000/private/moonbase",
186
+			FullName:       "example.com:8000/private/moonbase",
187
+			AmbiguousName:  "",
188
+			Hostname:       "example.com:8000",
189
+		},
190
+		{
191
+			RemoteName:     "privatebasee",
192
+			NormalizedName: "example.com:8000/privatebasee",
193
+			FullName:       "example.com:8000/privatebasee",
194
+			AmbiguousName:  "",
195
+			Hostname:       "example.com:8000",
196
+		},
197
+		{
198
+			RemoteName:     "library/ubuntu-12.04-base",
199
+			NormalizedName: "ubuntu-12.04-base",
200
+			FullName:       "docker.io/library/ubuntu-12.04-base",
201
+			AmbiguousName:  "index.docker.io/library/ubuntu-12.04-base",
202
+			Hostname:       "docker.io",
203
+		},
204
+	}
205
+
206
+	for _, tcase := range tcases {
207
+		refStrings := []string{tcase.NormalizedName, tcase.FullName}
208
+		if tcase.AmbiguousName != "" {
209
+			refStrings = append(refStrings, tcase.AmbiguousName)
210
+		}
211
+
212
+		var refs []Named
213
+		for _, r := range refStrings {
214
+			named, err := ParseNamed(r)
215
+			if err != nil {
216
+				t.Fatal(err)
217
+			}
218
+			refs = append(refs, named)
219
+			named, err = WithName(r)
220
+			if err != nil {
221
+				t.Fatal(err)
222
+			}
223
+			refs = append(refs, named)
224
+		}
225
+
226
+		for _, r := range refs {
227
+			if expected, actual := tcase.NormalizedName, r.Name(); expected != actual {
228
+				t.Fatalf("Invalid normalized reference for %q. Expected %q, got %q", r, expected, actual)
229
+			}
230
+			if expected, actual := tcase.FullName, r.FullName(); expected != actual {
231
+				t.Fatalf("Invalid normalized reference for %q. Expected %q, got %q", r, expected, actual)
232
+			}
233
+			if expected, actual := tcase.Hostname, r.Hostname(); expected != actual {
234
+				t.Fatalf("Invalid hostname for %q. Expected %q, got %q", r, expected, actual)
235
+			}
236
+			if expected, actual := tcase.RemoteName, r.RemoteName(); expected != actual {
237
+				t.Fatalf("Invalid remoteName for %q. Expected %q, got %q", r, expected, actual)
238
+			}
239
+
240
+		}
241
+	}
242
+}
243
+
244
+func TestParseReferenceWithTagAndDigest(t *testing.T) {
245
+	ref, err := ParseNamed("busybox:latest@sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa")
246
+	if err != nil {
247
+		t.Fatal(err)
248
+	}
249
+	if _, isTagged := ref.(NamedTagged); isTagged {
250
+		t.Fatalf("Reference from %q should not support tag", ref)
251
+	}
252
+	if _, isCanonical := ref.(Canonical); !isCanonical {
253
+		t.Fatalf("Reference from %q should not support digest", ref)
254
+	}
255
+	if expected, actual := "busybox@sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa", ref.String(); actual != expected {
256
+		t.Fatalf("Invalid parsed reference for %q: expected %q, got %q", ref, expected, actual)
257
+	}
258
+}
259
+
260
+func TestInvalidReferenceComponents(t *testing.T) {
261
+	if _, err := WithName("-foo"); err == nil {
262
+		t.Fatal("Expected WithName to detect invalid name")
263
+	}
264
+	ref, err := WithName("busybox")
265
+	if err != nil {
266
+		t.Fatal(err)
267
+	}
268
+	if _, err := WithTag(ref, "-foo"); err == nil {
269
+		t.Fatal("Expected WithName to detect invalid tag")
270
+	}
271
+	if _, err := WithDigest(ref, digest.Digest("foo")); err == nil {
272
+		t.Fatal("Expected WithName to detect invalid digest")
273
+	}
274
+}
... ...
@@ -14,9 +14,6 @@ import (
14 14
 	"github.com/docker/docker/image"
15 15
 )
16 16
 
17
-// DefaultTag defines the default tag used when performing images related actions and no tag string is specified
18
-const DefaultTag = "latest"
19
-
20 17
 var (
21 18
 	// ErrDoesNotExist is returned if a reference is not found in the
22 19
 	// store.
... ...
@@ -7,9 +7,7 @@ import (
7 7
 	"net/url"
8 8
 	"strings"
9 9
 
10
-	distreference "github.com/docker/distribution/reference"
11 10
 	registrytypes "github.com/docker/docker/api/types/registry"
12
-	"github.com/docker/docker/image/v1"
13 11
 	"github.com/docker/docker/opts"
14 12
 	flag "github.com/docker/docker/pkg/mflag"
15 13
 	"github.com/docker/docker/reference"
... ...
@@ -182,28 +180,15 @@ func ValidateMirror(val string) (string, error) {
182 182
 
183 183
 // ValidateIndexName validates an index name.
184 184
 func ValidateIndexName(val string) (string, error) {
185
-	// 'index.docker.io' => 'docker.io'
186
-	if val == "index."+IndexName {
187
-		val = IndexName
185
+	if val == reference.LegacyDefaultHostname {
186
+		val = reference.DefaultHostname
188 187
 	}
189 188
 	if strings.HasPrefix(val, "-") || strings.HasSuffix(val, "-") {
190 189
 		return "", fmt.Errorf("Invalid index name (%s). Cannot begin or end with a hyphen.", val)
191 190
 	}
192
-	// *TODO: Check if valid hostname[:port]/ip[:port]?
193 191
 	return val, nil
194 192
 }
195 193
 
196
-func validateRemoteName(remoteName reference.Named) error {
197
-	remoteNameStr := remoteName.Name()
198
-	if !strings.Contains(remoteNameStr, "/") {
199
-		// the repository name must not be a valid image ID
200
-		if err := v1.ValidateID(remoteNameStr); err == nil {
201
-			return fmt.Errorf("Invalid repository name (%s), cannot specify 64-byte hexadecimal strings", remoteName)
202
-		}
203
-	}
204
-	return nil
205
-}
206
-
207 194
 func validateNoSchema(reposName string) error {
208 195
 	if strings.Contains(reposName, "://") {
209 196
 		// It cannot contain a scheme!
... ...
@@ -212,29 +197,6 @@ func validateNoSchema(reposName string) error {
212 212
 	return nil
213 213
 }
214 214
 
215
-// ValidateRepositoryName validates a repository name
216
-func ValidateRepositoryName(reposName reference.Named) error {
217
-	_, _, err := loadRepositoryName(reposName)
218
-	return err
219
-}
220
-
221
-// loadRepositoryName returns the repo name splitted into index name
222
-// and remote repo name. It returns an error if the name is not valid.
223
-func loadRepositoryName(reposName reference.Named) (string, reference.Named, error) {
224
-	if err := validateNoSchema(reposName.Name()); err != nil {
225
-		return "", nil, err
226
-	}
227
-	indexName, remoteName, err := splitReposName(reposName)
228
-
229
-	if indexName, err = ValidateIndexName(indexName); err != nil {
230
-		return "", nil, err
231
-	}
232
-	if err = validateRemoteName(remoteName); err != nil {
233
-		return "", nil, err
234
-	}
235
-	return indexName, remoteName, nil
236
-}
237
-
238 215
 // newIndexInfo returns IndexInfo configuration from indexName
239 216
 func newIndexInfo(config *registrytypes.ServiceConfig, indexName string) (*registrytypes.IndexInfo, error) {
240 217
 	var err error
... ...
@@ -267,75 +229,14 @@ func GetAuthConfigKey(index *registrytypes.IndexInfo) string {
267 267
 	return index.Name
268 268
 }
269 269
 
270
-// splitReposName breaks a reposName into an index name and remote name
271
-func splitReposName(reposName reference.Named) (indexName string, remoteName reference.Named, err error) {
272
-	var remoteNameStr string
273
-	indexName, remoteNameStr = distreference.SplitHostname(reposName)
274
-	if indexName == "" || (!strings.Contains(indexName, ".") &&
275
-		!strings.Contains(indexName, ":") && indexName != "localhost") {
276
-		// This is a Docker Index repos (ex: samalba/hipache or ubuntu)
277
-		// 'docker.io'
278
-		indexName = IndexName
279
-		remoteName = reposName
280
-	} else {
281
-		remoteName, err = reference.WithName(remoteNameStr)
282
-	}
283
-	return
284
-}
285
-
286 270
 // newRepositoryInfo validates and breaks down a repository name into a RepositoryInfo
287
-func newRepositoryInfo(config *registrytypes.ServiceConfig, reposName reference.Named) (*RepositoryInfo, error) {
288
-	if err := validateNoSchema(reposName.Name()); err != nil {
289
-		return nil, err
290
-	}
291
-
292
-	repoInfo := &RepositoryInfo{}
293
-	var (
294
-		indexName string
295
-		err       error
296
-	)
297
-
298
-	indexName, repoInfo.RemoteName, err = loadRepositoryName(reposName)
299
-	if err != nil {
300
-		return nil, err
301
-	}
302
-
303
-	repoInfo.Index, err = newIndexInfo(config, indexName)
271
+func newRepositoryInfo(config *registrytypes.ServiceConfig, name reference.Named) (*RepositoryInfo, error) {
272
+	index, err := newIndexInfo(config, name.Hostname())
304 273
 	if err != nil {
305 274
 		return nil, err
306 275
 	}
307
-
308
-	if repoInfo.Index.Official {
309
-		repoInfo.LocalName, err = normalizeLibraryRepoName(repoInfo.RemoteName)
310
-		if err != nil {
311
-			return nil, err
312
-		}
313
-		repoInfo.RemoteName = repoInfo.LocalName
314
-
315
-		// If the normalized name does not contain a '/' (e.g. "foo")
316
-		// then it is an official repo.
317
-		if strings.IndexRune(repoInfo.RemoteName.Name(), '/') == -1 {
318
-			repoInfo.Official = true
319
-			// Fix up remote name for official repos.
320
-			repoInfo.RemoteName, err = reference.WithName("library/" + repoInfo.RemoteName.Name())
321
-			if err != nil {
322
-				return nil, err
323
-			}
324
-		}
325
-
326
-		repoInfo.CanonicalName, err = reference.WithName("docker.io/" + repoInfo.RemoteName.Name())
327
-		if err != nil {
328
-			return nil, err
329
-		}
330
-	} else {
331
-		repoInfo.LocalName, err = localNameFromRemote(repoInfo.Index.Name, repoInfo.RemoteName)
332
-		if err != nil {
333
-			return nil, err
334
-		}
335
-		repoInfo.CanonicalName = repoInfo.LocalName
336
-	}
337
-
338
-	return repoInfo, nil
276
+	official := !strings.ContainsRune(name.Name(), '/')
277
+	return &RepositoryInfo{name, index, official}, nil
339 278
 }
340 279
 
341 280
 // ParseRepositoryInfo performs the breakdown of a repository name into a RepositoryInfo, but
... ...
@@ -354,70 +255,3 @@ func ParseSearchIndexInfo(reposName string) (*registrytypes.IndexInfo, error) {
354 354
 	}
355 355
 	return indexInfo, nil
356 356
 }
357
-
358
-// NormalizeLocalName transforms a repository name into a normalized LocalName
359
-// Passes through the name without transformation on error (image id, etc)
360
-// It does not use the repository info because we don't want to load
361
-// the repository index and do request over the network.
362
-func NormalizeLocalName(name reference.Named) reference.Named {
363
-	indexName, remoteName, err := loadRepositoryName(name)
364
-	if err != nil {
365
-		return name
366
-	}
367
-
368
-	var officialIndex bool
369
-	// Return any configured index info, first.
370
-	if index, ok := emptyServiceConfig.IndexConfigs[indexName]; ok {
371
-		officialIndex = index.Official
372
-	}
373
-
374
-	if officialIndex {
375
-		localName, err := normalizeLibraryRepoName(remoteName)
376
-		if err != nil {
377
-			return name
378
-		}
379
-		return localName
380
-	}
381
-	localName, err := localNameFromRemote(indexName, remoteName)
382
-	if err != nil {
383
-		return name
384
-	}
385
-	return localName
386
-}
387
-
388
-// normalizeLibraryRepoName removes the library prefix from
389
-// the repository name for official repos.
390
-func normalizeLibraryRepoName(name reference.Named) (reference.Named, error) {
391
-	if strings.HasPrefix(name.Name(), "library/") {
392
-		// If pull "library/foo", it's stored locally under "foo"
393
-		return reference.WithName(strings.SplitN(name.Name(), "/", 2)[1])
394
-	}
395
-	return name, nil
396
-}
397
-
398
-// localNameFromRemote combines the index name and the repo remote name
399
-// to generate a repo local name.
400
-func localNameFromRemote(indexName string, remoteName reference.Named) (reference.Named, error) {
401
-	return reference.WithName(indexName + "/" + remoteName.Name())
402
-}
403
-
404
-// NormalizeLocalReference transforms a reference to use a normalized LocalName
405
-// for the name poriton. Passes through the reference without transformation on
406
-// error.
407
-func NormalizeLocalReference(ref reference.Named) reference.Named {
408
-	localName := NormalizeLocalName(ref)
409
-	if tagged, isTagged := ref.(reference.NamedTagged); isTagged {
410
-		newRef, err := reference.WithTag(localName, tagged.Tag())
411
-		if err != nil {
412
-			return ref
413
-		}
414
-		return newRef
415
-	} else if digested, isCanonical := ref.(reference.Canonical); isCanonical {
416
-		newRef, err := reference.WithDigest(localName, digested.Digest())
417
-		if err != nil {
418
-			return ref
419
-		}
420
-		return newRef
421
-	}
422
-	return localName
423
-}
... ...
@@ -356,7 +356,6 @@ func handlerGetDeleteTags(w http.ResponseWriter, r *http.Request) {
356 356
 		apiError(w, "Could not parse repository", 400)
357 357
 		return
358 358
 	}
359
-	repositoryName = NormalizeLocalName(repositoryName)
360 359
 	tags, exists := testRepositories[repositoryName.String()]
361 360
 	if !exists {
362 361
 		apiError(w, "Repository not found", 404)
... ...
@@ -380,7 +379,6 @@ func handlerGetTag(w http.ResponseWriter, r *http.Request) {
380 380
 		apiError(w, "Could not parse repository", 400)
381 381
 		return
382 382
 	}
383
-	repositoryName = NormalizeLocalName(repositoryName)
384 383
 	tagName := vars["tag"]
385 384
 	tags, exists := testRepositories[repositoryName.String()]
386 385
 	if !exists {
... ...
@@ -405,7 +403,6 @@ func handlerPutTag(w http.ResponseWriter, r *http.Request) {
405 405
 		apiError(w, "Could not parse repository", 400)
406 406
 		return
407 407
 	}
408
-	repositoryName = NormalizeLocalName(repositoryName)
409 408
 	tagName := vars["tag"]
410 409
 	tags, exists := testRepositories[repositoryName.String()]
411 410
 	if !exists {
... ...
@@ -307,71 +307,24 @@ func TestPushImageLayerRegistry(t *testing.T) {
307 307
 	}
308 308
 }
309 309
 
310
-func TestValidateRepositoryName(t *testing.T) {
311
-	validRepoNames := []string{
312
-		"docker/docker",
313
-		"library/debian",
314
-		"debian",
315
-		"docker.io/docker/docker",
316
-		"docker.io/library/debian",
317
-		"docker.io/debian",
318
-		"index.docker.io/docker/docker",
319
-		"index.docker.io/library/debian",
320
-		"index.docker.io/debian",
321
-		"127.0.0.1:5000/docker/docker",
322
-		"127.0.0.1:5000/library/debian",
323
-		"127.0.0.1:5000/debian",
324
-		"thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev",
325
-	}
326
-	invalidRepoNames := []string{
327
-		"https://github.com/docker/docker",
328
-		"docker/Docker",
329
-		"-docker",
330
-		"-docker/docker",
331
-		"-docker.io/docker/docker",
332
-		"docker///docker",
333
-		"docker.io/docker/Docker",
334
-		"docker.io/docker///docker",
335
-		"1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
336
-		"docker.io/1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
337
-	}
338
-
339
-	for _, name := range invalidRepoNames {
340
-		named, err := reference.WithName(name)
341
-		if err == nil {
342
-			err := ValidateRepositoryName(named)
343
-			assertNotEqual(t, err, nil, "Expected invalid repo name: "+name)
344
-		}
345
-	}
346
-
347
-	for _, name := range validRepoNames {
348
-		named, err := reference.WithName(name)
349
-		if err != nil {
350
-			t.Fatalf("could not parse valid name: %s", name)
351
-		}
352
-		err = ValidateRepositoryName(named)
353
-		assertEqual(t, err, nil, "Expected valid repo name: "+name)
354
-	}
355
-}
356
-
357 310
 func TestParseRepositoryInfo(t *testing.T) {
358
-	withName := func(name string) reference.Named {
359
-		named, err := reference.WithName(name)
360
-		if err != nil {
361
-			t.Fatalf("could not parse reference %s", name)
362
-		}
363
-		return named
311
+	type staticRepositoryInfo struct {
312
+		Index         *registrytypes.IndexInfo
313
+		RemoteName    string
314
+		CanonicalName string
315
+		LocalName     string
316
+		Official      bool
364 317
 	}
365 318
 
366
-	expectedRepoInfos := map[string]RepositoryInfo{
319
+	expectedRepoInfos := map[string]staticRepositoryInfo{
367 320
 		"fooo/bar": {
368 321
 			Index: &registrytypes.IndexInfo{
369 322
 				Name:     IndexName,
370 323
 				Official: true,
371 324
 			},
372
-			RemoteName:    withName("fooo/bar"),
373
-			LocalName:     withName("fooo/bar"),
374
-			CanonicalName: withName("docker.io/fooo/bar"),
325
+			RemoteName:    "fooo/bar",
326
+			LocalName:     "fooo/bar",
327
+			CanonicalName: "docker.io/fooo/bar",
375 328
 			Official:      false,
376 329
 		},
377 330
 		"library/ubuntu": {
... ...
@@ -379,9 +332,9 @@ func TestParseRepositoryInfo(t *testing.T) {
379 379
 				Name:     IndexName,
380 380
 				Official: true,
381 381
 			},
382
-			RemoteName:    withName("library/ubuntu"),
383
-			LocalName:     withName("ubuntu"),
384
-			CanonicalName: withName("docker.io/library/ubuntu"),
382
+			RemoteName:    "library/ubuntu",
383
+			LocalName:     "ubuntu",
384
+			CanonicalName: "docker.io/library/ubuntu",
385 385
 			Official:      true,
386 386
 		},
387 387
 		"nonlibrary/ubuntu": {
... ...
@@ -389,9 +342,9 @@ func TestParseRepositoryInfo(t *testing.T) {
389 389
 				Name:     IndexName,
390 390
 				Official: true,
391 391
 			},
392
-			RemoteName:    withName("nonlibrary/ubuntu"),
393
-			LocalName:     withName("nonlibrary/ubuntu"),
394
-			CanonicalName: withName("docker.io/nonlibrary/ubuntu"),
392
+			RemoteName:    "nonlibrary/ubuntu",
393
+			LocalName:     "nonlibrary/ubuntu",
394
+			CanonicalName: "docker.io/nonlibrary/ubuntu",
395 395
 			Official:      false,
396 396
 		},
397 397
 		"ubuntu": {
... ...
@@ -399,9 +352,9 @@ func TestParseRepositoryInfo(t *testing.T) {
399 399
 				Name:     IndexName,
400 400
 				Official: true,
401 401
 			},
402
-			RemoteName:    withName("library/ubuntu"),
403
-			LocalName:     withName("ubuntu"),
404
-			CanonicalName: withName("docker.io/library/ubuntu"),
402
+			RemoteName:    "library/ubuntu",
403
+			LocalName:     "ubuntu",
404
+			CanonicalName: "docker.io/library/ubuntu",
405 405
 			Official:      true,
406 406
 		},
407 407
 		"other/library": {
... ...
@@ -409,9 +362,9 @@ func TestParseRepositoryInfo(t *testing.T) {
409 409
 				Name:     IndexName,
410 410
 				Official: true,
411 411
 			},
412
-			RemoteName:    withName("other/library"),
413
-			LocalName:     withName("other/library"),
414
-			CanonicalName: withName("docker.io/other/library"),
412
+			RemoteName:    "other/library",
413
+			LocalName:     "other/library",
414
+			CanonicalName: "docker.io/other/library",
415 415
 			Official:      false,
416 416
 		},
417 417
 		"127.0.0.1:8000/private/moonbase": {
... ...
@@ -419,9 +372,9 @@ func TestParseRepositoryInfo(t *testing.T) {
419 419
 				Name:     "127.0.0.1:8000",
420 420
 				Official: false,
421 421
 			},
422
-			RemoteName:    withName("private/moonbase"),
423
-			LocalName:     withName("127.0.0.1:8000/private/moonbase"),
424
-			CanonicalName: withName("127.0.0.1:8000/private/moonbase"),
422
+			RemoteName:    "private/moonbase",
423
+			LocalName:     "127.0.0.1:8000/private/moonbase",
424
+			CanonicalName: "127.0.0.1:8000/private/moonbase",
425 425
 			Official:      false,
426 426
 		},
427 427
 		"127.0.0.1:8000/privatebase": {
... ...
@@ -429,9 +382,9 @@ func TestParseRepositoryInfo(t *testing.T) {
429 429
 				Name:     "127.0.0.1:8000",
430 430
 				Official: false,
431 431
 			},
432
-			RemoteName:    withName("privatebase"),
433
-			LocalName:     withName("127.0.0.1:8000/privatebase"),
434
-			CanonicalName: withName("127.0.0.1:8000/privatebase"),
432
+			RemoteName:    "privatebase",
433
+			LocalName:     "127.0.0.1:8000/privatebase",
434
+			CanonicalName: "127.0.0.1:8000/privatebase",
435 435
 			Official:      false,
436 436
 		},
437 437
 		"localhost:8000/private/moonbase": {
... ...
@@ -439,9 +392,9 @@ func TestParseRepositoryInfo(t *testing.T) {
439 439
 				Name:     "localhost:8000",
440 440
 				Official: false,
441 441
 			},
442
-			RemoteName:    withName("private/moonbase"),
443
-			LocalName:     withName("localhost:8000/private/moonbase"),
444
-			CanonicalName: withName("localhost:8000/private/moonbase"),
442
+			RemoteName:    "private/moonbase",
443
+			LocalName:     "localhost:8000/private/moonbase",
444
+			CanonicalName: "localhost:8000/private/moonbase",
445 445
 			Official:      false,
446 446
 		},
447 447
 		"localhost:8000/privatebase": {
... ...
@@ -449,9 +402,9 @@ func TestParseRepositoryInfo(t *testing.T) {
449 449
 				Name:     "localhost:8000",
450 450
 				Official: false,
451 451
 			},
452
-			RemoteName:    withName("privatebase"),
453
-			LocalName:     withName("localhost:8000/privatebase"),
454
-			CanonicalName: withName("localhost:8000/privatebase"),
452
+			RemoteName:    "privatebase",
453
+			LocalName:     "localhost:8000/privatebase",
454
+			CanonicalName: "localhost:8000/privatebase",
455 455
 			Official:      false,
456 456
 		},
457 457
 		"example.com/private/moonbase": {
... ...
@@ -459,9 +412,9 @@ func TestParseRepositoryInfo(t *testing.T) {
459 459
 				Name:     "example.com",
460 460
 				Official: false,
461 461
 			},
462
-			RemoteName:    withName("private/moonbase"),
463
-			LocalName:     withName("example.com/private/moonbase"),
464
-			CanonicalName: withName("example.com/private/moonbase"),
462
+			RemoteName:    "private/moonbase",
463
+			LocalName:     "example.com/private/moonbase",
464
+			CanonicalName: "example.com/private/moonbase",
465 465
 			Official:      false,
466 466
 		},
467 467
 		"example.com/privatebase": {
... ...
@@ -469,9 +422,9 @@ func TestParseRepositoryInfo(t *testing.T) {
469 469
 				Name:     "example.com",
470 470
 				Official: false,
471 471
 			},
472
-			RemoteName:    withName("privatebase"),
473
-			LocalName:     withName("example.com/privatebase"),
474
-			CanonicalName: withName("example.com/privatebase"),
472
+			RemoteName:    "privatebase",
473
+			LocalName:     "example.com/privatebase",
474
+			CanonicalName: "example.com/privatebase",
475 475
 			Official:      false,
476 476
 		},
477 477
 		"example.com:8000/private/moonbase": {
... ...
@@ -479,9 +432,9 @@ func TestParseRepositoryInfo(t *testing.T) {
479 479
 				Name:     "example.com:8000",
480 480
 				Official: false,
481 481
 			},
482
-			RemoteName:    withName("private/moonbase"),
483
-			LocalName:     withName("example.com:8000/private/moonbase"),
484
-			CanonicalName: withName("example.com:8000/private/moonbase"),
482
+			RemoteName:    "private/moonbase",
483
+			LocalName:     "example.com:8000/private/moonbase",
484
+			CanonicalName: "example.com:8000/private/moonbase",
485 485
 			Official:      false,
486 486
 		},
487 487
 		"example.com:8000/privatebase": {
... ...
@@ -489,9 +442,9 @@ func TestParseRepositoryInfo(t *testing.T) {
489 489
 				Name:     "example.com:8000",
490 490
 				Official: false,
491 491
 			},
492
-			RemoteName:    withName("privatebase"),
493
-			LocalName:     withName("example.com:8000/privatebase"),
494
-			CanonicalName: withName("example.com:8000/privatebase"),
492
+			RemoteName:    "privatebase",
493
+			LocalName:     "example.com:8000/privatebase",
494
+			CanonicalName: "example.com:8000/privatebase",
495 495
 			Official:      false,
496 496
 		},
497 497
 		"localhost/private/moonbase": {
... ...
@@ -499,9 +452,9 @@ func TestParseRepositoryInfo(t *testing.T) {
499 499
 				Name:     "localhost",
500 500
 				Official: false,
501 501
 			},
502
-			RemoteName:    withName("private/moonbase"),
503
-			LocalName:     withName("localhost/private/moonbase"),
504
-			CanonicalName: withName("localhost/private/moonbase"),
502
+			RemoteName:    "private/moonbase",
503
+			LocalName:     "localhost/private/moonbase",
504
+			CanonicalName: "localhost/private/moonbase",
505 505
 			Official:      false,
506 506
 		},
507 507
 		"localhost/privatebase": {
... ...
@@ -509,9 +462,9 @@ func TestParseRepositoryInfo(t *testing.T) {
509 509
 				Name:     "localhost",
510 510
 				Official: false,
511 511
 			},
512
-			RemoteName:    withName("privatebase"),
513
-			LocalName:     withName("localhost/privatebase"),
514
-			CanonicalName: withName("localhost/privatebase"),
512
+			RemoteName:    "privatebase",
513
+			LocalName:     "localhost/privatebase",
514
+			CanonicalName: "localhost/privatebase",
515 515
 			Official:      false,
516 516
 		},
517 517
 		IndexName + "/public/moonbase": {
... ...
@@ -519,9 +472,9 @@ func TestParseRepositoryInfo(t *testing.T) {
519 519
 				Name:     IndexName,
520 520
 				Official: true,
521 521
 			},
522
-			RemoteName:    withName("public/moonbase"),
523
-			LocalName:     withName("public/moonbase"),
524
-			CanonicalName: withName("docker.io/public/moonbase"),
522
+			RemoteName:    "public/moonbase",
523
+			LocalName:     "public/moonbase",
524
+			CanonicalName: "docker.io/public/moonbase",
525 525
 			Official:      false,
526 526
 		},
527 527
 		"index." + IndexName + "/public/moonbase": {
... ...
@@ -529,9 +482,9 @@ func TestParseRepositoryInfo(t *testing.T) {
529 529
 				Name:     IndexName,
530 530
 				Official: true,
531 531
 			},
532
-			RemoteName:    withName("public/moonbase"),
533
-			LocalName:     withName("public/moonbase"),
534
-			CanonicalName: withName("docker.io/public/moonbase"),
532
+			RemoteName:    "public/moonbase",
533
+			LocalName:     "public/moonbase",
534
+			CanonicalName: "docker.io/public/moonbase",
535 535
 			Official:      false,
536 536
 		},
537 537
 		"ubuntu-12.04-base": {
... ...
@@ -539,9 +492,9 @@ func TestParseRepositoryInfo(t *testing.T) {
539 539
 				Name:     IndexName,
540 540
 				Official: true,
541 541
 			},
542
-			RemoteName:    withName("library/ubuntu-12.04-base"),
543
-			LocalName:     withName("ubuntu-12.04-base"),
544
-			CanonicalName: withName("docker.io/library/ubuntu-12.04-base"),
542
+			RemoteName:    "library/ubuntu-12.04-base",
543
+			LocalName:     "ubuntu-12.04-base",
544
+			CanonicalName: "docker.io/library/ubuntu-12.04-base",
545 545
 			Official:      true,
546 546
 		},
547 547
 		IndexName + "/ubuntu-12.04-base": {
... ...
@@ -549,9 +502,9 @@ func TestParseRepositoryInfo(t *testing.T) {
549 549
 				Name:     IndexName,
550 550
 				Official: true,
551 551
 			},
552
-			RemoteName:    withName("library/ubuntu-12.04-base"),
553
-			LocalName:     withName("ubuntu-12.04-base"),
554
-			CanonicalName: withName("docker.io/library/ubuntu-12.04-base"),
552
+			RemoteName:    "library/ubuntu-12.04-base",
553
+			LocalName:     "ubuntu-12.04-base",
554
+			CanonicalName: "docker.io/library/ubuntu-12.04-base",
555 555
 			Official:      true,
556 556
 		},
557 557
 		"index." + IndexName + "/ubuntu-12.04-base": {
... ...
@@ -559,9 +512,9 @@ func TestParseRepositoryInfo(t *testing.T) {
559 559
 				Name:     IndexName,
560 560
 				Official: true,
561 561
 			},
562
-			RemoteName:    withName("library/ubuntu-12.04-base"),
563
-			LocalName:     withName("ubuntu-12.04-base"),
564
-			CanonicalName: withName("docker.io/library/ubuntu-12.04-base"),
562
+			RemoteName:    "library/ubuntu-12.04-base",
563
+			LocalName:     "ubuntu-12.04-base",
564
+			CanonicalName: "docker.io/library/ubuntu-12.04-base",
565 565
 			Official:      true,
566 566
 		},
567 567
 	}
... ...
@@ -577,9 +530,9 @@ func TestParseRepositoryInfo(t *testing.T) {
577 577
 			t.Error(err)
578 578
 		} else {
579 579
 			checkEqual(t, repoInfo.Index.Name, expectedRepoInfo.Index.Name, reposName)
580
-			checkEqual(t, repoInfo.RemoteName.String(), expectedRepoInfo.RemoteName.String(), reposName)
581
-			checkEqual(t, repoInfo.LocalName.String(), expectedRepoInfo.LocalName.String(), reposName)
582
-			checkEqual(t, repoInfo.CanonicalName.String(), expectedRepoInfo.CanonicalName.String(), reposName)
580
+			checkEqual(t, repoInfo.RemoteName(), expectedRepoInfo.RemoteName, reposName)
581
+			checkEqual(t, repoInfo.Name(), expectedRepoInfo.LocalName, reposName)
582
+			checkEqual(t, repoInfo.FullName(), expectedRepoInfo.CanonicalName, reposName)
583 583
 			checkEqual(t, repoInfo.Index.Official, expectedRepoInfo.Index.Official, reposName)
584 584
 			checkEqual(t, repoInfo.Official, expectedRepoInfo.Official, reposName)
585 585
 		}
... ...
@@ -806,82 +759,6 @@ func TestSearchRepositories(t *testing.T) {
806 806
 	assertEqual(t, results.Results[0].StarCount, 42, "Expected 'fakeimage' to have 42 stars")
807 807
 }
808 808
 
809
-func TestValidRemoteName(t *testing.T) {
810
-	validRepositoryNames := []string{
811
-		// Sanity check.
812
-		"docker/docker",
813
-
814
-		// Allow 64-character non-hexadecimal names (hexadecimal names are forbidden).
815
-		"thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev",
816
-
817
-		// Allow embedded hyphens.
818
-		"docker-rules/docker",
819
-
820
-		// Allow multiple hyphens as well.
821
-		"docker---rules/docker",
822
-
823
-		//Username doc and image name docker being tested.
824
-		"doc/docker",
825
-
826
-		// single character names are now allowed.
827
-		"d/docker",
828
-		"jess/t",
829
-
830
-		// Consecutive underscores.
831
-		"dock__er/docker",
832
-	}
833
-	for _, repositoryName := range validRepositoryNames {
834
-		repositoryRef, err := reference.WithName(repositoryName)
835
-		if err != nil {
836
-			t.Errorf("Repository name should be valid: %v. Error: %v", repositoryName, err)
837
-		}
838
-		if err := validateRemoteName(repositoryRef); err != nil {
839
-			t.Errorf("Repository name should be valid: %v. Error: %v", repositoryName, err)
840
-		}
841
-	}
842
-
843
-	invalidRepositoryNames := []string{
844
-		// Disallow capital letters.
845
-		"docker/Docker",
846
-
847
-		// Only allow one slash.
848
-		"docker///docker",
849
-
850
-		// Disallow 64-character hexadecimal.
851
-		"1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
852
-
853
-		// Disallow leading and trailing hyphens in namespace.
854
-		"-docker/docker",
855
-		"docker-/docker",
856
-		"-docker-/docker",
857
-
858
-		// Don't allow underscores everywhere (as opposed to hyphens).
859
-		"____/____",
860
-
861
-		"_docker/_docker",
862
-
863
-		// Disallow consecutive periods.
864
-		"dock..er/docker",
865
-		"dock_.er/docker",
866
-		"dock-.er/docker",
867
-
868
-		// No repository.
869
-		"docker/",
870
-
871
-		//namespace too long
872
-		"this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255/docker",
873
-	}
874
-	for _, repositoryName := range invalidRepositoryNames {
875
-		repositoryRef, err := reference.ParseNamed(repositoryName)
876
-		if err != nil {
877
-			continue
878
-		}
879
-		if err := validateRemoteName(repositoryRef); err == nil {
880
-			t.Errorf("Repository name should be invalid: %v", repositoryName)
881
-		}
882
-	}
883
-}
884
-
885 809
 func TestTrustedLocation(t *testing.T) {
886 810
 	for _, url := range []string{"http://example.com", "https://example.com:7777", "http://docker.io", "http://test.docker.com", "https://fakedocker.com"} {
887 811
 		req, _ := http.NewRequest("GET", url, nil)
... ...
@@ -11,7 +11,7 @@ import (
11 11
 func (s *Service) lookupV1Endpoints(repoName reference.Named) (endpoints []APIEndpoint, err error) {
12 12
 	var cfg = tlsconfig.ServerDefault
13 13
 	tlsConfig := &cfg
14
-	nameString := repoName.Name()
14
+	nameString := repoName.FullName()
15 15
 	if strings.HasPrefix(nameString, DefaultNamespace+"/") {
16 16
 		endpoints = append(endpoints, APIEndpoint{
17 17
 			URL:          DefaultV1Registry,
... ...
@@ -12,7 +12,7 @@ import (
12 12
 func (s *Service) lookupV2Endpoints(repoName reference.Named) (endpoints []APIEndpoint, err error) {
13 13
 	var cfg = tlsconfig.ServerDefault
14 14
 	tlsConfig := &cfg
15
-	nameString := repoName.Name()
15
+	nameString := repoName.FullName()
16 16
 	if strings.HasPrefix(nameString, DefaultNamespace+"/") {
17 17
 		// v2 mirrors
18 18
 		for _, mirror := range s.Config.Mirrors {
... ...
@@ -312,7 +312,7 @@ func (r *Session) GetRemoteImageLayer(imgID, registry string, imgSize int64) (io
312 312
 // argument, and returns data from the first one that answers the query
313 313
 // successfully.
314 314
 func (r *Session) GetRemoteTag(registries []string, repositoryRef reference.Named, askedTag string) (string, error) {
315
-	repository := repositoryRef.Name()
315
+	repository := repositoryRef.RemoteName()
316 316
 
317 317
 	if strings.Count(repository, "/") == 0 {
318 318
 		// This will be removed once the registry supports auto-resolution on
... ...
@@ -350,7 +350,7 @@ func (r *Session) GetRemoteTag(registries []string, repositoryRef reference.Name
350 350
 // the first one that answers the query successfully. It returns a map with
351 351
 // tag names as the keys and image IDs as the values.
352 352
 func (r *Session) GetRemoteTags(registries []string, repositoryRef reference.Named) (map[string]string, error) {
353
-	repository := repositoryRef.Name()
353
+	repository := repositoryRef.RemoteName()
354 354
 
355 355
 	if strings.Count(repository, "/") == 0 {
356 356
 		// This will be removed once the registry supports auto-resolution on
... ...
@@ -403,8 +403,8 @@ func buildEndpointsList(headers []string, indexEp string) ([]string, error) {
403 403
 }
404 404
 
405 405
 // GetRepositoryData returns lists of images and endpoints for the repository
406
-func (r *Session) GetRepositoryData(remote reference.Named) (*RepositoryData, error) {
407
-	repositoryTarget := fmt.Sprintf("%srepositories/%s/images", r.indexEndpoint.VersionString(1), remote.Name())
406
+func (r *Session) GetRepositoryData(name reference.Named) (*RepositoryData, error) {
407
+	repositoryTarget := fmt.Sprintf("%srepositories/%s/images", r.indexEndpoint.VersionString(1), name.RemoteName())
408 408
 
409 409
 	logrus.Debugf("[registry] Calling GET %s", repositoryTarget)
410 410
 
... ...
@@ -438,7 +438,7 @@ func (r *Session) GetRepositoryData(remote reference.Named) (*RepositoryData, er
438 438
 		if err != nil {
439 439
 			logrus.Debugf("Error reading response body: %s", err)
440 440
 		}
441
-		return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to pull repository %s: %q", res.StatusCode, remote.Name(), errBody), res)
441
+		return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to pull repository %s: %q", res.StatusCode, name.RemoteName(), errBody), res)
442 442
 	}
443 443
 
444 444
 	var endpoints []string
... ...
@@ -593,7 +593,7 @@ func (r *Session) PushImageLayerRegistry(imgID string, layer io.Reader, registry
593 593
 func (r *Session) PushRegistryTag(remote reference.Named, revision, tag, registry string) error {
594 594
 	// "jsonify" the string
595 595
 	revision = "\"" + revision + "\""
596
-	path := fmt.Sprintf("repositories/%s/tags/%s", remote.Name(), tag)
596
+	path := fmt.Sprintf("repositories/%s/tags/%s", remote.RemoteName(), tag)
597 597
 
598 598
 	req, err := http.NewRequest("PUT", registry+path, strings.NewReader(revision))
599 599
 	if err != nil {
... ...
@@ -607,7 +607,7 @@ func (r *Session) PushRegistryTag(remote reference.Named, revision, tag, registr
607 607
 	}
608 608
 	res.Body.Close()
609 609
 	if res.StatusCode != 200 && res.StatusCode != 201 {
610
-		return httputils.NewHTTPRequestError(fmt.Sprintf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, remote.Name()), res)
610
+		return httputils.NewHTTPRequestError(fmt.Sprintf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, remote.RemoteName()), res)
611 611
 	}
612 612
 	return nil
613 613
 }
... ...
@@ -633,7 +633,7 @@ func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData,
633 633
 	if validate {
634 634
 		suffix = "images"
635 635
 	}
636
-	u := fmt.Sprintf("%srepositories/%s/%s", r.indexEndpoint.VersionString(1), remote.Name(), suffix)
636
+	u := fmt.Sprintf("%srepositories/%s/%s", r.indexEndpoint.VersionString(1), remote.RemoteName(), suffix)
637 637
 	logrus.Debugf("[registry] PUT %s", u)
638 638
 	logrus.Debugf("Image list pushed to index:\n%s", imgListJSON)
639 639
 	headers := map[string][]string{
... ...
@@ -671,7 +671,7 @@ func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData,
671 671
 			if err != nil {
672 672
 				logrus.Debugf("Error reading response body: %s", err)
673 673
 			}
674
-			return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push repository %s: %q", res.StatusCode, remote.Name(), errBody), res)
674
+			return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push repository %s: %q", res.StatusCode, remote.RemoteName(), errBody), res)
675 675
 		}
676 676
 		tokens = res.Header["X-Docker-Token"]
677 677
 		logrus.Debugf("Auth token: %v", tokens)
... ...
@@ -689,7 +689,7 @@ func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData,
689 689
 			if err != nil {
690 690
 				logrus.Debugf("Error reading response body: %s", err)
691 691
 			}
692
-			return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push checksums %s: %q", res.StatusCode, remote.Name(), errBody), res)
692
+			return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push checksums %s: %q", res.StatusCode, remote.RemoteName(), errBody), res)
693 693
 		}
694 694
 	}
695 695
 
... ...
@@ -60,17 +60,9 @@ const (
60 60
 
61 61
 // RepositoryInfo describes a repository
62 62
 type RepositoryInfo struct {
63
+	reference.Named
63 64
 	// Index points to registry information
64 65
 	Index *registrytypes.IndexInfo
65
-	// RemoteName is the remote name of the repository, such as
66
-	// "library/ubuntu-12.04-base"
67
-	RemoteName reference.Named
68
-	// LocalName is the local name of the repository, such as
69
-	// "ubuntu-12.04-base"
70
-	LocalName reference.Named
71
-	// CanonicalName is the canonical name of the repository, such as
72
-	// "docker.io/library/ubuntu-12.04-base"
73
-	CanonicalName reference.Named
74 66
 	// Official indicates whether the repository is considered official.
75 67
 	// If the registry is official, and the normalized name does not
76 68
 	// contain a '/' (e.g. "foo"), then it is considered an official repo.