Browse code

Merge pull request #30457 from dmcgowan/distribution-reference-update-2

reference: use distribution reference and remove fork

Vincent Demeester authored on 2017/02/08 06:01:25
Showing 104 changed files
... ...
@@ -4,9 +4,9 @@ import (
4 4
 	"io"
5 5
 	"net/http"
6 6
 
7
+	"github.com/docker/distribution/reference"
7 8
 	enginetypes "github.com/docker/docker/api/types"
8 9
 	"github.com/docker/docker/api/types/filters"
9
-	"github.com/docker/docker/reference"
10 10
 	"golang.org/x/net/context"
11 11
 )
12 12
 
... ...
@@ -7,13 +7,12 @@ import (
7 7
 	"strconv"
8 8
 	"strings"
9 9
 
10
-	distreference "github.com/docker/distribution/reference"
10
+	"github.com/docker/distribution/reference"
11 11
 	"github.com/docker/docker/api/server/httputils"
12 12
 	"github.com/docker/docker/api/types"
13 13
 	"github.com/docker/docker/api/types/filters"
14 14
 	"github.com/docker/docker/pkg/ioutils"
15 15
 	"github.com/docker/docker/pkg/streamformatter"
16
-	"github.com/docker/docker/reference"
17 16
 	"github.com/pkg/errors"
18 17
 	"golang.org/x/net/context"
19 18
 )
... ...
@@ -47,39 +46,27 @@ func parseHeaders(headers http.Header) (map[string][]string, *types.AuthConfig)
47 47
 // be returned.
48 48
 func parseRemoteRef(remote string) (reference.Named, string, error) {
49 49
 	// Parse remote reference, supporting remotes with name and tag
50
-	// NOTE: Using distribution reference to handle references
51
-	// containing both a name and digest
52
-	remoteRef, err := distreference.ParseNamed(remote)
50
+	remoteRef, err := reference.ParseNormalizedNamed(remote)
53 51
 	if err != nil {
54 52
 		return nil, "", err
55 53
 	}
56 54
 
57
-	var tag string
58
-	if t, ok := remoteRef.(distreference.Tagged); ok {
59
-		tag = t.Tag()
55
+	type canonicalWithTag interface {
56
+		reference.Canonical
57
+		Tag() string
60 58
 	}
61 59
 
62
-	// Convert distribution reference to docker reference
63
-	// TODO: remove when docker reference changes reconciled upstream
64
-	ref, err := reference.WithName(remoteRef.Name())
65
-	if err != nil {
66
-		return nil, "", err
67
-	}
68
-	if d, ok := remoteRef.(distreference.Digested); ok {
69
-		ref, err = reference.WithDigest(ref, d.Digest())
70
-		if err != nil {
71
-			return nil, "", err
72
-		}
73
-	} else if tag != "" {
74
-		ref, err = reference.WithTag(ref, tag)
60
+	if canonical, ok := remoteRef.(canonicalWithTag); ok {
61
+		remoteRef, err = reference.WithDigest(reference.TrimNamed(remoteRef), canonical.Digest())
75 62
 		if err != nil {
76 63
 			return nil, "", err
77 64
 		}
78
-	} else {
79
-		ref = reference.WithDefaultTag(ref)
65
+		return remoteRef, canonical.Tag(), nil
80 66
 	}
81 67
 
82
-	return ref, tag, nil
68
+	remoteRef = reference.TagNameOnly(remoteRef)
69
+
70
+	return remoteRef, "", nil
83 71
 }
84 72
 
85 73
 func (pr *pluginRouter) getPrivileges(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
... ...
@@ -188,24 +175,24 @@ func getName(ref reference.Named, tag, name string) (string, error) {
188 188
 				if err != nil {
189 189
 					return "", err
190 190
 				}
191
-				name = nt.String()
191
+				name = reference.FamiliarString(nt)
192 192
 			} else {
193
-				name = reference.WithDefaultTag(trimmed).String()
193
+				name = reference.FamiliarString(reference.TagNameOnly(trimmed))
194 194
 			}
195 195
 		} else {
196
-			name = ref.String()
196
+			name = reference.FamiliarString(ref)
197 197
 		}
198 198
 	} else {
199
-		localRef, err := reference.ParseNamed(name)
199
+		localRef, err := reference.ParseNormalizedNamed(name)
200 200
 		if err != nil {
201 201
 			return "", err
202 202
 		}
203 203
 		if _, ok := localRef.(reference.Canonical); ok {
204 204
 			return "", errors.New("cannot use digest in plugin tag")
205 205
 		}
206
-		if distreference.IsNameOnly(localRef) {
206
+		if reference.IsNameOnly(localRef) {
207 207
 			// TODO: log change in name to out stream
208
-			name = reference.WithDefaultTag(localRef).String()
208
+			name = reference.FamiliarString(reference.TagNameOnly(localRef))
209 209
 		}
210 210
 	}
211 211
 	return name, nil
212 212
deleted file mode 100644
... ...
@@ -1,34 +0,0 @@
1
-package reference
2
-
3
-import (
4
-	distreference "github.com/docker/distribution/reference"
5
-)
6
-
7
-// Parse parses the given references and returns the repository and
8
-// tag (if present) from it. If there is an error during parsing, it will
9
-// return an error.
10
-func Parse(ref string) (string, string, error) {
11
-	distributionRef, err := distreference.ParseNamed(ref)
12
-	if err != nil {
13
-		return "", "", err
14
-	}
15
-
16
-	tag := GetTagFromNamedRef(distributionRef)
17
-	return distributionRef.Name(), tag, nil
18
-}
19
-
20
-// GetTagFromNamedRef returns a tag from the specified reference.
21
-// This function is necessary as long as the docker "server" api makes the distinction between repository
22
-// and tags.
23
-func GetTagFromNamedRef(ref distreference.Named) string {
24
-	var tag string
25
-	switch x := ref.(type) {
26
-	case distreference.Digested:
27
-		tag = x.Digest().String()
28
-	case distreference.NamedTagged:
29
-		tag = x.Tag()
30
-	default:
31
-		tag = "latest"
32
-	}
33
-	return tag
34
-}
35 1
deleted file mode 100644
... ...
@@ -1,73 +0,0 @@
1
-package reference
2
-
3
-import (
4
-	_ "crypto/sha256"
5
-	"testing"
6
-)
7
-
8
-func TestParse(t *testing.T) {
9
-	testCases := []struct {
10
-		ref           string
11
-		expectedName  string
12
-		expectedTag   string
13
-		expectedError bool
14
-	}{
15
-		{
16
-			ref:           "",
17
-			expectedName:  "",
18
-			expectedTag:   "",
19
-			expectedError: true,
20
-		},
21
-		{
22
-			ref:           "repository",
23
-			expectedName:  "repository",
24
-			expectedTag:   "latest",
25
-			expectedError: false,
26
-		},
27
-		{
28
-			ref:           "repository:tag",
29
-			expectedName:  "repository",
30
-			expectedTag:   "tag",
31
-			expectedError: false,
32
-		},
33
-		{
34
-			ref:           "test.com/repository",
35
-			expectedName:  "test.com/repository",
36
-			expectedTag:   "latest",
37
-			expectedError: false,
38
-		},
39
-		{
40
-			ref:           "test.com:5000/test/repository",
41
-			expectedName:  "test.com:5000/test/repository",
42
-			expectedTag:   "latest",
43
-			expectedError: false,
44
-		},
45
-		{
46
-			ref:           "test.com:5000/repo@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
47
-			expectedName:  "test.com:5000/repo",
48
-			expectedTag:   "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
49
-			expectedError: false,
50
-		},
51
-		{
52
-			ref:           "test.com:5000/repo:tag@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
53
-			expectedName:  "test.com:5000/repo",
54
-			expectedTag:   "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
55
-			expectedError: false,
56
-		},
57
-	}
58
-
59
-	for _, c := range testCases {
60
-		name, tag, err := Parse(c.ref)
61
-		if err != nil && c.expectedError {
62
-			continue
63
-		} else if err != nil {
64
-			t.Fatalf("error with %s: %s", c.ref, err.Error())
65
-		}
66
-		if name != c.expectedName {
67
-			t.Fatalf("expected name %s, got %s", c.expectedName, name)
68
-		}
69
-		if tag != c.expectedTag {
70
-			t.Fatalf("expected tag %s, got %s", c.expectedTag, tag)
71
-		}
72
-	}
73
-}
... ...
@@ -9,11 +9,11 @@ import (
9 9
 	"os"
10 10
 	"time"
11 11
 
12
+	"github.com/docker/distribution/reference"
12 13
 	"github.com/docker/docker/api/types"
13 14
 	"github.com/docker/docker/api/types/backend"
14 15
 	"github.com/docker/docker/api/types/container"
15 16
 	"github.com/docker/docker/image"
16
-	"github.com/docker/docker/reference"
17 17
 	"golang.org/x/net/context"
18 18
 )
19 19
 
... ...
@@ -11,6 +11,7 @@ import (
11 11
 	"strings"
12 12
 
13 13
 	"github.com/Sirupsen/logrus"
14
+	"github.com/docker/distribution/reference"
14 15
 	apierrors "github.com/docker/docker/api/errors"
15 16
 	"github.com/docker/docker/api/types"
16 17
 	"github.com/docker/docker/api/types/backend"
... ...
@@ -19,7 +20,6 @@ import (
19 19
 	"github.com/docker/docker/builder/dockerfile/parser"
20 20
 	"github.com/docker/docker/image"
21 21
 	"github.com/docker/docker/pkg/stringid"
22
-	"github.com/docker/docker/reference"
23 22
 	perrors "github.com/pkg/errors"
24 23
 	"golang.org/x/net/context"
25 24
 )
... ...
@@ -178,23 +178,16 @@ func sanitizeRepoAndTags(names []string) ([]reference.Named, error) {
178 178
 			continue
179 179
 		}
180 180
 
181
-		ref, err := reference.ParseNamed(repo)
181
+		ref, err := reference.ParseNormalizedNamed(repo)
182 182
 		if err != nil {
183 183
 			return nil, err
184 184
 		}
185 185
 
186
-		ref = reference.WithDefaultTag(ref)
187
-
188 186
 		if _, isCanonical := ref.(reference.Canonical); isCanonical {
189 187
 			return nil, errors.New("build tag cannot contain a digest")
190 188
 		}
191 189
 
192
-		if _, isTagged := ref.(reference.NamedTagged); !isTagged {
193
-			ref, err = reference.WithTag(ref, reference.DefaultTag)
194
-			if err != nil {
195
-				return nil, err
196
-			}
197
-		}
190
+		ref = reference.TagNameOnly(ref)
198 191
 
199 192
 		nameWithTag := ref.String()
200 193
 
... ...
@@ -168,11 +168,7 @@ func createContainer(ctx context.Context, dockerCli *command.DockerCli, config *
168 168
 		return nil, err
169 169
 	}
170 170
 	if named, ok := ref.(reference.Named); ok {
171
-		if reference.IsNameOnly(named) {
172
-			namedRef = reference.EnsureTagged(named)
173
-		} else {
174
-			namedRef = named
175
-		}
171
+		namedRef = reference.TagNameOnly(named)
176 172
 
177 173
 		if taggedRef, ok := namedRef.(reference.NamedTagged); ok && command.IsTrusted() {
178 174
 			var err error
... ...
@@ -94,12 +94,12 @@ func (ctx *DiskUsageContext) Write() {
94 94
 		tag := "<none>"
95 95
 		if len(i.RepoTags) > 0 && !isDangling(*i) {
96 96
 			// Only show the first tag
97
-			ref, err := reference.ParseNamed(i.RepoTags[0])
97
+			ref, err := reference.ParseNormalizedNamed(i.RepoTags[0])
98 98
 			if err != nil {
99 99
 				continue
100 100
 			}
101 101
 			if nt, ok := ref.(reference.NamedTagged); ok {
102
-				repo = ref.Name()
102
+				repo = reference.FamiliarName(ref)
103 103
 				tag = nt.Tag()
104 104
 			}
105 105
 		}
... ...
@@ -94,7 +94,7 @@ func imageFormat(ctx ImageContext, images []types.ImageSummary, format func(subC
94 94
 			repoTags := map[string][]string{}
95 95
 			repoDigests := map[string][]string{}
96 96
 
97
-			for _, refString := range append(image.RepoTags) {
97
+			for _, refString := range image.RepoTags {
98 98
 				ref, err := reference.ParseNormalizedNamed(refString)
99 99
 				if err != nil {
100 100
 					continue
... ...
@@ -104,7 +104,7 @@ func imageFormat(ctx ImageContext, images []types.ImageSummary, format func(subC
104 104
 					repoTags[familiarRef] = append(repoTags[familiarRef], nt.Tag())
105 105
 				}
106 106
 			}
107
-			for _, refString := range append(image.RepoDigests) {
107
+			for _, refString := range image.RepoDigests {
108 108
 				ref, err := reference.ParseNormalizedNamed(refString)
109 109
 				if err != nil {
110 110
 					continue
... ...
@@ -5,7 +5,7 @@ import (
5 5
 	"strings"
6 6
 	"time"
7 7
 
8
-	distreference "github.com/docker/distribution/reference"
8
+	"github.com/docker/distribution/reference"
9 9
 	mounttypes "github.com/docker/docker/api/types/mount"
10 10
 	"github.com/docker/docker/api/types/swarm"
11 11
 	"github.com/docker/docker/cli/command/inspect"
... ...
@@ -409,11 +409,12 @@ func (c *serviceContext) Replicas() string {
409 409
 func (c *serviceContext) Image() string {
410 410
 	c.AddHeader(imageHeader)
411 411
 	image := c.service.Spec.TaskTemplate.ContainerSpec.Image
412
-	if ref, err := distreference.ParseNamed(image); err == nil {
413
-		// update image string for display
414
-		namedTagged, ok := ref.(distreference.NamedTagged)
415
-		if ok {
416
-			image = namedTagged.Name() + ":" + namedTagged.Tag()
412
+	if ref, err := reference.ParseNormalizedNamed(image); err == nil {
413
+		// update image string for display, (strips any digest)
414
+		if nt, ok := ref.(reference.NamedTagged); ok {
415
+			if namedTagged, err := reference.WithTag(reference.TrimNamed(nt), nt.Tag()); err == nil {
416
+				image = reference.FamiliarString(namedTagged)
417
+			}
417 418
 		}
418 419
 	}
419 420
 
... ...
@@ -397,9 +397,7 @@ func rewriteDockerfileFrom(ctx context.Context, dockerfile io.Reader, translator
397 397
 			if err != nil {
398 398
 				return nil, nil, err
399 399
 			}
400
-			if reference.IsNameOnly(ref) {
401
-				ref = reference.EnsureTagged(ref)
402
-			}
400
+			ref = reference.TagNameOnly(ref)
403 401
 			if ref, ok := ref.(reference.NamedTagged); ok && command.IsTrusted() {
404 402
 				trustedRef, err := translator(ctx, ref)
405 403
 				if err != nil {
... ...
@@ -42,7 +42,6 @@ func NewPullCommand(dockerCli *command.DockerCli) *cobra.Command {
42 42
 }
43 43
 
44 44
 func runPull(dockerCli *command.DockerCli, opts pullOptions) error {
45
-	var distributionRef reference.Named
46 45
 	distributionRef, err := reference.ParseNormalizedNamed(opts.remote)
47 46
 	if err != nil {
48 47
 		return err
... ...
@@ -52,9 +51,10 @@ func runPull(dockerCli *command.DockerCli, opts pullOptions) error {
52 52
 	}
53 53
 
54 54
 	if !opts.all && reference.IsNameOnly(distributionRef) {
55
-		taggedRef := reference.EnsureTagged(distributionRef)
56
-		fmt.Fprintf(dockerCli.Out(), "Using default tag: %s\n", taggedRef.Tag())
57
-		distributionRef = taggedRef
55
+		distributionRef = reference.TagNameOnly(distributionRef)
56
+		if tagged, ok := distributionRef.(reference.Tagged); ok {
57
+			fmt.Fprintf(dockerCli.Out(), "Using default tag: %s\n", tagged.Tag())
58
+		}
58 59
 	}
59 60
 
60 61
 	// Resolve the Repository name from fqn to RepositoryInfo
... ...
@@ -129,15 +129,15 @@ func PushTrustedReference(cli *command.DockerCli, repoInfo *registry.RepositoryI
129 129
 
130 130
 		// Initialize the notary repository with a remotely managed snapshot key
131 131
 		if err := repo.Initialize([]string{rootKeyID}, data.CanonicalSnapshotRole); err != nil {
132
-			return trust.NotaryError(repoInfo.FullName(), err)
132
+			return trust.NotaryError(repoInfo.Name.Name(), err)
133 133
 		}
134
-		fmt.Fprintf(cli.Out(), "Finished initializing %q\n", repoInfo.FullName())
134
+		fmt.Fprintf(cli.Out(), "Finished initializing %q\n", repoInfo.Name.Name())
135 135
 		err = repo.AddTarget(target, data.CanonicalTargetsRole)
136 136
 	case nil:
137 137
 		// already initialized and we have successfully downloaded the latest metadata
138 138
 		err = addTargetToAllSignableRoles(repo, target)
139 139
 	default:
140
-		return trust.NotaryError(repoInfo.FullName(), err)
140
+		return trust.NotaryError(repoInfo.Name.Name(), err)
141 141
 	}
142 142
 
143 143
 	if err == nil {
... ...
@@ -145,11 +145,11 @@ func PushTrustedReference(cli *command.DockerCli, repoInfo *registry.RepositoryI
145 145
 	}
146 146
 
147 147
 	if err != nil {
148
-		fmt.Fprintf(cli.Out(), "Failed to sign %q:%s - %s\n", repoInfo.FullName(), tag, err.Error())
149
-		return trust.NotaryError(repoInfo.FullName(), err)
148
+		fmt.Fprintf(cli.Out(), "Failed to sign %q:%s - %s\n", repoInfo.Name.Name(), tag, err.Error())
149
+		return trust.NotaryError(repoInfo.Name.Name(), err)
150 150
 	}
151 151
 
152
-	fmt.Fprintf(cli.Out(), "Successfully signed %q:%s\n", repoInfo.FullName(), tag)
152
+	fmt.Fprintf(cli.Out(), "Successfully signed %q:%s\n", repoInfo.Name.Name(), tag)
153 153
 	return nil
154 154
 }
155 155
 
... ...
@@ -342,12 +342,12 @@ func TrustedReference(ctx context.Context, cli *command.DockerCli, ref reference
342 342
 
343 343
 	t, err := notaryRepo.GetTargetByName(ref.Tag(), trust.ReleasesRole, data.CanonicalTargetsRole)
344 344
 	if err != nil {
345
-		return nil, trust.NotaryError(repoInfo.FullName(), err)
345
+		return nil, trust.NotaryError(repoInfo.Name.Name(), err)
346 346
 	}
347 347
 	// Only list tags in the top level targets role or the releases delegation role - ignore
348 348
 	// all other delegation roles
349 349
 	if t.Role != trust.ReleasesRole && t.Role != data.CanonicalTargetsRole {
350
-		return nil, trust.NotaryError(repoInfo.FullName(), fmt.Errorf("No trust data for %s", ref.Tag()))
350
+		return nil, trust.NotaryError(repoInfo.Name.Name(), fmt.Errorf("No trust data for %s", ref.Tag()))
351 351
 	}
352 352
 	r, err := convertTarget(t.Target)
353 353
 	if err != nil {
... ...
@@ -7,7 +7,6 @@ import (
7 7
 
8 8
 	"github.com/docker/distribution/reference"
9 9
 	"github.com/docker/docker/api/types"
10
-	registrytypes "github.com/docker/docker/api/types/registry"
11 10
 	"github.com/docker/docker/cli"
12 11
 	"github.com/docker/docker/cli/command"
13 12
 	"github.com/docker/docker/cli/command/image"
... ...
@@ -54,20 +53,6 @@ func newInstallCommand(dockerCli *command.DockerCli) *cobra.Command {
54 54
 	return cmd
55 55
 }
56 56
 
57
-func getRepoIndexFromUnnormalizedRef(ref reference.Named) (*registrytypes.IndexInfo, error) {
58
-	named, err := reference.ParseNormalizedNamed(ref.Name())
59
-	if err != nil {
60
-		return nil, err
61
-	}
62
-
63
-	repoInfo, err := registry.ParseRepositoryInfo(named)
64
-	if err != nil {
65
-		return nil, err
66
-	}
67
-
68
-	return repoInfo.Index, nil
69
-}
70
-
71 57
 type pluginRegistryService struct {
72 58
 	registry.Service
73 59
 }
... ...
@@ -104,9 +89,10 @@ func buildPullConfig(ctx context.Context, dockerCli *command.DockerCli, opts plu
104 104
 
105 105
 	_, isCanonical := ref.(reference.Canonical)
106 106
 	if command.IsTrusted() && !isCanonical {
107
+		ref = reference.TagNameOnly(ref)
107 108
 		nt, ok := ref.(reference.NamedTagged)
108 109
 		if !ok {
109
-			nt = reference.EnsureTagged(ref)
110
+			return types.PluginInstallOptions{}, fmt.Errorf("invalid name: %s", ref.String())
110 111
 		}
111 112
 
112 113
 		ctx := context.Background()
... ...
@@ -148,7 +134,7 @@ func runInstall(dockerCli *command.DockerCli, opts pluginOptions) error {
148 148
 		if _, ok := aref.(reference.Canonical); ok {
149 149
 			return fmt.Errorf("invalid name: %s", opts.localName)
150 150
 		}
151
-		localName = reference.FamiliarString(reference.EnsureTagged(aref))
151
+		localName = reference.FamiliarString(reference.TagNameOnly(aref))
152 152
 	}
153 153
 
154 154
 	ctx := context.Background()
... ...
@@ -40,10 +40,7 @@ func runPush(dockerCli *command.DockerCli, name string) error {
40 40
 		return fmt.Errorf("invalid name: %s", name)
41 41
 	}
42 42
 
43
-	taggedRef, ok := named.(reference.NamedTagged)
44
-	if !ok {
45
-		taggedRef = reference.EnsureTagged(named)
46
-	}
43
+	named = reference.TagNameOnly(named)
47 44
 
48 45
 	ctx := context.Background()
49 46
 
... ...
@@ -58,7 +55,7 @@ func runPush(dockerCli *command.DockerCli, name string) error {
58 58
 		return err
59 59
 	}
60 60
 
61
-	responseBody, err := dockerCli.Client().PluginPush(ctx, reference.FamiliarString(taggedRef), encodedAuth)
61
+	responseBody, err := dockerCli.Client().PluginPush(ctx, reference.FamiliarString(named), encodedAuth)
62 62
 	if err != nil {
63 63
 		return err
64 64
 	}
... ...
@@ -5,10 +5,10 @@ import (
5 5
 	"fmt"
6 6
 	"strings"
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/pkg/jsonmessage"
11
-	"github.com/docker/docker/reference"
12 12
 	"github.com/pkg/errors"
13 13
 	"github.com/spf13/cobra"
14 14
 )
... ...
@@ -49,19 +49,19 @@ func runUpgrade(dockerCli *command.DockerCli, opts pluginOptions) error {
49 49
 	if opts.remote == "" {
50 50
 		opts.remote = p.PluginReference
51 51
 	}
52
-	remote, err := reference.ParseNamed(opts.remote)
52
+	remote, err := reference.ParseNormalizedNamed(opts.remote)
53 53
 	if err != nil {
54 54
 		return errors.Wrap(err, "error parsing remote upgrade image reference")
55 55
 	}
56
-	remote = reference.WithDefaultTag(remote)
56
+	remote = reference.TagNameOnly(remote)
57 57
 
58
-	old, err := reference.ParseNamed(p.PluginReference)
58
+	old, err := reference.ParseNormalizedNamed(p.PluginReference)
59 59
 	if err != nil {
60 60
 		return errors.Wrap(err, "error parsing current image reference")
61 61
 	}
62
-	old = reference.WithDefaultTag(old)
62
+	old = reference.TagNameOnly(old)
63 63
 
64
-	fmt.Fprintf(dockerCli.Out(), "Upgrading plugin %s from %s to %s\n", p.Name, old, remote)
64
+	fmt.Fprintf(dockerCli.Out(), "Upgrading plugin %s from %s to %s\n", p.Name, reference.FamiliarString(old), reference.FamiliarString(remote))
65 65
 	if !opts.skipRemoteCheck && remote.String() != old.String() {
66 66
 		if !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), "Plugin images do not match, are you sure?") {
67 67
 			return errors.New("canceling upgrade request")
... ...
@@ -33,10 +33,12 @@ func resolveServiceImageDigest(dockerCli *command.DockerCli, service *swarm.Serv
33 33
 		namedRef, ok := ref.(reference.Named)
34 34
 		if !ok {
35 35
 			return errors.New("failed to resolve image digest using content trust: reference is not named")
36
-
37 36
 		}
38
-
39
-		taggedRef := reference.EnsureTagged(namedRef)
37
+		namedRef = reference.TagNameOnly(namedRef)
38
+		taggedRef, ok := namedRef.(reference.NamedTagged)
39
+		if !ok {
40
+			return errors.New("failed to resolve image digest using content trust: reference is not tagged")
41
+		}
40 42
 
41 43
 		resolvedImage, err := trustedResolveDigest(context.Background(), dockerCli, taggedRef)
42 44
 		if err != nil {
... ...
@@ -65,12 +67,12 @@ func trustedResolveDigest(ctx context.Context, cli *command.DockerCli, ref refer
65 65
 
66 66
 	t, err := notaryRepo.GetTargetByName(ref.Tag(), trust.ReleasesRole, data.CanonicalTargetsRole)
67 67
 	if err != nil {
68
-		return nil, trust.NotaryError(repoInfo.FullName(), err)
68
+		return nil, trust.NotaryError(repoInfo.Name.Name(), err)
69 69
 	}
70 70
 	// Only get the tag if it's in the top level targets role or the releases delegation role
71 71
 	// ignore it if it's in any other delegation roles
72 72
 	if t.Role != trust.ReleasesRole && t.Role != data.CanonicalTargetsRole {
73
-		return nil, trust.NotaryError(repoInfo.FullName(), fmt.Errorf("No trust data for %s", reference.FamiliarString(ref)))
73
+		return nil, trust.NotaryError(repoInfo.Name.Name(), fmt.Errorf("No trust data for %s", reference.FamiliarString(ref)))
74 74
 	}
75 75
 
76 76
 	logrus.Debugf("retrieving target for %s role\n", t.Role)
... ...
@@ -10,7 +10,7 @@ import (
10 10
 
11 11
 	"golang.org/x/net/context"
12 12
 
13
-	distreference "github.com/docker/distribution/reference"
13
+	"github.com/docker/distribution/reference"
14 14
 	"github.com/docker/docker/api/types/swarm"
15 15
 	"github.com/docker/docker/cli/command"
16 16
 	"github.com/docker/docker/cli/command/idresolver"
... ...
@@ -129,13 +129,15 @@ func print(out io.Writer, ctx context.Context, tasks []swarm.Task, resolver *idr
129 129
 
130 130
 		image := task.Spec.ContainerSpec.Image
131 131
 		if !noTrunc {
132
-			ref, err := distreference.ParseNamed(image)
132
+			ref, err := reference.ParseNormalizedNamed(image)
133 133
 			if err == nil {
134
-				// update image string for display
135
-				namedTagged, ok := ref.(distreference.NamedTagged)
136
-				if ok {
137
-					image = namedTagged.Name() + ":" + namedTagged.Tag()
134
+				// update image string for display, (strips any digest)
135
+				if nt, ok := ref.(reference.NamedTagged); ok {
136
+					if namedTagged, err := reference.WithTag(reference.TrimNamed(nt), nt.Tag()); err == nil {
137
+						image = reference.FamiliarString(namedTagged)
138
+					}
138 139
 				}
140
+
139 141
 			}
140 142
 		}
141 143
 
... ...
@@ -148,7 +148,7 @@ func GetNotaryRepository(streams command.Streams, repoInfo *registry.RepositoryI
148 148
 	}
149 149
 
150 150
 	scope := auth.RepositoryScope{
151
-		Repository: repoInfo.FullName(),
151
+		Repository: repoInfo.Name.Name(),
152 152
 		Actions:    actions,
153 153
 		Class:      repoInfo.Class,
154 154
 	}
... ...
@@ -166,7 +166,7 @@ func GetNotaryRepository(streams command.Streams, repoInfo *registry.RepositoryI
166 166
 
167 167
 	return client.NewNotaryRepository(
168 168
 		trustDirectory(),
169
-		repoInfo.FullName(),
169
+		repoInfo.Name.Name(),
170 170
 		server,
171 171
 		tr,
172 172
 		getPassphraseRetriever(streams),
... ...
@@ -5,9 +5,8 @@ import (
5 5
 	"errors"
6 6
 	"net/url"
7 7
 
8
-	distreference "github.com/docker/distribution/reference"
8
+	"github.com/docker/distribution/reference"
9 9
 	"github.com/docker/docker/api/types"
10
-	"github.com/docker/docker/api/types/reference"
11 10
 	"golang.org/x/net/context"
12 11
 )
13 12
 
... ...
@@ -15,17 +14,20 @@ import (
15 15
 func (cli *Client) ContainerCommit(ctx context.Context, container string, options types.ContainerCommitOptions) (types.IDResponse, error) {
16 16
 	var repository, tag string
17 17
 	if options.Reference != "" {
18
-		distributionRef, err := distreference.ParseNamed(options.Reference)
18
+		ref, err := reference.ParseNormalizedNamed(options.Reference)
19 19
 		if err != nil {
20 20
 			return types.IDResponse{}, err
21 21
 		}
22 22
 
23
-		if _, isCanonical := distributionRef.(distreference.Canonical); isCanonical {
23
+		if _, isCanonical := ref.(reference.Canonical); isCanonical {
24 24
 			return types.IDResponse{}, errors.New("refusing to create a tag with a digest reference")
25 25
 		}
26
+		ref = reference.TagNameOnly(ref)
26 27
 
27
-		tag = reference.GetTagFromNamedRef(distributionRef)
28
-		repository = distributionRef.Name()
28
+		if tagged, ok := ref.(reference.Tagged); ok {
29
+			tag = tagged.Tag()
30
+		}
31
+		repository = reference.FamiliarName(ref)
29 32
 	}
30 33
 
31 34
 	query := url.Values{}
... ...
@@ -6,21 +6,21 @@ import (
6 6
 
7 7
 	"golang.org/x/net/context"
8 8
 
9
+	"github.com/docker/distribution/reference"
9 10
 	"github.com/docker/docker/api/types"
10
-	"github.com/docker/docker/api/types/reference"
11 11
 )
12 12
 
13 13
 // ImageCreate creates a new image based in the parent options.
14 14
 // It returns the JSON content in the response body.
15 15
 func (cli *Client) ImageCreate(ctx context.Context, parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) {
16
-	repository, tag, err := reference.Parse(parentReference)
16
+	ref, err := reference.ParseNormalizedNamed(parentReference)
17 17
 	if err != nil {
18 18
 		return nil, err
19 19
 	}
20 20
 
21 21
 	query := url.Values{}
22
-	query.Set("fromImage", repository)
23
-	query.Set("tag", tag)
22
+	query.Set("fromImage", reference.FamiliarName(ref))
23
+	query.Set("tag", getAPITagFromNamedRef(ref))
24 24
 	resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth)
25 25
 	if err != nil {
26 26
 		return nil, err
... ...
@@ -15,7 +15,7 @@ import (
15 15
 func (cli *Client) ImageImport(ctx context.Context, source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) {
16 16
 	if ref != "" {
17 17
 		//Check if the given image name can be resolved
18
-		if _, err := reference.ParseNamed(ref); err != nil {
18
+		if _, err := reference.ParseNormalizedNamed(ref); err != nil {
19 19
 			return nil, err
20 20
 		}
21 21
 	}
... ...
@@ -7,8 +7,8 @@ import (
7 7
 
8 8
 	"golang.org/x/net/context"
9 9
 
10
+	"github.com/docker/distribution/reference"
10 11
 	"github.com/docker/docker/api/types"
11
-	"github.com/docker/docker/api/types/reference"
12 12
 )
13 13
 
14 14
 // ImagePull requests the docker host to pull an image from a remote registry.
... ...
@@ -19,16 +19,16 @@ import (
19 19
 // FIXME(vdemeester): there is currently used in a few way in docker/docker
20 20
 // - if not in trusted content, ref is used to pass the whole reference, and tag is empty
21 21
 // - if in trusted content, ref is used to pass the reference name, and tag for the digest
22
-func (cli *Client) ImagePull(ctx context.Context, ref string, options types.ImagePullOptions) (io.ReadCloser, error) {
23
-	repository, tag, err := reference.Parse(ref)
22
+func (cli *Client) ImagePull(ctx context.Context, refStr string, options types.ImagePullOptions) (io.ReadCloser, error) {
23
+	ref, err := reference.ParseNormalizedNamed(refStr)
24 24
 	if err != nil {
25 25
 		return nil, err
26 26
 	}
27 27
 
28 28
 	query := url.Values{}
29
-	query.Set("fromImage", repository)
30
-	if tag != "" && !options.All {
31
-		query.Set("tag", tag)
29
+	query.Set("fromImage", reference.FamiliarName(ref))
30
+	if !options.All {
31
+		query.Set("tag", getAPITagFromNamedRef(ref))
32 32
 	}
33 33
 
34 34
 	resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth)
... ...
@@ -44,3 +44,18 @@ func (cli *Client) ImagePull(ctx context.Context, ref string, options types.Imag
44 44
 	}
45 45
 	return resp.body, nil
46 46
 }
47
+
48
+// getAPITagFromNamedRef returns a tag from the specified reference.
49
+// This function is necessary as long as the docker "server" api expects
50
+// digests to be sent as tags and makes a distinction between the name
51
+// and tag/digest part of a reference.
52
+func getAPITagFromNamedRef(ref reference.Named) string {
53
+	if digested, ok := ref.(reference.Digested); ok {
54
+		return digested.Digest().String()
55
+	}
56
+	ref = reference.TagNameOnly(ref)
57
+	if tagged, ok := ref.(reference.Tagged); ok {
58
+		return tagged.Tag()
59
+	}
60
+	return ""
61
+}
... ...
@@ -21,7 +21,7 @@ func TestImagePullReferenceParseError(t *testing.T) {
21 21
 	}
22 22
 	// An empty reference is an invalid reference
23 23
 	_, err := client.ImagePull(context.Background(), "", types.ImagePullOptions{})
24
-	if err == nil || err.Error() != "repository name must have at least one component" {
24
+	if err == nil || !strings.Contains(err.Error(), "invalid reference format") {
25 25
 		t.Fatalf("expected an error, got %v", err)
26 26
 	}
27 27
 }
... ...
@@ -8,7 +8,7 @@ import (
8 8
 
9 9
 	"golang.org/x/net/context"
10 10
 
11
-	distreference "github.com/docker/distribution/reference"
11
+	"github.com/docker/distribution/reference"
12 12
 	"github.com/docker/docker/api/types"
13 13
 )
14 14
 
... ...
@@ -16,31 +16,33 @@ import (
16 16
 // It executes the privileged function if the operation is unauthorized
17 17
 // and it tries one more time.
18 18
 // It's up to the caller to handle the io.ReadCloser and close it properly.
19
-func (cli *Client) ImagePush(ctx context.Context, ref string, options types.ImagePushOptions) (io.ReadCloser, error) {
20
-	distributionRef, err := distreference.ParseNamed(ref)
19
+func (cli *Client) ImagePush(ctx context.Context, image string, options types.ImagePushOptions) (io.ReadCloser, error) {
20
+	ref, err := reference.ParseNormalizedNamed(image)
21 21
 	if err != nil {
22 22
 		return nil, err
23 23
 	}
24 24
 
25
-	if _, isCanonical := distributionRef.(distreference.Canonical); isCanonical {
25
+	if _, isCanonical := ref.(reference.Canonical); isCanonical {
26 26
 		return nil, errors.New("cannot push a digest reference")
27 27
 	}
28 28
 
29
-	var tag = ""
30
-	if nameTaggedRef, isNamedTagged := distributionRef.(distreference.NamedTagged); isNamedTagged {
29
+	tag := ""
30
+	name := reference.FamiliarName(ref)
31
+
32
+	if nameTaggedRef, isNamedTagged := ref.(reference.NamedTagged); isNamedTagged {
31 33
 		tag = nameTaggedRef.Tag()
32 34
 	}
33 35
 
34 36
 	query := url.Values{}
35 37
 	query.Set("tag", tag)
36 38
 
37
-	resp, err := cli.tryImagePush(ctx, distributionRef.Name(), query, options.RegistryAuth)
39
+	resp, err := cli.tryImagePush(ctx, name, query, options.RegistryAuth)
38 40
 	if resp.statusCode == http.StatusUnauthorized && options.PrivilegeFunc != nil {
39 41
 		newAuthHeader, privilegeErr := options.PrivilegeFunc()
40 42
 		if privilegeErr != nil {
41 43
 			return nil, privilegeErr
42 44
 		}
43
-		resp, err = cli.tryImagePush(ctx, distributionRef.Name(), query, newAuthHeader)
45
+		resp, err = cli.tryImagePush(ctx, name, query, newAuthHeader)
44 46
 	}
45 47
 	if err != nil {
46 48
 		return nil, err
... ...
@@ -21,7 +21,7 @@ func TestImagePushReferenceError(t *testing.T) {
21 21
 	}
22 22
 	// An empty reference is an invalid reference
23 23
 	_, err := client.ImagePush(context.Background(), "", types.ImagePushOptions{})
24
-	if err == nil || err.Error() != "repository name must have at least one component" {
24
+	if err == nil || !strings.Contains(err.Error(), "invalid reference format") {
25 25
 		t.Fatalf("expected an error, got %v", err)
26 26
 	}
27 27
 	// An canonical reference cannot be pushed
... ...
@@ -3,32 +3,33 @@ package client
3 3
 import (
4 4
 	"net/url"
5 5
 
6
-	distreference "github.com/docker/distribution/reference"
7
-	"github.com/docker/docker/api/types/reference"
6
+	"github.com/docker/distribution/reference"
8 7
 	"github.com/pkg/errors"
9 8
 	"golang.org/x/net/context"
10 9
 )
11 10
 
12 11
 // ImageTag tags an image in the docker host
13 12
 func (cli *Client) ImageTag(ctx context.Context, source, target string) error {
14
-	if _, err := distreference.ParseNamed(source); err != nil {
13
+	if _, err := reference.ParseNormalizedNamed(source); err != nil {
15 14
 		return errors.Wrapf(err, "Error parsing reference: %q is not a valid repository/tag", source)
16 15
 	}
17 16
 
18
-	distributionRef, err := distreference.ParseNamed(target)
17
+	ref, err := reference.ParseNormalizedNamed(target)
19 18
 	if err != nil {
20 19
 		return errors.Wrapf(err, "Error parsing reference: %q is not a valid repository/tag", target)
21 20
 	}
22 21
 
23
-	if _, isCanonical := distributionRef.(distreference.Canonical); isCanonical {
22
+	if _, isCanonical := ref.(reference.Canonical); isCanonical {
24 23
 		return errors.New("refusing to create a tag with a digest reference")
25 24
 	}
26 25
 
27
-	tag := reference.GetTagFromNamedRef(distributionRef)
26
+	ref = reference.TagNameOnly(ref)
28 27
 
29 28
 	query := url.Values{}
30
-	query.Set("repo", distributionRef.Name())
31
-	query.Set("tag", tag)
29
+	query.Set("repo", reference.FamiliarName(ref))
30
+	if tagged, ok := ref.(reference.Tagged); ok {
31
+		query.Set("tag", tagged.Tag())
32
+	}
32 33
 
33 34
 	resp, err := cli.post(ctx, "/images/"+source+"/tag", query, nil, nil)
34 35
 	ensureReaderClosed(resp)
... ...
@@ -15,7 +15,7 @@ import (
15 15
 // PluginInstall installs a plugin
16 16
 func (cli *Client) PluginInstall(ctx context.Context, name string, options types.PluginInstallOptions) (rc io.ReadCloser, err error) {
17 17
 	query := url.Values{}
18
-	if _, err := reference.ParseNamed(options.RemoteRef); err != nil {
18
+	if _, err := reference.ParseNormalizedNamed(options.RemoteRef); err != nil {
19 19
 		return nil, errors.Wrap(err, "invalid remote reference")
20 20
 	}
21 21
 	query.Set("remote", options.RemoteRef)
... ...
@@ -14,7 +14,7 @@ import (
14 14
 // PluginUpgrade upgrades a plugin
15 15
 func (cli *Client) PluginUpgrade(ctx context.Context, name string, options types.PluginInstallOptions) (rc io.ReadCloser, err error) {
16 16
 	query := url.Values{}
17
-	if _, err := reference.ParseNamed(options.RemoteRef); err != nil {
17
+	if _, err := reference.ParseNormalizedNamed(options.RemoteRef); err != nil {
18 18
 		return nil, errors.Wrap(err, "invalid remote reference")
19 19
 	}
20 20
 	query.Set("remote", options.RemoteRef)
... ...
@@ -52,7 +52,7 @@ import (
52 52
 	"time"
53 53
 
54 54
 	"github.com/Sirupsen/logrus"
55
-	distreference "github.com/docker/distribution/reference"
55
+	"github.com/docker/distribution/reference"
56 56
 	apierrors "github.com/docker/docker/api/errors"
57 57
 	apitypes "github.com/docker/docker/api/types"
58 58
 	"github.com/docker/docker/api/types/backend"
... ...
@@ -66,13 +66,11 @@ import (
66 66
 	"github.com/docker/docker/pkg/ioutils"
67 67
 	"github.com/docker/docker/pkg/signal"
68 68
 	"github.com/docker/docker/pkg/stdcopy"
69
-	"github.com/docker/docker/reference"
70 69
 	"github.com/docker/docker/runconfig"
71 70
 	swarmapi "github.com/docker/swarmkit/api"
72 71
 	"github.com/docker/swarmkit/manager/encryption"
73 72
 	swarmnode "github.com/docker/swarmkit/node"
74 73
 	gogotypes "github.com/gogo/protobuf/types"
75
-	"github.com/opencontainers/go-digest"
76 74
 	"github.com/pkg/errors"
77 75
 	"golang.org/x/net/context"
78 76
 )
... ...
@@ -829,50 +827,46 @@ func (c *Cluster) GetServices(options apitypes.ServiceListOptions) ([]types.Serv
829 829
 }
830 830
 
831 831
 // imageWithDigestString takes an image such as name or name:tag
832
-// and returns the image pinned to a digest, such as name@sha256:34234...
833
-// Due to the difference between the docker/docker/reference, and the
834
-// docker/distribution/reference packages, we're parsing the image twice.
835
-// As the two packages converge, this function should be simplified.
836
-// TODO(nishanttotla): After the packages converge, the function must
837
-// convert distreference.Named -> distreference.Canonical, and the logic simplified.
832
+// and returns the image pinned to a digest, such as name@sha256:34234
838 833
 func (c *Cluster) imageWithDigestString(ctx context.Context, image string, authConfig *apitypes.AuthConfig) (string, error) {
839
-	if _, err := digest.Parse(image); err == nil {
840
-		return "", errors.New("image reference is an image ID")
841
-	}
842
-	ref, err := distreference.ParseNamed(image)
834
+	ref, err := reference.ParseAnyReference(image)
843 835
 	if err != nil {
844 836
 		return "", err
845 837
 	}
846
-	// only query registry if not a canonical reference (i.e. with digest)
847
-	if _, ok := ref.(distreference.Canonical); !ok {
848
-		// create a docker/docker/reference Named object because GetRepository needs it
849
-		dockerRef, err := reference.ParseNamed(image)
850
-		if err != nil {
851
-			return "", err
838
+	namedRef, ok := ref.(reference.Named)
839
+	if !ok {
840
+		if _, ok := ref.(reference.Digested); ok {
841
+			return "", errors.New("image reference is an image ID")
852 842
 		}
853
-		dockerRef = reference.WithDefaultTag(dockerRef)
854
-		namedTaggedRef, ok := dockerRef.(reference.NamedTagged)
843
+		return "", errors.Errorf("unknown image reference format: %s", image)
844
+	}
845
+	// only query registry if not a canonical reference (i.e. with digest)
846
+	if _, ok := namedRef.(reference.Canonical); !ok {
847
+		namedRef = reference.TagNameOnly(namedRef)
848
+
849
+		taggedRef, ok := namedRef.(reference.NamedTagged)
855 850
 		if !ok {
856
-			return "", errors.New("unable to cast image to NamedTagged reference object")
851
+			return "", errors.Errorf("image reference not tagged: %s", image)
857 852
 		}
858 853
 
859
-		repo, _, err := c.config.Backend.GetRepository(ctx, namedTaggedRef, authConfig)
854
+		repo, _, err := c.config.Backend.GetRepository(ctx, taggedRef, authConfig)
860 855
 		if err != nil {
861 856
 			return "", err
862 857
 		}
863
-		dscrptr, err := repo.Tags(ctx).Get(ctx, namedTaggedRef.Tag())
858
+		dscrptr, err := repo.Tags(ctx).Get(ctx, taggedRef.Tag())
864 859
 		if err != nil {
865 860
 			return "", err
866 861
 		}
867 862
 
868
-		namedDigestedRef, err := distreference.WithDigest(distreference.EnsureTagged(ref), dscrptr.Digest)
863
+		namedDigestedRef, err := reference.WithDigest(taggedRef, dscrptr.Digest)
869 864
 		if err != nil {
870 865
 			return "", err
871 866
 		}
872
-		return namedDigestedRef.String(), nil
867
+		// return familiar form until interface updated to return type
868
+		return reference.FamiliarString(namedDigestedRef), nil
873 869
 	}
874 870
 	// reference already contains a digest, so just return it
875
-	return ref.String(), nil
871
+	return reference.FamiliarString(ref), nil
876 872
 }
877 873
 
878 874
 // CreateService creates a new service in a managed swarm cluster.
... ...
@@ -5,6 +5,7 @@ import (
5 5
 	"time"
6 6
 
7 7
 	"github.com/docker/distribution"
8
+	"github.com/docker/distribution/reference"
8 9
 	"github.com/docker/docker/api/types"
9 10
 	"github.com/docker/docker/api/types/backend"
10 11
 	"github.com/docker/docker/api/types/container"
... ...
@@ -14,7 +15,6 @@ import (
14 14
 	swarmtypes "github.com/docker/docker/api/types/swarm"
15 15
 	clustertypes "github.com/docker/docker/daemon/cluster/provider"
16 16
 	"github.com/docker/docker/plugin"
17
-	"github.com/docker/docker/reference"
18 17
 	"github.com/docker/libnetwork"
19 18
 	"github.com/docker/libnetwork/cluster"
20 19
 	networktypes "github.com/docker/libnetwork/types"
... ...
@@ -12,13 +12,13 @@ import (
12 12
 	"time"
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/api/types/backend"
17 18
 	containertypes "github.com/docker/docker/api/types/container"
18 19
 	"github.com/docker/docker/api/types/events"
19 20
 	"github.com/docker/docker/daemon/cluster/convert"
20 21
 	executorpkg "github.com/docker/docker/daemon/cluster/executor"
21
-	"github.com/docker/docker/reference"
22 22
 	"github.com/docker/libnetwork"
23 23
 	"github.com/docker/swarmkit/agent/exec"
24 24
 	"github.com/docker/swarmkit/api"
... ...
@@ -61,7 +61,7 @@ func (c *containerAdapter) pullImage(ctx context.Context) error {
61 61
 
62 62
 	// Skip pulling if the image is referenced by digest and already
63 63
 	// exists locally.
64
-	named, err := reference.ParseNamed(spec.Image)
64
+	named, err := reference.ParseNormalizedNamed(spec.Image)
65 65
 	if err == nil {
66 66
 		if _, ok := named.(reference.Canonical); ok {
67 67
 			_, err := c.backend.LookupImage(spec.Image)
... ...
@@ -10,6 +10,7 @@ import (
10 10
 
11 11
 	"github.com/Sirupsen/logrus"
12 12
 
13
+	"github.com/docker/distribution/reference"
13 14
 	"github.com/docker/docker/api/types"
14 15
 	enginecontainer "github.com/docker/docker/api/types/container"
15 16
 	"github.com/docker/docker/api/types/events"
... ...
@@ -18,7 +19,6 @@ import (
18 18
 	"github.com/docker/docker/api/types/network"
19 19
 	volumetypes "github.com/docker/docker/api/types/volume"
20 20
 	clustertypes "github.com/docker/docker/daemon/cluster/provider"
21
-	"github.com/docker/docker/reference"
22 21
 	"github.com/docker/go-connections/nat"
23 22
 	"github.com/docker/swarmkit/agent/exec"
24 23
 	"github.com/docker/swarmkit/api"
... ...
@@ -132,11 +132,11 @@ func (c *containerConfig) name() string {
132 132
 
133 133
 func (c *containerConfig) image() string {
134 134
 	raw := c.spec().Image
135
-	ref, err := reference.ParseNamed(raw)
135
+	ref, err := reference.ParseNormalizedNamed(raw)
136 136
 	if err != nil {
137 137
 		return raw
138 138
 	}
139
-	return reference.WithDefaultTag(ref).String()
139
+	return reference.FamiliarString(reference.TagNameOnly(ref))
140 140
 }
141 141
 
142 142
 func (c *containerConfig) portBindings() nat.PortMap {
... ...
@@ -2,12 +2,12 @@ package daemon
2 2
 
3 3
 import (
4 4
 	"encoding/json"
5
-	"fmt"
6 5
 	"io"
7 6
 	"runtime"
8 7
 	"strings"
9 8
 	"time"
10 9
 
10
+	"github.com/docker/distribution/reference"
11 11
 	"github.com/docker/docker/api/types/backend"
12 12
 	containertypes "github.com/docker/docker/api/types/container"
13 13
 	"github.com/docker/docker/builder/dockerfile"
... ...
@@ -16,7 +16,7 @@ import (
16 16
 	"github.com/docker/docker/image"
17 17
 	"github.com/docker/docker/layer"
18 18
 	"github.com/docker/docker/pkg/ioutils"
19
-	"github.com/docker/docker/reference"
19
+	"github.com/pkg/errors"
20 20
 )
21 21
 
22 22
 // merge merges two Config, the image container configuration (defaults values),
... ...
@@ -128,7 +128,7 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str
128 128
 
129 129
 	// It is not possible to commit a running container on Windows and on Solaris.
130 130
 	if (runtime.GOOS == "windows" || runtime.GOOS == "solaris") && container.IsRunning() {
131
-		return "", fmt.Errorf("%+v does not support commit of a running container", runtime.GOOS)
131
+		return "", errors.Errorf("%+v does not support commit of a running container", runtime.GOOS)
132 132
 	}
133 133
 
134 134
 	if c.Pause && !container.IsPaused() {
... ...
@@ -228,10 +228,13 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str
228 228
 
229 229
 	imageRef := ""
230 230
 	if c.Repo != "" {
231
-		newTag, err := reference.WithName(c.Repo) // todo: should move this to API layer
231
+		newTag, err := reference.ParseNormalizedNamed(c.Repo) // todo: should move this to API layer
232 232
 		if err != nil {
233 233
 			return "", err
234 234
 		}
235
+		if !reference.IsNameOnly(newTag) {
236
+			return "", errors.Errorf("unexpected repository name: %s", c.Repo)
237
+		}
235 238
 		if c.Tag != "" {
236 239
 			if newTag, err = reference.WithTag(newTag, c.Tag); err != nil {
237 240
 				return "", err
... ...
@@ -240,7 +243,7 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str
240 240
 		if err := daemon.TagImageWithReference(id, newTag); err != nil {
241 241
 			return "", err
242 242
 		}
243
-		imageRef = newTag.String()
243
+		imageRef = reference.FamiliarString(newTag)
244 244
 	}
245 245
 
246 246
 	attributes := map[string]string{
... ...
@@ -46,7 +46,7 @@ import (
46 46
 	"github.com/docker/docker/pkg/system"
47 47
 	"github.com/docker/docker/pkg/truncindex"
48 48
 	"github.com/docker/docker/plugin"
49
-	"github.com/docker/docker/reference"
49
+	refstore "github.com/docker/docker/reference"
50 50
 	"github.com/docker/docker/registry"
51 51
 	"github.com/docker/docker/runconfig"
52 52
 	volumedrivers "github.com/docker/docker/volume/drivers"
... ...
@@ -76,7 +76,7 @@ type Daemon struct {
76 76
 	repository                string
77 77
 	containers                container.Store
78 78
 	execCommands              *exec.Store
79
-	referenceStore            reference.Store
79
+	referenceStore            refstore.Store
80 80
 	downloadManager           *xfer.LayerDownloadManager
81 81
 	uploadManager             *xfer.LayerUploadManager
82 82
 	distributionMetadataStore dmetadata.Store
... ...
@@ -637,7 +637,7 @@ func NewDaemon(config *Config, registryService registry.Service, containerdRemot
637 637
 
638 638
 	eventsService := events.New()
639 639
 
640
-	referenceStore, err := reference.NewReferenceStore(filepath.Join(imageRoot, "repositories.json"))
640
+	referenceStore, err := refstore.NewReferenceStore(filepath.Join(imageRoot, "repositories.json"))
641 641
 	if err != nil {
642 642
 		return nil, fmt.Errorf("Couldn't create Tag store repositories: %s", err)
643 643
 	}
... ...
@@ -16,7 +16,7 @@ import (
16 16
 	"github.com/docker/docker/pkg/idtools"
17 17
 	"github.com/docker/docker/pkg/parsers/kernel"
18 18
 	"github.com/docker/docker/pkg/sysinfo"
19
-	"github.com/docker/docker/reference"
19
+	refstore "github.com/docker/docker/reference"
20 20
 	"github.com/docker/libnetwork"
21 21
 	nwconfig "github.com/docker/libnetwork/config"
22 22
 	"github.com/docker/libnetwork/drivers/solaris/bridge"
... ...
@@ -491,7 +491,7 @@ func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container
491 491
 	return daemon.Unmount(container)
492 492
 }
493 493
 
494
-func restoreCustomImage(is image.Store, ls layer.Store, rs reference.Store) error {
494
+func restoreCustomImage(is image.Store, ls layer.Store, rs refstore.Store) error {
495 495
 	// Solaris has no custom images to register
496 496
 	return nil
497 497
 }
... ...
@@ -2,29 +2,13 @@ package daemon
2 2
 
3 3
 import (
4 4
 	"fmt"
5
-	"strings"
6 5
 
7 6
 	"github.com/docker/docker/api/errors"
8
-	"github.com/docker/docker/reference"
9 7
 )
10 8
 
11 9
 func (d *Daemon) imageNotExistToErrcode(err error) error {
12 10
 	if dne, isDNE := err.(ErrImageDoesNotExist); isDNE {
13
-		if strings.Contains(dne.RefOrID, "@") {
14
-			e := fmt.Errorf("No such image: %s", dne.RefOrID)
15
-			return errors.NewRequestNotFoundError(e)
16
-		}
17
-		tag := reference.DefaultTag
18
-		ref, err := reference.ParseNamed(dne.RefOrID)
19
-		if err != nil {
20
-			e := fmt.Errorf("No such image: %s:%s", dne.RefOrID, tag)
21
-			return errors.NewRequestNotFoundError(e)
22
-		}
23
-		if tagged, isTagged := ref.(reference.NamedTagged); isTagged {
24
-			tag = tagged.Tag()
25
-		}
26
-		e := fmt.Errorf("No such image: %s:%s", ref.Name(), tag)
27
-		return errors.NewRequestNotFoundError(e)
11
+		return errors.NewRequestNotFoundError(dne)
28 12
 	}
29 13
 	return err
30 14
 }
... ...
@@ -1,9 +1,9 @@
1 1
 package events
2 2
 
3 3
 import (
4
+	"github.com/docker/distribution/reference"
4 5
 	"github.com/docker/docker/api/types/events"
5 6
 	"github.com/docker/docker/api/types/filters"
6
-	"github.com/docker/docker/reference"
7 7
 )
8 8
 
9 9
 // Filter can filter out docker events from a stream
... ...
@@ -102,9 +102,9 @@ func (ef *Filter) matchImage(ev events.Message) bool {
102 102
 }
103 103
 
104 104
 func stripTag(image string) string {
105
-	ref, err := reference.ParseNamed(image)
105
+	ref, err := reference.ParseNormalizedNamed(image)
106 106
 	if err != nil {
107 107
 		return image
108 108
 	}
109
-	return ref.Name()
109
+	return reference.FamiliarName(ref)
110 110
 }
... ...
@@ -3,45 +3,55 @@ package daemon
3 3
 import (
4 4
 	"fmt"
5 5
 
6
+	"github.com/docker/distribution/reference"
6 7
 	"github.com/docker/docker/builder"
7 8
 	"github.com/docker/docker/image"
8 9
 	"github.com/docker/docker/pkg/stringid"
9
-	"github.com/docker/docker/reference"
10 10
 )
11 11
 
12 12
 // ErrImageDoesNotExist is error returned when no image can be found for a reference.
13 13
 type ErrImageDoesNotExist struct {
14
-	RefOrID string
14
+	ref reference.Reference
15 15
 }
16 16
 
17 17
 func (e ErrImageDoesNotExist) Error() string {
18
-	return fmt.Sprintf("no such id: %s", e.RefOrID)
18
+	ref := e.ref
19
+	if named, ok := ref.(reference.Named); ok {
20
+		ref = reference.TagNameOnly(named)
21
+	}
22
+	return fmt.Sprintf("No such image: %s", reference.FamiliarString(ref))
19 23
 }
20 24
 
21 25
 // GetImageID returns an image ID corresponding to the image referred to by
22 26
 // refOrID.
23 27
 func (daemon *Daemon) GetImageID(refOrID string) (image.ID, error) {
24
-	id, ref, err := reference.ParseIDOrReference(refOrID)
28
+	ref, err := reference.ParseAnyReference(refOrID)
25 29
 	if err != nil {
26 30
 		return "", err
27 31
 	}
28
-	if id != "" {
29
-		if _, err := daemon.imageStore.Get(image.IDFromDigest(id)); err != nil {
30
-			return "", ErrImageDoesNotExist{refOrID}
32
+	namedRef, ok := ref.(reference.Named)
33
+	if !ok {
34
+		digested, ok := ref.(reference.Digested)
35
+		if !ok {
36
+			return "", ErrImageDoesNotExist{ref}
31 37
 		}
32
-		return image.IDFromDigest(id), nil
38
+		id := image.IDFromDigest(digested.Digest())
39
+		if _, err := daemon.imageStore.Get(id); err != nil {
40
+			return "", ErrImageDoesNotExist{ref}
41
+		}
42
+		return id, nil
33 43
 	}
34 44
 
35
-	if id, err := daemon.referenceStore.Get(ref); err == nil {
45
+	if id, err := daemon.referenceStore.Get(namedRef); err == nil {
36 46
 		return image.IDFromDigest(id), nil
37 47
 	}
38 48
 
39 49
 	// deprecated: repo:shortid https://github.com/docker/docker/pull/799
40
-	if tagged, ok := ref.(reference.NamedTagged); ok {
50
+	if tagged, ok := namedRef.(reference.Tagged); ok {
41 51
 		if tag := tagged.Tag(); stringid.IsShortID(stringid.TruncateID(tag)) {
42 52
 			if id, err := daemon.imageStore.Search(tag); err == nil {
43
-				for _, namedRef := range daemon.referenceStore.References(id.Digest()) {
44
-					if namedRef.Name() == ref.Name() {
53
+				for _, storeRef := range daemon.referenceStore.References(id.Digest()) {
54
+					if storeRef.Name() == namedRef.Name() {
45 55
 						return id, nil
46 56
 					}
47 57
 				}
... ...
@@ -54,7 +64,7 @@ func (daemon *Daemon) GetImageID(refOrID string) (image.ID, error) {
54 54
 		return id, nil
55 55
 	}
56 56
 
57
-	return "", ErrImageDoesNotExist{refOrID}
57
+	return "", ErrImageDoesNotExist{ref}
58 58
 }
59 59
 
60 60
 // GetImage returns an image corresponding to the image referred to by refOrID.
... ...
@@ -5,12 +5,12 @@ import (
5 5
 	"strings"
6 6
 	"time"
7 7
 
8
+	"github.com/docker/distribution/reference"
8 9
 	"github.com/docker/docker/api/errors"
9 10
 	"github.com/docker/docker/api/types"
10 11
 	"github.com/docker/docker/container"
11 12
 	"github.com/docker/docker/image"
12 13
 	"github.com/docker/docker/pkg/stringid"
13
-	"github.com/docker/docker/reference"
14 14
 )
15 15
 
16 16
 type conflictType int
... ...
@@ -89,7 +89,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
89 89
 			}
90 90
 		}
91 91
 
92
-		parsedRef, err := reference.ParseNamed(imageRef)
92
+		parsedRef, err := reference.ParseNormalizedNamed(imageRef)
93 93
 		if err != nil {
94 94
 			return nil, err
95 95
 		}
... ...
@@ -99,7 +99,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
99 99
 			return nil, err
100 100
 		}
101 101
 
102
-		untaggedRecord := types.ImageDeleteResponseItem{Untagged: parsedRef.String()}
102
+		untaggedRecord := types.ImageDeleteResponseItem{Untagged: reference.FamiliarString(parsedRef)}
103 103
 
104 104
 		daemon.LogImageEvent(imgID.String(), imgID.String(), "untag")
105 105
 		records = append(records, untaggedRecord)
... ...
@@ -126,7 +126,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
126 126
 							return records, err
127 127
 						}
128 128
 
129
-						untaggedRecord := types.ImageDeleteResponseItem{Untagged: repoRef.String()}
129
+						untaggedRecord := types.ImageDeleteResponseItem{Untagged: reference.FamiliarString(repoRef)}
130 130
 						records = append(records, untaggedRecord)
131 131
 					} else {
132 132
 						remainingRefs = append(remainingRefs, repoRef)
... ...
@@ -162,7 +162,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
162 162
 					return nil, err
163 163
 				}
164 164
 
165
-				untaggedRecord := types.ImageDeleteResponseItem{Untagged: parsedRef.String()}
165
+				untaggedRecord := types.ImageDeleteResponseItem{Untagged: reference.FamiliarString(parsedRef)}
166 166
 
167 167
 				daemon.LogImageEvent(imgID.String(), imgID.String(), "untag")
168 168
 				records = append(records, untaggedRecord)
... ...
@@ -232,7 +232,8 @@ func (daemon *Daemon) getContainerUsingImage(imageID image.ID) *container.Contai
232 232
 // optional tag or digest reference. If tag or digest is omitted, the default
233 233
 // tag is used. Returns the resolved image reference and an error.
234 234
 func (daemon *Daemon) removeImageRef(ref reference.Named) (reference.Named, error) {
235
-	ref = reference.WithDefaultTag(ref)
235
+	ref = reference.TagNameOnly(ref)
236
+
236 237
 	// Ignore the boolean value returned, as far as we're concerned, this
237 238
 	// is an idempotent operation and it's okay if the reference didn't
238 239
 	// exist in the first place.
... ...
@@ -255,7 +256,7 @@ func (daemon *Daemon) removeAllReferencesToImageID(imgID image.ID, records *[]ty
255 255
 			return err
256 256
 		}
257 257
 
258
-		untaggedRecord := types.ImageDeleteResponseItem{Untagged: parsedRef.String()}
258
+		untaggedRecord := types.ImageDeleteResponseItem{Untagged: reference.FamiliarString(parsedRef)}
259 259
 
260 260
 		daemon.LogImageEvent(imgID.String(), imgID.String(), "untag")
261 261
 		*records = append(*records, untaggedRecord)
... ...
@@ -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/image"
8 9
 	"github.com/docker/docker/layer"
9
-	"github.com/docker/docker/reference"
10 10
 )
11 11
 
12 12
 // ImageHistory returns a slice of ImageHistory structures for the specified image
... ...
@@ -64,7 +64,7 @@ func (daemon *Daemon) ImageHistory(name string) ([]*image.HistoryResponseItem, e
64 64
 		var tags []string
65 65
 		for _, r := range daemon.referenceStore.References(id.Digest()) {
66 66
 			if _, ok := r.(reference.NamedTagged); ok {
67
-				tags = append(tags, r.String())
67
+				tags = append(tags, reference.FamiliarString(r))
68 68
 			}
69 69
 		}
70 70
 
... ...
@@ -3,9 +3,9 @@ package daemon
3 3
 import (
4 4
 	"time"
5 5
 
6
+	"github.com/docker/distribution/reference"
6 7
 	"github.com/docker/docker/api/types"
7 8
 	"github.com/docker/docker/layer"
8
-	"github.com/docker/docker/reference"
9 9
 	"github.com/pkg/errors"
10 10
 )
11 11
 
... ...
@@ -23,9 +23,9 @@ func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) {
23 23
 	for _, ref := range refs {
24 24
 		switch ref.(type) {
25 25
 		case reference.NamedTagged:
26
-			repoTags = append(repoTags, ref.String())
26
+			repoTags = append(repoTags, reference.FamiliarString(ref))
27 27
 		case reference.Canonical:
28
-			repoDigests = append(repoDigests, ref.String())
28
+			repoDigests = append(repoDigests, reference.FamiliarString(ref))
29 29
 		}
30 30
 	}
31 31
 
... ...
@@ -5,12 +5,12 @@ import (
5 5
 	"strings"
6 6
 
7 7
 	dist "github.com/docker/distribution"
8
+	"github.com/docker/distribution/reference"
8 9
 	"github.com/docker/docker/api/types"
9 10
 	"github.com/docker/docker/builder"
10 11
 	"github.com/docker/docker/distribution"
11 12
 	progressutils "github.com/docker/docker/distribution/utils"
12 13
 	"github.com/docker/docker/pkg/progress"
13
-	"github.com/docker/docker/reference"
14 14
 	"github.com/docker/docker/registry"
15 15
 	"github.com/opencontainers/go-digest"
16 16
 	"golang.org/x/net/context"
... ...
@@ -24,7 +24,7 @@ func (daemon *Daemon) PullImage(ctx context.Context, image, tag string, metaHead
24 24
 	// compatibility.
25 25
 	image = strings.TrimSuffix(image, ":")
26 26
 
27
-	ref, err := reference.ParseNamed(image)
27
+	ref, err := reference.ParseNormalizedNamed(image)
28 28
 	if err != nil {
29 29
 		return err
30 30
 	}
... ...
@@ -48,11 +48,11 @@ func (daemon *Daemon) PullImage(ctx context.Context, image, tag string, metaHead
48 48
 
49 49
 // PullOnBuild tells Docker to pull image referenced by `name`.
50 50
 func (daemon *Daemon) PullOnBuild(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer) (builder.Image, error) {
51
-	ref, err := reference.ParseNamed(name)
51
+	ref, err := reference.ParseNormalizedNamed(name)
52 52
 	if err != nil {
53 53
 		return nil, err
54 54
 	}
55
-	ref = reference.WithDefaultTag(ref)
55
+	ref = reference.TagNameOnly(ref)
56 56
 
57 57
 	pullRegistryAuth := &types.AuthConfig{}
58 58
 	if len(authConfigs) > 0 {
... ...
@@ -118,12 +118,12 @@ func (daemon *Daemon) GetRepository(ctx context.Context, ref reference.NamedTagg
118 118
 		return nil, false, err
119 119
 	}
120 120
 	// makes sure name is not empty or `scratch`
121
-	if err := distribution.ValidateRepoName(repoInfo.Name()); err != nil {
121
+	if err := distribution.ValidateRepoName(repoInfo.Name); err != nil {
122 122
 		return nil, false, err
123 123
 	}
124 124
 
125 125
 	// get endpoints
126
-	endpoints, err := daemon.RegistryService.LookupPullEndpoints(repoInfo.Hostname())
126
+	endpoints, err := daemon.RegistryService.LookupPullEndpoints(reference.Domain(repoInfo.Name))
127 127
 	if err != nil {
128 128
 		return nil, false, err
129 129
 	}
... ...
@@ -4,17 +4,17 @@ import (
4 4
 	"io"
5 5
 
6 6
 	"github.com/docker/distribution/manifest/schema2"
7
+	"github.com/docker/distribution/reference"
7 8
 	"github.com/docker/docker/api/types"
8 9
 	"github.com/docker/docker/distribution"
9 10
 	progressutils "github.com/docker/docker/distribution/utils"
10 11
 	"github.com/docker/docker/pkg/progress"
11
-	"github.com/docker/docker/reference"
12 12
 	"golang.org/x/net/context"
13 13
 )
14 14
 
15 15
 // PushImage initiates a push operation on the repository named localName.
16 16
 func (daemon *Daemon) PushImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
17
-	ref, err := reference.ParseNamed(image)
17
+	ref, err := reference.ParseNormalizedNamed(image)
18 18
 	if err != nil {
19 19
 		return err
20 20
 	}
... ...
@@ -1,8 +1,8 @@
1 1
 package daemon
2 2
 
3 3
 import (
4
+	"github.com/docker/distribution/reference"
4 5
 	"github.com/docker/docker/image"
5
-	"github.com/docker/docker/reference"
6 6
 )
7 7
 
8 8
 // TagImage creates the tag specified by newTag, pointing to the image named
... ...
@@ -13,12 +13,12 @@ func (daemon *Daemon) TagImage(imageName, repository, tag string) error {
13 13
 		return err
14 14
 	}
15 15
 
16
-	newTag, err := reference.WithName(repository)
16
+	newTag, err := reference.ParseNormalizedNamed(repository)
17 17
 	if err != nil {
18 18
 		return err
19 19
 	}
20 20
 	if tag != "" {
21
-		if newTag, err = reference.WithTag(newTag, tag); err != nil {
21
+		if newTag, err = reference.WithTag(reference.TrimNamed(newTag), tag); err != nil {
22 22
 			return err
23 23
 		}
24 24
 	}
... ...
@@ -32,6 +32,6 @@ func (daemon *Daemon) TagImageWithReference(imageID image.ID, newTag reference.N
32 32
 		return err
33 33
 	}
34 34
 
35
-	daemon.LogImageEvent(imageID.String(), newTag.String(), "tag")
35
+	daemon.LogImageEvent(imageID.String(), reference.FamiliarString(newTag), "tag")
36 36
 	return nil
37 37
 }
... ...
@@ -135,7 +135,7 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs
135 135
 				var found bool
136 136
 				var matchErr error
137 137
 				for _, pattern := range imageFilters.Get("reference") {
138
-					found, matchErr = reference.Match(pattern, ref)
138
+					found, matchErr = reference.FamiliarMatch(pattern, ref)
139 139
 					if matchErr != nil {
140 140
 						return nil, matchErr
141 141
 					}
... ...
@@ -145,10 +145,10 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs
145 145
 				}
146 146
 			}
147 147
 			if _, ok := ref.(reference.Canonical); ok {
148
-				newImage.RepoDigests = append(newImage.RepoDigests, ref.String())
148
+				newImage.RepoDigests = append(newImage.RepoDigests, reference.FamiliarString(ref))
149 149
 			}
150 150
 			if _, ok := ref.(reference.NamedTagged); ok {
151
-				newImage.RepoTags = append(newImage.RepoTags, ref.String())
151
+				newImage.RepoTags = append(newImage.RepoTags, reference.FamiliarString(ref))
152 152
 			}
153 153
 		}
154 154
 		if newImage.RepoDigests == nil && newImage.RepoTags == nil {
... ...
@@ -2,13 +2,13 @@ package daemon
2 2
 
3 3
 import (
4 4
 	"encoding/json"
5
-	"errors"
6 5
 	"io"
7 6
 	"net/http"
8 7
 	"net/url"
9 8
 	"runtime"
10 9
 	"time"
11 10
 
11
+	"github.com/docker/distribution/reference"
12 12
 	"github.com/docker/docker/api/types/container"
13 13
 	"github.com/docker/docker/builder/dockerfile"
14 14
 	"github.com/docker/docker/dockerversion"
... ...
@@ -18,7 +18,7 @@ import (
18 18
 	"github.com/docker/docker/pkg/httputils"
19 19
 	"github.com/docker/docker/pkg/progress"
20 20
 	"github.com/docker/docker/pkg/streamformatter"
21
-	"github.com/docker/docker/reference"
21
+	"github.com/pkg/errors"
22 22
 )
23 23
 
24 24
 // ImportImage imports an image, getting the archived layer data either from
... ...
@@ -35,11 +35,10 @@ func (daemon *Daemon) ImportImage(src string, repository, tag string, msg string
35 35
 
36 36
 	if repository != "" {
37 37
 		var err error
38
-		newRef, err = reference.ParseNamed(repository)
38
+		newRef, err = reference.ParseNormalizedNamed(repository)
39 39
 		if err != nil {
40 40
 			return err
41 41
 		}
42
-
43 42
 		if _, isCanonical := newRef.(reference.Canonical); isCanonical {
44 43
 			return errors.New("cannot import digest reference")
45 44
 		}
... ...
@@ -6,13 +6,13 @@ import (
6 6
 	"time"
7 7
 
8 8
 	"github.com/Sirupsen/logrus"
9
+	"github.com/docker/distribution/reference"
9 10
 	"github.com/docker/docker/api/types"
10 11
 	"github.com/docker/docker/api/types/filters"
11 12
 	timetypes "github.com/docker/docker/api/types/time"
12 13
 	"github.com/docker/docker/image"
13 14
 	"github.com/docker/docker/layer"
14 15
 	"github.com/docker/docker/pkg/directory"
15
-	"github.com/docker/docker/reference"
16 16
 	"github.com/docker/docker/runconfig"
17 17
 	"github.com/docker/docker/volume"
18 18
 	"github.com/docker/libnetwork"
... ...
@@ -14,7 +14,7 @@ import (
14 14
 	"github.com/docker/docker/image"
15 15
 	"github.com/docker/docker/layer"
16 16
 	"github.com/docker/docker/pkg/progress"
17
-	"github.com/docker/docker/reference"
17
+	refstore "github.com/docker/docker/reference"
18 18
 	"github.com/docker/docker/registry"
19 19
 	"github.com/docker/libtrust"
20 20
 	"github.com/opencontainers/go-digest"
... ...
@@ -44,7 +44,7 @@ type Config struct {
44 44
 	ImageStore ImageConfigStore
45 45
 	// ReferenceStore manages tags. This value is optional, when excluded
46 46
 	// content will not be tagged.
47
-	ReferenceStore reference.Store
47
+	ReferenceStore refstore.Store
48 48
 	// RequireSchema2 ensures that only schema2 manifests are used.
49 49
 	RequireSchema2 bool
50 50
 }
... ...
@@ -7,12 +7,12 @@ import (
7 7
 
8 8
 	"github.com/Sirupsen/logrus"
9 9
 	"github.com/docker/distribution"
10
+	"github.com/docker/distribution/reference"
10 11
 	"github.com/docker/distribution/registry/api/errcode"
11 12
 	"github.com/docker/distribution/registry/api/v2"
12 13
 	"github.com/docker/distribution/registry/client"
13 14
 	"github.com/docker/distribution/registry/client/auth"
14 15
 	"github.com/docker/docker/distribution/xfer"
15
-	"github.com/docker/docker/reference"
16 16
 	"github.com/pkg/errors"
17 17
 )
18 18
 
... ...
@@ -78,11 +78,11 @@ func TranslatePullError(err error, ref reference.Named) error {
78 78
 		switch v.Code {
79 79
 		case errcode.ErrorCodeDenied:
80 80
 			// ErrorCodeDenied is used when access to the repository was denied
81
-			newErr = errors.Errorf("repository %s not found: does not exist or no pull access", ref.Name())
81
+			newErr = errors.Errorf("repository %s not found: does not exist or no pull access", reference.FamiliarName(ref))
82 82
 		case v2.ErrorCodeManifestUnknown:
83
-			newErr = errors.Errorf("manifest for %s not found", ref.String())
83
+			newErr = errors.Errorf("manifest for %s not found", reference.FamiliarString(ref))
84 84
 		case v2.ErrorCodeNameUnknown:
85
-			newErr = errors.Errorf("repository %s not found", ref.Name())
85
+			newErr = errors.Errorf("repository %s not found", reference.FamiliarName(ref))
86 86
 		}
87 87
 		if newErr != nil {
88 88
 			logrus.Infof("Translating %q to %q", err, newErr)
... ...
@@ -1,14 +1,14 @@
1 1
 package distribution
2 2
 
3 3
 import (
4
-	"errors"
5 4
 	"fmt"
6 5
 
7 6
 	"github.com/Sirupsen/logrus"
7
+	"github.com/docker/distribution/reference"
8 8
 	"github.com/docker/docker/api"
9 9
 	"github.com/docker/docker/distribution/metadata"
10 10
 	"github.com/docker/docker/pkg/progress"
11
-	"github.com/docker/docker/reference"
11
+	refstore "github.com/docker/docker/reference"
12 12
 	"github.com/docker/docker/registry"
13 13
 	"github.com/opencontainers/go-digest"
14 14
 	"golang.org/x/net/context"
... ...
@@ -56,12 +56,12 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo
56 56
 		return err
57 57
 	}
58 58
 
59
-	// makes sure name is not empty or `scratch`
60
-	if err := ValidateRepoName(repoInfo.Name()); err != nil {
59
+	// makes sure name is not `scratch`
60
+	if err := ValidateRepoName(repoInfo.Name); err != nil {
61 61
 		return err
62 62
 	}
63 63
 
64
-	endpoints, err := imagePullConfig.RegistryService.LookupPullEndpoints(repoInfo.Hostname())
64
+	endpoints, err := imagePullConfig.RegistryService.LookupPullEndpoints(reference.Domain(repoInfo.Name))
65 65
 	if err != nil {
66 66
 		return err
67 67
 	}
... ...
@@ -105,7 +105,7 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo
105 105
 			}
106 106
 		}
107 107
 
108
-		logrus.Debugf("Trying to pull %s from %s %s", repoInfo.Name(), endpoint.URL, endpoint.Version)
108
+		logrus.Debugf("Trying to pull %s from %s %s", reference.FamiliarName(repoInfo.Name), endpoint.URL, endpoint.Version)
109 109
 
110 110
 		puller, err := newPuller(endpoint, repoInfo, imagePullConfig)
111 111
 		if err != nil {
... ...
@@ -147,12 +147,12 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo
147 147
 			return TranslatePullError(err, ref)
148 148
 		}
149 149
 
150
-		imagePullConfig.ImageEventLogger(ref.String(), repoInfo.Name(), "pull")
150
+		imagePullConfig.ImageEventLogger(reference.FamiliarString(ref), reference.FamiliarName(repoInfo.Name), "pull")
151 151
 		return nil
152 152
 	}
153 153
 
154 154
 	if lastErr == nil {
155
-		lastErr = fmt.Errorf("no endpoints found for %s", ref.String())
155
+		lastErr = fmt.Errorf("no endpoints found for %s", reference.FamiliarString(ref))
156 156
 	}
157 157
 
158 158
 	return TranslatePullError(lastErr, ref)
... ...
@@ -171,17 +171,14 @@ func writeStatus(requestedTag string, out progress.Output, layersDownloaded bool
171 171
 }
172 172
 
173 173
 // ValidateRepoName validates the name of a repository.
174
-func ValidateRepoName(name string) error {
175
-	if name == "" {
176
-		return errors.New("Repository name can't be empty")
177
-	}
178
-	if name == api.NoBaseImageSpecifier {
174
+func ValidateRepoName(name reference.Named) error {
175
+	if reference.FamiliarName(name) == api.NoBaseImageSpecifier {
179 176
 		return fmt.Errorf("'%s' is a reserved name", api.NoBaseImageSpecifier)
180 177
 	}
181 178
 	return nil
182 179
 }
183 180
 
184
-func addDigestReference(store reference.Store, ref reference.Named, dgst digest.Digest, id digest.Digest) error {
181
+func addDigestReference(store refstore.Store, ref reference.Named, dgst digest.Digest, id digest.Digest) error {
185 182
 	dgstRef, err := reference.WithDigest(reference.TrimNamed(ref), dgst)
186 183
 	if err != nil {
187 184
 		return err
... ...
@@ -193,7 +190,7 @@ func addDigestReference(store reference.Store, ref reference.Named, dgst digest.
193 193
 			logrus.Errorf("Image ID for digest %s changed from %s to %s, cannot update", dgst.String(), oldTagID, id)
194 194
 		}
195 195
 		return nil
196
-	} else if err != reference.ErrDoesNotExist {
196
+	} else if err != refstore.ErrDoesNotExist {
197 197
 		return err
198 198
 	}
199 199
 
... ...
@@ -12,6 +12,7 @@ import (
12 12
 	"time"
13 13
 
14 14
 	"github.com/Sirupsen/logrus"
15
+	"github.com/docker/distribution/reference"
15 16
 	"github.com/docker/distribution/registry/client/transport"
16 17
 	"github.com/docker/docker/distribution/metadata"
17 18
 	"github.com/docker/docker/distribution/xfer"
... ...
@@ -22,7 +23,6 @@ import (
22 22
 	"github.com/docker/docker/pkg/ioutils"
23 23
 	"github.com/docker/docker/pkg/progress"
24 24
 	"github.com/docker/docker/pkg/stringid"
25
-	"github.com/docker/docker/reference"
26 25
 	"github.com/docker/docker/registry"
27 26
 	"golang.org/x/net/context"
28 27
 )
... ...
@@ -67,23 +67,23 @@ func (p *v1Puller) Pull(ctx context.Context, ref reference.Named) error {
67 67
 		// TODO(dmcgowan): Check if should fallback
68 68
 		return err
69 69
 	}
70
-	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.")
70
+	progress.Message(p.config.ProgressOutput, "", p.repoInfo.Name.Name()+": this image was pulled from a legacy registry.  Important: This registry version will not be supported in future versions of docker.")
71 71
 
72 72
 	return nil
73 73
 }
74 74
 
75 75
 func (p *v1Puller) pullRepository(ctx context.Context, ref reference.Named) error {
76
-	progress.Message(p.config.ProgressOutput, "", "Pulling repository "+p.repoInfo.FullName())
76
+	progress.Message(p.config.ProgressOutput, "", "Pulling repository "+p.repoInfo.Name.Name())
77 77
 
78 78
 	tagged, isTagged := ref.(reference.NamedTagged)
79 79
 
80
-	repoData, err := p.session.GetRepositoryData(p.repoInfo)
80
+	repoData, err := p.session.GetRepositoryData(p.repoInfo.Name)
81 81
 	if err != nil {
82 82
 		if strings.Contains(err.Error(), "HTTP code: 404") {
83 83
 			if isTagged {
84
-				return fmt.Errorf("Error: image %s:%s not found", p.repoInfo.RemoteName(), tagged.Tag())
84
+				return fmt.Errorf("Error: image %s:%s not found", reference.Path(p.repoInfo.Name), tagged.Tag())
85 85
 			}
86
-			return fmt.Errorf("Error: image %s not found", p.repoInfo.RemoteName())
86
+			return fmt.Errorf("Error: image %s not found", reference.Path(p.repoInfo.Name))
87 87
 		}
88 88
 		// Unexpected HTTP error
89 89
 		return err
... ...
@@ -92,13 +92,13 @@ func (p *v1Puller) pullRepository(ctx context.Context, ref reference.Named) erro
92 92
 	logrus.Debug("Retrieving the tag list")
93 93
 	var tagsList map[string]string
94 94
 	if !isTagged {
95
-		tagsList, err = p.session.GetRemoteTags(repoData.Endpoints, p.repoInfo)
95
+		tagsList, err = p.session.GetRemoteTags(repoData.Endpoints, p.repoInfo.Name)
96 96
 	} else {
97 97
 		var tagID string
98 98
 		tagsList = make(map[string]string)
99
-		tagID, err = p.session.GetRemoteTag(repoData.Endpoints, p.repoInfo, tagged.Tag())
99
+		tagID, err = p.session.GetRemoteTag(repoData.Endpoints, p.repoInfo.Name, tagged.Tag())
100 100
 		if err == registry.ErrRepoNotFound {
101
-			return fmt.Errorf("Tag %s not found in repository %s", tagged.Tag(), p.repoInfo.FullName())
101
+			return fmt.Errorf("Tag %s not found in repository %s", tagged.Tag(), p.repoInfo.Name.Name())
102 102
 		}
103 103
 		tagsList[tagged.Tag()] = tagID
104 104
 	}
... ...
@@ -127,7 +127,7 @@ func (p *v1Puller) pullRepository(ctx context.Context, ref reference.Named) erro
127 127
 		}
128 128
 	}
129 129
 
130
-	writeStatus(ref.String(), p.config.ProgressOutput, layersDownloaded)
130
+	writeStatus(reference.FamiliarString(ref), p.config.ProgressOutput, layersDownloaded)
131 131
 	return nil
132 132
 }
133 133
 
... ...
@@ -137,7 +137,7 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit
137 137
 		return nil
138 138
 	}
139 139
 
140
-	localNameRef, err := reference.WithTag(p.repoInfo, img.Tag)
140
+	localNameRef, err := reference.WithTag(p.repoInfo.Name, img.Tag)
141 141
 	if err != nil {
142 142
 		retErr := fmt.Errorf("Image (id: %s) has invalid tag: %s", img.ID, img.Tag)
143 143
 		logrus.Debug(retErr.Error())
... ...
@@ -148,15 +148,15 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit
148 148
 		return err
149 149
 	}
150 150
 
151
-	progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s", img.Tag, p.repoInfo.FullName())
151
+	progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s", img.Tag, p.repoInfo.Name.Name())
152 152
 	success := false
153 153
 	var lastErr error
154 154
 	for _, ep := range p.repoInfo.Index.Mirrors {
155 155
 		ep += "v1/"
156
-		progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), fmt.Sprintf("Pulling image (%s) from %s, mirror: %s", img.Tag, p.repoInfo.FullName(), ep))
156
+		progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), fmt.Sprintf("Pulling image (%s) from %s, mirror: %s", img.Tag, p.repoInfo.Name.Name(), ep))
157 157
 		if err = p.pullImage(ctx, img.ID, ep, localNameRef, layersDownloaded); err != nil {
158 158
 			// Don't report errors when pulling from mirrors.
159
-			logrus.Debugf("Error pulling image (%s) from %s, mirror: %s, %s", img.Tag, p.repoInfo.FullName(), ep, err)
159
+			logrus.Debugf("Error pulling image (%s) from %s, mirror: %s, %s", img.Tag, p.repoInfo.Name.Name(), ep, err)
160 160
 			continue
161 161
 		}
162 162
 		success = true
... ...
@@ -164,12 +164,12 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit
164 164
 	}
165 165
 	if !success {
166 166
 		for _, ep := range repoData.Endpoints {
167
-			progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s, endpoint: %s", img.Tag, p.repoInfo.FullName(), ep)
167
+			progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s, endpoint: %s", img.Tag, p.repoInfo.Name.Name(), ep)
168 168
 			if err = p.pullImage(ctx, img.ID, ep, localNameRef, layersDownloaded); err != nil {
169 169
 				// It's not ideal that only the last error is returned, it would be better to concatenate the errors.
170 170
 				// As the error is also given to the output stream the user will see the error.
171 171
 				lastErr = err
172
-				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)
172
+				progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Error pulling image (%s) from %s, endpoint: %s, %s", img.Tag, p.repoInfo.Name.Name(), ep, err)
173 173
 				continue
174 174
 			}
175 175
 			success = true
... ...
@@ -177,7 +177,7 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit
177 177
 		}
178 178
 	}
179 179
 	if !success {
180
-		err := fmt.Errorf("Error pulling image (%s) from %s, %v", img.Tag, p.repoInfo.FullName(), lastErr)
180
+		err := fmt.Errorf("Error pulling image (%s) from %s, %v", img.Tag, p.repoInfo.Name.Name(), lastErr)
181 181
 		progress.Update(p.config.ProgressOutput, stringid.TruncateID(img.ID), err.Error())
182 182
 		return err
183 183
 	}
... ...
@@ -15,6 +15,7 @@ import (
15 15
 	"github.com/docker/distribution/manifest/manifestlist"
16 16
 	"github.com/docker/distribution/manifest/schema1"
17 17
 	"github.com/docker/distribution/manifest/schema2"
18
+	"github.com/docker/distribution/reference"
18 19
 	"github.com/docker/distribution/registry/api/errcode"
19 20
 	"github.com/docker/distribution/registry/client/auth"
20 21
 	"github.com/docker/distribution/registry/client/transport"
... ...
@@ -26,7 +27,7 @@ import (
26 26
 	"github.com/docker/docker/pkg/ioutils"
27 27
 	"github.com/docker/docker/pkg/progress"
28 28
 	"github.com/docker/docker/pkg/stringid"
29
-	"github.com/docker/docker/reference"
29
+	refstore "github.com/docker/docker/reference"
30 30
 	"github.com/docker/docker/registry"
31 31
 	"github.com/opencontainers/go-digest"
32 32
 	"golang.org/x/net/context"
... ...
@@ -124,7 +125,7 @@ func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named) (e
124 124
 		}
125 125
 	}
126 126
 
127
-	writeStatus(ref.String(), p.config.ProgressOutput, layersDownloaded)
127
+	writeStatus(reference.FamiliarString(ref), p.config.ProgressOutput, layersDownloaded)
128 128
 
129 129
 	return nil
130 130
 }
... ...
@@ -317,7 +318,7 @@ func (ld *v2LayerDescriptor) truncateDownloadFile() error {
317 317
 
318 318
 func (ld *v2LayerDescriptor) Registered(diffID layer.DiffID) {
319 319
 	// Cache mapping from this layer's DiffID to the blobsum
320
-	ld.V2MetadataService.Add(diffID, metadata.V2Metadata{Digest: ld.digest, SourceRepository: ld.repoInfo.FullName()})
320
+	ld.V2MetadataService.Add(diffID, metadata.V2Metadata{Digest: ld.digest, SourceRepository: ld.repoInfo.Name.Name()})
321 321
 }
322 322
 
323 323
 func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdated bool, err error) {
... ...
@@ -343,7 +344,7 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdat
343 343
 		}
344 344
 		tagOrDigest = digested.Digest().String()
345 345
 	} else {
346
-		return false, fmt.Errorf("internal error: reference has neither a tag nor a digest: %s", ref.String())
346
+		return false, fmt.Errorf("internal error: reference has neither a tag nor a digest: %s", reference.FamiliarString(ref))
347 347
 	}
348 348
 
349 349
 	if manifest == nil {
... ...
@@ -371,8 +372,8 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdat
371 371
 	// the other side speaks the v2 protocol.
372 372
 	p.confirmedV2 = true
373 373
 
374
-	logrus.Debugf("Pulling ref from V2 registry: %s", ref.String())
375
-	progress.Message(p.config.ProgressOutput, tagOrDigest, "Pulling from "+p.repo.Named().Name())
374
+	logrus.Debugf("Pulling ref from V2 registry: %s", reference.FamiliarString(ref))
375
+	progress.Message(p.config.ProgressOutput, tagOrDigest, "Pulling from "+reference.FamiliarName(p.repo.Named()))
376 376
 
377 377
 	var (
378 378
 		id             digest.Digest
... ...
@@ -410,7 +411,7 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdat
410 410
 			if oldTagID == id {
411 411
 				return false, addDigestReference(p.config.ReferenceStore, ref, manifestDigest, id)
412 412
 			}
413
-		} else if err != reference.ErrDoesNotExist {
413
+		} else if err != refstore.ErrDoesNotExist {
414 414
 			return false, err
415 415
 		}
416 416
 
... ...
@@ -802,13 +803,13 @@ func verifySchema1Manifest(signedManifest *schema1.SignedManifest, ref reference
802 802
 	m = &signedManifest.Manifest
803 803
 
804 804
 	if m.SchemaVersion != 1 {
805
-		return nil, fmt.Errorf("unsupported schema version %d for %q", m.SchemaVersion, ref.String())
805
+		return nil, fmt.Errorf("unsupported schema version %d for %q", m.SchemaVersion, reference.FamiliarString(ref))
806 806
 	}
807 807
 	if len(m.FSLayers) != len(m.History) {
808
-		return nil, fmt.Errorf("length of history not equal to number of layers for %q", ref.String())
808
+		return nil, fmt.Errorf("length of history not equal to number of layers for %q", reference.FamiliarString(ref))
809 809
 	}
810 810
 	if len(m.FSLayers) == 0 {
811
-		return nil, fmt.Errorf("no FSLayers in manifest for %q", ref.String())
811
+		return nil, fmt.Errorf("no FSLayers in manifest for %q", reference.FamiliarString(ref))
812 812
 	}
813 813
 	return m, nil
814 814
 }
... ...
@@ -9,7 +9,7 @@ import (
9 9
 	"testing"
10 10
 
11 11
 	"github.com/docker/distribution/manifest/schema1"
12
-	"github.com/docker/docker/reference"
12
+	"github.com/docker/distribution/reference"
13 13
 	"github.com/opencontainers/go-digest"
14 14
 )
15 15
 
... ...
@@ -113,7 +113,7 @@ func TestValidateManifest(t *testing.T) {
113 113
 	if runtime.GOOS == "windows" {
114 114
 		t.Skip("Needs fixing on Windows")
115 115
 	}
116
-	expectedDigest, err := reference.ParseNamed("repo@sha256:02fee8c3220ba806531f606525eceb83f4feb654f62b207191b1c9209188dedd")
116
+	expectedDigest, err := reference.ParseNormalizedNamed("repo@sha256:02fee8c3220ba806531f606525eceb83f4feb654f62b207191b1c9209188dedd")
117 117
 	if err != nil {
118 118
 		t.Fatal("could not parse reference")
119 119
 	}
... ...
@@ -7,9 +7,9 @@ import (
7 7
 	"io"
8 8
 
9 9
 	"github.com/Sirupsen/logrus"
10
+	"github.com/docker/distribution/reference"
10 11
 	"github.com/docker/docker/distribution/metadata"
11 12
 	"github.com/docker/docker/pkg/progress"
12
-	"github.com/docker/docker/reference"
13 13
 	"github.com/docker/docker/registry"
14 14
 	"golang.org/x/net/context"
15 15
 )
... ...
@@ -64,16 +64,16 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo
64 64
 		return err
65 65
 	}
66 66
 
67
-	endpoints, err := imagePushConfig.RegistryService.LookupPushEndpoints(repoInfo.Hostname())
67
+	endpoints, err := imagePushConfig.RegistryService.LookupPushEndpoints(reference.Domain(repoInfo.Name))
68 68
 	if err != nil {
69 69
 		return err
70 70
 	}
71 71
 
72
-	progress.Messagef(imagePushConfig.ProgressOutput, "", "The push refers to a repository [%s]", repoInfo.FullName())
72
+	progress.Messagef(imagePushConfig.ProgressOutput, "", "The push refers to a repository [%s]", repoInfo.Name.Name())
73 73
 
74
-	associations := imagePushConfig.ReferenceStore.ReferencesByName(repoInfo)
74
+	associations := imagePushConfig.ReferenceStore.ReferencesByName(repoInfo.Name)
75 75
 	if len(associations) == 0 {
76
-		return fmt.Errorf("An image does not exist locally with the tag: %s", repoInfo.Name())
76
+		return fmt.Errorf("An image does not exist locally with the tag: %s", reference.FamiliarName(repoInfo.Name))
77 77
 	}
78 78
 
79 79
 	var (
... ...
@@ -106,7 +106,7 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo
106 106
 			}
107 107
 		}
108 108
 
109
-		logrus.Debugf("Trying to push %s to %s %s", repoInfo.FullName(), endpoint.URL, endpoint.Version)
109
+		logrus.Debugf("Trying to push %s to %s %s", repoInfo.Name.Name(), endpoint.URL, endpoint.Version)
110 110
 
111 111
 		pusher, err := NewPusher(ref, endpoint, repoInfo, imagePushConfig)
112 112
 		if err != nil {
... ...
@@ -135,12 +135,12 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo
135 135
 			return err
136 136
 		}
137 137
 
138
-		imagePushConfig.ImageEventLogger(ref.String(), repoInfo.Name(), "push")
138
+		imagePushConfig.ImageEventLogger(reference.FamiliarString(ref), reference.FamiliarName(repoInfo.Name), "push")
139 139
 		return nil
140 140
 	}
141 141
 
142 142
 	if lastErr == nil {
143
-		lastErr = fmt.Errorf("no endpoints found for %s", repoInfo.FullName())
143
+		lastErr = fmt.Errorf("no endpoints found for %s", repoInfo.Name.Name())
144 144
 	}
145 145
 	return lastErr
146 146
 }
... ...
@@ -5,6 +5,7 @@ import (
5 5
 	"sync"
6 6
 
7 7
 	"github.com/Sirupsen/logrus"
8
+	"github.com/docker/distribution/reference"
8 9
 	"github.com/docker/distribution/registry/client/transport"
9 10
 	"github.com/docker/docker/distribution/metadata"
10 11
 	"github.com/docker/docker/dockerversion"
... ...
@@ -14,7 +15,6 @@ import (
14 14
 	"github.com/docker/docker/pkg/ioutils"
15 15
 	"github.com/docker/docker/pkg/progress"
16 16
 	"github.com/docker/docker/pkg/stringid"
17
-	"github.com/docker/docker/reference"
18 17
 	"github.com/docker/docker/registry"
19 18
 	"github.com/opencontainers/go-digest"
20 19
 	"golang.org/x/net/context"
... ...
@@ -356,8 +356,8 @@ func (p *v1Pusher) pushImageToEndpoint(ctx context.Context, endpoint string, ima
356 356
 		}
357 357
 		if topImage, isTopImage := img.(*v1TopImage); isTopImage {
358 358
 			for _, tag := range tags[topImage.imageID] {
359
-				progress.Messagef(p.config.ProgressOutput, "", "Pushing tag for rev [%s] on {%s}", stringid.TruncateID(v1ID), endpoint+"repositories/"+p.repoInfo.RemoteName()+"/tags/"+tag)
360
-				if err := p.session.PushRegistryTag(p.repoInfo, v1ID, tag, endpoint); err != nil {
359
+				progress.Messagef(p.config.ProgressOutput, "", "Pushing tag for rev [%s] on {%s}", stringid.TruncateID(v1ID), endpoint+"repositories/"+reference.Path(p.repoInfo.Name)+"/tags/"+tag)
360
+				if err := p.session.PushRegistryTag(p.repoInfo.Name, v1ID, tag, endpoint); err != nil {
361 361
 					return err
362 362
 				}
363 363
 			}
... ...
@@ -385,7 +385,7 @@ func (p *v1Pusher) pushRepository(ctx context.Context) error {
385 385
 
386 386
 	// Register all the images in a repository with the registry
387 387
 	// If an image is not in this list it will not be associated with the repository
388
-	repoData, err := p.session.PushImageJSONIndex(p.repoInfo, imageIndex, false, nil)
388
+	repoData, err := p.session.PushImageJSONIndex(p.repoInfo.Name, imageIndex, false, nil)
389 389
 	if err != nil {
390 390
 		return err
391 391
 	}
... ...
@@ -395,7 +395,7 @@ func (p *v1Pusher) pushRepository(ctx context.Context) error {
395 395
 			return err
396 396
 		}
397 397
 	}
398
-	_, err = p.session.PushImageJSONIndex(p.repoInfo, imageIndex, true, repoData.Endpoints)
398
+	_, err = p.session.PushImageJSONIndex(p.repoInfo.Name, imageIndex, true, repoData.Endpoints)
399 399
 	return err
400 400
 }
401 401
 
... ...
@@ -15,7 +15,7 @@ import (
15 15
 	"github.com/docker/distribution"
16 16
 	"github.com/docker/distribution/manifest/schema1"
17 17
 	"github.com/docker/distribution/manifest/schema2"
18
-	distreference "github.com/docker/distribution/reference"
18
+	"github.com/docker/distribution/reference"
19 19
 	"github.com/docker/distribution/registry/client"
20 20
 	apitypes "github.com/docker/docker/api/types"
21 21
 	"github.com/docker/docker/distribution/metadata"
... ...
@@ -24,7 +24,6 @@ import (
24 24
 	"github.com/docker/docker/pkg/ioutils"
25 25
 	"github.com/docker/docker/pkg/progress"
26 26
 	"github.com/docker/docker/pkg/stringid"
27
-	"github.com/docker/docker/reference"
28 27
 	"github.com/docker/docker/registry"
29 28
 	"github.com/opencontainers/go-digest"
30 29
 )
... ...
@@ -83,7 +82,7 @@ func (p *v2Pusher) pushV2Repository(ctx context.Context) (err error) {
83 83
 	if namedTagged, isNamedTagged := p.ref.(reference.NamedTagged); isNamedTagged {
84 84
 		imageID, err := p.config.ReferenceStore.Get(p.ref)
85 85
 		if err != nil {
86
-			return fmt.Errorf("tag does not exist: %s", p.ref.String())
86
+			return fmt.Errorf("tag does not exist: %s", reference.FamiliarString(p.ref))
87 87
 		}
88 88
 
89 89
 		return p.pushV2Tag(ctx, namedTagged, imageID)
... ...
@@ -105,23 +104,23 @@ func (p *v2Pusher) pushV2Repository(ctx context.Context) (err error) {
105 105
 	}
106 106
 
107 107
 	if pushed == 0 {
108
-		return fmt.Errorf("no tags to push for %s", p.repoInfo.Name())
108
+		return fmt.Errorf("no tags to push for %s", reference.FamiliarName(p.repoInfo.Name))
109 109
 	}
110 110
 
111 111
 	return nil
112 112
 }
113 113
 
114 114
 func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, id digest.Digest) error {
115
-	logrus.Debugf("Pushing repository: %s", ref.String())
115
+	logrus.Debugf("Pushing repository: %s", reference.FamiliarString(ref))
116 116
 
117 117
 	imgConfig, err := p.config.ImageStore.Get(id)
118 118
 	if err != nil {
119
-		return fmt.Errorf("could not find image from tag %s: %v", ref.String(), err)
119
+		return fmt.Errorf("could not find image from tag %s: %v", reference.FamiliarString(ref), err)
120 120
 	}
121 121
 
122 122
 	rootfs, err := p.config.ImageStore.RootFSFromConfig(imgConfig)
123 123
 	if err != nil {
124
-		return fmt.Errorf("unable to get rootfs for image %s: %s", ref.String(), err)
124
+		return fmt.Errorf("unable to get rootfs for image %s: %s", reference.FamiliarString(ref), err)
125 125
 	}
126 126
 
127 127
 	l, err := p.config.LayerStore.Get(rootfs.ChainID())
... ...
@@ -140,7 +139,7 @@ func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, id
140 140
 	descriptorTemplate := v2PushDescriptor{
141 141
 		v2MetadataService: p.v2MetadataService,
142 142
 		hmacKey:           hmacKey,
143
-		repoInfo:          p.repoInfo,
143
+		repoInfo:          p.repoInfo.Name,
144 144
 		ref:               p.ref,
145 145
 		repo:              p.repo,
146 146
 		pushState:         &p.pushState,
... ...
@@ -181,7 +180,7 @@ func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, id
181 181
 
182 182
 		logrus.Warnf("failed to upload schema2 manifest: %v - falling back to schema1", err)
183 183
 
184
-		manifestRef, err := distreference.WithTag(p.repo.Named(), ref.Tag())
184
+		manifestRef, err := reference.WithTag(p.repo.Named(), ref.Tag())
185 185
 		if err != nil {
186 186
 			return err
187 187
 		}
... ...
@@ -248,7 +247,7 @@ type v2PushDescriptor struct {
248 248
 }
249 249
 
250 250
 func (pd *v2PushDescriptor) Key() string {
251
-	return "v2push:" + pd.ref.FullName() + " " + pd.layer.DiffID().String()
251
+	return "v2push:" + pd.ref.Name() + " " + pd.layer.DiffID().String()
252 252
 }
253 253
 
254 254
 func (pd *v2PushDescriptor) ID() string {
... ...
@@ -304,23 +303,22 @@ func (pd *v2PushDescriptor) Upload(ctx context.Context, progressOutput progress.
304 304
 		createOpts := []distribution.BlobCreateOption{}
305 305
 
306 306
 		if len(mountCandidate.SourceRepository) > 0 {
307
-			namedRef, err := reference.WithName(mountCandidate.SourceRepository)
307
+			namedRef, err := reference.ParseNormalizedNamed(mountCandidate.SourceRepository)
308 308
 			if err != nil {
309
-				logrus.Errorf("failed to parse source repository reference %v: %v", namedRef.String(), err)
309
+				logrus.Errorf("failed to parse source repository reference %v: %v", reference.FamiliarString(namedRef), err)
310 310
 				pd.v2MetadataService.Remove(mountCandidate)
311 311
 				continue
312 312
 			}
313 313
 
314
-			// TODO (brianbland): We need to construct a reference where the Name is
315
-			// only the full remote name, so clean this up when distribution has a
316
-			// richer reference package
317
-			remoteRef, err := distreference.WithName(namedRef.RemoteName())
314
+			// Candidates are always under same domain, create remote reference
315
+			// with only path to set mount from with
316
+			remoteRef, err := reference.WithName(reference.Path(namedRef))
318 317
 			if err != nil {
319
-				logrus.Errorf("failed to make remote reference out of %q: %v", namedRef.RemoteName(), namedRef.RemoteName())
318
+				logrus.Errorf("failed to make remote reference out of %q: %v", reference.Path(namedRef), err)
320 319
 				continue
321 320
 			}
322 321
 
323
-			canonicalRef, err := distreference.WithDigest(distreference.TrimNamed(remoteRef), mountCandidate.Digest)
322
+			canonicalRef, err := reference.WithDigest(reference.TrimNamed(remoteRef), mountCandidate.Digest)
324 323
 			if err != nil {
325 324
 				logrus.Errorf("failed to make canonical reference: %v", err)
326 325
 				continue
... ...
@@ -347,7 +345,7 @@ func (pd *v2PushDescriptor) Upload(ctx context.Context, progressOutput progress.
347 347
 			// Cache mapping from this layer's DiffID to the blobsum
348 348
 			if err := pd.v2MetadataService.TagAndAdd(diffID, pd.hmacKey, metadata.V2Metadata{
349 349
 				Digest:           err.Descriptor.Digest,
350
-				SourceRepository: pd.repoInfo.FullName(),
350
+				SourceRepository: pd.repoInfo.Name(),
351 351
 			}); err != nil {
352 352
 				return distribution.Descriptor{}, xfer.DoNotRetry{Err: err}
353 353
 			}
... ...
@@ -455,7 +453,7 @@ func (pd *v2PushDescriptor) uploadUsingSession(
455 455
 	// Cache mapping from this layer's DiffID to the blobsum
456 456
 	if err := pd.v2MetadataService.TagAndAdd(diffID, pd.hmacKey, metadata.V2Metadata{
457 457
 		Digest:           pushDigest,
458
-		SourceRepository: pd.repoInfo.FullName(),
458
+		SourceRepository: pd.repoInfo.Name(),
459 459
 	}); err != nil {
460 460
 		return distribution.Descriptor{}, xfer.DoNotRetry{Err: err}
461 461
 	}
... ...
@@ -490,7 +488,7 @@ func (pd *v2PushDescriptor) layerAlreadyExists(
490 490
 	// filter the metadata
491 491
 	candidates := []metadata.V2Metadata{}
492 492
 	for _, meta := range v2Metadata {
493
-		if len(meta.SourceRepository) > 0 && !checkOtherRepositories && meta.SourceRepository != pd.repoInfo.FullName() {
493
+		if len(meta.SourceRepository) > 0 && !checkOtherRepositories && meta.SourceRepository != pd.repoInfo.Name() {
494 494
 			continue
495 495
 		}
496 496
 		candidates = append(candidates, meta)
... ...
@@ -521,16 +519,16 @@ func (pd *v2PushDescriptor) layerAlreadyExists(
521 521
 attempts:
522 522
 	for _, dgst := range layerDigests {
523 523
 		meta := digestToMetadata[dgst]
524
-		logrus.Debugf("Checking for presence of layer %s (%s) in %s", diffID, dgst, pd.repoInfo.FullName())
524
+		logrus.Debugf("Checking for presence of layer %s (%s) in %s", diffID, dgst, pd.repoInfo.Name())
525 525
 		desc, err = pd.repo.Blobs(ctx).Stat(ctx, dgst)
526 526
 		pd.checkedDigests[meta.Digest] = struct{}{}
527 527
 		switch err {
528 528
 		case nil:
529
-			if m, ok := digestToMetadata[desc.Digest]; !ok || m.SourceRepository != pd.repoInfo.FullName() || !metadata.CheckV2MetadataHMAC(m, pd.hmacKey) {
529
+			if m, ok := digestToMetadata[desc.Digest]; !ok || m.SourceRepository != pd.repoInfo.Name() || !metadata.CheckV2MetadataHMAC(m, pd.hmacKey) {
530 530
 				// cache mapping from this layer's DiffID to the blobsum
531 531
 				if err := pd.v2MetadataService.TagAndAdd(diffID, pd.hmacKey, metadata.V2Metadata{
532 532
 					Digest:           desc.Digest,
533
-					SourceRepository: pd.repoInfo.FullName(),
533
+					SourceRepository: pd.repoInfo.Name(),
534 534
 				}); err != nil {
535 535
 					return distribution.Descriptor{}, false, xfer.DoNotRetry{Err: err}
536 536
 				}
... ...
@@ -539,12 +537,12 @@ attempts:
539 539
 			exists = true
540 540
 			break attempts
541 541
 		case distribution.ErrBlobUnknown:
542
-			if meta.SourceRepository == pd.repoInfo.FullName() {
542
+			if meta.SourceRepository == pd.repoInfo.Name() {
543 543
 				// remove the mapping to the target repository
544 544
 				pd.v2MetadataService.Remove(*meta)
545 545
 			}
546 546
 		default:
547
-			logrus.WithError(err).Debugf("Failed to check for presence of layer %s (%s) in %s", diffID, dgst, pd.repoInfo.FullName())
547
+			logrus.WithError(err).Debugf("Failed to check for presence of layer %s (%s) in %s", diffID, dgst, pd.repoInfo.Name())
548 548
 		}
549 549
 	}
550 550
 
... ...
@@ -598,11 +596,11 @@ func getRepositoryMountCandidates(
598 598
 	candidates := []metadata.V2Metadata{}
599 599
 	for _, meta := range v2Metadata {
600 600
 		sourceRepo, err := reference.ParseNamed(meta.SourceRepository)
601
-		if err != nil || repoInfo.Hostname() != sourceRepo.Hostname() {
601
+		if err != nil || reference.Domain(repoInfo) != reference.Domain(sourceRepo) {
602 602
 			continue
603 603
 		}
604 604
 		// target repository is not a viable candidate
605
-		if meta.SourceRepository == repoInfo.FullName() {
605
+		if meta.SourceRepository == repoInfo.Name() {
606 606
 			continue
607 607
 		}
608 608
 		candidates = append(candidates, meta)
... ...
@@ -653,7 +651,7 @@ func sortV2MetadataByLikenessAndAge(repoInfo reference.Named, hmacKey []byte, ma
653 653
 	sort.Stable(byLikeness{
654 654
 		arr:            marr,
655 655
 		hmacKey:        hmacKey,
656
-		pathComponents: getPathComponents(repoInfo.FullName()),
656
+		pathComponents: getPathComponents(repoInfo.Name()),
657 657
 	})
658 658
 }
659 659
 
... ...
@@ -670,11 +668,6 @@ func numOfMatchingPathComponents(pth string, matchComponents []string) int {
670 670
 }
671 671
 
672 672
 func getPathComponents(path string) []string {
673
-	// make sure to add docker.io/ prefix to the path
674
-	named, err := reference.ParseNamed(path)
675
-	if err == nil {
676
-		path = named.FullName()
677
-	}
678 673
 	return strings.Split(path, "/")
679 674
 }
680 675
 
... ...
@@ -8,11 +8,10 @@ import (
8 8
 	"github.com/docker/distribution"
9 9
 	"github.com/docker/distribution/context"
10 10
 	"github.com/docker/distribution/manifest/schema2"
11
-	distreference "github.com/docker/distribution/reference"
11
+	"github.com/docker/distribution/reference"
12 12
 	"github.com/docker/docker/distribution/metadata"
13 13
 	"github.com/docker/docker/layer"
14 14
 	"github.com/docker/docker/pkg/progress"
15
-	"github.com/docker/docker/reference"
16 15
 	"github.com/opencontainers/go-digest"
17 16
 )
18 17
 
... ...
@@ -43,8 +42,8 @@ func TestGetRepositoryMountCandidates(t *testing.T) {
43 43
 			name:          "one item matching",
44 44
 			targetRepo:    "busybox",
45 45
 			maxCandidates: -1,
46
-			metadata:      []metadata.V2Metadata{taggedMetadata("hash", "1", "hello-world")},
47
-			candidates:    []metadata.V2Metadata{taggedMetadata("hash", "1", "hello-world")},
46
+			metadata:      []metadata.V2Metadata{taggedMetadata("hash", "1", "docker.io/library/hello-world")},
47
+			candidates:    []metadata.V2Metadata{taggedMetadata("hash", "1", "docker.io/library/hello-world")},
48 48
 		},
49 49
 		{
50 50
 			name:          "allow missing SourceRepository",
... ...
@@ -63,13 +62,13 @@ func TestGetRepositoryMountCandidates(t *testing.T) {
63 63
 			maxCandidates: -1,
64 64
 			metadata: []metadata.V2Metadata{
65 65
 				{Digest: digest.Digest("1"), SourceRepository: "docker.io/user/foo"},
66
-				{Digest: digest.Digest("3"), SourceRepository: "user/bar"},
67
-				{Digest: digest.Digest("2"), SourceRepository: "app"},
66
+				{Digest: digest.Digest("3"), SourceRepository: "docker.io/user/bar"},
67
+				{Digest: digest.Digest("2"), SourceRepository: "docker.io/library/app"},
68 68
 			},
69 69
 			candidates: []metadata.V2Metadata{
70
-				{Digest: digest.Digest("3"), SourceRepository: "user/bar"},
70
+				{Digest: digest.Digest("3"), SourceRepository: "docker.io/user/bar"},
71 71
 				{Digest: digest.Digest("1"), SourceRepository: "docker.io/user/foo"},
72
-				{Digest: digest.Digest("2"), SourceRepository: "app"},
72
+				{Digest: digest.Digest("2"), SourceRepository: "docker.io/library/app"},
73 73
 			},
74 74
 		},
75 75
 		{
... ...
@@ -78,10 +77,10 @@ func TestGetRepositoryMountCandidates(t *testing.T) {
78 78
 			targetRepo:    "127.0.0.1/foo/bar",
79 79
 			maxCandidates: -1,
80 80
 			metadata: []metadata.V2Metadata{
81
-				taggedMetadata("hash", "1", "hello-world"),
81
+				taggedMetadata("hash", "1", "docker.io/library/hello-world"),
82 82
 				taggedMetadata("efgh", "2", "127.0.0.1/hello-world"),
83
-				taggedMetadata("abcd", "3", "busybox"),
84
-				taggedMetadata("hash", "4", "busybox"),
83
+				taggedMetadata("abcd", "3", "docker.io/library/busybox"),
84
+				taggedMetadata("hash", "4", "docker.io/library/busybox"),
85 85
 				taggedMetadata("hash", "5", "127.0.0.1/foo"),
86 86
 				taggedMetadata("hash", "6", "127.0.0.1/bar"),
87 87
 				taggedMetadata("efgh", "7", "127.0.0.1/foo/bar"),
... ...
@@ -105,23 +104,25 @@ func TestGetRepositoryMountCandidates(t *testing.T) {
105 105
 			targetRepo:    "user/app",
106 106
 			maxCandidates: 3,
107 107
 			metadata: []metadata.V2Metadata{
108
-				taggedMetadata("abcd", "1", "user/app1"),
109
-				taggedMetadata("abcd", "2", "user/app/base"),
110
-				taggedMetadata("hash", "3", "user/app"),
108
+				taggedMetadata("abcd", "1", "docker.io/user/app1"),
109
+				taggedMetadata("abcd", "2", "docker.io/user/app/base"),
110
+				taggedMetadata("hash", "3", "docker.io/user/app"),
111 111
 				taggedMetadata("abcd", "4", "127.0.0.1/user/app"),
112
-				taggedMetadata("hash", "5", "user/foo"),
113
-				taggedMetadata("hash", "6", "app/bar"),
112
+				taggedMetadata("hash", "5", "docker.io/user/foo"),
113
+				taggedMetadata("hash", "6", "docker.io/app/bar"),
114 114
 			},
115 115
 			candidates: []metadata.V2Metadata{
116 116
 				// first by matching hash
117
-				taggedMetadata("abcd", "2", "user/app/base"),
118
-				taggedMetadata("abcd", "1", "user/app1"),
117
+				taggedMetadata("abcd", "2", "docker.io/user/app/base"),
118
+				taggedMetadata("abcd", "1", "docker.io/user/app1"),
119 119
 				// then by longest matching prefix
120
-				taggedMetadata("hash", "3", "user/app"),
120
+				// "docker.io/usr/app" is excluded since candidates must
121
+				// be from a different repository
122
+				taggedMetadata("hash", "5", "docker.io/user/foo"),
121 123
 			},
122 124
 		},
123 125
 	} {
124
-		repoInfo, err := reference.ParseNamed(tc.targetRepo)
126
+		repoInfo, err := reference.ParseNormalizedNamed(tc.targetRepo)
125 127
 		if err != nil {
126 128
 			t.Fatalf("[%s] failed to parse reference name: %v", tc.name, err)
127 129
 		}
... ...
@@ -204,10 +205,13 @@ func TestLayerAlreadyExists(t *testing.T) {
204 204
 				{Digest: digest.Digest("apple"), SourceRepository: "docker.io/library/hello-world"},
205 205
 				{Digest: digest.Digest("orange"), SourceRepository: "docker.io/busybox/subapp"},
206 206
 				{Digest: digest.Digest("pear"), SourceRepository: "docker.io/busybox"},
207
-				{Digest: digest.Digest("plum"), SourceRepository: "busybox"},
207
+				{Digest: digest.Digest("plum"), SourceRepository: "docker.io/library/busybox"},
208 208
 				{Digest: digest.Digest("banana"), SourceRepository: "127.0.0.1/busybox"},
209 209
 			},
210
-			expectedRequests: []string{"plum", "pear", "apple", "orange", "banana"},
210
+			expectedRequests: []string{"plum", "apple", "pear", "orange", "banana"},
211
+			expectedRemovals: []metadata.V2Metadata{
212
+				{Digest: digest.Digest("plum"), SourceRepository: "docker.io/library/busybox"},
213
+			},
211 214
 		},
212 215
 		{
213 216
 			name:               "find existing blob",
... ...
@@ -374,7 +378,7 @@ func TestLayerAlreadyExists(t *testing.T) {
374 374
 			},
375 375
 		},
376 376
 	} {
377
-		repoInfo, err := reference.ParseNamed(tc.targetRepo)
377
+		repoInfo, err := reference.ParseNormalizedNamed(tc.targetRepo)
378 378
 		if err != nil {
379 379
 			t.Fatalf("[%s] failed to parse reference name: %v", tc.name, err)
380 380
 		}
... ...
@@ -476,7 +480,7 @@ type mockRepo struct {
476 476
 
477 477
 var _ distribution.Repository = &mockRepo{}
478 478
 
479
-func (m *mockRepo) Named() distreference.Named {
479
+func (m *mockRepo) Named() reference.Named {
480 480
 	m.t.Fatalf("Named() not implemented")
481 481
 	return nil
482 482
 }
... ...
@@ -8,7 +8,7 @@ import (
8 8
 
9 9
 	"github.com/docker/distribution"
10 10
 	"github.com/docker/distribution/manifest/schema2"
11
-	distreference "github.com/docker/distribution/reference"
11
+	"github.com/docker/distribution/reference"
12 12
 	"github.com/docker/distribution/registry/client"
13 13
 	"github.com/docker/distribution/registry/client/auth"
14 14
 	"github.com/docker/distribution/registry/client/transport"
... ...
@@ -55,10 +55,10 @@ func init() {
55 55
 // providing timeout settings and authentication support, and also verifies the
56 56
 // remote API version.
57 57
 func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, endpoint registry.APIEndpoint, metaHeaders http.Header, authConfig *types.AuthConfig, actions ...string) (repo distribution.Repository, foundVersion bool, err error) {
58
-	repoName := repoInfo.FullName()
58
+	repoName := repoInfo.Name.Name()
59 59
 	// If endpoint does not support CanonicalName, use the RemoteName instead
60 60
 	if endpoint.TrimHostname {
61
-		repoName = repoInfo.RemoteName()
61
+		repoName = reference.Path(repoInfo.Name)
62 62
 	}
63 63
 
64 64
 	direct := &net.Dialer{
... ...
@@ -122,7 +122,7 @@ func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, end
122 122
 	}
123 123
 	tr := transport.NewTransport(base, modifiers...)
124 124
 
125
-	repoNameRef, err := distreference.ParseNamed(repoName)
125
+	repoNameRef, err := reference.WithName(repoName)
126 126
 	if err != nil {
127 127
 		return nil, foundVersion, fallbackError{
128 128
 			err:         err,
... ...
@@ -12,11 +12,11 @@ import (
12 12
 	"testing"
13 13
 
14 14
 	"github.com/Sirupsen/logrus"
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/archive"
18 19
 	"github.com/docker/docker/pkg/stringid"
19
-	"github.com/docker/docker/reference"
20 20
 	"github.com/docker/docker/registry"
21 21
 	"golang.org/x/net/context"
22 22
 )
... ...
@@ -61,9 +61,9 @@ func testTokenPassThru(t *testing.T, ts *httptest.Server) {
61 61
 		TrimHostname: false,
62 62
 		TLSConfig:    nil,
63 63
 	}
64
-	n, _ := reference.ParseNamed("testremotename")
64
+	n, _ := reference.ParseNormalizedNamed("testremotename")
65 65
 	repoInfo := &registry.RepositoryInfo{
66
-		Named: n,
66
+		Name: n,
67 67
 		Index: &registrytypes.IndexInfo{
68 68
 			Name:     "testrepo",
69 69
 			Mirrors:  nil,
... ...
@@ -11,6 +11,7 @@ import (
11 11
 
12 12
 	"github.com/Sirupsen/logrus"
13 13
 	"github.com/docker/distribution"
14
+	"github.com/docker/distribution/reference"
14 15
 	"github.com/docker/docker/image"
15 16
 	"github.com/docker/docker/image/v1"
16 17
 	"github.com/docker/docker/layer"
... ...
@@ -21,7 +22,6 @@ import (
21 21
 	"github.com/docker/docker/pkg/stringid"
22 22
 	"github.com/docker/docker/pkg/symlink"
23 23
 	"github.com/docker/docker/pkg/system"
24
-	"github.com/docker/docker/reference"
25 24
 	"github.com/opencontainers/go-digest"
26 25
 )
27 26
 
... ...
@@ -117,7 +117,7 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool)
117 117
 
118 118
 		imageRefCount = 0
119 119
 		for _, repoTag := range m.RepoTags {
120
-			named, err := reference.ParseNamed(repoTag)
120
+			named, err := reference.ParseNormalizedNamed(repoTag)
121 121
 			if err != nil {
122 122
 				return err
123 123
 			}
... ...
@@ -126,7 +126,7 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool)
126 126
 				return fmt.Errorf("invalid tag %q", repoTag)
127 127
 			}
128 128
 			l.setLoadedTag(ref, imgID.Digest(), outStream)
129
-			outStream.Write([]byte(fmt.Sprintf("Loaded image: %s\n", ref)))
129
+			outStream.Write([]byte(fmt.Sprintf("Loaded image: %s\n", reference.FamiliarString(ref))))
130 130
 			imageRefCount++
131 131
 		}
132 132
 
... ...
@@ -201,7 +201,7 @@ func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string,
201 201
 
202 202
 func (l *tarexporter) setLoadedTag(ref reference.NamedTagged, imgID digest.Digest, outStream io.Writer) error {
203 203
 	if prevID, err := l.rs.Get(ref); err == nil && prevID != imgID {
204
-		fmt.Fprintf(outStream, "The image %s already exists, renaming the old one with ID %s to empty string\n", ref.String(), string(prevID)) // todo: this message is wrong in case of multiple tags
204
+		fmt.Fprintf(outStream, "The image %s already exists, renaming the old one with ID %s to empty string\n", reference.FamiliarString(ref), string(prevID)) // todo: this message is wrong in case of multiple tags
205 205
 	}
206 206
 
207 207
 	if err := l.rs.AddTag(ref, imgID, true); err != nil {
... ...
@@ -249,7 +249,7 @@ func (l *tarexporter) legacyLoad(tmpDir string, outStream io.Writer, progressOut
249 249
 			if !ok {
250 250
 				return fmt.Errorf("invalid target ID: %v", oldID)
251 251
 			}
252
-			named, err := reference.WithName(name)
252
+			named, err := reference.ParseNormalizedNamed(name)
253 253
 			if err != nil {
254 254
 				return err
255 255
 			}
... ...
@@ -10,12 +10,12 @@ import (
10 10
 	"time"
11 11
 
12 12
 	"github.com/docker/distribution"
13
+	"github.com/docker/distribution/reference"
13 14
 	"github.com/docker/docker/image"
14 15
 	"github.com/docker/docker/image/v1"
15 16
 	"github.com/docker/docker/layer"
16 17
 	"github.com/docker/docker/pkg/archive"
17 18
 	"github.com/docker/docker/pkg/system"
18
-	"github.com/docker/docker/reference"
19 19
 	"github.com/opencontainers/go-digest"
20 20
 	"github.com/pkg/errors"
21 21
 )
... ...
@@ -51,16 +51,12 @@ func (l *tarexporter) parseNames(names []string) (map[image.ID]*imageDescriptor,
51 51
 		}
52 52
 
53 53
 		if ref != nil {
54
-			var tagged reference.NamedTagged
55 54
 			if _, ok := ref.(reference.Canonical); ok {
56 55
 				return
57 56
 			}
58
-			var ok bool
59
-			if tagged, ok = ref.(reference.NamedTagged); !ok {
60
-				var err error
61
-				if tagged, err = reference.WithTag(ref, reference.DefaultTag); err != nil {
62
-					return
63
-				}
57
+			tagged, ok := reference.TagNameOnly(ref).(reference.NamedTagged)
58
+			if !ok {
59
+				return
64 60
 			}
65 61
 
66 62
 			for _, t := range imgDescr[id].refs {
... ...
@@ -73,19 +69,26 @@ func (l *tarexporter) parseNames(names []string) (map[image.ID]*imageDescriptor,
73 73
 	}
74 74
 
75 75
 	for _, name := range names {
76
-		id, ref, err := reference.ParseIDOrReference(name)
76
+		ref, err := reference.ParseAnyReference(name)
77 77
 		if err != nil {
78 78
 			return nil, err
79 79
 		}
80
-		if id != "" {
81
-			_, err := l.is.Get(image.IDFromDigest(id))
82
-			if err != nil {
83
-				return nil, err
80
+		namedRef, ok := ref.(reference.Named)
81
+		if !ok {
82
+			// Check if digest ID reference
83
+			if digested, ok := ref.(reference.Digested); ok {
84
+				id := image.IDFromDigest(digested.Digest())
85
+				_, err := l.is.Get(id)
86
+				if err != nil {
87
+					return nil, err
88
+				}
89
+				addAssoc(id, nil)
90
+				continue
84 91
 			}
85
-			addAssoc(image.IDFromDigest(id), nil)
86
-			continue
92
+			return nil, errors.Errorf("invalid reference: %v", name)
87 93
 		}
88
-		if ref.Name() == string(digest.Canonical) {
94
+
95
+		if reference.FamiliarName(namedRef) == string(digest.Canonical) {
89 96
 			imgID, err := l.is.Search(name)
90 97
 			if err != nil {
91 98
 				return nil, err
... ...
@@ -93,8 +96,8 @@ func (l *tarexporter) parseNames(names []string) (map[image.ID]*imageDescriptor,
93 93
 			addAssoc(imgID, nil)
94 94
 			continue
95 95
 		}
96
-		if reference.IsNameOnly(ref) {
97
-			assocs := l.rs.ReferencesByName(ref)
96
+		if reference.IsNameOnly(namedRef) {
97
+			assocs := l.rs.ReferencesByName(namedRef)
98 98
 			for _, assoc := range assocs {
99 99
 				addAssoc(image.IDFromDigest(assoc.ID), assoc.Ref)
100 100
 			}
... ...
@@ -107,11 +110,11 @@ func (l *tarexporter) parseNames(names []string) (map[image.ID]*imageDescriptor,
107 107
 			}
108 108
 			continue
109 109
 		}
110
-		id, err = l.rs.Get(ref)
110
+		id, err := l.rs.Get(namedRef)
111 111
 		if err != nil {
112 112
 			return nil, err
113 113
 		}
114
-		addAssoc(image.IDFromDigest(id), ref)
114
+		addAssoc(image.IDFromDigest(id), namedRef)
115 115
 
116 116
 	}
117 117
 	return imgDescr, nil
... ...
@@ -144,11 +147,12 @@ func (s *saveSession) save(outStream io.Writer) error {
144 144
 		var layers []string
145 145
 
146 146
 		for _, ref := range imageDescr.refs {
147
-			if _, ok := reposLegacy[ref.Name()]; !ok {
148
-				reposLegacy[ref.Name()] = make(map[string]string)
147
+			familiarName := reference.FamiliarName(ref)
148
+			if _, ok := reposLegacy[familiarName]; !ok {
149
+				reposLegacy[familiarName] = make(map[string]string)
149 150
 			}
150
-			reposLegacy[ref.Name()][ref.Tag()] = imageDescr.layers[len(imageDescr.layers)-1]
151
-			repoTags = append(repoTags, ref.String())
151
+			reposLegacy[familiarName][ref.Tag()] = imageDescr.layers[len(imageDescr.layers)-1]
152
+			repoTags = append(repoTags, reference.FamiliarString(ref))
152 153
 		}
153 154
 
154 155
 		for _, l := range imageDescr.layers {
... ...
@@ -4,7 +4,7 @@ import (
4 4
 	"github.com/docker/distribution"
5 5
 	"github.com/docker/docker/image"
6 6
 	"github.com/docker/docker/layer"
7
-	"github.com/docker/docker/reference"
7
+	refstore "github.com/docker/docker/reference"
8 8
 )
9 9
 
10 10
 const (
... ...
@@ -26,7 +26,7 @@ type manifestItem struct {
26 26
 type tarexporter struct {
27 27
 	is             image.Store
28 28
 	ls             layer.Store
29
-	rs             reference.Store
29
+	rs             refstore.Store
30 30
 	loggerImgEvent LogImageEvent
31 31
 }
32 32
 
... ...
@@ -37,7 +37,7 @@ type LogImageEvent interface {
37 37
 }
38 38
 
39 39
 // NewTarExporter returns new Exporter for tar packages
40
-func NewTarExporter(is image.Store, ls layer.Store, rs reference.Store, loggerImgEvent LogImageEvent) image.Exporter {
40
+func NewTarExporter(is image.Store, ls layer.Store, rs refstore.Store, loggerImgEvent LogImageEvent) image.Exporter {
41 41
 	return &tarexporter{
42 42
 		is:             is,
43 43
 		ls:             ls,
... ...
@@ -335,7 +335,7 @@ func (s *DockerSuite) TestImagesFormat(c *check.C) {
335 335
 	expected := []string{"myimage", "myimage"}
336 336
 	var names []string
337 337
 	names = append(names, lines...)
338
-	c.Assert(expected, checker.DeepEquals, names, check.Commentf("Expected array with truncated names: %v, got: %v", expected, names))
338
+	c.Assert(names, checker.DeepEquals, expected, check.Commentf("Expected array with truncated names: %v, got: %v", expected, names))
339 339
 }
340 340
 
341 341
 // ImagesDefaultFormatAndQuiet
... ...
@@ -14,12 +14,13 @@ import (
14 14
 	"encoding/json"
15 15
 
16 16
 	"github.com/Sirupsen/logrus"
17
+	"github.com/docker/distribution/reference"
17 18
 	"github.com/docker/docker/distribution/metadata"
18 19
 	"github.com/docker/docker/image"
19 20
 	imagev1 "github.com/docker/docker/image/v1"
20 21
 	"github.com/docker/docker/layer"
21 22
 	"github.com/docker/docker/pkg/ioutils"
22
-	"github.com/docker/docker/reference"
23
+	refstore "github.com/docker/docker/reference"
23 24
 	"github.com/opencontainers/go-digest"
24 25
 )
25 26
 
... ...
@@ -56,7 +57,7 @@ var (
56 56
 
57 57
 // Migrate takes an old graph directory and transforms the metadata into the
58 58
 // new format.
59
-func Migrate(root, driverName string, ls layer.Store, is image.Store, rs reference.Store, ms metadata.Store) error {
59
+func Migrate(root, driverName string, ls layer.Store, is image.Store, rs refstore.Store, ms metadata.Store) error {
60 60
 	graphDir := filepath.Join(root, graphDirName)
61 61
 	if _, err := os.Lstat(graphDir); os.IsNotExist(err) {
62 62
 		return nil
... ...
@@ -322,11 +323,15 @@ func migrateRefs(root, driverName string, rs refAdder, mappings map[string]image
322 322
 	for name, repo := range repos.Repositories {
323 323
 		for tag, id := range repo {
324 324
 			if strongID, exists := mappings[id]; exists {
325
-				ref, err := reference.WithName(name)
325
+				ref, err := reference.ParseNormalizedNamed(name)
326 326
 				if err != nil {
327 327
 					logrus.Errorf("migrate tags: invalid name %q, %q", name, err)
328 328
 					continue
329 329
 				}
330
+				if !reference.IsNameOnly(ref) {
331
+					logrus.Errorf("migrate tags: invalid name %q, unexpected tag or digest", name)
332
+					continue
333
+				}
330 334
 				if dgst, err := digest.Parse(tag); err == nil {
331 335
 					canonical, err := reference.WithDigest(reference.TrimNamed(ref), dgst)
332 336
 					if err != nil {
... ...
@@ -334,7 +339,7 @@ func migrateRefs(root, driverName string, rs refAdder, mappings map[string]image
334 334
 						continue
335 335
 					}
336 336
 					if err := rs.AddDigest(canonical, strongID.Digest(), false); err != nil {
337
-						logrus.Errorf("can't migrate digest %q for %q, err: %q", ref.String(), strongID, err)
337
+						logrus.Errorf("can't migrate digest %q for %q, err: %q", reference.FamiliarString(ref), strongID, err)
338 338
 					}
339 339
 				} else {
340 340
 					tagRef, err := reference.WithTag(ref, tag)
... ...
@@ -343,7 +348,7 @@ func migrateRefs(root, driverName string, rs refAdder, mappings map[string]image
343 343
 						continue
344 344
 					}
345 345
 					if err := rs.AddTag(tagRef, strongID.Digest(), false); err != nil {
346
-						logrus.Errorf("can't migrate tag %q for %q, err: %q", ref.String(), strongID, err)
346
+						logrus.Errorf("can't migrate tag %q for %q, err: %q", reference.FamiliarString(ref), strongID, err)
347 347
 					}
348 348
 				}
349 349
 				logrus.Infof("migrated tag %s:%s to point to %s", name, tag, strongID)
... ...
@@ -13,10 +13,10 @@ import (
13 13
 	"runtime"
14 14
 	"testing"
15 15
 
16
+	"github.com/docker/distribution/reference"
16 17
 	"github.com/docker/docker/distribution/metadata"
17 18
 	"github.com/docker/docker/image"
18 19
 	"github.com/docker/docker/layer"
19
-	"github.com/docker/docker/reference"
20 20
 	"github.com/opencontainers/go-digest"
21 21
 )
22 22
 
... ...
@@ -40,9 +40,9 @@ func TestMigrateRefs(t *testing.T) {
40 40
 	}
41 41
 
42 42
 	expected := map[string]string{
43
-		"busybox:latest": "sha256:fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9",
44
-		"busybox@sha256:16a2a52884c2a9481ed267c2d46483eac7693b813a63132368ab098a71303f8a": "sha256:fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9",
45
-		"registry:2": "sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae",
43
+		"docker.io/library/busybox:latest":                                                                  "sha256:fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9",
44
+		"docker.io/library/busybox@sha256:16a2a52884c2a9481ed267c2d46483eac7693b813a63132368ab098a71303f8a": "sha256:fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9",
45
+		"docker.io/library/registry:2":                                                                      "sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae",
46 46
 	}
47 47
 
48 48
 	if !reflect.DeepEqual(expected, ta.refs) {
... ...
@@ -18,6 +18,7 @@ import (
18 18
 
19 19
 	"github.com/Sirupsen/logrus"
20 20
 	"github.com/docker/distribution/manifest/schema2"
21
+	"github.com/docker/distribution/reference"
21 22
 	"github.com/docker/docker/api/types"
22 23
 	"github.com/docker/docker/api/types/filters"
23 24
 	"github.com/docker/docker/distribution"
... ...
@@ -30,7 +31,7 @@ import (
30 30
 	"github.com/docker/docker/pkg/pools"
31 31
 	"github.com/docker/docker/pkg/progress"
32 32
 	"github.com/docker/docker/plugin/v2"
33
-	"github.com/docker/docker/reference"
33
+	refstore "github.com/docker/docker/reference"
34 34
 	"github.com/opencontainers/go-digest"
35 35
 	"github.com/pkg/errors"
36 36
 	"golang.org/x/net/context"
... ...
@@ -232,11 +233,11 @@ func (pm *Manager) Upgrade(ctx context.Context, ref reference.Named, name string
232 232
 	defer pm.muGC.RUnlock()
233 233
 
234 234
 	// revalidate because Pull is public
235
-	nameref, err := reference.ParseNamed(name)
235
+	nameref, err := reference.ParseNormalizedNamed(name)
236 236
 	if err != nil {
237 237
 		return errors.Wrapf(err, "failed to parse %q", name)
238 238
 	}
239
-	name = reference.WithDefaultTag(nameref).String()
239
+	name = reference.FamiliarString(reference.TagNameOnly(nameref))
240 240
 
241 241
 	tmpRootFSDir, err := ioutil.TempDir(pm.tmpDir(), ".rootfs")
242 242
 	defer os.RemoveAll(tmpRootFSDir)
... ...
@@ -277,11 +278,11 @@ func (pm *Manager) Pull(ctx context.Context, ref reference.Named, name string, m
277 277
 	defer pm.muGC.RUnlock()
278 278
 
279 279
 	// revalidate because Pull is public
280
-	nameref, err := reference.ParseNamed(name)
280
+	nameref, err := reference.ParseNormalizedNamed(name)
281 281
 	if err != nil {
282 282
 		return errors.Wrapf(err, "failed to parse %q", name)
283 283
 	}
284
-	name = reference.WithDefaultTag(nameref).String()
284
+	name = reference.FamiliarString(reference.TagNameOnly(nameref))
285 285
 
286 286
 	if err := pm.config.Store.validateName(name); err != nil {
287 287
 		return err
... ...
@@ -370,7 +371,7 @@ func (pm *Manager) Push(ctx context.Context, name string, metaHeader http.Header
370 370
 		return err
371 371
 	}
372 372
 
373
-	ref, err := reference.ParseNamed(p.Name())
373
+	ref, err := reference.ParseNormalizedNamed(p.Name())
374 374
 	if err != nil {
375 375
 		return errors.Wrapf(err, "plugin has invalid name %v for push", p.Name())
376 376
 	}
... ...
@@ -448,8 +449,8 @@ func (r *pluginReference) References(id digest.Digest) []reference.Named {
448 448
 	return []reference.Named{r.name}
449 449
 }
450 450
 
451
-func (r *pluginReference) ReferencesByName(ref reference.Named) []reference.Association {
452
-	return []reference.Association{
451
+func (r *pluginReference) ReferencesByName(ref reference.Named) []refstore.Association {
452
+	return []refstore.Association{
453 453
 		{
454 454
 			Ref: r.name,
455 455
 			ID:  r.pluginID,
... ...
@@ -459,7 +460,7 @@ func (r *pluginReference) ReferencesByName(ref reference.Named) []reference.Asso
459 459
 
460 460
 func (r *pluginReference) Get(ref reference.Named) (digest.Digest, error) {
461 461
 	if r.name.String() != ref.String() {
462
-		return digest.Digest(""), reference.ErrDoesNotExist
462
+		return digest.Digest(""), refstore.ErrDoesNotExist
463 463
 	}
464 464
 	return r.pluginID, nil
465 465
 }
... ...
@@ -664,15 +665,14 @@ func (pm *Manager) CreateFromContext(ctx context.Context, tarCtx io.ReadCloser,
664 664
 	pm.muGC.RLock()
665 665
 	defer pm.muGC.RUnlock()
666 666
 
667
-	ref, err := reference.ParseNamed(options.RepoName)
667
+	ref, err := reference.ParseNormalizedNamed(options.RepoName)
668 668
 	if err != nil {
669 669
 		return errors.Wrapf(err, "failed to parse reference %v", options.RepoName)
670 670
 	}
671 671
 	if _, ok := ref.(reference.Canonical); ok {
672 672
 		return errors.Errorf("canonical references are not permitted")
673 673
 	}
674
-	taggedRef := reference.WithDefaultTag(ref)
675
-	name := taggedRef.String()
674
+	name := reference.FamiliarString(reference.TagNameOnly(ref))
676 675
 
677 676
 	if err := pm.config.Store.validateName(name); err != nil { // fast check, real check is in createPlugin()
678 677
 		return err
... ...
@@ -754,7 +754,7 @@ func (pm *Manager) CreateFromContext(ctx context.Context, tarCtx io.ReadCloser,
754 754
 	if err != nil {
755 755
 		return err
756 756
 	}
757
-	p.PluginObj.PluginReference = taggedRef.String()
757
+	p.PluginObj.PluginReference = name
758 758
 
759 759
 	pm.config.LogPluginEvent(p.PluginObj.ID, name, "create")
760 760
 
... ...
@@ -7,9 +7,9 @@ import (
7 7
 	"io"
8 8
 	"net/http"
9 9
 
10
+	"github.com/docker/distribution/reference"
10 11
 	"github.com/docker/docker/api/types"
11 12
 	"github.com/docker/docker/api/types/filters"
12
-	"github.com/docker/docker/reference"
13 13
 	"golang.org/x/net/context"
14 14
 )
15 15
 
... ...
@@ -5,10 +5,10 @@ import (
5 5
 	"strings"
6 6
 
7 7
 	"github.com/Sirupsen/logrus"
8
+	"github.com/docker/distribution/reference"
8 9
 	"github.com/docker/docker/pkg/plugingetter"
9 10
 	"github.com/docker/docker/pkg/plugins"
10 11
 	"github.com/docker/docker/plugin/v2"
11
-	"github.com/docker/docker/reference"
12 12
 	"github.com/pkg/errors"
13 13
 )
14 14
 
... ...
@@ -230,19 +230,19 @@ func (ps *Store) resolvePluginID(idOrName string) (string, error) {
230 230
 		return idOrName, nil
231 231
 	}
232 232
 
233
-	ref, err := reference.ParseNamed(idOrName)
233
+	ref, err := reference.ParseNormalizedNamed(idOrName)
234 234
 	if err != nil {
235 235
 		return "", errors.WithStack(ErrNotFound(idOrName))
236 236
 	}
237 237
 	if _, ok := ref.(reference.Canonical); ok {
238
-		logrus.Warnf("canonical references cannot be resolved: %v", ref.String())
238
+		logrus.Warnf("canonical references cannot be resolved: %v", reference.FamiliarString(ref))
239 239
 		return "", errors.WithStack(ErrNotFound(idOrName))
240 240
 	}
241 241
 
242
-	fullRef := reference.WithDefaultTag(ref)
242
+	ref = reference.TagNameOnly(ref)
243 243
 
244 244
 	for _, p := range ps.plugins {
245
-		if p.PluginObj.Name == fullRef.String() {
245
+		if p.PluginObj.Name == reference.FamiliarString(ref) {
246 246
 			return p.PluginObj.ID, nil
247 247
 		}
248 248
 	}
249 249
deleted file mode 100644
... ...
@@ -1,194 +0,0 @@
1
-package reference
2
-
3
-import (
4
-	"fmt"
5
-
6
-	distreference "github.com/docker/distribution/reference"
7
-	"github.com/docker/docker/pkg/stringid"
8
-	"github.com/opencontainers/go-digest"
9
-	"github.com/pkg/errors"
10
-)
11
-
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
-)
22
-
23
-// Named is an object with a full name
24
-type Named interface {
25
-	// Name returns normalized repository name, like "ubuntu".
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
35
-}
36
-
37
-// NamedTagged is an object including a name and tag.
38
-type NamedTagged interface {
39
-	Named
40
-	Tag() string
41
-}
42
-
43
-// Canonical reference is an object with a fully unique
44
-// name including a name with hostname and digest
45
-type Canonical interface {
46
-	Named
47
-	Digest() digest.Digest
48
-}
49
-
50
-// ParseNamed parses s and returns a syntactically valid reference implementing
51
-// the Named interface. The reference must have a name, otherwise an error is
52
-// returned.
53
-// If an error was encountered it is returned, along with a nil Reference.
54
-func ParseNamed(s string) (Named, error) {
55
-	named, err := distreference.ParseNormalizedNamed(s)
56
-	if err != nil {
57
-		return nil, errors.Wrapf(err, "failed to parse reference %q", s)
58
-	}
59
-	if err := validateName(distreference.FamiliarName(named)); err != nil {
60
-		return nil, err
61
-	}
62
-
63
-	// Ensure returned reference cannot have tag and digest
64
-	if canonical, isCanonical := named.(distreference.Canonical); isCanonical {
65
-		r, err := distreference.WithDigest(distreference.TrimNamed(named), canonical.Digest())
66
-		if err != nil {
67
-			return nil, err
68
-		}
69
-		return &canonicalRef{namedRef{r}}, nil
70
-	}
71
-	if tagged, isTagged := named.(distreference.NamedTagged); isTagged {
72
-		r, err := distreference.WithTag(distreference.TrimNamed(named), tagged.Tag())
73
-		if err != nil {
74
-			return nil, err
75
-		}
76
-		return &taggedRef{namedRef{r}}, nil
77
-	}
78
-
79
-	return &namedRef{named}, nil
80
-}
81
-
82
-// TrimNamed removes any tag or digest from the named reference
83
-func TrimNamed(ref Named) Named {
84
-	return &namedRef{distreference.TrimNamed(ref)}
85
-}
86
-
87
-// WithName returns a named object representing the given string. If the input
88
-// is invalid ErrReferenceInvalidFormat will be returned.
89
-func WithName(name string) (Named, error) {
90
-	r, err := distreference.ParseNormalizedNamed(name)
91
-	if err != nil {
92
-		return nil, err
93
-	}
94
-	if err := validateName(distreference.FamiliarName(r)); err != nil {
95
-		return nil, err
96
-	}
97
-	if !distreference.IsNameOnly(r) {
98
-		return nil, distreference.ErrReferenceInvalidFormat
99
-	}
100
-	return &namedRef{r}, nil
101
-}
102
-
103
-// WithTag combines the name from "name" and the tag from "tag" to form a
104
-// reference incorporating both the name and the tag.
105
-func WithTag(name Named, tag string) (NamedTagged, error) {
106
-	r, err := distreference.WithTag(name, tag)
107
-	if err != nil {
108
-		return nil, err
109
-	}
110
-	return &taggedRef{namedRef{r}}, nil
111
-}
112
-
113
-// WithDigest combines the name from "name" and the digest from "digest" to form
114
-// a reference incorporating both the name and the digest.
115
-func WithDigest(name Named, digest digest.Digest) (Canonical, error) {
116
-	r, err := distreference.WithDigest(name, digest)
117
-	if err != nil {
118
-		return nil, err
119
-	}
120
-	return &canonicalRef{namedRef{r}}, nil
121
-}
122
-
123
-type namedRef struct {
124
-	distreference.Named
125
-}
126
-type taggedRef struct {
127
-	namedRef
128
-}
129
-type canonicalRef struct {
130
-	namedRef
131
-}
132
-
133
-func (r *namedRef) Name() string {
134
-	return distreference.FamiliarName(r.Named)
135
-}
136
-
137
-func (r *namedRef) String() string {
138
-	return distreference.FamiliarString(r.Named)
139
-}
140
-
141
-func (r *namedRef) FullName() string {
142
-	return r.Named.Name()
143
-}
144
-func (r *namedRef) Hostname() string {
145
-	return distreference.Domain(r.Named)
146
-}
147
-func (r *namedRef) RemoteName() string {
148
-	return distreference.Path(r.Named)
149
-}
150
-func (r *taggedRef) Tag() string {
151
-	return r.namedRef.Named.(distreference.NamedTagged).Tag()
152
-}
153
-func (r *canonicalRef) Digest() digest.Digest {
154
-	return r.namedRef.Named.(distreference.Canonical).Digest()
155
-}
156
-
157
-// WithDefaultTag adds a default tag to a reference if it only has a repo name.
158
-func WithDefaultTag(ref Named) Named {
159
-	if IsNameOnly(ref) {
160
-		ref, _ = WithTag(ref, DefaultTag)
161
-	}
162
-	return ref
163
-}
164
-
165
-// IsNameOnly returns true if reference only contains a repo name.
166
-func IsNameOnly(ref Named) bool {
167
-	if _, ok := ref.(NamedTagged); ok {
168
-		return false
169
-	}
170
-	if _, ok := ref.(Canonical); ok {
171
-		return false
172
-	}
173
-	return true
174
-}
175
-
176
-// ParseIDOrReference parses string for an image ID or a reference. ID can be
177
-// without a default prefix.
178
-func ParseIDOrReference(idOrRef string) (digest.Digest, Named, error) {
179
-	if err := stringid.ValidateID(idOrRef); err == nil {
180
-		idOrRef = "sha256:" + idOrRef
181
-	}
182
-	if dgst, err := digest.Parse(idOrRef); err == nil {
183
-		return dgst, nil, nil
184
-	}
185
-	ref, err := ParseNamed(idOrRef)
186
-	return "", ref, err
187
-}
188
-
189
-func validateName(name string) error {
190
-	if err := stringid.ValidateID(name); err == nil {
191
-		return fmt.Errorf("Invalid repository name (%s), cannot specify 64-byte hexadecimal strings", name)
192
-	}
193
-	return nil
194
-}
195 1
deleted file mode 100644
... ...
@@ -1,275 +0,0 @@
1
-package reference
2
-
3
-import (
4
-	"testing"
5
-
6
-	"github.com/opencontainers/go-digest"
7
-)
8
-
9
-func TestValidateReferenceName(t *testing.T) {
10
-	validRepoNames := []string{
11
-		"docker/docker",
12
-		"library/debian",
13
-		"debian",
14
-		"docker.io/docker/docker",
15
-		"docker.io/library/debian",
16
-		"docker.io/debian",
17
-		"index.docker.io/docker/docker",
18
-		"index.docker.io/library/debian",
19
-		"index.docker.io/debian",
20
-		"127.0.0.1:5000/docker/docker",
21
-		"127.0.0.1:5000/library/debian",
22
-		"127.0.0.1:5000/debian",
23
-		"thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev",
24
-	}
25
-	invalidRepoNames := []string{
26
-		"https://github.com/docker/docker",
27
-		"docker/Docker",
28
-		"-docker",
29
-		"-docker/docker",
30
-		"-docker.io/docker/docker",
31
-		"docker///docker",
32
-		"docker.io/docker/Docker",
33
-		"docker.io/docker///docker",
34
-		"1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
35
-		"docker.io/1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
36
-	}
37
-
38
-	for _, name := range invalidRepoNames {
39
-		_, err := ParseNamed(name)
40
-		if err == nil {
41
-			t.Fatalf("Expected invalid repo name for %q", name)
42
-		}
43
-	}
44
-
45
-	for _, name := range validRepoNames {
46
-		_, err := ParseNamed(name)
47
-		if err != nil {
48
-			t.Fatalf("Error parsing repo name %s, got: %q", name, err)
49
-		}
50
-	}
51
-}
52
-
53
-func TestValidateRemoteName(t *testing.T) {
54
-	validRepositoryNames := []string{
55
-		// Sanity check.
56
-		"docker/docker",
57
-
58
-		// Allow 64-character non-hexadecimal names (hexadecimal names are forbidden).
59
-		"thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev",
60
-
61
-		// Allow embedded hyphens.
62
-		"docker-rules/docker",
63
-
64
-		// Allow multiple hyphens as well.
65
-		"docker---rules/docker",
66
-
67
-		//Username doc and image name docker being tested.
68
-		"doc/docker",
69
-
70
-		// single character names are now allowed.
71
-		"d/docker",
72
-		"jess/t",
73
-
74
-		// Consecutive underscores.
75
-		"dock__er/docker",
76
-	}
77
-	for _, repositoryName := range validRepositoryNames {
78
-		_, err := ParseNamed(repositoryName)
79
-		if err != nil {
80
-			t.Errorf("Repository name should be valid: %v. Error: %v", repositoryName, err)
81
-		}
82
-	}
83
-
84
-	invalidRepositoryNames := []string{
85
-		// Disallow capital letters.
86
-		"docker/Docker",
87
-
88
-		// Only allow one slash.
89
-		"docker///docker",
90
-
91
-		// Disallow 64-character hexadecimal.
92
-		"1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
93
-
94
-		// Disallow leading and trailing hyphens in namespace.
95
-		"-docker/docker",
96
-		"docker-/docker",
97
-		"-docker-/docker",
98
-
99
-		// Don't allow underscores everywhere (as opposed to hyphens).
100
-		"____/____",
101
-
102
-		"_docker/_docker",
103
-
104
-		// Disallow consecutive periods.
105
-		"dock..er/docker",
106
-		"dock_.er/docker",
107
-		"dock-.er/docker",
108
-
109
-		// No repository.
110
-		"docker/",
111
-
112
-		//namespace too long
113
-		"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",
114
-	}
115
-	for _, repositoryName := range invalidRepositoryNames {
116
-		if _, err := ParseNamed(repositoryName); err == nil {
117
-			t.Errorf("Repository name should be invalid: %v", repositoryName)
118
-		}
119
-	}
120
-}
121
-
122
-func TestParseRepositoryInfo(t *testing.T) {
123
-	type tcase struct {
124
-		RemoteName, NormalizedName, FullName, AmbiguousName, Hostname string
125
-	}
126
-
127
-	tcases := []tcase{
128
-		{
129
-			RemoteName:     "fooo/bar",
130
-			NormalizedName: "fooo/bar",
131
-			FullName:       "docker.io/fooo/bar",
132
-			AmbiguousName:  "index.docker.io/fooo/bar",
133
-			Hostname:       "docker.io",
134
-		},
135
-		{
136
-			RemoteName:     "library/ubuntu",
137
-			NormalizedName: "ubuntu",
138
-			FullName:       "docker.io/library/ubuntu",
139
-			AmbiguousName:  "library/ubuntu",
140
-			Hostname:       "docker.io",
141
-		},
142
-		{
143
-			RemoteName:     "nonlibrary/ubuntu",
144
-			NormalizedName: "nonlibrary/ubuntu",
145
-			FullName:       "docker.io/nonlibrary/ubuntu",
146
-			AmbiguousName:  "",
147
-			Hostname:       "docker.io",
148
-		},
149
-		{
150
-			RemoteName:     "other/library",
151
-			NormalizedName: "other/library",
152
-			FullName:       "docker.io/other/library",
153
-			AmbiguousName:  "",
154
-			Hostname:       "docker.io",
155
-		},
156
-		{
157
-			RemoteName:     "private/moonbase",
158
-			NormalizedName: "127.0.0.1:8000/private/moonbase",
159
-			FullName:       "127.0.0.1:8000/private/moonbase",
160
-			AmbiguousName:  "",
161
-			Hostname:       "127.0.0.1:8000",
162
-		},
163
-		{
164
-			RemoteName:     "privatebase",
165
-			NormalizedName: "127.0.0.1:8000/privatebase",
166
-			FullName:       "127.0.0.1:8000/privatebase",
167
-			AmbiguousName:  "",
168
-			Hostname:       "127.0.0.1:8000",
169
-		},
170
-		{
171
-			RemoteName:     "private/moonbase",
172
-			NormalizedName: "example.com/private/moonbase",
173
-			FullName:       "example.com/private/moonbase",
174
-			AmbiguousName:  "",
175
-			Hostname:       "example.com",
176
-		},
177
-		{
178
-			RemoteName:     "privatebase",
179
-			NormalizedName: "example.com/privatebase",
180
-			FullName:       "example.com/privatebase",
181
-			AmbiguousName:  "",
182
-			Hostname:       "example.com",
183
-		},
184
-		{
185
-			RemoteName:     "private/moonbase",
186
-			NormalizedName: "example.com:8000/private/moonbase",
187
-			FullName:       "example.com:8000/private/moonbase",
188
-			AmbiguousName:  "",
189
-			Hostname:       "example.com:8000",
190
-		},
191
-		{
192
-			RemoteName:     "privatebasee",
193
-			NormalizedName: "example.com:8000/privatebasee",
194
-			FullName:       "example.com:8000/privatebasee",
195
-			AmbiguousName:  "",
196
-			Hostname:       "example.com:8000",
197
-		},
198
-		{
199
-			RemoteName:     "library/ubuntu-12.04-base",
200
-			NormalizedName: "ubuntu-12.04-base",
201
-			FullName:       "docker.io/library/ubuntu-12.04-base",
202
-			AmbiguousName:  "index.docker.io/library/ubuntu-12.04-base",
203
-			Hostname:       "docker.io",
204
-		},
205
-	}
206
-
207
-	for _, tcase := range tcases {
208
-		refStrings := []string{tcase.NormalizedName, tcase.FullName}
209
-		if tcase.AmbiguousName != "" {
210
-			refStrings = append(refStrings, tcase.AmbiguousName)
211
-		}
212
-
213
-		var refs []Named
214
-		for _, r := range refStrings {
215
-			named, err := ParseNamed(r)
216
-			if err != nil {
217
-				t.Fatal(err)
218
-			}
219
-			refs = append(refs, named)
220
-			named, err = WithName(r)
221
-			if err != nil {
222
-				t.Fatal(err)
223
-			}
224
-			refs = append(refs, named)
225
-		}
226
-
227
-		for _, r := range refs {
228
-			if expected, actual := tcase.NormalizedName, r.Name(); expected != actual {
229
-				t.Fatalf("Invalid normalized reference for %q. Expected %q, got %q", r, expected, actual)
230
-			}
231
-			if expected, actual := tcase.FullName, r.FullName(); expected != actual {
232
-				t.Fatalf("Invalid fullName for %q. Expected %q, got %q", r, expected, actual)
233
-			}
234
-			if expected, actual := tcase.Hostname, r.Hostname(); expected != actual {
235
-				t.Fatalf("Invalid hostname for %q. Expected %q, got %q", r, expected, actual)
236
-			}
237
-			if expected, actual := tcase.RemoteName, r.RemoteName(); expected != actual {
238
-				t.Fatalf("Invalid remoteName for %q. Expected %q, got %q", r, expected, actual)
239
-			}
240
-
241
-		}
242
-	}
243
-}
244
-
245
-func TestParseReferenceWithTagAndDigest(t *testing.T) {
246
-	ref, err := ParseNamed("busybox:latest@sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa")
247
-	if err != nil {
248
-		t.Fatal(err)
249
-	}
250
-	if _, isTagged := ref.(NamedTagged); isTagged {
251
-		t.Fatalf("Reference from %q should not support tag", ref)
252
-	}
253
-	if _, isCanonical := ref.(Canonical); !isCanonical {
254
-		t.Fatalf("Reference from %q should not support digest", ref)
255
-	}
256
-	if expected, actual := "busybox@sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa", ref.String(); actual != expected {
257
-		t.Fatalf("Invalid parsed reference for %q: expected %q, got %q", ref, expected, actual)
258
-	}
259
-}
260
-
261
-func TestInvalidReferenceComponents(t *testing.T) {
262
-	if _, err := WithName("-foo"); err == nil {
263
-		t.Fatal("Expected WithName to detect invalid name")
264
-	}
265
-	ref, err := WithName("busybox")
266
-	if err != nil {
267
-		t.Fatal(err)
268
-	}
269
-	if _, err := WithTag(ref, "-foo"); err == nil {
270
-		t.Fatal("Expected WithName to detect invalid tag")
271
-	}
272
-	if _, err := WithDigest(ref, digest.Digest("foo")); err == nil {
273
-		t.Fatal("Expected WithName to detect invalid digest")
274
-	}
275
-}
... ...
@@ -9,6 +9,7 @@ import (
9 9
 	"sort"
10 10
 	"sync"
11 11
 
12
+	"github.com/docker/distribution/reference"
12 13
 	"github.com/docker/docker/pkg/ioutils"
13 14
 	"github.com/opencontainers/go-digest"
14 15
 )
... ...
@@ -21,18 +22,18 @@ var (
21 21
 
22 22
 // An Association is a tuple associating a reference with an image ID.
23 23
 type Association struct {
24
-	Ref Named
24
+	Ref reference.Named
25 25
 	ID  digest.Digest
26 26
 }
27 27
 
28 28
 // Store provides the set of methods which can operate on a tag store.
29 29
 type Store interface {
30
-	References(id digest.Digest) []Named
31
-	ReferencesByName(ref Named) []Association
32
-	AddTag(ref Named, id digest.Digest, force bool) error
33
-	AddDigest(ref Canonical, id digest.Digest, force bool) error
34
-	Delete(ref Named) (bool, error)
35
-	Get(ref Named) (digest.Digest, error)
30
+	References(id digest.Digest) []reference.Named
31
+	ReferencesByName(ref reference.Named) []Association
32
+	AddTag(ref reference.Named, id digest.Digest, force bool) error
33
+	AddDigest(ref reference.Canonical, id digest.Digest, force bool) error
34
+	Delete(ref reference.Named) (bool, error)
35
+	Get(ref reference.Named) (digest.Digest, error)
36 36
 }
37 37
 
38 38
 type store struct {
... ...
@@ -44,24 +45,28 @@ type store struct {
44 44
 	Repositories map[string]repository
45 45
 	// referencesByIDCache is a cache of references indexed by ID, to speed
46 46
 	// up References.
47
-	referencesByIDCache map[digest.Digest]map[string]Named
47
+	referencesByIDCache map[digest.Digest]map[string]reference.Named
48 48
 }
49 49
 
50 50
 // Repository maps tags to digests. The key is a stringified Reference,
51 51
 // including the repository name.
52 52
 type repository map[string]digest.Digest
53 53
 
54
-type lexicalRefs []Named
54
+type lexicalRefs []reference.Named
55 55
 
56
-func (a lexicalRefs) Len() int           { return len(a) }
57
-func (a lexicalRefs) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
58
-func (a lexicalRefs) Less(i, j int) bool { return a[i].String() < a[j].String() }
56
+func (a lexicalRefs) Len() int      { return len(a) }
57
+func (a lexicalRefs) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
58
+func (a lexicalRefs) Less(i, j int) bool {
59
+	return a[i].String() < a[j].String()
60
+}
59 61
 
60 62
 type lexicalAssociations []Association
61 63
 
62
-func (a lexicalAssociations) Len() int           { return len(a) }
63
-func (a lexicalAssociations) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
64
-func (a lexicalAssociations) Less(i, j int) bool { return a[i].Ref.String() < a[j].Ref.String() }
64
+func (a lexicalAssociations) Len() int      { return len(a) }
65
+func (a lexicalAssociations) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
66
+func (a lexicalAssociations) Less(i, j int) bool {
67
+	return a[i].Ref.String() < a[j].Ref.String()
68
+}
65 69
 
66 70
 // NewReferenceStore creates a new reference store, tied to a file path where
67 71
 // the set of references are serialized in JSON format.
... ...
@@ -74,7 +79,7 @@ func NewReferenceStore(jsonPath string) (Store, error) {
74 74
 	store := &store{
75 75
 		jsonPath:            abspath,
76 76
 		Repositories:        make(map[string]repository),
77
-		referencesByIDCache: make(map[digest.Digest]map[string]Named),
77
+		referencesByIDCache: make(map[digest.Digest]map[string]reference.Named),
78 78
 	}
79 79
 	// Load the json file if it exists, otherwise create it.
80 80
 	if err := store.reload(); os.IsNotExist(err) {
... ...
@@ -89,43 +94,45 @@ func NewReferenceStore(jsonPath string) (Store, error) {
89 89
 
90 90
 // AddTag adds a tag reference to the store. If force is set to true, existing
91 91
 // references can be overwritten. This only works for tags, not digests.
92
-func (store *store) AddTag(ref Named, id digest.Digest, force bool) error {
93
-	if _, isCanonical := ref.(Canonical); isCanonical {
92
+func (store *store) AddTag(ref reference.Named, id digest.Digest, force bool) error {
93
+	if _, isCanonical := ref.(reference.Canonical); isCanonical {
94 94
 		return errors.New("refusing to create a tag with a digest reference")
95 95
 	}
96
-	return store.addReference(WithDefaultTag(ref), id, force)
96
+	return store.addReference(reference.TagNameOnly(ref), id, force)
97 97
 }
98 98
 
99 99
 // AddDigest adds a digest reference to the store.
100
-func (store *store) AddDigest(ref Canonical, id digest.Digest, force bool) error {
100
+func (store *store) AddDigest(ref reference.Canonical, id digest.Digest, force bool) error {
101 101
 	return store.addReference(ref, id, force)
102 102
 }
103 103
 
104
-func (store *store) addReference(ref Named, id digest.Digest, force bool) error {
105
-	if ref.Name() == string(digest.Canonical) {
104
+func (store *store) addReference(ref reference.Named, id digest.Digest, force bool) error {
105
+	refName := reference.FamiliarName(ref)
106
+	refStr := reference.FamiliarString(ref)
107
+
108
+	if refName == string(digest.Canonical) {
106 109
 		return errors.New("refusing to create an ambiguous tag using digest algorithm as name")
107 110
 	}
108 111
 
109 112
 	store.mu.Lock()
110 113
 	defer store.mu.Unlock()
111 114
 
112
-	repository, exists := store.Repositories[ref.Name()]
115
+	repository, exists := store.Repositories[refName]
113 116
 	if !exists || repository == nil {
114 117
 		repository = make(map[string]digest.Digest)
115
-		store.Repositories[ref.Name()] = repository
118
+		store.Repositories[refName] = repository
116 119
 	}
117 120
 
118
-	refStr := ref.String()
119 121
 	oldID, exists := repository[refStr]
120 122
 
121 123
 	if exists {
122 124
 		// force only works for tags
123
-		if digested, isDigest := ref.(Canonical); isDigest {
125
+		if digested, isDigest := ref.(reference.Canonical); isDigest {
124 126
 			return fmt.Errorf("Cannot overwrite digest %s", digested.Digest().String())
125 127
 		}
126 128
 
127 129
 		if !force {
128
-			return fmt.Errorf("Conflict: Tag %s is already set to image %s, if you want to replace it, please use -f option", ref.String(), oldID.String())
130
+			return fmt.Errorf("Conflict: Tag %s is already set to image %s, if you want to replace it, please use -f option", refStr, oldID.String())
129 131
 		}
130 132
 
131 133
 		if store.referencesByIDCache[oldID] != nil {
... ...
@@ -138,7 +145,7 @@ func (store *store) addReference(ref Named, id digest.Digest, force bool) error
138 138
 
139 139
 	repository[refStr] = id
140 140
 	if store.referencesByIDCache[id] == nil {
141
-		store.referencesByIDCache[id] = make(map[string]Named)
141
+		store.referencesByIDCache[id] = make(map[string]reference.Named)
142 142
 	}
143 143
 	store.referencesByIDCache[id][refStr] = ref
144 144
 
... ...
@@ -147,24 +154,24 @@ func (store *store) addReference(ref Named, id digest.Digest, force bool) error
147 147
 
148 148
 // Delete deletes a reference from the store. It returns true if a deletion
149 149
 // happened, or false otherwise.
150
-func (store *store) Delete(ref Named) (bool, error) {
151
-	ref = WithDefaultTag(ref)
150
+func (store *store) Delete(ref reference.Named) (bool, error) {
151
+	ref = reference.TagNameOnly(ref)
152
+
153
+	refName := reference.FamiliarName(ref)
154
+	refStr := reference.FamiliarString(ref)
152 155
 
153 156
 	store.mu.Lock()
154 157
 	defer store.mu.Unlock()
155 158
 
156
-	repoName := ref.Name()
157
-
158
-	repository, exists := store.Repositories[repoName]
159
+	repository, exists := store.Repositories[refName]
159 160
 	if !exists {
160 161
 		return false, ErrDoesNotExist
161 162
 	}
162 163
 
163
-	refStr := ref.String()
164 164
 	if id, exists := repository[refStr]; exists {
165 165
 		delete(repository, refStr)
166 166
 		if len(repository) == 0 {
167
-			delete(store.Repositories, repoName)
167
+			delete(store.Repositories, refName)
168 168
 		}
169 169
 		if store.referencesByIDCache[id] != nil {
170 170
 			delete(store.referencesByIDCache[id], refStr)
... ...
@@ -179,18 +186,34 @@ func (store *store) Delete(ref Named) (bool, error) {
179 179
 }
180 180
 
181 181
 // Get retrieves an item from the store by reference
182
-func (store *store) Get(ref Named) (digest.Digest, error) {
183
-	ref = WithDefaultTag(ref)
182
+func (store *store) Get(ref reference.Named) (digest.Digest, error) {
183
+	if canonical, ok := ref.(reference.Canonical); ok {
184
+		// If reference contains both tag and digest, only
185
+		// lookup by digest as it takes precendent over
186
+		// tag, until tag/digest combos are stored.
187
+		if _, ok := ref.(reference.Tagged); ok {
188
+			var err error
189
+			ref, err = reference.WithDigest(reference.TrimNamed(canonical), canonical.Digest())
190
+			if err != nil {
191
+				return "", err
192
+			}
193
+		}
194
+	} else {
195
+		ref = reference.TagNameOnly(ref)
196
+	}
197
+
198
+	refName := reference.FamiliarName(ref)
199
+	refStr := reference.FamiliarString(ref)
184 200
 
185 201
 	store.mu.RLock()
186 202
 	defer store.mu.RUnlock()
187 203
 
188
-	repository, exists := store.Repositories[ref.Name()]
204
+	repository, exists := store.Repositories[refName]
189 205
 	if !exists || repository == nil {
190 206
 		return "", ErrDoesNotExist
191 207
 	}
192 208
 
193
-	id, exists := repository[ref.String()]
209
+	id, exists := repository[refStr]
194 210
 	if !exists {
195 211
 		return "", ErrDoesNotExist
196 212
 	}
... ...
@@ -200,7 +223,7 @@ func (store *store) Get(ref Named) (digest.Digest, error) {
200 200
 
201 201
 // References returns a slice of references to the given ID. The slice
202 202
 // will be nil if there are no references to this ID.
203
-func (store *store) References(id digest.Digest) []Named {
203
+func (store *store) References(id digest.Digest) []reference.Named {
204 204
 	store.mu.RLock()
205 205
 	defer store.mu.RUnlock()
206 206
 
... ...
@@ -208,7 +231,7 @@ func (store *store) References(id digest.Digest) []Named {
208 208
 	// 1) We must not return a mutable
209 209
 	// 2) It would be ugly to expose the extraneous map keys to callers.
210 210
 
211
-	var references []Named
211
+	var references []reference.Named
212 212
 	for _, ref := range store.referencesByIDCache[id] {
213 213
 		references = append(references, ref)
214 214
 	}
... ...
@@ -221,18 +244,20 @@ func (store *store) References(id digest.Digest) []Named {
221 221
 // ReferencesByName returns the references for a given repository name.
222 222
 // If there are no references known for this repository name,
223 223
 // ReferencesByName returns nil.
224
-func (store *store) ReferencesByName(ref Named) []Association {
224
+func (store *store) ReferencesByName(ref reference.Named) []Association {
225
+	refName := reference.FamiliarName(ref)
226
+
225 227
 	store.mu.RLock()
226 228
 	defer store.mu.RUnlock()
227 229
 
228
-	repository, exists := store.Repositories[ref.Name()]
230
+	repository, exists := store.Repositories[refName]
229 231
 	if !exists {
230 232
 		return nil
231 233
 	}
232 234
 
233 235
 	var associations []Association
234 236
 	for refStr, refID := range repository {
235
-		ref, err := ParseNamed(refStr)
237
+		ref, err := reference.ParseNormalizedNamed(refStr)
236 238
 		if err != nil {
237 239
 			// Should never happen
238 240
 			return nil
... ...
@@ -270,13 +295,13 @@ func (store *store) reload() error {
270 270
 
271 271
 	for _, repository := range store.Repositories {
272 272
 		for refStr, refID := range repository {
273
-			ref, err := ParseNamed(refStr)
273
+			ref, err := reference.ParseNormalizedNamed(refStr)
274 274
 			if err != nil {
275 275
 				// Should never happen
276 276
 				continue
277 277
 			}
278 278
 			if store.referencesByIDCache[refID] == nil {
279
-				store.referencesByIDCache[refID] = make(map[string]Named)
279
+				store.referencesByIDCache[refID] = make(map[string]reference.Named)
280 280
 			}
281 281
 			store.referencesByIDCache[refID][refStr] = ref
282 282
 		}
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"strings"
9 9
 	"testing"
10 10
 
11
+	"github.com/docker/distribution/reference"
11 12
 	"github.com/opencontainers/go-digest"
12 13
 )
13 14
 
... ...
@@ -45,7 +46,7 @@ func TestLoad(t *testing.T) {
45 45
 	}
46 46
 
47 47
 	for refStr, expectedID := range saveLoadTestCases {
48
-		ref, err := ParseNamed(refStr)
48
+		ref, err := reference.ParseNormalizedNamed(refStr)
49 49
 		if err != nil {
50 50
 			t.Fatalf("failed to parse reference: %v", err)
51 51
 		}
... ...
@@ -74,11 +75,11 @@ func TestSave(t *testing.T) {
74 74
 	}
75 75
 
76 76
 	for refStr, id := range saveLoadTestCases {
77
-		ref, err := ParseNamed(refStr)
77
+		ref, err := reference.ParseNormalizedNamed(refStr)
78 78
 		if err != nil {
79 79
 			t.Fatalf("failed to parse reference: %v", err)
80 80
 		}
81
-		if canonical, ok := ref.(Canonical); ok {
81
+		if canonical, ok := ref.(reference.Canonical); ok {
82 82
 			err = store.AddDigest(canonical, id, false)
83 83
 			if err != nil {
84 84
 				t.Fatalf("could not add digest reference %s: %v", refStr, err)
... ...
@@ -120,7 +121,7 @@ func TestAddDeleteGet(t *testing.T) {
120 120
 	testImageID3 := digest.Digest("sha256:9655aef5fd742a1b4e1b7b163aa9f1c76c186304bf39102283d80927c916ca9e")
121 121
 
122 122
 	// Try adding a reference with no tag or digest
123
-	nameOnly, err := WithName("username/repo")
123
+	nameOnly, err := reference.ParseNormalizedNamed("username/repo")
124 124
 	if err != nil {
125 125
 		t.Fatalf("could not parse reference: %v", err)
126 126
 	}
... ...
@@ -129,7 +130,7 @@ func TestAddDeleteGet(t *testing.T) {
129 129
 	}
130 130
 
131 131
 	// Add a few references
132
-	ref1, err := ParseNamed("username/repo1:latest")
132
+	ref1, err := reference.ParseNormalizedNamed("username/repo1:latest")
133 133
 	if err != nil {
134 134
 		t.Fatalf("could not parse reference: %v", err)
135 135
 	}
... ...
@@ -137,7 +138,7 @@ func TestAddDeleteGet(t *testing.T) {
137 137
 		t.Fatalf("error adding to store: %v", err)
138 138
 	}
139 139
 
140
-	ref2, err := ParseNamed("username/repo1:old")
140
+	ref2, err := reference.ParseNormalizedNamed("username/repo1:old")
141 141
 	if err != nil {
142 142
 		t.Fatalf("could not parse reference: %v", err)
143 143
 	}
... ...
@@ -145,7 +146,7 @@ func TestAddDeleteGet(t *testing.T) {
145 145
 		t.Fatalf("error adding to store: %v", err)
146 146
 	}
147 147
 
148
-	ref3, err := ParseNamed("username/repo1:alias")
148
+	ref3, err := reference.ParseNormalizedNamed("username/repo1:alias")
149 149
 	if err != nil {
150 150
 		t.Fatalf("could not parse reference: %v", err)
151 151
 	}
... ...
@@ -153,7 +154,7 @@ func TestAddDeleteGet(t *testing.T) {
153 153
 		t.Fatalf("error adding to store: %v", err)
154 154
 	}
155 155
 
156
-	ref4, err := ParseNamed("username/repo2:latest")
156
+	ref4, err := reference.ParseNormalizedNamed("username/repo2:latest")
157 157
 	if err != nil {
158 158
 		t.Fatalf("could not parse reference: %v", err)
159 159
 	}
... ...
@@ -161,11 +162,11 @@ func TestAddDeleteGet(t *testing.T) {
161 161
 		t.Fatalf("error adding to store: %v", err)
162 162
 	}
163 163
 
164
-	ref5, err := ParseNamed("username/repo3@sha256:58153dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c")
164
+	ref5, err := reference.ParseNormalizedNamed("username/repo3@sha256:58153dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c")
165 165
 	if err != nil {
166 166
 		t.Fatalf("could not parse reference: %v", err)
167 167
 	}
168
-	if err = store.AddDigest(ref5.(Canonical), testImageID2, false); err != nil {
168
+	if err = store.AddDigest(ref5.(reference.Canonical), testImageID2, false); err != nil {
169 169
 		t.Fatalf("error adding to store: %v", err)
170 170
 	}
171 171
 
... ...
@@ -228,7 +229,7 @@ func TestAddDeleteGet(t *testing.T) {
228 228
 	}
229 229
 
230 230
 	// Get should return ErrDoesNotExist for a nonexistent repo
231
-	nonExistRepo, err := ParseNamed("username/nonexistrepo:latest")
231
+	nonExistRepo, err := reference.ParseNormalizedNamed("username/nonexistrepo:latest")
232 232
 	if err != nil {
233 233
 		t.Fatalf("could not parse reference: %v", err)
234 234
 	}
... ...
@@ -237,7 +238,7 @@ func TestAddDeleteGet(t *testing.T) {
237 237
 	}
238 238
 
239 239
 	// Get should return ErrDoesNotExist for a nonexistent tag
240
-	nonExistTag, err := ParseNamed("username/repo1:nonexist")
240
+	nonExistTag, err := reference.ParseNormalizedNamed("username/repo1:nonexist")
241 241
 	if err != nil {
242 242
 		t.Fatalf("could not parse reference: %v", err)
243 243
 	}
... ...
@@ -263,7 +264,7 @@ func TestAddDeleteGet(t *testing.T) {
263 263
 	}
264 264
 
265 265
 	// Check ReferencesByName
266
-	repoName, err := WithName("username/repo1")
266
+	repoName, err := reference.ParseNormalizedNamed("username/repo1")
267 267
 	if err != nil {
268 268
 		t.Fatalf("could not parse reference: %v", err)
269 269
 	}
... ...
@@ -334,7 +335,7 @@ func TestInvalidTags(t *testing.T) {
334 334
 	id := digest.Digest("sha256:470022b8af682154f57a2163d030eb369549549cba00edc69e1b99b46bb924d6")
335 335
 
336 336
 	// sha256 as repo name
337
-	ref, err := ParseNamed("sha256:abc")
337
+	ref, err := reference.ParseNormalizedNamed("sha256:abc")
338 338
 	if err != nil {
339 339
 		t.Fatal(err)
340 340
 	}
... ...
@@ -344,7 +345,7 @@ func TestInvalidTags(t *testing.T) {
344 344
 	}
345 345
 
346 346
 	// setting digest as a tag
347
-	ref, err = ParseNamed("registry@sha256:367eb40fd0330a7e464777121e39d2f5b3e8e23a1e159342e53ab05c9e4d94e6")
347
+	ref, err = reference.ParseNormalizedNamed("registry@sha256:367eb40fd0330a7e464777121e39d2f5b3e8e23a1e159342e53ab05c9e4d94e6")
348 348
 	if err != nil {
349 349
 		t.Fatal(err)
350 350
 	}
... ...
@@ -10,7 +10,6 @@ import (
10 10
 	"github.com/docker/distribution/reference"
11 11
 	registrytypes "github.com/docker/docker/api/types/registry"
12 12
 	"github.com/docker/docker/opts"
13
-	forkedref "github.com/docker/docker/reference"
14 13
 	"github.com/pkg/errors"
15 14
 	"github.com/spf13/pflag"
16 15
 )
... ...
@@ -271,8 +270,9 @@ func ValidateMirror(val string) (string, error) {
271 271
 
272 272
 // ValidateIndexName validates an index name.
273 273
 func ValidateIndexName(val string) (string, error) {
274
-	if val == forkedref.LegacyDefaultHostname {
275
-		val = forkedref.DefaultHostname
274
+	// TODO: upstream this to check to reference package
275
+	if val == "index.docker.io" {
276
+		val = "docker.io"
276 277
 	}
277 278
 	if strings.HasPrefix(val, "-") || strings.HasSuffix(val, "-") {
278 279
 		return "", fmt.Errorf("Invalid index name (%s). Cannot begin or end with a hyphen.", val)
... ...
@@ -328,13 +328,8 @@ func newRepositoryInfo(config *serviceConfig, name reference.Named) (*Repository
328 328
 	}
329 329
 	official := !strings.ContainsRune(reference.FamiliarName(name), '/')
330 330
 
331
-	// TODO: remove used of forked reference package
332
-	nameref, err := forkedref.ParseNamed(name.String())
333
-	if err != nil {
334
-		return nil, err
335
-	}
336 331
 	return &RepositoryInfo{
337
-		Named:    nameref,
332
+		Name:     reference.TrimNamed(name),
338 333
 		Index:    index,
339 334
 		Official: official,
340 335
 	}, nil
... ...
@@ -17,8 +17,8 @@ import (
17 17
 	"testing"
18 18
 	"time"
19 19
 
20
+	"github.com/docker/distribution/reference"
20 21
 	registrytypes "github.com/docker/docker/api/types/registry"
21
-	"github.com/docker/docker/reference"
22 22
 	"github.com/gorilla/mux"
23 23
 
24 24
 	"github.com/Sirupsen/logrus"
... ...
@@ -14,7 +14,6 @@ import (
14 14
 	"github.com/docker/distribution/registry/client/transport"
15 15
 	"github.com/docker/docker/api/types"
16 16
 	registrytypes "github.com/docker/docker/api/types/registry"
17
-	forkedref "github.com/docker/docker/reference"
18 17
 )
19 18
 
20 19
 var (
... ...
@@ -202,7 +201,7 @@ func TestGetRemoteImageLayer(t *testing.T) {
202 202
 
203 203
 func TestGetRemoteTag(t *testing.T) {
204 204
 	r := spawnTestRegistrySession(t)
205
-	repoRef, err := forkedref.ParseNamed(REPO)
205
+	repoRef, err := reference.ParseNormalizedNamed(REPO)
206 206
 	if err != nil {
207 207
 		t.Fatal(err)
208 208
 	}
... ...
@@ -212,7 +211,7 @@ func TestGetRemoteTag(t *testing.T) {
212 212
 	}
213 213
 	assertEqual(t, tag, imageID, "Expected tag test to map to "+imageID)
214 214
 
215
-	bazRef, err := forkedref.ParseNamed("foo42/baz")
215
+	bazRef, err := reference.ParseNormalizedNamed("foo42/baz")
216 216
 	if err != nil {
217 217
 		t.Fatal(err)
218 218
 	}
... ...
@@ -224,7 +223,7 @@ func TestGetRemoteTag(t *testing.T) {
224 224
 
225 225
 func TestGetRemoteTags(t *testing.T) {
226 226
 	r := spawnTestRegistrySession(t)
227
-	repoRef, err := forkedref.ParseNamed(REPO)
227
+	repoRef, err := reference.ParseNormalizedNamed(REPO)
228 228
 	if err != nil {
229 229
 		t.Fatal(err)
230 230
 	}
... ...
@@ -236,7 +235,7 @@ func TestGetRemoteTags(t *testing.T) {
236 236
 	assertEqual(t, tags["latest"], imageID, "Expected tag latest to map to "+imageID)
237 237
 	assertEqual(t, tags["test"], imageID, "Expected tag test to map to "+imageID)
238 238
 
239
-	bazRef, err := forkedref.ParseNamed("foo42/baz")
239
+	bazRef, err := reference.ParseNormalizedNamed("foo42/baz")
240 240
 	if err != nil {
241 241
 		t.Fatal(err)
242 242
 	}
... ...
@@ -253,7 +252,7 @@ func TestGetRepositoryData(t *testing.T) {
253 253
 		t.Fatal(err)
254 254
 	}
255 255
 	host := "http://" + parsedURL.Host + "/v1/"
256
-	repoRef, err := forkedref.ParseNamed(REPO)
256
+	repoRef, err := reference.ParseNormalizedNamed(REPO)
257 257
 	if err != nil {
258 258
 		t.Fatal(err)
259 259
 	}
... ...
@@ -516,9 +515,9 @@ func TestParseRepositoryInfo(t *testing.T) {
516 516
 			t.Error(err)
517 517
 		} else {
518 518
 			checkEqual(t, repoInfo.Index.Name, expectedRepoInfo.Index.Name, reposName)
519
-			checkEqual(t, repoInfo.RemoteName(), expectedRepoInfo.RemoteName, reposName)
520
-			checkEqual(t, repoInfo.Name(), expectedRepoInfo.LocalName, reposName)
521
-			checkEqual(t, repoInfo.FullName(), expectedRepoInfo.CanonicalName, reposName)
519
+			checkEqual(t, reference.Path(repoInfo.Name), expectedRepoInfo.RemoteName, reposName)
520
+			checkEqual(t, reference.FamiliarName(repoInfo.Name), expectedRepoInfo.LocalName, reposName)
521
+			checkEqual(t, repoInfo.Name.Name(), expectedRepoInfo.CanonicalName, reposName)
522 522
 			checkEqual(t, repoInfo.Index.Official, expectedRepoInfo.Index.Official, reposName)
523 523
 			checkEqual(t, repoInfo.Official, expectedRepoInfo.Official, reposName)
524 524
 		}
... ...
@@ -689,7 +688,7 @@ func TestMirrorEndpointLookup(t *testing.T) {
689 689
 
690 690
 func TestPushRegistryTag(t *testing.T) {
691 691
 	r := spawnTestRegistrySession(t)
692
-	repoRef, err := forkedref.ParseNamed(REPO)
692
+	repoRef, err := reference.ParseNormalizedNamed(REPO)
693 693
 	if err != nil {
694 694
 		t.Fatal(err)
695 695
 	}
... ...
@@ -711,7 +710,7 @@ func TestPushImageJSONIndex(t *testing.T) {
711 711
 			Checksum: "sha256:bea7bf2e4bacd479344b737328db47b18880d09096e6674165533aa994f5e9f2",
712 712
 		},
713 713
 	}
714
-	repoRef, err := forkedref.ParseNamed(REPO)
714
+	repoRef, err := reference.ParseNormalizedNamed(REPO)
715 715
 	if err != nil {
716 716
 		t.Fatal(err)
717 717
 	}
... ...
@@ -19,6 +19,7 @@ import (
19 19
 	"strings"
20 20
 
21 21
 	"github.com/Sirupsen/logrus"
22
+	"github.com/docker/distribution/reference"
22 23
 	"github.com/docker/distribution/registry/api/errcode"
23 24
 	"github.com/docker/docker/api/types"
24 25
 	registrytypes "github.com/docker/docker/api/types/registry"
... ...
@@ -26,7 +27,6 @@ import (
26 26
 	"github.com/docker/docker/pkg/ioutils"
27 27
 	"github.com/docker/docker/pkg/stringid"
28 28
 	"github.com/docker/docker/pkg/tarsum"
29
-	"github.com/docker/docker/reference"
30 29
 )
31 30
 
32 31
 var (
... ...
@@ -324,7 +324,7 @@ func (r *Session) GetRemoteImageLayer(imgID, registry string, imgSize int64) (io
324 324
 // argument, and returns data from the first one that answers the query
325 325
 // successfully.
326 326
 func (r *Session) GetRemoteTag(registries []string, repositoryRef reference.Named, askedTag string) (string, error) {
327
-	repository := repositoryRef.RemoteName()
327
+	repository := reference.Path(repositoryRef)
328 328
 
329 329
 	if strings.Count(repository, "/") == 0 {
330 330
 		// This will be removed once the registry supports auto-resolution on
... ...
@@ -362,7 +362,7 @@ func (r *Session) GetRemoteTag(registries []string, repositoryRef reference.Name
362 362
 // the first one that answers the query successfully. It returns a map with
363 363
 // tag names as the keys and image IDs as the values.
364 364
 func (r *Session) GetRemoteTags(registries []string, repositoryRef reference.Named) (map[string]string, error) {
365
-	repository := repositoryRef.RemoteName()
365
+	repository := reference.Path(repositoryRef)
366 366
 
367 367
 	if strings.Count(repository, "/") == 0 {
368 368
 		// This will be removed once the registry supports auto-resolution on
... ...
@@ -416,7 +416,7 @@ func buildEndpointsList(headers []string, indexEp string) ([]string, error) {
416 416
 
417 417
 // GetRepositoryData returns lists of images and endpoints for the repository
418 418
 func (r *Session) GetRepositoryData(name reference.Named) (*RepositoryData, error) {
419
-	repositoryTarget := fmt.Sprintf("%srepositories/%s/images", r.indexEndpoint.String(), name.RemoteName())
419
+	repositoryTarget := fmt.Sprintf("%srepositories/%s/images", r.indexEndpoint.String(), reference.Path(name))
420 420
 
421 421
 	logrus.Debugf("[registry] Calling GET %s", repositoryTarget)
422 422
 
... ...
@@ -450,7 +450,7 @@ func (r *Session) GetRepositoryData(name reference.Named) (*RepositoryData, erro
450 450
 		if err != nil {
451 451
 			logrus.Debugf("Error reading response body: %s", err)
452 452
 		}
453
-		return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to pull repository %s: %q", res.StatusCode, name.RemoteName(), errBody), res)
453
+		return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to pull repository %s: %q", res.StatusCode, reference.Path(name), errBody), res)
454 454
 	}
455 455
 
456 456
 	var endpoints []string
... ...
@@ -605,7 +605,7 @@ func (r *Session) PushImageLayerRegistry(imgID string, layer io.Reader, registry
605 605
 func (r *Session) PushRegistryTag(remote reference.Named, revision, tag, registry string) error {
606 606
 	// "jsonify" the string
607 607
 	revision = "\"" + revision + "\""
608
-	path := fmt.Sprintf("repositories/%s/tags/%s", remote.RemoteName(), tag)
608
+	path := fmt.Sprintf("repositories/%s/tags/%s", reference.Path(remote), tag)
609 609
 
610 610
 	req, err := http.NewRequest("PUT", registry+path, strings.NewReader(revision))
611 611
 	if err != nil {
... ...
@@ -619,7 +619,7 @@ func (r *Session) PushRegistryTag(remote reference.Named, revision, tag, registr
619 619
 	}
620 620
 	res.Body.Close()
621 621
 	if res.StatusCode != 200 && res.StatusCode != 201 {
622
-		return httputils.NewHTTPRequestError(fmt.Sprintf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, remote.RemoteName()), res)
622
+		return httputils.NewHTTPRequestError(fmt.Sprintf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, reference.Path(remote)), res)
623 623
 	}
624 624
 	return nil
625 625
 }
... ...
@@ -645,7 +645,7 @@ func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData,
645 645
 	if validate {
646 646
 		suffix = "images"
647 647
 	}
648
-	u := fmt.Sprintf("%srepositories/%s/%s", r.indexEndpoint.String(), remote.RemoteName(), suffix)
648
+	u := fmt.Sprintf("%srepositories/%s/%s", r.indexEndpoint.String(), reference.Path(remote), suffix)
649 649
 	logrus.Debugf("[registry] PUT %s", u)
650 650
 	logrus.Debugf("Image list pushed to index:\n%s", imgListJSON)
651 651
 	headers := map[string][]string{
... ...
@@ -683,7 +683,7 @@ func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData,
683 683
 			if err != nil {
684 684
 				logrus.Debugf("Error reading response body: %s", err)
685 685
 			}
686
-			return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push repository %s: %q", res.StatusCode, remote.RemoteName(), errBody), res)
686
+			return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push repository %s: %q", res.StatusCode, reference.Path(remote), errBody), res)
687 687
 		}
688 688
 		tokens = res.Header["X-Docker-Token"]
689 689
 		logrus.Debugf("Auth token: %v", tokens)
... ...
@@ -701,7 +701,7 @@ func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData,
701 701
 			if err != nil {
702 702
 				logrus.Debugf("Error reading response body: %s", err)
703 703
 			}
704
-			return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push checksums %s: %q", res.StatusCode, remote.RemoteName(), errBody), res)
704
+			return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push checksums %s: %q", res.StatusCode, reference.Path(remote), errBody), res)
705 705
 		}
706 706
 	}
707 707
 
... ...
@@ -1,8 +1,8 @@
1 1
 package registry
2 2
 
3 3
 import (
4
+	"github.com/docker/distribution/reference"
4 5
 	registrytypes "github.com/docker/docker/api/types/registry"
5
-	"github.com/docker/docker/reference"
6 6
 )
7 7
 
8 8
 // RepositoryData tracks the image list, list of endpoints for a repository
... ...
@@ -57,7 +57,7 @@ var apiVersions = map[APIVersion]string{
57 57
 
58 58
 // RepositoryInfo describes a repository
59 59
 type RepositoryInfo struct {
60
-	reference.Named
60
+	Name reference.Named
61 61
 	// Index points to registry information
62 62
 	Index *registrytypes.IndexInfo
63 63
 	// Official indicates whether the repository is considered official.
... ...
@@ -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 129ad8ea0c3760d878b34cffdb9c3be874a7b2f7
46
+github.com/docker/distribution 545102ea07aa9796f189d82f606b7c27d7aa3ed3
47 47
 github.com/vbatts/tar-split v0.10.1
48 48
 github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb
49 49
 
... ...
@@ -102,7 +102,7 @@ github.com/docker/containerd 78fb8f45890a601e0fd9051cf9f9f74923e950fd
102 102
 github.com/tonistiigi/fifo 1405643975692217d6720f8b54aeee1bf2cd5cf4
103 103
 
104 104
 # cluster
105
-github.com/docker/swarmkit 78ae345f449ac69aa741c762df7e5f0020f70275
105
+github.com/docker/swarmkit 3ca4775ba4a5519e2225c3337c7db8901ec39d26
106 106
 github.com/golang/mock bd3c8e81be01eef76d4b503f5e687d2d1354d2d9
107 107
 github.com/gogo/protobuf 8d70fb3182befc465c4a1eac8ad4d38ff49778e2
108 108
 github.com/cloudflare/cfssl 7fb22c8cba7ecaf98e4082d22d65800cf45e042a
... ...
@@ -22,8 +22,8 @@ type Manifest interface {
22 22
 	References() []Descriptor
23 23
 
24 24
 	// Payload provides the serialized format of the manifest, in addition to
25
-	// the mediatype.
26
-	Payload() (mediatype string, payload []byte, err error)
25
+	// the media type.
26
+	Payload() (mediaType string, payload []byte, err error)
27 27
 }
28 28
 
29 29
 // ManifestBuilder creates a manifest allowing one to include dependencies.
... ...
@@ -94,20 +94,20 @@ var mappings = make(map[string]UnmarshalFunc, 0)
94 94
 func UnmarshalManifest(ctHeader string, p []byte) (Manifest, Descriptor, error) {
95 95
 	// Need to look up by the actual media type, not the raw contents of
96 96
 	// the header. Strip semicolons and anything following them.
97
-	var mediatype string
97
+	var mediaType string
98 98
 	if ctHeader != "" {
99 99
 		var err error
100
-		mediatype, _, err = mime.ParseMediaType(ctHeader)
100
+		mediaType, _, err = mime.ParseMediaType(ctHeader)
101 101
 		if err != nil {
102 102
 			return nil, Descriptor{}, err
103 103
 		}
104 104
 	}
105 105
 
106
-	unmarshalFunc, ok := mappings[mediatype]
106
+	unmarshalFunc, ok := mappings[mediaType]
107 107
 	if !ok {
108 108
 		unmarshalFunc, ok = mappings[""]
109 109
 		if !ok {
110
-			return nil, Descriptor{}, fmt.Errorf("unsupported manifest mediatype and no default available: %s", mediatype)
110
+			return nil, Descriptor{}, fmt.Errorf("unsupported manifest media type and no default available: %s", mediaType)
111 111
 		}
112 112
 	}
113 113
 
... ...
@@ -116,10 +116,10 @@ func UnmarshalManifest(ctHeader string, p []byte) (Manifest, Descriptor, error)
116 116
 
117 117
 // RegisterManifestSchema registers an UnmarshalFunc for a given schema type.  This
118 118
 // should be called from specific
119
-func RegisterManifestSchema(mediatype string, u UnmarshalFunc) error {
120
-	if _, ok := mappings[mediatype]; ok {
121
-		return fmt.Errorf("manifest mediatype registration would overwrite existing: %s", mediatype)
119
+func RegisterManifestSchema(mediaType string, u UnmarshalFunc) error {
120
+	if _, ok := mappings[mediaType]; ok {
121
+		return fmt.Errorf("manifest media type registration would overwrite existing: %s", mediaType)
122 122
 	}
123
-	mappings[mediatype] = u
123
+	mappings[mediaType] = u
124 124
 	return nil
125 125
 }
... ...
@@ -1,5 +1,7 @@
1 1
 package reference
2 2
 
3
+import "path"
4
+
3 5
 // IsNameOnly returns true if reference only contains a repo name.
4 6
 func IsNameOnly(ref Named) bool {
5 7
 	if _, ok := ref.(NamedTagged); ok {
... ...
@@ -14,7 +16,7 @@ func IsNameOnly(ref Named) bool {
14 14
 // FamiliarName returns the familiar name string
15 15
 // for the given named, familiarizing if needed.
16 16
 func FamiliarName(ref Named) string {
17
-	if nn, ok := ref.(NormalizedNamed); ok {
17
+	if nn, ok := ref.(normalizedNamed); ok {
18 18
 		return nn.Familiar().Name()
19 19
 	}
20 20
 	return ref.Name()
... ...
@@ -23,8 +25,18 @@ func FamiliarName(ref Named) string {
23 23
 // FamiliarString returns the familiar string representation
24 24
 // for the given reference, familiarizing if needed.
25 25
 func FamiliarString(ref Reference) string {
26
-	if nn, ok := ref.(NormalizedNamed); ok {
26
+	if nn, ok := ref.(normalizedNamed); ok {
27 27
 		return nn.Familiar().String()
28 28
 	}
29 29
 	return ref.String()
30 30
 }
31
+
32
+// FamiliarMatch reports whether ref matches the specified pattern.
33
+// See https://godoc.org/path#Match for supported patterns.
34
+func FamiliarMatch(pattern string, ref Reference) (bool, error) {
35
+	matched, err := path.Match(pattern, FamiliarString(ref))
36
+	if namedRef, isNamed := ref.(Named); isNamed && !matched {
37
+		matched, _ = path.Match(pattern, FamiliarName(namedRef))
38
+	}
39
+	return matched, err
40
+}
... ...
@@ -12,16 +12,16 @@ import (
12 12
 var (
13 13
 	legacyDefaultDomain = "index.docker.io"
14 14
 	defaultDomain       = "docker.io"
15
-	defaultRepoPrefix   = "library/"
15
+	officialRepoName    = "library"
16 16
 	defaultTag          = "latest"
17 17
 )
18 18
 
19
-// NormalizedNamed represents a name which has been
19
+// normalizedNamed represents a name which has been
20 20
 // normalized and has a familiar form. A familiar name
21 21
 // is what is used in Docker UI. An example normalized
22 22
 // name is "docker.io/library/ubuntu" and corresponding
23 23
 // familiar name of "ubuntu".
24
-type NormalizedNamed interface {
24
+type normalizedNamed interface {
25 25
 	Named
26 26
 	Familiar() Named
27 27
 }
... ...
@@ -30,7 +30,7 @@ type NormalizedNamed interface {
30 30
 // transforming a familiar name from Docker UI to a fully
31 31
 // qualified reference. If the value may be an identifier
32 32
 // use ParseAnyReference.
33
-func ParseNormalizedNamed(s string) (NormalizedNamed, error) {
33
+func ParseNormalizedNamed(s string) (Named, error) {
34 34
 	if ok := anchoredIdentifierRegexp.MatchString(s); ok {
35 35
 		return nil, fmt.Errorf("invalid repository name (%s), cannot specify 64-byte hexadecimal strings", s)
36 36
 	}
... ...
@@ -49,7 +49,7 @@ func ParseNormalizedNamed(s string) (NormalizedNamed, error) {
49 49
 	if err != nil {
50 50
 		return nil, err
51 51
 	}
52
-	named, isNamed := ref.(NormalizedNamed)
52
+	named, isNamed := ref.(Named)
53 53
 	if !isNamed {
54 54
 		return nil, fmt.Errorf("reference %s has no name", ref.String())
55 55
 	}
... ...
@@ -70,7 +70,7 @@ func splitDockerDomain(name string) (domain, remainder string) {
70 70
 		domain = defaultDomain
71 71
 	}
72 72
 	if domain == defaultDomain && !strings.ContainsRune(remainder, '/') {
73
-		remainder = defaultRepoPrefix + remainder
73
+		remainder = officialRepoName + "/" + remainder
74 74
 	}
75 75
 	return
76 76
 }
... ...
@@ -89,7 +89,10 @@ func familiarizeName(named namedRepository) repository {
89 89
 
90 90
 	if repo.domain == defaultDomain {
91 91
 		repo.domain = ""
92
-		repo.path = strings.TrimPrefix(repo.path, defaultRepoPrefix)
92
+		// Handle official repositories which have the pattern "library/<official repo name>"
93
+		if split := strings.Split(repo.path, "/"); len(split) == 2 && split[0] == officialRepoName {
94
+			repo.path = split[1]
95
+		}
93 96
 	}
94 97
 	return repo
95 98
 }
... ...
@@ -120,11 +123,10 @@ func (c canonicalReference) Familiar() Named {
120 120
 	}
121 121
 }
122 122
 
123
-// EnsureTagged adds the default tag "latest" to a reference if it only has
123
+// TagNameOnly adds the default tag "latest" to a reference if it only has
124 124
 // a repo name.
125
-func EnsureTagged(ref Named) NamedTagged {
126
-	namedTagged, ok := ref.(NamedTagged)
127
-	if !ok {
125
+func TagNameOnly(ref Named) Named {
126
+	if IsNameOnly(ref) {
128 127
 		namedTagged, err := WithTag(ref, defaultTag)
129 128
 		if err != nil {
130 129
 			// Default tag must be valid, to create a NamedTagged
... ...
@@ -134,7 +136,7 @@ func EnsureTagged(ref Named) NamedTagged {
134 134
 		}
135 135
 		return namedTagged
136 136
 	}
137
-	return namedTagged
137
+	return ref
138 138
 }
139 139
 
140 140
 // ParseAnyReference parses a reference string as a possible identifier,
... ...
@@ -20,14 +20,13 @@
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 22
 //
23
-//      identifier                      := /[a-f0-9]{64}/
24
-//      short-identifier                := /[a-f0-9]{6,64}/
23
+//	identifier                      := /[a-f0-9]{64}/
24
+//	short-identifier                := /[a-f0-9]{6,64}/
25 25
 package reference
26 26
 
27 27
 import (
28 28
 	"errors"
29 29
 	"fmt"
30
-	"path"
31 30
 	"strings"
32 31
 
33 32
 	"github.com/opencontainers/go-digest"
... ...
@@ -56,6 +55,9 @@ var (
56 56
 
57 57
 	// ErrNameTooLong is returned when a repository name is longer than NameTotalLengthMax.
58 58
 	ErrNameTooLong = fmt.Errorf("repository name must not be more than %v characters", NameTotalLengthMax)
59
+
60
+	// ErrNameNotCanonical is returned when a name is not canonical.
61
+	ErrNameNotCanonical = errors.New("repository name must be canonical")
59 62
 )
60 63
 
61 64
 // Reference is an opaque object reference identifier that may include
... ...
@@ -232,18 +234,17 @@ func Parse(s string) (Reference, error) {
232 232
 }
233 233
 
234 234
 // ParseNamed parses s and returns a syntactically valid reference implementing
235
-// the Named interface. The reference must have a name, otherwise an error is
236
-// returned.
235
+// the Named interface. The reference must have a name and be in the canonical
236
+// form, otherwise an error is returned.
237 237
 // If an error was encountered it is returned, along with a nil Reference.
238 238
 // NOTE: ParseNamed will not handle short digests.
239 239
 func ParseNamed(s string) (Named, error) {
240
-	ref, err := Parse(s)
240
+	named, err := ParseNormalizedNamed(s)
241 241
 	if err != nil {
242 242
 		return nil, err
243 243
 	}
244
-	named, isNamed := ref.(Named)
245
-	if !isNamed {
246
-		return nil, fmt.Errorf("reference %s has no name", ref.String())
244
+	if named.String() != s {
245
+		return nil, ErrNameNotCanonical
247 246
 	}
248 247
 	return named, nil
249 248
 }
... ...
@@ -317,16 +318,6 @@ func WithDigest(name Named, digest digest.Digest) (Canonical, error) {
317 317
 	}, nil
318 318
 }
319 319
 
320
-// Match reports whether ref matches the specified pattern.
321
-// See https://godoc.org/path#Match for supported patterns.
322
-func Match(pattern string, ref Reference) (bool, error) {
323
-	matched, err := path.Match(pattern, ref.String())
324
-	if namedRef, isNamed := ref.(Named); isNamed && !matched {
325
-		matched, _ = path.Match(pattern, namedRef.Name())
326
-	}
327
-	return matched, err
328
-}
329
-
330 320
 // TrimNamed removes any tag or digest from the named reference.
331 321
 func TrimNamed(ref Named) Named {
332 322
 	domain, path := SplitHostname(ref)
... ...
@@ -408,7 +399,7 @@ func (r repository) Path() string {
408 408
 type digestReference digest.Digest
409 409
 
410 410
 func (d digestReference) String() string {
411
-	return d.String()
411
+	return digest.Digest(d).String()
412 412
 }
413 413
 
414 414
 func (d digestReference) Digest() digest.Digest {
... ...
@@ -35,7 +35,7 @@ type Namespace interface {
35 35
 	// reference.
36 36
 	Repository(ctx context.Context, name reference.Named) (Repository, error)
37 37
 
38
-	// Repositories fills 'repos' with a lexigraphically sorted catalog of repositories
38
+	// Repositories fills 'repos' with a lexicographically sorted catalog of repositories
39 39
 	// up to the size of 'repos' and returns the value 'n' for the number of entries
40 40
 	// which were filled.  'last' contains an offset in the catalog, and 'err' will be
41 41
 	// set to io.EOF if there are no more entries to obtain.
... ...
@@ -26,7 +26,7 @@ func NewInMemoryBlobDescriptorCacheProvider() cache.BlobDescriptorCacheProvider
26 26
 }
27 27
 
28 28
 func (imbdcp *inMemoryBlobDescriptorCacheProvider) RepositoryScoped(repo string) (distribution.BlobDescriptorService, error) {
29
-	if _, err := reference.ParseNamed(repo); err != nil {
29
+	if _, err := reference.ParseNormalizedNamed(repo); err != nil {
30 30
 		return nil, err
31 31
 	}
32 32
 
... ...
@@ -39,6 +39,7 @@ type Agent struct {
39 39
 	ready     chan struct{}
40 40
 	leaving   chan struct{}
41 41
 	leaveOnce sync.Once
42
+	left      chan struct{} // closed after "run" processes "leaving" and will no longer accept new assignments
42 43
 	stopped   chan struct{} // requests shutdown
43 44
 	stopOnce  sync.Once     // only allow stop to be called once
44 45
 	closed    chan struct{} // only closed in run
... ...
@@ -56,6 +57,7 @@ func New(config *Config) (*Agent, error) {
56 56
 		sessionq: make(chan sessionOperation),
57 57
 		started:  make(chan struct{}),
58 58
 		leaving:  make(chan struct{}),
59
+		left:     make(chan struct{}),
59 60
 		stopped:  make(chan struct{}),
60 61
 		closed:   make(chan struct{}),
61 62
 		ready:    make(chan struct{}),
... ...
@@ -96,6 +98,16 @@ func (a *Agent) Leave(ctx context.Context) error {
96 96
 		close(a.leaving)
97 97
 	})
98 98
 
99
+	// Do not call Wait until we have confirmed that the agent is no longer
100
+	// accepting assignments. Starting a worker might race with Wait.
101
+	select {
102
+	case <-a.left:
103
+	case <-a.closed:
104
+		return ErrClosed
105
+	case <-ctx.Done():
106
+		return ctx.Err()
107
+	}
108
+
99 109
 	// agent could be closed while Leave is in progress
100 110
 	var err error
101 111
 	ch := make(chan struct{})
... ...
@@ -215,6 +227,8 @@ func (a *Agent) run(ctx context.Context) {
215 215
 			if err := a.worker.Assign(ctx, nil); err != nil {
216 216
 				log.G(ctx).WithError(err).Error("failed removing all assignments")
217 217
 			}
218
+
219
+			close(a.left)
218 220
 		case msg := <-session.assignments:
219 221
 			// if we have left, accept no more assignments
220 222
 			if leaving == nil {
... ...
@@ -104,17 +104,21 @@ func Resolve(ctx context.Context, task *api.Task, executor Executor) (Controller
104 104
 
105 105
 	// depending on the tasks state, a failed controller resolution has varying
106 106
 	// impact. The following expresses that impact.
107
-	if task.Status.State < api.TaskStateStarting {
108
-		if err != nil {
109
-			// before the task has been started, we consider it a rejection.
110
-			status.Message = "resolving controller failed"
111
-			status.Err = err.Error()
107
+	if err != nil {
108
+		status.Message = "resolving controller failed"
109
+		status.Err = err.Error()
110
+		// before the task has been started, we consider it a rejection.
111
+		// if task is running, consider the task has failed
112
+		// otherwise keep the existing state
113
+		if task.Status.State < api.TaskStateStarting {
112 114
 			status.State = api.TaskStateRejected
113
-		} else if task.Status.State < api.TaskStateAccepted {
114
-			// we always want to proceed to accepted when we resolve the contoller
115
-			status.Message = "accepted"
116
-			status.State = api.TaskStateAccepted
115
+		} else if task.Status.State <= api.TaskStateRunning {
116
+			status.State = api.TaskStateFailed
117 117
 		}
118
+	} else if task.Status.State < api.TaskStateAccepted {
119
+		// we always want to proceed to accepted when we resolve the controller
120
+		status.Message = "accepted"
121
+		status.State = api.TaskStateAccepted
118 122
 	}
119 123
 
120 124
 	return ctlr, status, err
... ...
@@ -30,7 +30,7 @@ service NodeCA {
30 30
 }
31 31
 
32 32
 message NodeCertificateStatusRequest {
33
-	string node_id = 1 [(gogoproto.customname) = "NodeID"];
33
+	string node_id = 1;
34 34
 }
35 35
 
36 36
 message NodeCertificateStatusResponse {
... ...
@@ -54,7 +54,7 @@ message IssueNodeCertificateRequest {
54 54
 }
55 55
 
56 56
 message IssueNodeCertificateResponse {
57
-	string node_id = 1 [(gogoproto.customname) = "NodeID"];
57
+	string node_id = 1;
58 58
 	NodeSpec.Membership node_membership = 2;
59 59
 }
60 60
 
... ...
@@ -119,7 +119,7 @@ service Control {
119 119
 }
120 120
 
121 121
 message GetNodeRequest {
122
-	string node_id = 1 [(gogoproto.customname) = "NodeID"];
122
+	string node_id = 1;
123 123
 }
124 124
 
125 125
 message GetNodeResponse {
... ...
@@ -129,7 +129,7 @@ message GetNodeResponse {
129 129
 message ListNodesRequest {
130 130
 	message Filters {
131 131
 		repeated string names = 1;
132
-		repeated string id_prefixes = 2 [(gogoproto.customname) = "IDPrefixes"];
132
+		repeated string id_prefixes = 2;
133 133
 		map<string, string> labels = 3;
134 134
 		repeated NodeSpec.Membership memberships = 4;
135 135
 		repeated NodeRole roles = 5;
... ...
@@ -148,7 +148,7 @@ message ListNodesResponse {
148 148
 // to request a new availability for a node, such as PAUSE. Invalid updates
149 149
 // will be denied and cause an error.
150 150
 message UpdateNodeRequest {
151
-	string node_id = 1 [(gogoproto.customname) = "NodeID"];
151
+	string node_id = 1;
152 152
 	Version node_version = 2;
153 153
 	NodeSpec spec = 3;
154 154
 }
... ...
@@ -159,7 +159,7 @@ message UpdateNodeResponse {
159 159
 
160 160
 // RemoveNodeRequest requests to delete the specified node from store.
161 161
 message RemoveNodeRequest {
162
-	string node_id = 1 [(gogoproto.customname) = "NodeID"];
162
+	string node_id = 1;
163 163
 	bool force = 2;
164 164
 }
165 165
 
... ...
@@ -167,7 +167,7 @@ message RemoveNodeResponse {
167 167
 }
168 168
 
169 169
 message GetTaskRequest {
170
-	string task_id = 1 [(gogoproto.customname) = "TaskID"];
170
+	string task_id = 1;
171 171
 }
172 172
 
173 173
 message GetTaskResponse {
... ...
@@ -175,7 +175,7 @@ message GetTaskResponse {
175 175
 }
176 176
 
177 177
 message RemoveTaskRequest {
178
-	string task_id = 1 [(gogoproto.customname) = "TaskID"];
178
+	string task_id = 1;
179 179
 }
180 180
 
181 181
 message RemoveTaskResponse {
... ...
@@ -184,10 +184,10 @@ message RemoveTaskResponse {
184 184
 message ListTasksRequest {
185 185
 	message Filters {
186 186
 		repeated string names = 1;
187
-		repeated string id_prefixes = 2 [(gogoproto.customname) = "IDPrefixes"];
187
+		repeated string id_prefixes = 2;
188 188
 		map<string, string> labels = 3;
189
-		repeated string service_ids = 4 [(gogoproto.customname) = "ServiceIDs"];
190
-		repeated string node_ids = 5 [(gogoproto.customname) = "NodeIDs"];
189
+		repeated string service_ids = 4;
190
+		repeated string node_ids = 5;
191 191
 		repeated docker.swarmkit.v1.TaskState desired_states = 6;
192 192
 		// NamePrefixes matches all objects with the given prefixes
193 193
 		repeated string name_prefixes = 7;
... ...
@@ -209,7 +209,7 @@ message CreateServiceResponse {
209 209
 }
210 210
 
211 211
 message GetServiceRequest {
212
-	string service_id = 1 [(gogoproto.customname) = "ServiceID"];
212
+	string service_id = 1;
213 213
 }
214 214
 
215 215
 message GetServiceResponse {
... ...
@@ -217,7 +217,7 @@ message GetServiceResponse {
217 217
 }
218 218
 
219 219
 message UpdateServiceRequest {
220
-	string service_id = 1 [(gogoproto.customname) = "ServiceID"];
220
+	string service_id = 1;
221 221
 	Version service_version = 2;
222 222
 	ServiceSpec spec = 3;
223 223
 }
... ...
@@ -227,7 +227,7 @@ message UpdateServiceResponse {
227 227
 }
228 228
 
229 229
 message RemoveServiceRequest {
230
-	string service_id = 1 [(gogoproto.customname) = "ServiceID"];
230
+	string service_id = 1;
231 231
 }
232 232
 
233 233
 message RemoveServiceResponse {
... ...
@@ -236,7 +236,7 @@ message RemoveServiceResponse {
236 236
 message ListServicesRequest {
237 237
 	message Filters {
238 238
 		repeated string names = 1;
239
-		repeated string id_prefixes = 2 [(gogoproto.customname) = "IDPrefixes"];
239
+		repeated string id_prefixes = 2;
240 240
 		map<string, string> labels = 3;
241 241
 		// NamePrefixes matches all objects with the given prefixes
242 242
 		repeated string name_prefixes = 4;
... ...
@@ -259,7 +259,7 @@ message CreateNetworkResponse {
259 259
 
260 260
 message GetNetworkRequest {
261 261
 	string name = 1;
262
-	string network_id = 2 [(gogoproto.customname) = "NetworkID"];
262
+	string network_id = 2;
263 263
 }
264 264
 
265 265
 message GetNetworkResponse {
... ...
@@ -268,7 +268,7 @@ message GetNetworkResponse {
268 268
 
269 269
 message RemoveNetworkRequest {
270 270
 	string name = 1;
271
-	string network_id = 2 [(gogoproto.customname) = "NetworkID"];
271
+	string network_id = 2;
272 272
 }
273 273
 
274 274
 message RemoveNetworkResponse {}
... ...
@@ -276,7 +276,7 @@ message RemoveNetworkResponse {}
276 276
 message ListNetworksRequest {
277 277
 	message Filters {
278 278
 		repeated string names = 1;
279
-		repeated string id_prefixes = 2 [(gogoproto.customname) = "IDPrefixes"];
279
+		repeated string id_prefixes = 2;
280 280
 		map<string, string> labels = 3;
281 281
 		// NamePrefixes matches all objects with the given prefixes
282 282
 		repeated string name_prefixes = 4;
... ...
@@ -290,7 +290,7 @@ message ListNetworksResponse {
290 290
 }
291 291
 
292 292
 message GetClusterRequest {
293
-	string cluster_id = 1 [(gogoproto.customname) = "ClusterID"];
293
+	string cluster_id = 1;
294 294
 }
295 295
 
296 296
 message GetClusterResponse {
... ...
@@ -300,7 +300,7 @@ message GetClusterResponse {
300 300
 message ListClustersRequest {
301 301
 	message Filters {
302 302
 		repeated string names = 1;
303
-		repeated string id_prefixes = 2 [(gogoproto.customname) = "IDPrefixes"];
303
+		repeated string id_prefixes = 2;
304 304
 		map<string, string> labels = 3;
305 305
 		// NamePrefixes matches all objects with the given prefixes
306 306
 		repeated string name_prefixes = 4;
... ...
@@ -328,7 +328,7 @@ message KeyRotation {
328 328
 
329 329
 message UpdateClusterRequest {
330 330
 	// ClusterID is the cluster ID to update.
331
-	string cluster_id = 1 [(gogoproto.customname) = "ClusterID"];
331
+	string cluster_id = 1;
332 332
 
333 333
 	// ClusterVersion is the version of the cluster being updated.
334 334
 	Version cluster_version = 2;
... ...
@@ -346,7 +346,7 @@ message UpdateClusterResponse {
346 346
 
347 347
 // GetSecretRequest is the request to get a `Secret` object given a secret id.
348 348
 message GetSecretRequest {
349
-	string secret_id = 1 [(gogoproto.customname) = "SecretID"];
349
+	string secret_id = 1;
350 350
 }
351 351
 
352 352
 // GetSecretResponse contains the Secret corresponding to the id in
... ...
@@ -358,7 +358,7 @@ message GetSecretResponse {
358 358
 
359 359
 message UpdateSecretRequest {
360 360
 	// SecretID is the secret ID to update.
361
-	string secret_id = 1 [(gogoproto.customname) = "SecretID"];
361
+	string secret_id = 1;
362 362
 
363 363
 	// SecretVersion is the version of the secret being updated.
364 364
 	Version secret_version = 2;
... ...
@@ -378,7 +378,7 @@ message UpdateSecretResponse {
378 378
 message ListSecretsRequest {
379 379
 	message Filters {
380 380
 		repeated string names = 1;
381
-		repeated string id_prefixes = 2 [(gogoproto.customname) = "IDPrefixes"];
381
+		repeated string id_prefixes = 2;
382 382
 		map<string, string> labels = 3;
383 383
 		repeated string name_prefixes = 4;
384 384
 	}
... ...
@@ -410,7 +410,7 @@ message CreateSecretResponse {
410 410
 // RemoveSecretRequest contains the ID of the secret that should be removed.  This
411 411
 // removes all versions of the secret.
412 412
 message RemoveSecretRequest {
413
-	string secret_id = 1 [(gogoproto.customname) = "SecretID"];
413
+	string secret_id = 1;
414 414
 }
415 415
 
416 416
 // RemoveSecretResponse is an empty object indicating the successful removal of
... ...
@@ -66,7 +66,7 @@ message SessionRequest {
66 66
 	// SessionID is empty or invalid, a new SessionID will be assigned.
67 67
 	//
68 68
 	// See SessionMessage.SessionID for details.
69
-	string session_id = 2 [(gogoproto.customname) = "SessionID"];
69
+	string session_id = 2;
70 70
 }
71 71
 
72 72
 // SessionMessage instructs an agent on various actions as part of the current
... ...
@@ -115,7 +115,7 @@ message SessionMessage {
115 115
 	// We considered placing this field in a GRPC header. Because this is a
116 116
 	// critical feature of the protocol, we thought it should be represented
117 117
 	// directly in the RPC message set.
118
-	string session_id = 1 [(gogoproto.customname) = "SessionID"];
118
+	string session_id = 1;
119 119
 
120 120
 	// Node identifies the registering node.
121 121
 	Node node = 2;
... ...
@@ -130,7 +130,7 @@ message SessionMessage {
130 130
 
131 131
 // HeartbeatRequest provides identifying properties for a single heartbeat.
132 132
 message HeartbeatRequest {
133
-	string session_id = 1 [(gogoproto.customname) = "SessionID"];
133
+	string session_id = 1;
134 134
 }
135 135
 
136 136
 message HeartbeatResponse {
... ...
@@ -142,10 +142,10 @@ message HeartbeatResponse {
142 142
 message UpdateTaskStatusRequest {
143 143
 	// Tasks should contain all statuses for running tasks. Only the status
144 144
 	// field must be set. The spec is not required.
145
-	string session_id = 1 [(gogoproto.customname) = "SessionID"];
145
+	string session_id = 1;
146 146
 
147 147
 	message TaskStatusUpdate {
148
-		string task_id = 1 [(gogoproto.customname) = "TaskID"];
148
+		string task_id = 1;
149 149
 		TaskStatus status = 2;
150 150
 	}
151 151
 
... ...
@@ -157,7 +157,7 @@ message  UpdateTaskStatusResponse{
157 157
 }
158 158
 
159 159
 message TasksRequest {
160
-	string session_id = 1 [(gogoproto.customname) = "SessionID"];
160
+	string session_id = 1;
161 161
 }
162 162
 
163 163
 message TasksMessage {
... ...
@@ -167,7 +167,7 @@ message TasksMessage {
167 167
 }
168 168
 
169 169
 message AssignmentsRequest {
170
-	string session_id = 1 [(gogoproto.customname) = "SessionID"];
170
+	string session_id = 1;
171 171
 }
172 172
 
173 173
 message Assignment {
... ...
@@ -54,16 +54,16 @@ message LogSubscriptionOptions {
54 54
 // possible. For example, if they want to listen to all the tasks of a service,
55 55
 // they should use the service id, rather than specifying the individual tasks.
56 56
 message LogSelector {
57
-	repeated string service_ids = 1 [(gogoproto.customname) = "ServiceIDs"];
58
-	repeated string node_ids = 2 [(gogoproto.customname) = "NodeIDs"];
59
-	repeated string task_ids = 3 [(gogoproto.customname) = "TaskIDs"];
57
+	repeated string service_ids = 1;
58
+	repeated string node_ids = 2;
59
+	repeated string task_ids = 3;
60 60
 }
61 61
 
62 62
 // LogContext marks the context from which a log message was generated.
63 63
 message LogContext {
64
-	string service_id = 1 [(gogoproto.customname) = "ServiceID"];
65
-	string node_id = 2 [(gogoproto.customname) = "NodeID"];
66
-	string task_id = 3 [(gogoproto.customname) = "TaskID"];
64
+	string service_id = 1;
65
+	string node_id = 2;
66
+	string task_id = 3;
67 67
 }
68 68
 
69 69
 // LogMessage
... ...
@@ -147,7 +147,7 @@ message ListenSubscriptionsRequest { }
147 147
 // If Options.Follow == false, the worker should end the subscription on its own.
148 148
 message SubscriptionMessage {
149 149
 	// ID identifies the subscription.
150
-	string id = 1 [(gogoproto.customname) = "ID"];
150
+	string id = 1;
151 151
 
152 152
 	// Selector defines which sources should be sent for the subscription.
153 153
 	LogSelector selector = 2;
... ...
@@ -163,7 +163,7 @@ message SubscriptionMessage {
163 163
 message PublishLogsMessage {
164 164
 	// SubscriptionID identifies which subscription the set of messages should
165 165
 	// be sent to. We can think of this as a "mail box" for the subscription.
166
-	string subscription_id = 1 [(gogoproto.customname) = "SubscriptionID"];
166
+	string subscription_id = 1;
167 167
 
168 168
 	// Messages is the log message for publishing.
169 169
 	repeated LogMessage messages = 2 [(gogoproto.nullable) = false];
... ...
@@ -25,7 +25,7 @@ message Meta {
25 25
 // Node provides the internal node state as seen by the cluster.
26 26
 message Node {
27 27
 	// ID specifies the identity of the node.
28
-	string id = 1 [(gogoproto.customname) = "ID"];
28
+	string id = 1;
29 29
 
30 30
 	Meta meta = 2 [(gogoproto.nullable) = false];
31 31
 
... ...
@@ -63,7 +63,7 @@ message Node {
63 63
 }
64 64
 
65 65
 message Service {
66
-	string id = 1 [(gogoproto.customname) = "ID"];
66
+	string id = 1;
67 67
 
68 68
 	Meta meta = 2 [(gogoproto.nullable) = false];
69 69
 
... ...
@@ -101,7 +101,7 @@ message Endpoint {
101 101
 	// and the IP addresses the target service will be made available under.
102 102
 	message VirtualIP {
103 103
 		// NetworkID for which this endpoint attachment was created.
104
-		string network_id = 1 [(gogoproto.customname) = "NetworkID"];
104
+		string network_id = 1;
105 105
 
106 106
 		// A virtual IP is used to address this service in IP
107 107
 		// layer that the client can use to send requests to
... ...
@@ -123,7 +123,7 @@ message Endpoint {
123 123
 // immutable and idempotent. Once it is dispatched to a node, it will not be
124 124
 // dispatched to another node.
125 125
 message Task {
126
-	string id = 1 [(gogoproto.customname) = "ID"];
126
+	string id = 1;
127 127
 
128 128
 	Meta meta = 2 [(gogoproto.nullable) = false];
129 129
 
... ...
@@ -133,7 +133,7 @@ message Task {
133 133
 
134 134
 	// ServiceID indicates the service under which this task is orchestrated. This
135 135
 	// should almost always be set.
136
-	string service_id = 4 [(gogoproto.customname) = "ServiceID"];
136
+	string service_id = 4;
137 137
 
138 138
 	// Slot is the service slot number for a task.
139 139
 	// For example, if a replicated service has replicas = 2, there will be a
... ...
@@ -142,7 +142,7 @@ message Task {
142 142
 
143 143
 	// NodeID indicates the node to which the task is assigned. If this field
144 144
 	// is empty or not set, the task is unassigned.
145
-	string node_id = 6 [(gogoproto.customname) = "NodeID"];
145
+	string node_id = 6;
146 146
 
147 147
 	// Annotations defines the names and labels for the runtime, as set by
148 148
 	// the cluster manager.
... ...
@@ -204,7 +204,7 @@ message NetworkAttachment {
204 204
 }
205 205
 
206 206
 message Network {
207
-	string id = 1 [(gogoproto.customname) = "ID"];
207
+	string id = 1;
208 208
 
209 209
 	Meta meta = 2 [(gogoproto.nullable) = false];
210 210
 
... ...
@@ -220,7 +220,7 @@ message Network {
220 220
 
221 221
 // Cluster provides global cluster settings.
222 222
 message Cluster {
223
-	string id = 1 [(gogoproto.customname) = "ID"];
223
+	string id = 1;
224 224
 
225 225
 	Meta meta = 2 [(gogoproto.nullable) = false];
226 226
 
... ...
@@ -256,7 +256,7 @@ message Cluster {
256 256
 // information that is generated from the secret data in the `spec`, such as
257 257
 // the digest and size of the secret data.
258 258
 message Secret {
259
-	string id = 1 [(gogoproto.customname) = "ID"];
259
+	string id = 1;
260 260
 
261 261
 	Meta meta = 2 [(gogoproto.nullable) = false];
262 262
 
... ...
@@ -40,10 +40,10 @@ service RaftMembership {
40 40
 message RaftMember {
41 41
 	// RaftID specifies the internal ID used by the manager in a raft context, it can never be modified
42 42
 	// and is used only for information purposes
43
-	uint64 raft_id = 1 [(gogoproto.customname) = "RaftID"];
43
+	uint64 raft_id = 1;
44 44
 
45 45
 	// NodeID is the node's ID.
46
-	string node_id = 2 [(gogoproto.customname) = "NodeID"];
46
+	string node_id = 2;
47 47
 
48 48
 	// Addr specifies the address of the member
49 49
 	string addr = 3;
... ...
@@ -59,7 +59,7 @@ message JoinRequest {
59 59
 
60 60
 message JoinResponse {
61 61
 	// RaftID is the ID assigned to the new member.
62
-	uint64 raft_id = 1 [(gogoproto.customname) = "RaftID"];
62
+	uint64 raft_id = 1;
63 63
 
64 64
 	// Members is the membership set of the cluster.
65 65
 	repeated RaftMember members = 2;
... ...
@@ -84,7 +84,7 @@ message ProcessRaftMessageResponse {}
84 84
 
85 85
 message ResolveAddressRequest {
86 86
 	// raft_id is the ID to resolve to an address.
87
-	uint64 raft_id = 1 [(gogoproto.customname) = "RaftID"];
87
+	uint64 raft_id = 1;
88 88
 }
89 89
 
90 90
 message ResolveAddressResponse {
... ...
@@ -96,7 +96,7 @@ message ResolveAddressResponse {
96 96
 // over the raft backend with a request ID to track when the
97 97
 // action is effectively applied
98 98
 message InternalRaftRequest {
99
-	uint64 id = 1 [(gogoproto.customname) = "ID"];
99
+	uint64 id = 1;
100 100
 
101 101
 	repeated StoreAction action = 2;
102 102
 }
... ...
@@ -20,15 +20,15 @@ service ResourceAllocator {
20 20
 
21 21
 message AttachNetworkRequest {
22 22
 	NetworkAttachmentConfig config = 1;
23
-	string container_id = 2 [(gogoproto.customname) = "ContainerID"];
23
+	string container_id = 2;
24 24
 }
25 25
 
26 26
 message AttachNetworkResponse {
27
-	string attachment_id = 1 [(gogoproto.customname) = "AttachmentID"];
27
+	string attachment_id = 1;
28 28
 }
29 29
 
30 30
 message DetachNetworkRequest {
31
-	string attachment_id = 1 [(gogoproto.customname) = "AttachmentID"];
31
+	string attachment_id = 1;
32 32
 }
33 33
 
34 34
 message DetachNetworkResponse {}
... ...
@@ -130,7 +130,7 @@ message TaskSpec {
130 130
 message NetworkAttachmentSpec {
131 131
 	// ContainerID spcifies a unique ID of the container for which
132 132
 	// this attachment is for.
133
-	string container_id = 1 [(gogoproto.customname) = "ContainerID"];
133
+	string container_id = 1;
134 134
 }
135 135
 
136 136
 
... ...
@@ -410,7 +410,7 @@ enum TaskState {
410 410
 
411 411
 // Container specific status.
412 412
 message ContainerStatus {
413
-	string container_id = 1 [(gogoproto.customname) = "ContainerID"];
413
+	string container_id = 1;
414 414
 
415 415
 	int32 pid = 2 [(gogoproto.customname) = "PID"];
416 416
 	int32 exit_code = 3;
... ...
@@ -574,7 +574,7 @@ message IPAMOptions {
574 574
 
575 575
 // Peer should be used anywhere where we are describing a remote peer.
576 576
 message Peer {
577
-	string node_id = 1 [(gogoproto.customname) = "NodeID"];
577
+	string node_id = 1;
578 578
 	string addr = 2;
579 579
 }
580 580
 
... ...
@@ -787,7 +787,7 @@ message EncryptionKey {
787 787
 message ManagerStatus {
788 788
 	// RaftID specifies the internal ID used by the manager in a raft context, it can never be modified
789 789
 	// and is used only for information purposes
790
-	uint64 raft_id = 1 [(gogoproto.customname) = "RaftID"];
790
+	uint64 raft_id = 1;
791 791
 
792 792
 	// Addr is the address advertised to raft.
793 793
 	string addr = 2;
... ...
@@ -805,7 +805,7 @@ message SecretReference {
805 805
 	// SecretID represents the ID of the specific Secret that we're
806 806
 	// referencing. This identifier exists so that SecretReferences don't leak
807 807
 	// any information about the secret contents.
808
-	string secret_id = 1 [(gogoproto.customname) = "SecretID"];
808
+	string secret_id = 1;
809 809
 
810 810
 	// SecretName is the name of the secret that this references, but this is just provided for
811 811
 	// lookup/display purposes.  The secret in the reference will be identified by its ID.
... ...
@@ -151,6 +151,25 @@ func (rca *RootCA) IssueAndSaveNewCertificates(kw KeyWriter, cn, ou, org string)
151 151
 	return &tlsKeyPair, nil
152 152
 }
153 153
 
154
+// Normally we can just call cert.Verify(opts), but since we actually want more information about
155
+// whether a certificate is not yet valid or expired, we also need to perform the expiry checks ourselves.
156
+func verifyCertificate(cert *x509.Certificate, opts x509.VerifyOptions, allowExpired bool) error {
157
+	_, err := cert.Verify(opts)
158
+	if invalidErr, ok := err.(x509.CertificateInvalidError); ok && invalidErr.Reason == x509.Expired {
159
+		now := time.Now().UTC()
160
+		if now.Before(cert.NotBefore) {
161
+			return errors.Wrapf(err, "certificate not valid before %s, and it is currently %s",
162
+				cert.NotBefore.UTC().Format(time.RFC1123), now.Format(time.RFC1123))
163
+		}
164
+		if allowExpired {
165
+			return nil
166
+		}
167
+		return errors.Wrapf(err, "certificate expires at %s, and it is currently %s",
168
+			cert.NotAfter.UTC().Format(time.RFC1123), now.Format(time.RFC1123))
169
+	}
170
+	return err
171
+}
172
+
154 173
 // RequestAndSaveNewCertificates gets new certificates issued, either by signing them locally if a signer is
155 174
 // available, or by requesting them from the remote server at remoteAddr.
156 175
 func (rca *RootCA) RequestAndSaveNewCertificates(ctx context.Context, kw KeyWriter, config CertificateRequestConfig) (*tls.Certificate, error) {
... ...
@@ -199,7 +218,7 @@ func (rca *RootCA) RequestAndSaveNewCertificates(ctx context.Context, kw KeyWrit
199 199
 		Roots: rca.Pool,
200 200
 	}
201 201
 	// Check to see if this certificate was signed by our CA, and isn't expired
202
-	if _, err := X509Cert.Verify(opts); err != nil {
202
+	if err := verifyCertificate(X509Cert, opts, false); err != nil {
203 203
 		return nil, err
204 204
 	}
205 205
 
... ...
@@ -15,6 +15,7 @@ import (
15 15
 
16 16
 	"github.com/Sirupsen/logrus"
17 17
 	cfconfig "github.com/cloudflare/cfssl/config"
18
+	events "github.com/docker/go-events"
18 19
 	"github.com/docker/swarmkit/api"
19 20
 	"github.com/docker/swarmkit/connectionbroker"
20 21
 	"github.com/docker/swarmkit/identity"
... ...
@@ -50,6 +51,13 @@ const (
50 50
 	base36DigestLen = 50
51 51
 )
52 52
 
53
+// RenewTLSExponentialBackoff sets the exponential backoff when trying to renew TLS certificates that have expired
54
+var RenewTLSExponentialBackoff = events.ExponentialBackoffConfig{
55
+	Base:   time.Second * 5,
56
+	Factor: time.Minute,
57
+	Max:    1 * time.Hour,
58
+}
59
+
53 60
 // SecurityConfig is used to represent a node's security configuration. It includes information about
54 61
 // the RootCA and ServerTLSCreds/ClientTLSCreds transport authenticators to be used for MTLS
55 62
 type SecurityConfig struct {
... ...
@@ -189,7 +197,7 @@ func GenerateJoinToken(rootCA *RootCA) string {
189 189
 
190 190
 func getCAHashFromToken(token string) (digest.Digest, error) {
191 191
 	split := strings.Split(token, "-")
192
-	if len(split) != 4 || split[0] != "SWMTKN" || split[1] != "1" {
192
+	if len(split) != 4 || split[0] != "SWMTKN" || split[1] != "1" || len(split[2]) != base36DigestLen || len(split[3]) != maxGeneratedSecretLength {
193 193
 		return "", errors.New("invalid join token")
194 194
 	}
195 195
 
... ...
@@ -242,7 +250,7 @@ func DownloadRootCA(ctx context.Context, paths CertPaths, token string, connBrok
242 242
 
243 243
 // LoadSecurityConfig loads TLS credentials from disk, or returns an error if
244 244
 // these credentials do not exist or are unusable.
245
-func LoadSecurityConfig(ctx context.Context, rootCA RootCA, krw *KeyReadWriter) (*SecurityConfig, error) {
245
+func LoadSecurityConfig(ctx context.Context, rootCA RootCA, krw *KeyReadWriter, allowExpired bool) (*SecurityConfig, error) {
246 246
 	ctx = log.WithModule(ctx, "tls")
247 247
 
248 248
 	// At this point we've successfully loaded the CA details from disk, or
... ...
@@ -273,7 +281,7 @@ func LoadSecurityConfig(ctx context.Context, rootCA RootCA, krw *KeyReadWriter)
273 273
 	}
274 274
 
275 275
 	// Check to see if this certificate was signed by our CA, and isn't expired
276
-	if _, err := X509Cert.Verify(opts); err != nil {
276
+	if err := verifyCertificate(X509Cert, opts, allowExpired); err != nil {
277 277
 		return nil, err
278 278
 	}
279 279
 
... ...
@@ -447,6 +455,7 @@ func RenewTLSConfig(ctx context.Context, s *SecurityConfig, connBroker *connecti
447 447
 
448 448
 	go func() {
449 449
 		var retry time.Duration
450
+		expBackoff := events.NewExponentialBackoff(RenewTLSExponentialBackoff)
450 451
 		defer close(updates)
451 452
 		for {
452 453
 			ctx = log.WithModule(ctx, "tls")
... ...
@@ -472,18 +481,12 @@ func RenewTLSConfig(ctx context.Context, s *SecurityConfig, connBroker *connecti
472 472
 					return
473 473
 				}
474 474
 			} else {
475
-				// If we have an expired certificate, we let's stick with the starting default in
476
-				// the hope that this is a temporary clock skew.
475
+				// If we have an expired certificate, try to renew immediately: the hope that this is a temporary clock skew, or
476
+				// we can issue our own TLS certs.
477 477
 				if validUntil.Before(time.Now()) {
478
-					log.WithError(err).Errorf("failed to create a new client TLS config")
479
-
480
-					select {
481
-					case updates <- CertificateUpdate{Err: errors.New("TLS certificate is expired")}:
482
-					case <-ctx.Done():
483
-						log.Info("shutting down certificate renewal routine")
484
-						return
485
-					}
486
-
478
+					log.Warn("the current TLS certificate is expired, so an attempt to renew it will be made immediately")
479
+					// retry immediately(ish) with exponential backoff
480
+					retry = expBackoff.Proceed(nil)
487 481
 				} else {
488 482
 					// Random retry time between 50% and 80% of the total time to expiration
489 483
 					retry = calculateRandomExpiry(validFrom, validUntil)
... ...
@@ -492,7 +495,7 @@ func RenewTLSConfig(ctx context.Context, s *SecurityConfig, connBroker *connecti
492 492
 
493 493
 			log.WithFields(logrus.Fields{
494 494
 				"time": time.Now().Add(retry),
495
-			}).Debugf("next certificate renewal scheduled")
495
+			}).Debugf("next certificate renewal scheduled for %v from now", retry)
496 496
 
497 497
 			select {
498 498
 			case <-time.After(retry):
... ...
@@ -508,8 +511,10 @@ func RenewTLSConfig(ctx context.Context, s *SecurityConfig, connBroker *connecti
508 508
 			var certUpdate CertificateUpdate
509 509
 			if err := RenewTLSConfigNow(ctx, s, connBroker); err != nil {
510 510
 				certUpdate.Err = err
511
+				expBackoff.Failure(nil, nil)
511 512
 			} else {
512 513
 				certUpdate.Role = s.ClientTLSCreds.Role()
514
+				expBackoff = events.NewExponentialBackoff(RenewTLSExponentialBackoff)
513 515
 			}
514 516
 
515 517
 			select {
... ...
@@ -110,7 +110,7 @@ func validateContainerSpec(container *api.ContainerSpec) error {
110 110
 		return grpc.Errorf(codes.InvalidArgument, "ContainerSpec: image reference must be provided")
111 111
 	}
112 112
 
113
-	if _, err := reference.ParseNamed(container.Image); err != nil {
113
+	if _, err := reference.ParseNormalizedNamed(container.Image); err != nil {
114 114
 		return grpc.Errorf(codes.InvalidArgument, "ContainerSpec: %q is not a valid repository/tag", container.Image)
115 115
 	}
116 116
 
... ...
@@ -275,6 +275,21 @@ func (s *Server) validateNetworks(networks []*api.NetworkAttachmentConfig) error
275 275
 	return nil
276 276
 }
277 277
 
278
+func validateMode(s *api.ServiceSpec) error {
279
+	m := s.GetMode()
280
+	switch m.(type) {
281
+	case *api.ServiceSpec_Replicated:
282
+		if int64(m.(*api.ServiceSpec_Replicated).Replicated.Replicas) < 0 {
283
+			return grpc.Errorf(codes.InvalidArgument, "Number of replicas must be non-negative")
284
+		}
285
+	case *api.ServiceSpec_Global:
286
+	default:
287
+		return grpc.Errorf(codes.InvalidArgument, "Unrecognized service mode")
288
+	}
289
+
290
+	return nil
291
+}
292
+
278 293
 func validateServiceSpec(spec *api.ServiceSpec) error {
279 294
 	if spec == nil {
280 295
 		return grpc.Errorf(codes.InvalidArgument, errInvalidArgument.Error())
... ...
@@ -291,6 +306,9 @@ func validateServiceSpec(spec *api.ServiceSpec) error {
291 291
 	if err := validateEndpointSpec(spec.Endpoint); err != nil {
292 292
 		return err
293 293
 	}
294
+	if err := validateMode(spec); err != nil {
295
+		return err
296
+	}
294 297
 	// Check to see if the Secret Reference portion of the spec is valid
295 298
 	if err := validateSecretRefsSpec(spec); err != nil {
296 299
 		return err
... ...
@@ -1,7 +1,6 @@
1 1
 package logbroker
2 2
 
3 3
 import (
4
-	"context"
5 4
 	"fmt"
6 5
 	"strings"
7 6
 	"sync"
... ...
@@ -12,6 +11,7 @@ import (
12 12
 	"github.com/docker/swarmkit/manager/state"
13 13
 	"github.com/docker/swarmkit/manager/state/store"
14 14
 	"github.com/docker/swarmkit/watch"
15
+	"golang.org/x/net/context"
15 16
 )
16 17
 
17 18
 type subscription struct {
... ...
@@ -102,15 +102,15 @@ func (r *Orchestrator) reconcile(ctx context.Context, service *api.Service) {
102 102
 	}
103 103
 
104 104
 	deploy := service.Spec.GetMode().(*api.ServiceSpec_Replicated)
105
-	specifiedSlots := int(deploy.Replicated.Replicas)
105
+	specifiedSlots := deploy.Replicated.Replicas
106 106
 
107 107
 	switch {
108
-	case specifiedSlots > numSlots:
108
+	case specifiedSlots > uint64(numSlots):
109 109
 		log.G(ctx).Debugf("Service %s was scaled up from %d to %d instances", service.ID, numSlots, specifiedSlots)
110 110
 		// Update all current tasks then add missing tasks
111 111
 		r.updater.Update(ctx, r.cluster, service, slotsSlice)
112 112
 		_, err = r.store.Batch(func(batch *store.Batch) error {
113
-			r.addTasks(ctx, batch, service, runningSlots, deadSlots, specifiedSlots-numSlots)
113
+			r.addTasks(ctx, batch, service, runningSlots, deadSlots, specifiedSlots-uint64(numSlots))
114 114
 			r.deleteTasksMap(ctx, batch, deadSlots)
115 115
 			return nil
116 116
 		})
... ...
@@ -118,7 +118,7 @@ func (r *Orchestrator) reconcile(ctx context.Context, service *api.Service) {
118 118
 			log.G(ctx).WithError(err).Errorf("reconcile batch failed")
119 119
 		}
120 120
 
121
-	case specifiedSlots < numSlots:
121
+	case specifiedSlots < uint64(numSlots):
122 122
 		// Update up to N tasks then remove the extra
123 123
 		log.G(ctx).Debugf("Service %s was scaled down from %d to %d instances", service.ID, numSlots, specifiedSlots)
124 124
 
... ...
@@ -165,7 +165,7 @@ func (r *Orchestrator) reconcile(ctx context.Context, service *api.Service) {
165 165
 			log.G(ctx).WithError(err).Errorf("reconcile batch failed")
166 166
 		}
167 167
 
168
-	case specifiedSlots == numSlots:
168
+	case specifiedSlots == uint64(numSlots):
169 169
 		_, err = r.store.Batch(func(batch *store.Batch) error {
170 170
 			r.deleteTasksMap(ctx, batch, deadSlots)
171 171
 			return nil
... ...
@@ -178,9 +178,9 @@ func (r *Orchestrator) reconcile(ctx context.Context, service *api.Service) {
178 178
 	}
179 179
 }
180 180
 
181
-func (r *Orchestrator) addTasks(ctx context.Context, batch *store.Batch, service *api.Service, runningSlots map[uint64]orchestrator.Slot, deadSlots map[uint64]orchestrator.Slot, count int) {
181
+func (r *Orchestrator) addTasks(ctx context.Context, batch *store.Batch, service *api.Service, runningSlots map[uint64]orchestrator.Slot, deadSlots map[uint64]orchestrator.Slot, count uint64) {
182 182
 	slot := uint64(0)
183
-	for i := 0; i < count; i++ {
183
+	for i := uint64(0); i < count; i++ {
184 184
 		// Find a slot number that is missing a running task
185 185
 		for {
186 186
 			slot++
... ...
@@ -1193,9 +1193,13 @@ func (n *Node) ProcessRaftMessage(ctx context.Context, msg *api.ProcessRaftMessa
1193 1193
 	ctx, cancel := n.WithContext(ctx)
1194 1194
 	defer cancel()
1195 1195
 
1196
-	if err := n.reportNewAddress(ctx, msg.Message.From); err != nil {
1197
-		log.G(ctx).WithError(err).Errorf("failed to report new address of %x to transport", msg.Message.From)
1198
-	}
1196
+	// TODO(aaronl): Address changes are temporarily disabled.
1197
+	// See https://github.com/docker/docker/issues/30455.
1198
+	// This should be reenabled in the future with additional
1199
+	// safeguards (perhaps storing multiple addresses per node).
1200
+	//if err := n.reportNewAddress(ctx, msg.Message.From); err != nil {
1201
+	//	log.G(ctx).WithError(err).Errorf("failed to report new address of %x to transport", msg.Message.From)
1202
+	//}
1199 1203
 
1200 1204
 	// Reject vote requests from unreachable peers
1201 1205
 	if msg.Message.Type == raftpb.MsgVote {
... ...
@@ -1,7 +1,6 @@
1 1
 package storage
2 2
 
3 3
 import (
4
-	"context"
5 4
 	"io"
6 5
 	"io/ioutil"
7 6
 	"os"
... ...
@@ -15,6 +14,7 @@ import (
15 15
 	"github.com/docker/swarmkit/log"
16 16
 	"github.com/docker/swarmkit/manager/encryption"
17 17
 	"github.com/pkg/errors"
18
+	"golang.org/x/net/context"
18 19
 )
19 20
 
20 21
 // This package wraps the github.com/coreos/etcd/wal package, and encrypts
... ...
@@ -568,7 +568,8 @@ func (n *Node) loadSecurityConfig(ctx context.Context) (*ca.SecurityConfig, erro
568 568
 		return nil, err
569 569
 	}
570 570
 	if err == nil {
571
-		securityConfig, err = ca.LoadSecurityConfig(ctx, rootCA, krw)
571
+		// if forcing a new cluster, we allow the certificates to be expired - a new set will be generated
572
+		securityConfig, err = ca.LoadSecurityConfig(ctx, rootCA, krw, n.config.ForceNewCluster)
572 573
 		if err != nil {
573 574
 			_, isInvalidKEK := errors.Cause(err).(ca.ErrInvalidKEK)
574 575
 			if isInvalidKEK {
... ...
@@ -606,7 +607,7 @@ func (n *Node) loadSecurityConfig(ctx context.Context) (*ca.SecurityConfig, erro
606 606
 		// - We wait for CreateSecurityConfig to finish since we need a certificate to operate.
607 607
 
608 608
 		// Attempt to load certificate from disk
609
-		securityConfig, err = ca.LoadSecurityConfig(ctx, rootCA, krw)
609
+		securityConfig, err = ca.LoadSecurityConfig(ctx, rootCA, krw, n.config.ForceNewCluster)
610 610
 		if err == nil {
611 611
 			log.G(ctx).WithFields(logrus.Fields{
612 612
 				"node.id": securityConfig.ClientTLSCreds.NodeID(),