Browse Source

Use distribution reference

Remove forked reference package. Use normalized named values
everywhere and familiar functions to convert back to familiar
strings for UX and storage compatibility.

Enforce that the source repository in the distribution metadata
is always a normalized string, ignore invalid values which are not.
Update distribution tests to use normalized values.

Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
Derek McGowan 8 years ago
parent
commit
3a1279393f
78 changed files with 538 additions and 1117 deletions
  1. 1 1
      api/server/router/plugin/backend.go
  2. 17 30
      api/server/router/plugin/plugin_routes.go
  3. 0 34
      api/types/reference/image_reference.go
  4. 0 73
      api/types/reference/image_reference_test.go
  5. 1 1
      builder/builder.go
  6. 3 10
      builder/dockerfile/builder.go
  7. 1 5
      cli/command/container/create.go
  8. 2 2
      cli/command/formatter/disk_usage.go
  9. 2 2
      cli/command/formatter/image.go
  10. 7 6
      cli/command/formatter/service.go
  11. 1 3
      cli/command/image/build.go
  12. 4 4
      cli/command/image/pull.go
  13. 8 8
      cli/command/image/trust.go
  14. 3 17
      cli/command/plugin/install.go
  15. 2 5
      cli/command/plugin/push.go
  16. 6 6
      cli/command/plugin/upgrade.go
  17. 7 5
      cli/command/service/trust.go
  18. 8 6
      cli/command/task/print.go
  19. 2 2
      cli/trust/trust.go
  20. 8 6
      client/container_commit.go
  21. 4 4
      client/image_create.go
  22. 1 1
      client/image_import.go
  23. 21 6
      client/image_pull.go
  24. 1 1
      client/image_pull_test.go
  25. 10 8
      client/image_push.go
  26. 1 1
      client/image_push_test.go
  27. 9 8
      client/image_tag.go
  28. 1 1
      client/plugin_install.go
  29. 1 1
      client/plugin_upgrade.go
  30. 21 27
      daemon/cluster/cluster.go
  31. 1 1
      daemon/cluster/executor/backend.go
  32. 2 2
      daemon/cluster/executor/container/adapter.go
  33. 3 3
      daemon/cluster/executor/container/container.go
  34. 8 5
      daemon/commit.go
  35. 3 3
      daemon/daemon.go
  36. 2 2
      daemon/daemon_solaris.go
  37. 1 17
      daemon/errors.go
  38. 3 3
      daemon/events/filter.go
  39. 23 13
      daemon/image.go
  40. 8 7
      daemon/image_delete.go
  41. 2 2
      daemon/image_history.go
  42. 3 3
      daemon/image_inspect.go
  43. 6 6
      daemon/image_pull.go
  44. 2 2
      daemon/image_push.go
  45. 4 4
      daemon/image_tag.go
  46. 3 3
      daemon/images.go
  47. 3 4
      daemon/import.go
  48. 1 1
      daemon/prune.go
  49. 2 2
      distribution/config.go
  50. 4 4
      distribution/errors.go
  51. 12 15
      distribution/pull.go
  52. 17 17
      distribution/pull_v1.go
  53. 11 10
      distribution/pull_v2.go
  54. 2 2
      distribution/pull_v2_test.go
  55. 8 8
      distribution/push.go
  56. 5 5
      distribution/push_v1.go
  57. 27 34
      distribution/push_v2.go
  58. 28 24
      distribution/push_v2_test.go
  59. 4 4
      distribution/registry.go
  60. 3 3
      distribution/registry_unit_test.go
  61. 5 5
      image/tarexport/load.go
  62. 28 24
      image/tarexport/save.go
  63. 3 3
      image/tarexport/tarexport.go
  64. 1 1
      integration-cli/docker_cli_images_test.go
  65. 10 5
      migrate/v1/migratev1.go
  66. 4 4
      migrate/v1/migratev1_test.go
  67. 13 13
      plugin/backend_linux.go
  68. 1 1
      plugin/backend_unsupported.go
  69. 5 5
      plugin/store.go
  70. 0 194
      reference/reference.go
  71. 0 275
      reference/reference_test.go
  72. 71 46
      reference/store.go
  73. 16 15
      reference/store_test.go
  74. 4 9
      registry/config.go
  75. 1 1
      registry/registry_mock_test.go
  76. 10 11
      registry/registry_test.go
  77. 10 10
      registry/session.go
  78. 2 2
      registry/types.go

+ 1 - 1
api/server/router/plugin/backend.go

@@ -4,9 +4,9 @@ import (
 	"io"
 	"io"
 	"net/http"
 	"net/http"
 
 
+	"github.com/docker/distribution/reference"
 	enginetypes "github.com/docker/docker/api/types"
 	enginetypes "github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/filters"
-	"github.com/docker/docker/reference"
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 )
 )
 
 

+ 17 - 30
api/server/router/plugin/plugin_routes.go

@@ -7,13 +7,12 @@ import (
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
 
 
-	distreference "github.com/docker/distribution/reference"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/server/httputils"
 	"github.com/docker/docker/api/server/httputils"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/streamformatter"
 	"github.com/docker/docker/pkg/streamformatter"
-	"github.com/docker/docker/reference"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 )
 )
@@ -47,39 +46,27 @@ func parseHeaders(headers http.Header) (map[string][]string, *types.AuthConfig)
 // be returned.
 // be returned.
 func parseRemoteRef(remote string) (reference.Named, string, error) {
 func parseRemoteRef(remote string) (reference.Named, string, error) {
 	// Parse remote reference, supporting remotes with name and tag
 	// Parse remote reference, supporting remotes with name and tag
-	// NOTE: Using distribution reference to handle references
-	// containing both a name and digest
-	remoteRef, err := distreference.ParseNamed(remote)
+	remoteRef, err := reference.ParseNormalizedNamed(remote)
 	if err != nil {
 	if err != nil {
 		return nil, "", err
 		return nil, "", err
 	}
 	}
 
 
-	var tag string
-	if t, ok := remoteRef.(distreference.Tagged); ok {
-		tag = t.Tag()
+	type canonicalWithTag interface {
+		reference.Canonical
+		Tag() string
 	}
 	}
 
 
-	// Convert distribution reference to docker reference
-	// TODO: remove when docker reference changes reconciled upstream
-	ref, err := reference.WithName(remoteRef.Name())
-	if err != nil {
-		return nil, "", err
-	}
-	if d, ok := remoteRef.(distreference.Digested); ok {
-		ref, err = reference.WithDigest(ref, d.Digest())
-		if err != nil {
-			return nil, "", err
-		}
-	} else if tag != "" {
-		ref, err = reference.WithTag(ref, tag)
+	if canonical, ok := remoteRef.(canonicalWithTag); ok {
+		remoteRef, err = reference.WithDigest(reference.TrimNamed(remoteRef), canonical.Digest())
 		if err != nil {
 		if err != nil {
 			return nil, "", err
 			return nil, "", err
 		}
 		}
-	} else {
-		ref = reference.WithDefaultTag(ref)
+		return remoteRef, canonical.Tag(), nil
 	}
 	}
 
 
-	return ref, tag, nil
+	remoteRef = reference.TagNameOnly(remoteRef)
+
+	return remoteRef, "", nil
 }
 }
 
 
 func (pr *pluginRouter) getPrivileges(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 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) {
 				if err != nil {
 				if err != nil {
 					return "", err
 					return "", err
 				}
 				}
-				name = nt.String()
+				name = reference.FamiliarString(nt)
 			} else {
 			} else {
-				name = reference.WithDefaultTag(trimmed).String()
+				name = reference.FamiliarString(reference.TagNameOnly(trimmed))
 			}
 			}
 		} else {
 		} else {
-			name = ref.String()
+			name = reference.FamiliarString(ref)
 		}
 		}
 	} else {
 	} else {
-		localRef, err := reference.ParseNamed(name)
+		localRef, err := reference.ParseNormalizedNamed(name)
 		if err != nil {
 		if err != nil {
 			return "", err
 			return "", err
 		}
 		}
 		if _, ok := localRef.(reference.Canonical); ok {
 		if _, ok := localRef.(reference.Canonical); ok {
 			return "", errors.New("cannot use digest in plugin tag")
 			return "", errors.New("cannot use digest in plugin tag")
 		}
 		}
-		if distreference.IsNameOnly(localRef) {
+		if reference.IsNameOnly(localRef) {
 			// TODO: log change in name to out stream
 			// TODO: log change in name to out stream
-			name = reference.WithDefaultTag(localRef).String()
+			name = reference.FamiliarString(reference.TagNameOnly(localRef))
 		}
 		}
 	}
 	}
 	return name, nil
 	return name, nil

+ 0 - 34
api/types/reference/image_reference.go

@@ -1,34 +0,0 @@
-package reference
-
-import (
-	distreference "github.com/docker/distribution/reference"
-)
-
-// Parse parses the given references and returns the repository and
-// tag (if present) from it. If there is an error during parsing, it will
-// return an error.
-func Parse(ref string) (string, string, error) {
-	distributionRef, err := distreference.ParseNamed(ref)
-	if err != nil {
-		return "", "", err
-	}
-
-	tag := GetTagFromNamedRef(distributionRef)
-	return distributionRef.Name(), tag, nil
-}
-
-// GetTagFromNamedRef returns a tag from the specified reference.
-// This function is necessary as long as the docker "server" api makes the distinction between repository
-// and tags.
-func GetTagFromNamedRef(ref distreference.Named) string {
-	var tag string
-	switch x := ref.(type) {
-	case distreference.Digested:
-		tag = x.Digest().String()
-	case distreference.NamedTagged:
-		tag = x.Tag()
-	default:
-		tag = "latest"
-	}
-	return tag
-}

+ 0 - 73
api/types/reference/image_reference_test.go

@@ -1,73 +0,0 @@
-package reference
-
-import (
-	_ "crypto/sha256"
-	"testing"
-)
-
-func TestParse(t *testing.T) {
-	testCases := []struct {
-		ref           string
-		expectedName  string
-		expectedTag   string
-		expectedError bool
-	}{
-		{
-			ref:           "",
-			expectedName:  "",
-			expectedTag:   "",
-			expectedError: true,
-		},
-		{
-			ref:           "repository",
-			expectedName:  "repository",
-			expectedTag:   "latest",
-			expectedError: false,
-		},
-		{
-			ref:           "repository:tag",
-			expectedName:  "repository",
-			expectedTag:   "tag",
-			expectedError: false,
-		},
-		{
-			ref:           "test.com/repository",
-			expectedName:  "test.com/repository",
-			expectedTag:   "latest",
-			expectedError: false,
-		},
-		{
-			ref:           "test.com:5000/test/repository",
-			expectedName:  "test.com:5000/test/repository",
-			expectedTag:   "latest",
-			expectedError: false,
-		},
-		{
-			ref:           "test.com:5000/repo@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
-			expectedName:  "test.com:5000/repo",
-			expectedTag:   "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
-			expectedError: false,
-		},
-		{
-			ref:           "test.com:5000/repo:tag@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
-			expectedName:  "test.com:5000/repo",
-			expectedTag:   "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
-			expectedError: false,
-		},
-	}
-
-	for _, c := range testCases {
-		name, tag, err := Parse(c.ref)
-		if err != nil && c.expectedError {
-			continue
-		} else if err != nil {
-			t.Fatalf("error with %s: %s", c.ref, err.Error())
-		}
-		if name != c.expectedName {
-			t.Fatalf("expected name %s, got %s", c.expectedName, name)
-		}
-		if tag != c.expectedTag {
-			t.Fatalf("expected tag %s, got %s", c.expectedTag, tag)
-		}
-	}
-}

+ 1 - 1
builder/builder.go

@@ -9,11 +9,11 @@ import (
 	"os"
 	"os"
 	"time"
 	"time"
 
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/backend"
 	"github.com/docker/docker/api/types/backend"
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/image"
-	"github.com/docker/docker/reference"
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 )
 )
 
 

+ 3 - 10
builder/dockerfile/builder.go

@@ -11,6 +11,7 @@ import (
 	"strings"
 	"strings"
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/distribution/reference"
 	apierrors "github.com/docker/docker/api/errors"
 	apierrors "github.com/docker/docker/api/errors"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/backend"
 	"github.com/docker/docker/api/types/backend"
@@ -19,7 +20,6 @@ import (
 	"github.com/docker/docker/builder/dockerfile/parser"
 	"github.com/docker/docker/builder/dockerfile/parser"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/stringid"
-	"github.com/docker/docker/reference"
 	perrors "github.com/pkg/errors"
 	perrors "github.com/pkg/errors"
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 )
 )
@@ -176,23 +176,16 @@ func sanitizeRepoAndTags(names []string) ([]reference.Named, error) {
 			continue
 			continue
 		}
 		}
 
 
-		ref, err := reference.ParseNamed(repo)
+		ref, err := reference.ParseNormalizedNamed(repo)
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
 
 
-		ref = reference.WithDefaultTag(ref)
-
 		if _, isCanonical := ref.(reference.Canonical); isCanonical {
 		if _, isCanonical := ref.(reference.Canonical); isCanonical {
 			return nil, errors.New("build tag cannot contain a digest")
 			return nil, errors.New("build tag cannot contain a digest")
 		}
 		}
 
 
-		if _, isTagged := ref.(reference.NamedTagged); !isTagged {
-			ref, err = reference.WithTag(ref, reference.DefaultTag)
-			if err != nil {
-				return nil, err
-			}
-		}
+		ref = reference.TagNameOnly(ref)
 
 
 		nameWithTag := ref.String()
 		nameWithTag := ref.String()
 
 

+ 1 - 5
cli/command/container/create.go

@@ -168,11 +168,7 @@ func createContainer(ctx context.Context, dockerCli *command.DockerCli, config *
 		return nil, err
 		return nil, err
 	}
 	}
 	if named, ok := ref.(reference.Named); ok {
 	if named, ok := ref.(reference.Named); ok {
-		if reference.IsNameOnly(named) {
-			namedRef = reference.EnsureTagged(named)
-		} else {
-			namedRef = named
-		}
+		namedRef = reference.TagNameOnly(named)
 
 
 		if taggedRef, ok := namedRef.(reference.NamedTagged); ok && command.IsTrusted() {
 		if taggedRef, ok := namedRef.(reference.NamedTagged); ok && command.IsTrusted() {
 			var err error
 			var err error

+ 2 - 2
cli/command/formatter/disk_usage.go

@@ -94,12 +94,12 @@ func (ctx *DiskUsageContext) Write() {
 		tag := "<none>"
 		tag := "<none>"
 		if len(i.RepoTags) > 0 && !isDangling(*i) {
 		if len(i.RepoTags) > 0 && !isDangling(*i) {
 			// Only show the first tag
 			// Only show the first tag
-			ref, err := reference.ParseNamed(i.RepoTags[0])
+			ref, err := reference.ParseNormalizedNamed(i.RepoTags[0])
 			if err != nil {
 			if err != nil {
 				continue
 				continue
 			}
 			}
 			if nt, ok := ref.(reference.NamedTagged); ok {
 			if nt, ok := ref.(reference.NamedTagged); ok {
-				repo = ref.Name()
+				repo = reference.FamiliarName(ref)
 				tag = nt.Tag()
 				tag = nt.Tag()
 			}
 			}
 		}
 		}

+ 2 - 2
cli/command/formatter/image.go

@@ -94,7 +94,7 @@ func imageFormat(ctx ImageContext, images []types.ImageSummary, format func(subC
 			repoTags := map[string][]string{}
 			repoTags := map[string][]string{}
 			repoDigests := map[string][]string{}
 			repoDigests := map[string][]string{}
 
 
-			for _, refString := range append(image.RepoTags) {
+			for _, refString := range image.RepoTags {
 				ref, err := reference.ParseNormalizedNamed(refString)
 				ref, err := reference.ParseNormalizedNamed(refString)
 				if err != nil {
 				if err != nil {
 					continue
 					continue
@@ -104,7 +104,7 @@ func imageFormat(ctx ImageContext, images []types.ImageSummary, format func(subC
 					repoTags[familiarRef] = append(repoTags[familiarRef], nt.Tag())
 					repoTags[familiarRef] = append(repoTags[familiarRef], nt.Tag())
 				}
 				}
 			}
 			}
-			for _, refString := range append(image.RepoDigests) {
+			for _, refString := range image.RepoDigests {
 				ref, err := reference.ParseNormalizedNamed(refString)
 				ref, err := reference.ParseNormalizedNamed(refString)
 				if err != nil {
 				if err != nil {
 					continue
 					continue

+ 7 - 6
cli/command/formatter/service.go

@@ -5,7 +5,7 @@ import (
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
-	distreference "github.com/docker/distribution/reference"
+	"github.com/docker/distribution/reference"
 	mounttypes "github.com/docker/docker/api/types/mount"
 	mounttypes "github.com/docker/docker/api/types/mount"
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/cli/command/inspect"
 	"github.com/docker/docker/cli/command/inspect"
@@ -409,11 +409,12 @@ func (c *serviceContext) Replicas() string {
 func (c *serviceContext) Image() string {
 func (c *serviceContext) Image() string {
 	c.AddHeader(imageHeader)
 	c.AddHeader(imageHeader)
 	image := c.service.Spec.TaskTemplate.ContainerSpec.Image
 	image := c.service.Spec.TaskTemplate.ContainerSpec.Image
-	if ref, err := distreference.ParseNamed(image); err == nil {
-		// update image string for display
-		namedTagged, ok := ref.(distreference.NamedTagged)
-		if ok {
-			image = namedTagged.Name() + ":" + namedTagged.Tag()
+	if ref, err := reference.ParseNormalizedNamed(image); err == nil {
+		// update image string for display, (strips any digest)
+		if nt, ok := ref.(reference.NamedTagged); ok {
+			if namedTagged, err := reference.WithTag(reference.TrimNamed(nt), nt.Tag()); err == nil {
+				image = reference.FamiliarString(namedTagged)
+			}
 		}
 		}
 	}
 	}
 
 

+ 1 - 3
cli/command/image/build.go

@@ -397,9 +397,7 @@ func rewriteDockerfileFrom(ctx context.Context, dockerfile io.Reader, translator
 			if err != nil {
 			if err != nil {
 				return nil, nil, err
 				return nil, nil, err
 			}
 			}
-			if reference.IsNameOnly(ref) {
-				ref = reference.EnsureTagged(ref)
-			}
+			ref = reference.TagNameOnly(ref)
 			if ref, ok := ref.(reference.NamedTagged); ok && command.IsTrusted() {
 			if ref, ok := ref.(reference.NamedTagged); ok && command.IsTrusted() {
 				trustedRef, err := translator(ctx, ref)
 				trustedRef, err := translator(ctx, ref)
 				if err != nil {
 				if err != nil {

+ 4 - 4
cli/command/image/pull.go

@@ -42,7 +42,6 @@ func NewPullCommand(dockerCli *command.DockerCli) *cobra.Command {
 }
 }
 
 
 func runPull(dockerCli *command.DockerCli, opts pullOptions) error {
 func runPull(dockerCli *command.DockerCli, opts pullOptions) error {
-	var distributionRef reference.Named
 	distributionRef, err := reference.ParseNormalizedNamed(opts.remote)
 	distributionRef, err := reference.ParseNormalizedNamed(opts.remote)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -52,9 +51,10 @@ func runPull(dockerCli *command.DockerCli, opts pullOptions) error {
 	}
 	}
 
 
 	if !opts.all && reference.IsNameOnly(distributionRef) {
 	if !opts.all && reference.IsNameOnly(distributionRef) {
-		taggedRef := reference.EnsureTagged(distributionRef)
-		fmt.Fprintf(dockerCli.Out(), "Using default tag: %s\n", taggedRef.Tag())
-		distributionRef = taggedRef
+		distributionRef = reference.TagNameOnly(distributionRef)
+		if tagged, ok := distributionRef.(reference.Tagged); ok {
+			fmt.Fprintf(dockerCli.Out(), "Using default tag: %s\n", tagged.Tag())
+		}
 	}
 	}
 
 
 	// Resolve the Repository name from fqn to RepositoryInfo
 	// Resolve the Repository name from fqn to RepositoryInfo

+ 8 - 8
cli/command/image/trust.go

@@ -129,15 +129,15 @@ func PushTrustedReference(cli *command.DockerCli, repoInfo *registry.RepositoryI
 
 
 		// Initialize the notary repository with a remotely managed snapshot key
 		// Initialize the notary repository with a remotely managed snapshot key
 		if err := repo.Initialize([]string{rootKeyID}, data.CanonicalSnapshotRole); err != nil {
 		if err := repo.Initialize([]string{rootKeyID}, data.CanonicalSnapshotRole); err != nil {
-			return trust.NotaryError(repoInfo.FullName(), err)
+			return trust.NotaryError(repoInfo.Name.Name(), err)
 		}
 		}
-		fmt.Fprintf(cli.Out(), "Finished initializing %q\n", repoInfo.FullName())
+		fmt.Fprintf(cli.Out(), "Finished initializing %q\n", repoInfo.Name.Name())
 		err = repo.AddTarget(target, data.CanonicalTargetsRole)
 		err = repo.AddTarget(target, data.CanonicalTargetsRole)
 	case nil:
 	case nil:
 		// already initialized and we have successfully downloaded the latest metadata
 		// already initialized and we have successfully downloaded the latest metadata
 		err = addTargetToAllSignableRoles(repo, target)
 		err = addTargetToAllSignableRoles(repo, target)
 	default:
 	default:
-		return trust.NotaryError(repoInfo.FullName(), err)
+		return trust.NotaryError(repoInfo.Name.Name(), err)
 	}
 	}
 
 
 	if err == nil {
 	if err == nil {
@@ -145,11 +145,11 @@ func PushTrustedReference(cli *command.DockerCli, repoInfo *registry.RepositoryI
 	}
 	}
 
 
 	if err != nil {
 	if err != nil {
-		fmt.Fprintf(cli.Out(), "Failed to sign %q:%s - %s\n", repoInfo.FullName(), tag, err.Error())
-		return trust.NotaryError(repoInfo.FullName(), err)
+		fmt.Fprintf(cli.Out(), "Failed to sign %q:%s - %s\n", repoInfo.Name.Name(), tag, err.Error())
+		return trust.NotaryError(repoInfo.Name.Name(), err)
 	}
 	}
 
 
-	fmt.Fprintf(cli.Out(), "Successfully signed %q:%s\n", repoInfo.FullName(), tag)
+	fmt.Fprintf(cli.Out(), "Successfully signed %q:%s\n", repoInfo.Name.Name(), tag)
 	return nil
 	return nil
 }
 }
 
 
@@ -342,12 +342,12 @@ func TrustedReference(ctx context.Context, cli *command.DockerCli, ref reference
 
 
 	t, err := notaryRepo.GetTargetByName(ref.Tag(), trust.ReleasesRole, data.CanonicalTargetsRole)
 	t, err := notaryRepo.GetTargetByName(ref.Tag(), trust.ReleasesRole, data.CanonicalTargetsRole)
 	if err != nil {
 	if err != nil {
-		return nil, trust.NotaryError(repoInfo.FullName(), err)
+		return nil, trust.NotaryError(repoInfo.Name.Name(), err)
 	}
 	}
 	// Only list tags in the top level targets role or the releases delegation role - ignore
 	// Only list tags in the top level targets role or the releases delegation role - ignore
 	// all other delegation roles
 	// all other delegation roles
 	if t.Role != trust.ReleasesRole && t.Role != data.CanonicalTargetsRole {
 	if t.Role != trust.ReleasesRole && t.Role != data.CanonicalTargetsRole {
-		return nil, trust.NotaryError(repoInfo.FullName(), fmt.Errorf("No trust data for %s", ref.Tag()))
+		return nil, trust.NotaryError(repoInfo.Name.Name(), fmt.Errorf("No trust data for %s", ref.Tag()))
 	}
 	}
 	r, err := convertTarget(t.Target)
 	r, err := convertTarget(t.Target)
 	if err != nil {
 	if err != nil {

+ 3 - 17
cli/command/plugin/install.go

@@ -7,7 +7,6 @@ import (
 
 
 	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
-	registrytypes "github.com/docker/docker/api/types/registry"
 	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cli/command"
 	"github.com/docker/docker/cli/command"
 	"github.com/docker/docker/cli/command/image"
 	"github.com/docker/docker/cli/command/image"
@@ -54,20 +53,6 @@ func newInstallCommand(dockerCli *command.DockerCli) *cobra.Command {
 	return cmd
 	return cmd
 }
 }
 
 
-func getRepoIndexFromUnnormalizedRef(ref reference.Named) (*registrytypes.IndexInfo, error) {
-	named, err := reference.ParseNormalizedNamed(ref.Name())
-	if err != nil {
-		return nil, err
-	}
-
-	repoInfo, err := registry.ParseRepositoryInfo(named)
-	if err != nil {
-		return nil, err
-	}
-
-	return repoInfo.Index, nil
-}
-
 type pluginRegistryService struct {
 type pluginRegistryService struct {
 	registry.Service
 	registry.Service
 }
 }
@@ -104,9 +89,10 @@ func buildPullConfig(ctx context.Context, dockerCli *command.DockerCli, opts plu
 
 
 	_, isCanonical := ref.(reference.Canonical)
 	_, isCanonical := ref.(reference.Canonical)
 	if command.IsTrusted() && !isCanonical {
 	if command.IsTrusted() && !isCanonical {
+		ref = reference.TagNameOnly(ref)
 		nt, ok := ref.(reference.NamedTagged)
 		nt, ok := ref.(reference.NamedTagged)
 		if !ok {
 		if !ok {
-			nt = reference.EnsureTagged(ref)
+			return types.PluginInstallOptions{}, fmt.Errorf("invalid name: %s", ref.String())
 		}
 		}
 
 
 		ctx := context.Background()
 		ctx := context.Background()
@@ -148,7 +134,7 @@ func runInstall(dockerCli *command.DockerCli, opts pluginOptions) error {
 		if _, ok := aref.(reference.Canonical); ok {
 		if _, ok := aref.(reference.Canonical); ok {
 			return fmt.Errorf("invalid name: %s", opts.localName)
 			return fmt.Errorf("invalid name: %s", opts.localName)
 		}
 		}
-		localName = reference.FamiliarString(reference.EnsureTagged(aref))
+		localName = reference.FamiliarString(reference.TagNameOnly(aref))
 	}
 	}
 
 
 	ctx := context.Background()
 	ctx := context.Background()

+ 2 - 5
cli/command/plugin/push.go

@@ -40,10 +40,7 @@ func runPush(dockerCli *command.DockerCli, name string) error {
 		return fmt.Errorf("invalid name: %s", name)
 		return fmt.Errorf("invalid name: %s", name)
 	}
 	}
 
 
-	taggedRef, ok := named.(reference.NamedTagged)
-	if !ok {
-		taggedRef = reference.EnsureTagged(named)
-	}
+	named = reference.TagNameOnly(named)
 
 
 	ctx := context.Background()
 	ctx := context.Background()
 
 
@@ -58,7 +55,7 @@ func runPush(dockerCli *command.DockerCli, name string) error {
 		return err
 		return err
 	}
 	}
 
 
-	responseBody, err := dockerCli.Client().PluginPush(ctx, reference.FamiliarString(taggedRef), encodedAuth)
+	responseBody, err := dockerCli.Client().PluginPush(ctx, reference.FamiliarString(named), encodedAuth)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}

+ 6 - 6
cli/command/plugin/upgrade.go

@@ -5,10 +5,10 @@ import (
 	"fmt"
 	"fmt"
 	"strings"
 	"strings"
 
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cli/command"
 	"github.com/docker/docker/cli/command"
 	"github.com/docker/docker/pkg/jsonmessage"
 	"github.com/docker/docker/pkg/jsonmessage"
-	"github.com/docker/docker/reference"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 	"github.com/spf13/cobra"
 )
 )
@@ -49,19 +49,19 @@ func runUpgrade(dockerCli *command.DockerCli, opts pluginOptions) error {
 	if opts.remote == "" {
 	if opts.remote == "" {
 		opts.remote = p.PluginReference
 		opts.remote = p.PluginReference
 	}
 	}
-	remote, err := reference.ParseNamed(opts.remote)
+	remote, err := reference.ParseNormalizedNamed(opts.remote)
 	if err != nil {
 	if err != nil {
 		return errors.Wrap(err, "error parsing remote upgrade image reference")
 		return errors.Wrap(err, "error parsing remote upgrade image reference")
 	}
 	}
-	remote = reference.WithDefaultTag(remote)
+	remote = reference.TagNameOnly(remote)
 
 
-	old, err := reference.ParseNamed(p.PluginReference)
+	old, err := reference.ParseNormalizedNamed(p.PluginReference)
 	if err != nil {
 	if err != nil {
 		return errors.Wrap(err, "error parsing current image reference")
 		return errors.Wrap(err, "error parsing current image reference")
 	}
 	}
-	old = reference.WithDefaultTag(old)
+	old = reference.TagNameOnly(old)
 
 
-	fmt.Fprintf(dockerCli.Out(), "Upgrading plugin %s from %s to %s\n", p.Name, old, remote)
+	fmt.Fprintf(dockerCli.Out(), "Upgrading plugin %s from %s to %s\n", p.Name, reference.FamiliarString(old), reference.FamiliarString(remote))
 	if !opts.skipRemoteCheck && remote.String() != old.String() {
 	if !opts.skipRemoteCheck && remote.String() != old.String() {
 		if !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), "Plugin images do not match, are you sure?") {
 		if !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), "Plugin images do not match, are you sure?") {
 			return errors.New("canceling upgrade request")
 			return errors.New("canceling upgrade request")

+ 7 - 5
cli/command/service/trust.go

@@ -33,10 +33,12 @@ func resolveServiceImageDigest(dockerCli *command.DockerCli, service *swarm.Serv
 		namedRef, ok := ref.(reference.Named)
 		namedRef, ok := ref.(reference.Named)
 		if !ok {
 		if !ok {
 			return errors.New("failed to resolve image digest using content trust: reference is not named")
 			return errors.New("failed to resolve image digest using content trust: reference is not named")
-
 		}
 		}
-
-		taggedRef := reference.EnsureTagged(namedRef)
+		namedRef = reference.TagNameOnly(namedRef)
+		taggedRef, ok := namedRef.(reference.NamedTagged)
+		if !ok {
+			return errors.New("failed to resolve image digest using content trust: reference is not tagged")
+		}
 
 
 		resolvedImage, err := trustedResolveDigest(context.Background(), dockerCli, taggedRef)
 		resolvedImage, err := trustedResolveDigest(context.Background(), dockerCli, taggedRef)
 		if err != nil {
 		if err != nil {
@@ -65,12 +67,12 @@ func trustedResolveDigest(ctx context.Context, cli *command.DockerCli, ref refer
 
 
 	t, err := notaryRepo.GetTargetByName(ref.Tag(), trust.ReleasesRole, data.CanonicalTargetsRole)
 	t, err := notaryRepo.GetTargetByName(ref.Tag(), trust.ReleasesRole, data.CanonicalTargetsRole)
 	if err != nil {
 	if err != nil {
-		return nil, trust.NotaryError(repoInfo.FullName(), err)
+		return nil, trust.NotaryError(repoInfo.Name.Name(), err)
 	}
 	}
 	// Only get the tag if it's in the top level targets role or the releases delegation role
 	// Only get the tag if it's in the top level targets role or the releases delegation role
 	// ignore it if it's in any other delegation roles
 	// ignore it if it's in any other delegation roles
 	if t.Role != trust.ReleasesRole && t.Role != data.CanonicalTargetsRole {
 	if t.Role != trust.ReleasesRole && t.Role != data.CanonicalTargetsRole {
-		return nil, trust.NotaryError(repoInfo.FullName(), fmt.Errorf("No trust data for %s", reference.FamiliarString(ref)))
+		return nil, trust.NotaryError(repoInfo.Name.Name(), fmt.Errorf("No trust data for %s", reference.FamiliarString(ref)))
 	}
 	}
 
 
 	logrus.Debugf("retrieving target for %s role\n", t.Role)
 	logrus.Debugf("retrieving target for %s role\n", t.Role)

+ 8 - 6
cli/command/task/print.go

@@ -10,7 +10,7 @@ import (
 
 
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 
 
-	distreference "github.com/docker/distribution/reference"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/cli/command"
 	"github.com/docker/docker/cli/command"
 	"github.com/docker/docker/cli/command/idresolver"
 	"github.com/docker/docker/cli/command/idresolver"
@@ -129,13 +129,15 @@ func print(out io.Writer, ctx context.Context, tasks []swarm.Task, resolver *idr
 
 
 		image := task.Spec.ContainerSpec.Image
 		image := task.Spec.ContainerSpec.Image
 		if !noTrunc {
 		if !noTrunc {
-			ref, err := distreference.ParseNamed(image)
+			ref, err := reference.ParseNormalizedNamed(image)
 			if err == nil {
 			if err == nil {
-				// update image string for display
-				namedTagged, ok := ref.(distreference.NamedTagged)
-				if ok {
-					image = namedTagged.Name() + ":" + namedTagged.Tag()
+				// update image string for display, (strips any digest)
+				if nt, ok := ref.(reference.NamedTagged); ok {
+					if namedTagged, err := reference.WithTag(reference.TrimNamed(nt), nt.Tag()); err == nil {
+						image = reference.FamiliarString(namedTagged)
+					}
 				}
 				}
+
 			}
 			}
 		}
 		}
 
 

+ 2 - 2
cli/trust/trust.go

@@ -148,7 +148,7 @@ func GetNotaryRepository(streams command.Streams, repoInfo *registry.RepositoryI
 	}
 	}
 
 
 	scope := auth.RepositoryScope{
 	scope := auth.RepositoryScope{
-		Repository: repoInfo.FullName(),
+		Repository: repoInfo.Name.Name(),
 		Actions:    actions,
 		Actions:    actions,
 		Class:      repoInfo.Class,
 		Class:      repoInfo.Class,
 	}
 	}
@@ -166,7 +166,7 @@ func GetNotaryRepository(streams command.Streams, repoInfo *registry.RepositoryI
 
 
 	return client.NewNotaryRepository(
 	return client.NewNotaryRepository(
 		trustDirectory(),
 		trustDirectory(),
-		repoInfo.FullName(),
+		repoInfo.Name.Name(),
 		server,
 		server,
 		tr,
 		tr,
 		getPassphraseRetriever(streams),
 		getPassphraseRetriever(streams),

+ 8 - 6
client/container_commit.go

@@ -5,9 +5,8 @@ import (
 	"errors"
 	"errors"
 	"net/url"
 	"net/url"
 
 
-	distreference "github.com/docker/distribution/reference"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/api/types/reference"
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 )
 )
 
 
@@ -15,17 +14,20 @@ import (
 func (cli *Client) ContainerCommit(ctx context.Context, container string, options types.ContainerCommitOptions) (types.IDResponse, error) {
 func (cli *Client) ContainerCommit(ctx context.Context, container string, options types.ContainerCommitOptions) (types.IDResponse, error) {
 	var repository, tag string
 	var repository, tag string
 	if options.Reference != "" {
 	if options.Reference != "" {
-		distributionRef, err := distreference.ParseNamed(options.Reference)
+		ref, err := reference.ParseNormalizedNamed(options.Reference)
 		if err != nil {
 		if err != nil {
 			return types.IDResponse{}, err
 			return types.IDResponse{}, err
 		}
 		}
 
 
-		if _, isCanonical := distributionRef.(distreference.Canonical); isCanonical {
+		if _, isCanonical := ref.(reference.Canonical); isCanonical {
 			return types.IDResponse{}, errors.New("refusing to create a tag with a digest reference")
 			return types.IDResponse{}, errors.New("refusing to create a tag with a digest reference")
 		}
 		}
+		ref = reference.TagNameOnly(ref)
 
 
-		tag = reference.GetTagFromNamedRef(distributionRef)
-		repository = distributionRef.Name()
+		if tagged, ok := ref.(reference.Tagged); ok {
+			tag = tagged.Tag()
+		}
+		repository = reference.FamiliarName(ref)
 	}
 	}
 
 
 	query := url.Values{}
 	query := url.Values{}

+ 4 - 4
client/image_create.go

@@ -6,21 +6,21 @@ import (
 
 
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/api/types/reference"
 )
 )
 
 
 // ImageCreate creates a new image based in the parent options.
 // ImageCreate creates a new image based in the parent options.
 // It returns the JSON content in the response body.
 // It returns the JSON content in the response body.
 func (cli *Client) ImageCreate(ctx context.Context, parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) {
 func (cli *Client) ImageCreate(ctx context.Context, parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) {
-	repository, tag, err := reference.Parse(parentReference)
+	ref, err := reference.ParseNormalizedNamed(parentReference)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
 	query := url.Values{}
 	query := url.Values{}
-	query.Set("fromImage", repository)
-	query.Set("tag", tag)
+	query.Set("fromImage", reference.FamiliarName(ref))
+	query.Set("tag", getAPITagFromNamedRef(ref))
 	resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth)
 	resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err

+ 1 - 1
client/image_import.go

@@ -15,7 +15,7 @@ import (
 func (cli *Client) ImageImport(ctx context.Context, source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) {
 func (cli *Client) ImageImport(ctx context.Context, source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) {
 	if ref != "" {
 	if ref != "" {
 		//Check if the given image name can be resolved
 		//Check if the given image name can be resolved
-		if _, err := reference.ParseNamed(ref); err != nil {
+		if _, err := reference.ParseNormalizedNamed(ref); err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
 	}
 	}

+ 21 - 6
client/image_pull.go

@@ -7,8 +7,8 @@ import (
 
 
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/api/types/reference"
 )
 )
 
 
 // ImagePull requests the docker host to pull an image from a remote registry.
 // ImagePull requests the docker host to pull an image from a remote registry.
@@ -19,16 +19,16 @@ import (
 // FIXME(vdemeester): there is currently used in a few way in docker/docker
 // FIXME(vdemeester): there is currently used in a few way in docker/docker
 // - if not in trusted content, ref is used to pass the whole reference, and tag is empty
 // - if not in trusted content, ref is used to pass the whole reference, and tag is empty
 // - if in trusted content, ref is used to pass the reference name, and tag for the digest
 // - if in trusted content, ref is used to pass the reference name, and tag for the digest
-func (cli *Client) ImagePull(ctx context.Context, ref string, options types.ImagePullOptions) (io.ReadCloser, error) {
-	repository, tag, err := reference.Parse(ref)
+func (cli *Client) ImagePull(ctx context.Context, refStr string, options types.ImagePullOptions) (io.ReadCloser, error) {
+	ref, err := reference.ParseNormalizedNamed(refStr)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
 	query := url.Values{}
 	query := url.Values{}
-	query.Set("fromImage", repository)
-	if tag != "" && !options.All {
-		query.Set("tag", tag)
+	query.Set("fromImage", reference.FamiliarName(ref))
+	if !options.All {
+		query.Set("tag", getAPITagFromNamedRef(ref))
 	}
 	}
 
 
 	resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth)
 	resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth)
@@ -44,3 +44,18 @@ func (cli *Client) ImagePull(ctx context.Context, ref string, options types.Imag
 	}
 	}
 	return resp.body, nil
 	return resp.body, nil
 }
 }
+
+// getAPITagFromNamedRef returns a tag from the specified reference.
+// This function is necessary as long as the docker "server" api expects
+// digests to be sent as tags and makes a distinction between the name
+// and tag/digest part of a reference.
+func getAPITagFromNamedRef(ref reference.Named) string {
+	if digested, ok := ref.(reference.Digested); ok {
+		return digested.Digest().String()
+	}
+	ref = reference.TagNameOnly(ref)
+	if tagged, ok := ref.(reference.Tagged); ok {
+		return tagged.Tag()
+	}
+	return ""
+}

+ 1 - 1
client/image_pull_test.go

@@ -21,7 +21,7 @@ func TestImagePullReferenceParseError(t *testing.T) {
 	}
 	}
 	// An empty reference is an invalid reference
 	// An empty reference is an invalid reference
 	_, err := client.ImagePull(context.Background(), "", types.ImagePullOptions{})
 	_, err := client.ImagePull(context.Background(), "", types.ImagePullOptions{})
-	if err == nil || err.Error() != "repository name must have at least one component" {
+	if err == nil || !strings.Contains(err.Error(), "invalid reference format") {
 		t.Fatalf("expected an error, got %v", err)
 		t.Fatalf("expected an error, got %v", err)
 	}
 	}
 }
 }

+ 10 - 8
client/image_push.go

@@ -8,7 +8,7 @@ import (
 
 
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 
 
-	distreference "github.com/docker/distribution/reference"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 )
 )
 
 
@@ -16,31 +16,33 @@ import (
 // It executes the privileged function if the operation is unauthorized
 // It executes the privileged function if the operation is unauthorized
 // and it tries one more time.
 // and it tries one more time.
 // It's up to the caller to handle the io.ReadCloser and close it properly.
 // It's up to the caller to handle the io.ReadCloser and close it properly.
-func (cli *Client) ImagePush(ctx context.Context, ref string, options types.ImagePushOptions) (io.ReadCloser, error) {
-	distributionRef, err := distreference.ParseNamed(ref)
+func (cli *Client) ImagePush(ctx context.Context, image string, options types.ImagePushOptions) (io.ReadCloser, error) {
+	ref, err := reference.ParseNormalizedNamed(image)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	if _, isCanonical := distributionRef.(distreference.Canonical); isCanonical {
+	if _, isCanonical := ref.(reference.Canonical); isCanonical {
 		return nil, errors.New("cannot push a digest reference")
 		return nil, errors.New("cannot push a digest reference")
 	}
 	}
 
 
-	var tag = ""
-	if nameTaggedRef, isNamedTagged := distributionRef.(distreference.NamedTagged); isNamedTagged {
+	tag := ""
+	name := reference.FamiliarName(ref)
+
+	if nameTaggedRef, isNamedTagged := ref.(reference.NamedTagged); isNamedTagged {
 		tag = nameTaggedRef.Tag()
 		tag = nameTaggedRef.Tag()
 	}
 	}
 
 
 	query := url.Values{}
 	query := url.Values{}
 	query.Set("tag", tag)
 	query.Set("tag", tag)
 
 
-	resp, err := cli.tryImagePush(ctx, distributionRef.Name(), query, options.RegistryAuth)
+	resp, err := cli.tryImagePush(ctx, name, query, options.RegistryAuth)
 	if resp.statusCode == http.StatusUnauthorized && options.PrivilegeFunc != nil {
 	if resp.statusCode == http.StatusUnauthorized && options.PrivilegeFunc != nil {
 		newAuthHeader, privilegeErr := options.PrivilegeFunc()
 		newAuthHeader, privilegeErr := options.PrivilegeFunc()
 		if privilegeErr != nil {
 		if privilegeErr != nil {
 			return nil, privilegeErr
 			return nil, privilegeErr
 		}
 		}
-		resp, err = cli.tryImagePush(ctx, distributionRef.Name(), query, newAuthHeader)
+		resp, err = cli.tryImagePush(ctx, name, query, newAuthHeader)
 	}
 	}
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err

+ 1 - 1
client/image_push_test.go

@@ -21,7 +21,7 @@ func TestImagePushReferenceError(t *testing.T) {
 	}
 	}
 	// An empty reference is an invalid reference
 	// An empty reference is an invalid reference
 	_, err := client.ImagePush(context.Background(), "", types.ImagePushOptions{})
 	_, err := client.ImagePush(context.Background(), "", types.ImagePushOptions{})
-	if err == nil || err.Error() != "repository name must have at least one component" {
+	if err == nil || !strings.Contains(err.Error(), "invalid reference format") {
 		t.Fatalf("expected an error, got %v", err)
 		t.Fatalf("expected an error, got %v", err)
 	}
 	}
 	// An canonical reference cannot be pushed
 	// An canonical reference cannot be pushed

+ 9 - 8
client/image_tag.go

@@ -3,32 +3,33 @@ package client
 import (
 import (
 	"net/url"
 	"net/url"
 
 
-	distreference "github.com/docker/distribution/reference"
-	"github.com/docker/docker/api/types/reference"
+	"github.com/docker/distribution/reference"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 )
 )
 
 
 // ImageTag tags an image in the docker host
 // ImageTag tags an image in the docker host
 func (cli *Client) ImageTag(ctx context.Context, source, target string) error {
 func (cli *Client) ImageTag(ctx context.Context, source, target string) error {
-	if _, err := distreference.ParseNamed(source); err != nil {
+	if _, err := reference.ParseNormalizedNamed(source); err != nil {
 		return errors.Wrapf(err, "Error parsing reference: %q is not a valid repository/tag", source)
 		return errors.Wrapf(err, "Error parsing reference: %q is not a valid repository/tag", source)
 	}
 	}
 
 
-	distributionRef, err := distreference.ParseNamed(target)
+	ref, err := reference.ParseNormalizedNamed(target)
 	if err != nil {
 	if err != nil {
 		return errors.Wrapf(err, "Error parsing reference: %q is not a valid repository/tag", target)
 		return errors.Wrapf(err, "Error parsing reference: %q is not a valid repository/tag", target)
 	}
 	}
 
 
-	if _, isCanonical := distributionRef.(distreference.Canonical); isCanonical {
+	if _, isCanonical := ref.(reference.Canonical); isCanonical {
 		return errors.New("refusing to create a tag with a digest reference")
 		return errors.New("refusing to create a tag with a digest reference")
 	}
 	}
 
 
-	tag := reference.GetTagFromNamedRef(distributionRef)
+	ref = reference.TagNameOnly(ref)
 
 
 	query := url.Values{}
 	query := url.Values{}
-	query.Set("repo", distributionRef.Name())
-	query.Set("tag", tag)
+	query.Set("repo", reference.FamiliarName(ref))
+	if tagged, ok := ref.(reference.Tagged); ok {
+		query.Set("tag", tagged.Tag())
+	}
 
 
 	resp, err := cli.post(ctx, "/images/"+source+"/tag", query, nil, nil)
 	resp, err := cli.post(ctx, "/images/"+source+"/tag", query, nil, nil)
 	ensureReaderClosed(resp)
 	ensureReaderClosed(resp)

+ 1 - 1
client/plugin_install.go

@@ -15,7 +15,7 @@ import (
 // PluginInstall installs a plugin
 // PluginInstall installs a plugin
 func (cli *Client) PluginInstall(ctx context.Context, name string, options types.PluginInstallOptions) (rc io.ReadCloser, err error) {
 func (cli *Client) PluginInstall(ctx context.Context, name string, options types.PluginInstallOptions) (rc io.ReadCloser, err error) {
 	query := url.Values{}
 	query := url.Values{}
-	if _, err := reference.ParseNamed(options.RemoteRef); err != nil {
+	if _, err := reference.ParseNormalizedNamed(options.RemoteRef); err != nil {
 		return nil, errors.Wrap(err, "invalid remote reference")
 		return nil, errors.Wrap(err, "invalid remote reference")
 	}
 	}
 	query.Set("remote", options.RemoteRef)
 	query.Set("remote", options.RemoteRef)

+ 1 - 1
client/plugin_upgrade.go

@@ -14,7 +14,7 @@ import (
 // PluginUpgrade upgrades a plugin
 // PluginUpgrade upgrades a plugin
 func (cli *Client) PluginUpgrade(ctx context.Context, name string, options types.PluginInstallOptions) (rc io.ReadCloser, err error) {
 func (cli *Client) PluginUpgrade(ctx context.Context, name string, options types.PluginInstallOptions) (rc io.ReadCloser, err error) {
 	query := url.Values{}
 	query := url.Values{}
-	if _, err := reference.ParseNamed(options.RemoteRef); err != nil {
+	if _, err := reference.ParseNormalizedNamed(options.RemoteRef); err != nil {
 		return nil, errors.Wrap(err, "invalid remote reference")
 		return nil, errors.Wrap(err, "invalid remote reference")
 	}
 	}
 	query.Set("remote", options.RemoteRef)
 	query.Set("remote", options.RemoteRef)

+ 21 - 27
daemon/cluster/cluster.go

@@ -52,7 +52,7 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
-	distreference "github.com/docker/distribution/reference"
+	"github.com/docker/distribution/reference"
 	apierrors "github.com/docker/docker/api/errors"
 	apierrors "github.com/docker/docker/api/errors"
 	apitypes "github.com/docker/docker/api/types"
 	apitypes "github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/backend"
 	"github.com/docker/docker/api/types/backend"
@@ -66,13 +66,11 @@ import (
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/signal"
 	"github.com/docker/docker/pkg/signal"
 	"github.com/docker/docker/pkg/stdcopy"
 	"github.com/docker/docker/pkg/stdcopy"
-	"github.com/docker/docker/reference"
 	"github.com/docker/docker/runconfig"
 	"github.com/docker/docker/runconfig"
 	swarmapi "github.com/docker/swarmkit/api"
 	swarmapi "github.com/docker/swarmkit/api"
 	"github.com/docker/swarmkit/manager/encryption"
 	"github.com/docker/swarmkit/manager/encryption"
 	swarmnode "github.com/docker/swarmkit/node"
 	swarmnode "github.com/docker/swarmkit/node"
 	gogotypes "github.com/gogo/protobuf/types"
 	gogotypes "github.com/gogo/protobuf/types"
-	"github.com/opencontainers/go-digest"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 )
 )
@@ -829,50 +827,46 @@ func (c *Cluster) GetServices(options apitypes.ServiceListOptions) ([]types.Serv
 }
 }
 
 
 // imageWithDigestString takes an image such as name or name:tag
 // imageWithDigestString takes an image such as name or name:tag
-// and returns the image pinned to a digest, such as name@sha256:34234...
-// Due to the difference between the docker/docker/reference, and the
-// docker/distribution/reference packages, we're parsing the image twice.
-// As the two packages converge, this function should be simplified.
-// TODO(nishanttotla): After the packages converge, the function must
-// convert distreference.Named -> distreference.Canonical, and the logic simplified.
+// and returns the image pinned to a digest, such as name@sha256:34234
 func (c *Cluster) imageWithDigestString(ctx context.Context, image string, authConfig *apitypes.AuthConfig) (string, error) {
 func (c *Cluster) imageWithDigestString(ctx context.Context, image string, authConfig *apitypes.AuthConfig) (string, error) {
-	if _, err := digest.Parse(image); err == nil {
-		return "", errors.New("image reference is an image ID")
-	}
-	ref, err := distreference.ParseNamed(image)
+	ref, err := reference.ParseAnyReference(image)
 	if err != nil {
 	if err != nil {
 		return "", err
 		return "", err
 	}
 	}
-	// only query registry if not a canonical reference (i.e. with digest)
-	if _, ok := ref.(distreference.Canonical); !ok {
-		// create a docker/docker/reference Named object because GetRepository needs it
-		dockerRef, err := reference.ParseNamed(image)
-		if err != nil {
-			return "", err
+	namedRef, ok := ref.(reference.Named)
+	if !ok {
+		if _, ok := ref.(reference.Digested); ok {
+			return "", errors.New("image reference is an image ID")
 		}
 		}
-		dockerRef = reference.WithDefaultTag(dockerRef)
-		namedTaggedRef, ok := dockerRef.(reference.NamedTagged)
+		return "", errors.Errorf("unknown image reference format: %s", image)
+	}
+	// only query registry if not a canonical reference (i.e. with digest)
+	if _, ok := namedRef.(reference.Canonical); !ok {
+		namedRef = reference.TagNameOnly(namedRef)
+
+		taggedRef, ok := namedRef.(reference.NamedTagged)
 		if !ok {
 		if !ok {
-			return "", errors.New("unable to cast image to NamedTagged reference object")
+			return "", errors.Errorf("image reference not tagged: %s", image)
 		}
 		}
 
 
-		repo, _, err := c.config.Backend.GetRepository(ctx, namedTaggedRef, authConfig)
+		repo, _, err := c.config.Backend.GetRepository(ctx, taggedRef, authConfig)
 		if err != nil {
 		if err != nil {
 			return "", err
 			return "", err
 		}
 		}
-		dscrptr, err := repo.Tags(ctx).Get(ctx, namedTaggedRef.Tag())
+		dscrptr, err := repo.Tags(ctx).Get(ctx, taggedRef.Tag())
 		if err != nil {
 		if err != nil {
 			return "", err
 			return "", err
 		}
 		}
 
 
-		namedDigestedRef, err := distreference.WithDigest(distreference.EnsureTagged(ref), dscrptr.Digest)
+		namedDigestedRef, err := reference.WithDigest(taggedRef, dscrptr.Digest)
 		if err != nil {
 		if err != nil {
 			return "", err
 			return "", err
 		}
 		}
-		return namedDigestedRef.String(), nil
+		// return familiar form until interface updated to return type
+		return reference.FamiliarString(namedDigestedRef), nil
 	}
 	}
 	// reference already contains a digest, so just return it
 	// reference already contains a digest, so just return it
-	return ref.String(), nil
+	return reference.FamiliarString(ref), nil
 }
 }
 
 
 // CreateService creates a new service in a managed swarm cluster.
 // CreateService creates a new service in a managed swarm cluster.

+ 1 - 1
daemon/cluster/executor/backend.go

@@ -5,6 +5,7 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/docker/distribution"
 	"github.com/docker/distribution"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/backend"
 	"github.com/docker/docker/api/types/backend"
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/container"
@@ -14,7 +15,6 @@ import (
 	swarmtypes "github.com/docker/docker/api/types/swarm"
 	swarmtypes "github.com/docker/docker/api/types/swarm"
 	clustertypes "github.com/docker/docker/daemon/cluster/provider"
 	clustertypes "github.com/docker/docker/daemon/cluster/provider"
 	"github.com/docker/docker/plugin"
 	"github.com/docker/docker/plugin"
-	"github.com/docker/docker/reference"
 	"github.com/docker/libnetwork"
 	"github.com/docker/libnetwork"
 	"github.com/docker/libnetwork/cluster"
 	"github.com/docker/libnetwork/cluster"
 	networktypes "github.com/docker/libnetwork/types"
 	networktypes "github.com/docker/libnetwork/types"

+ 2 - 2
daemon/cluster/executor/container/adapter.go

@@ -11,13 +11,13 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/backend"
 	"github.com/docker/docker/api/types/backend"
 	containertypes "github.com/docker/docker/api/types/container"
 	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/daemon/cluster/convert"
 	"github.com/docker/docker/daemon/cluster/convert"
 	executorpkg "github.com/docker/docker/daemon/cluster/executor"
 	executorpkg "github.com/docker/docker/daemon/cluster/executor"
-	"github.com/docker/docker/reference"
 	"github.com/docker/libnetwork"
 	"github.com/docker/libnetwork"
 	"github.com/docker/swarmkit/agent/exec"
 	"github.com/docker/swarmkit/agent/exec"
 	"github.com/docker/swarmkit/api"
 	"github.com/docker/swarmkit/api"
@@ -60,7 +60,7 @@ func (c *containerAdapter) pullImage(ctx context.Context) error {
 
 
 	// Skip pulling if the image is referenced by digest and already
 	// Skip pulling if the image is referenced by digest and already
 	// exists locally.
 	// exists locally.
-	named, err := reference.ParseNamed(spec.Image)
+	named, err := reference.ParseNormalizedNamed(spec.Image)
 	if err == nil {
 	if err == nil {
 		if _, ok := named.(reference.Canonical); ok {
 		if _, ok := named.(reference.Canonical); ok {
 			_, err := c.backend.LookupImage(spec.Image)
 			_, err := c.backend.LookupImage(spec.Image)

+ 3 - 3
daemon/cluster/executor/container/container.go

@@ -10,6 +10,7 @@ import (
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
 
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	enginecontainer "github.com/docker/docker/api/types/container"
 	enginecontainer "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/api/types/events"
@@ -18,7 +19,6 @@ import (
 	"github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/api/types/network"
 	volumetypes "github.com/docker/docker/api/types/volume"
 	volumetypes "github.com/docker/docker/api/types/volume"
 	clustertypes "github.com/docker/docker/daemon/cluster/provider"
 	clustertypes "github.com/docker/docker/daemon/cluster/provider"
-	"github.com/docker/docker/reference"
 	"github.com/docker/go-connections/nat"
 	"github.com/docker/go-connections/nat"
 	"github.com/docker/swarmkit/agent/exec"
 	"github.com/docker/swarmkit/agent/exec"
 	"github.com/docker/swarmkit/api"
 	"github.com/docker/swarmkit/api"
@@ -132,11 +132,11 @@ func (c *containerConfig) name() string {
 
 
 func (c *containerConfig) image() string {
 func (c *containerConfig) image() string {
 	raw := c.spec().Image
 	raw := c.spec().Image
-	ref, err := reference.ParseNamed(raw)
+	ref, err := reference.ParseNormalizedNamed(raw)
 	if err != nil {
 	if err != nil {
 		return raw
 		return raw
 	}
 	}
-	return reference.WithDefaultTag(ref).String()
+	return reference.FamiliarString(reference.TagNameOnly(ref))
 }
 }
 
 
 func (c *containerConfig) portBindings() nat.PortMap {
 func (c *containerConfig) portBindings() nat.PortMap {

+ 8 - 5
daemon/commit.go

@@ -2,12 +2,12 @@ package daemon
 
 
 import (
 import (
 	"encoding/json"
 	"encoding/json"
-	"fmt"
 	"io"
 	"io"
 	"runtime"
 	"runtime"
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types/backend"
 	"github.com/docker/docker/api/types/backend"
 	containertypes "github.com/docker/docker/api/types/container"
 	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/builder/dockerfile"
 	"github.com/docker/docker/builder/dockerfile"
@@ -16,7 +16,7 @@ import (
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/ioutils"
-	"github.com/docker/docker/reference"
+	"github.com/pkg/errors"
 )
 )
 
 
 // merge merges two Config, the image container configuration (defaults values),
 // merge merges two Config, the image container configuration (defaults values),
@@ -128,7 +128,7 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str
 
 
 	// It is not possible to commit a running container on Windows and on Solaris.
 	// It is not possible to commit a running container on Windows and on Solaris.
 	if (runtime.GOOS == "windows" || runtime.GOOS == "solaris") && container.IsRunning() {
 	if (runtime.GOOS == "windows" || runtime.GOOS == "solaris") && container.IsRunning() {
-		return "", fmt.Errorf("%+v does not support commit of a running container", runtime.GOOS)
+		return "", errors.Errorf("%+v does not support commit of a running container", runtime.GOOS)
 	}
 	}
 
 
 	if c.Pause && !container.IsPaused() {
 	if c.Pause && !container.IsPaused() {
@@ -228,10 +228,13 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str
 
 
 	imageRef := ""
 	imageRef := ""
 	if c.Repo != "" {
 	if c.Repo != "" {
-		newTag, err := reference.WithName(c.Repo) // todo: should move this to API layer
+		newTag, err := reference.ParseNormalizedNamed(c.Repo) // todo: should move this to API layer
 		if err != nil {
 		if err != nil {
 			return "", err
 			return "", err
 		}
 		}
+		if !reference.IsNameOnly(newTag) {
+			return "", errors.Errorf("unexpected repository name: %s", c.Repo)
+		}
 		if c.Tag != "" {
 		if c.Tag != "" {
 			if newTag, err = reference.WithTag(newTag, c.Tag); err != nil {
 			if newTag, err = reference.WithTag(newTag, c.Tag); err != nil {
 				return "", err
 				return "", err
@@ -240,7 +243,7 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str
 		if err := daemon.TagImageWithReference(id, newTag); err != nil {
 		if err := daemon.TagImageWithReference(id, newTag); err != nil {
 			return "", err
 			return "", err
 		}
 		}
-		imageRef = newTag.String()
+		imageRef = reference.FamiliarString(newTag)
 	}
 	}
 
 
 	attributes := map[string]string{
 	attributes := map[string]string{

+ 3 - 3
daemon/daemon.go

@@ -46,7 +46,7 @@ import (
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/pkg/truncindex"
 	"github.com/docker/docker/pkg/truncindex"
 	"github.com/docker/docker/plugin"
 	"github.com/docker/docker/plugin"
-	"github.com/docker/docker/reference"
+	refstore "github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/runconfig"
 	"github.com/docker/docker/runconfig"
 	volumedrivers "github.com/docker/docker/volume/drivers"
 	volumedrivers "github.com/docker/docker/volume/drivers"
@@ -76,7 +76,7 @@ type Daemon struct {
 	repository                string
 	repository                string
 	containers                container.Store
 	containers                container.Store
 	execCommands              *exec.Store
 	execCommands              *exec.Store
-	referenceStore            reference.Store
+	referenceStore            refstore.Store
 	downloadManager           *xfer.LayerDownloadManager
 	downloadManager           *xfer.LayerDownloadManager
 	uploadManager             *xfer.LayerUploadManager
 	uploadManager             *xfer.LayerUploadManager
 	distributionMetadataStore dmetadata.Store
 	distributionMetadataStore dmetadata.Store
@@ -637,7 +637,7 @@ func NewDaemon(config *Config, registryService registry.Service, containerdRemot
 
 
 	eventsService := events.New()
 	eventsService := events.New()
 
 
-	referenceStore, err := reference.NewReferenceStore(filepath.Join(imageRoot, "repositories.json"))
+	referenceStore, err := refstore.NewReferenceStore(filepath.Join(imageRoot, "repositories.json"))
 	if err != nil {
 	if err != nil {
 		return nil, fmt.Errorf("Couldn't create Tag store repositories: %s", err)
 		return nil, fmt.Errorf("Couldn't create Tag store repositories: %s", err)
 	}
 	}

+ 2 - 2
daemon/daemon_solaris.go

@@ -16,7 +16,7 @@ import (
 	"github.com/docker/docker/pkg/idtools"
 	"github.com/docker/docker/pkg/idtools"
 	"github.com/docker/docker/pkg/parsers/kernel"
 	"github.com/docker/docker/pkg/parsers/kernel"
 	"github.com/docker/docker/pkg/sysinfo"
 	"github.com/docker/docker/pkg/sysinfo"
-	"github.com/docker/docker/reference"
+	refstore "github.com/docker/docker/reference"
 	"github.com/docker/libnetwork"
 	"github.com/docker/libnetwork"
 	nwconfig "github.com/docker/libnetwork/config"
 	nwconfig "github.com/docker/libnetwork/config"
 	"github.com/docker/libnetwork/drivers/solaris/bridge"
 	"github.com/docker/libnetwork/drivers/solaris/bridge"
@@ -491,7 +491,7 @@ func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container
 	return daemon.Unmount(container)
 	return daemon.Unmount(container)
 }
 }
 
 
-func restoreCustomImage(is image.Store, ls layer.Store, rs reference.Store) error {
+func restoreCustomImage(is image.Store, ls layer.Store, rs refstore.Store) error {
 	// Solaris has no custom images to register
 	// Solaris has no custom images to register
 	return nil
 	return nil
 }
 }

+ 1 - 17
daemon/errors.go

@@ -2,29 +2,13 @@ package daemon
 
 
 import (
 import (
 	"fmt"
 	"fmt"
-	"strings"
 
 
 	"github.com/docker/docker/api/errors"
 	"github.com/docker/docker/api/errors"
-	"github.com/docker/docker/reference"
 )
 )
 
 
 func (d *Daemon) imageNotExistToErrcode(err error) error {
 func (d *Daemon) imageNotExistToErrcode(err error) error {
 	if dne, isDNE := err.(ErrImageDoesNotExist); isDNE {
 	if dne, isDNE := err.(ErrImageDoesNotExist); isDNE {
-		if strings.Contains(dne.RefOrID, "@") {
-			e := fmt.Errorf("No such image: %s", dne.RefOrID)
-			return errors.NewRequestNotFoundError(e)
-		}
-		tag := reference.DefaultTag
-		ref, err := reference.ParseNamed(dne.RefOrID)
-		if err != nil {
-			e := fmt.Errorf("No such image: %s:%s", dne.RefOrID, tag)
-			return errors.NewRequestNotFoundError(e)
-		}
-		if tagged, isTagged := ref.(reference.NamedTagged); isTagged {
-			tag = tagged.Tag()
-		}
-		e := fmt.Errorf("No such image: %s:%s", ref.Name(), tag)
-		return errors.NewRequestNotFoundError(e)
+		return errors.NewRequestNotFoundError(dne)
 	}
 	}
 	return err
 	return err
 }
 }

+ 3 - 3
daemon/events/filter.go

@@ -1,9 +1,9 @@
 package events
 package events
 
 
 import (
 import (
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/filters"
-	"github.com/docker/docker/reference"
 )
 )
 
 
 // Filter can filter out docker events from a stream
 // Filter can filter out docker events from a stream
@@ -102,9 +102,9 @@ func (ef *Filter) matchImage(ev events.Message) bool {
 }
 }
 
 
 func stripTag(image string) string {
 func stripTag(image string) string {
-	ref, err := reference.ParseNamed(image)
+	ref, err := reference.ParseNormalizedNamed(image)
 	if err != nil {
 	if err != nil {
 		return image
 		return image
 	}
 	}
-	return ref.Name()
+	return reference.FamiliarName(ref)
 }
 }

+ 23 - 13
daemon/image.go

@@ -3,45 +3,55 @@ package daemon
 import (
 import (
 	"fmt"
 	"fmt"
 
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/builder"
 	"github.com/docker/docker/builder"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/stringid"
-	"github.com/docker/docker/reference"
 )
 )
 
 
 // ErrImageDoesNotExist is error returned when no image can be found for a reference.
 // ErrImageDoesNotExist is error returned when no image can be found for a reference.
 type ErrImageDoesNotExist struct {
 type ErrImageDoesNotExist struct {
-	RefOrID string
+	ref reference.Reference
 }
 }
 
 
 func (e ErrImageDoesNotExist) Error() string {
 func (e ErrImageDoesNotExist) Error() string {
-	return fmt.Sprintf("no such id: %s", e.RefOrID)
+	ref := e.ref
+	if named, ok := ref.(reference.Named); ok {
+		ref = reference.TagNameOnly(named)
+	}
+	return fmt.Sprintf("No such image: %s", reference.FamiliarString(ref))
 }
 }
 
 
 // GetImageID returns an image ID corresponding to the image referred to by
 // GetImageID returns an image ID corresponding to the image referred to by
 // refOrID.
 // refOrID.
 func (daemon *Daemon) GetImageID(refOrID string) (image.ID, error) {
 func (daemon *Daemon) GetImageID(refOrID string) (image.ID, error) {
-	id, ref, err := reference.ParseIDOrReference(refOrID)
+	ref, err := reference.ParseAnyReference(refOrID)
 	if err != nil {
 	if err != nil {
 		return "", err
 		return "", err
 	}
 	}
-	if id != "" {
-		if _, err := daemon.imageStore.Get(image.IDFromDigest(id)); err != nil {
-			return "", ErrImageDoesNotExist{refOrID}
+	namedRef, ok := ref.(reference.Named)
+	if !ok {
+		digested, ok := ref.(reference.Digested)
+		if !ok {
+			return "", ErrImageDoesNotExist{ref}
 		}
 		}
-		return image.IDFromDigest(id), nil
+		id := image.IDFromDigest(digested.Digest())
+		if _, err := daemon.imageStore.Get(id); err != nil {
+			return "", ErrImageDoesNotExist{ref}
+		}
+		return id, nil
 	}
 	}
 
 
-	if id, err := daemon.referenceStore.Get(ref); err == nil {
+	if id, err := daemon.referenceStore.Get(namedRef); err == nil {
 		return image.IDFromDigest(id), nil
 		return image.IDFromDigest(id), nil
 	}
 	}
 
 
 	// deprecated: repo:shortid https://github.com/docker/docker/pull/799
 	// deprecated: repo:shortid https://github.com/docker/docker/pull/799
-	if tagged, ok := ref.(reference.NamedTagged); ok {
+	if tagged, ok := namedRef.(reference.Tagged); ok {
 		if tag := tagged.Tag(); stringid.IsShortID(stringid.TruncateID(tag)) {
 		if tag := tagged.Tag(); stringid.IsShortID(stringid.TruncateID(tag)) {
 			if id, err := daemon.imageStore.Search(tag); err == nil {
 			if id, err := daemon.imageStore.Search(tag); err == nil {
-				for _, namedRef := range daemon.referenceStore.References(id.Digest()) {
-					if namedRef.Name() == ref.Name() {
+				for _, storeRef := range daemon.referenceStore.References(id.Digest()) {
+					if storeRef.Name() == namedRef.Name() {
 						return id, nil
 						return id, nil
 					}
 					}
 				}
 				}
@@ -54,7 +64,7 @@ func (daemon *Daemon) GetImageID(refOrID string) (image.ID, error) {
 		return id, nil
 		return id, nil
 	}
 	}
 
 
-	return "", ErrImageDoesNotExist{refOrID}
+	return "", ErrImageDoesNotExist{ref}
 }
 }
 
 
 // GetImage returns an image corresponding to the image referred to by refOrID.
 // GetImage returns an image corresponding to the image referred to by refOrID.

+ 8 - 7
daemon/image_delete.go

@@ -5,12 +5,12 @@ import (
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/errors"
 	"github.com/docker/docker/api/errors"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/stringid"
-	"github.com/docker/docker/reference"
 )
 )
 
 
 type conflictType int
 type conflictType int
@@ -89,7 +89,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
 			}
 			}
 		}
 		}
 
 
-		parsedRef, err := reference.ParseNamed(imageRef)
+		parsedRef, err := reference.ParseNormalizedNamed(imageRef)
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
@@ -99,7 +99,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
 			return nil, err
 			return nil, err
 		}
 		}
 
 
-		untaggedRecord := types.ImageDeleteResponseItem{Untagged: parsedRef.String()}
+		untaggedRecord := types.ImageDeleteResponseItem{Untagged: reference.FamiliarString(parsedRef)}
 
 
 		daemon.LogImageEvent(imgID.String(), imgID.String(), "untag")
 		daemon.LogImageEvent(imgID.String(), imgID.String(), "untag")
 		records = append(records, untaggedRecord)
 		records = append(records, untaggedRecord)
@@ -126,7 +126,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
 							return records, err
 							return records, err
 						}
 						}
 
 
-						untaggedRecord := types.ImageDeleteResponseItem{Untagged: repoRef.String()}
+						untaggedRecord := types.ImageDeleteResponseItem{Untagged: reference.FamiliarString(repoRef)}
 						records = append(records, untaggedRecord)
 						records = append(records, untaggedRecord)
 					} else {
 					} else {
 						remainingRefs = append(remainingRefs, repoRef)
 						remainingRefs = append(remainingRefs, repoRef)
@@ -162,7 +162,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
 					return nil, err
 					return nil, err
 				}
 				}
 
 
-				untaggedRecord := types.ImageDeleteResponseItem{Untagged: parsedRef.String()}
+				untaggedRecord := types.ImageDeleteResponseItem{Untagged: reference.FamiliarString(parsedRef)}
 
 
 				daemon.LogImageEvent(imgID.String(), imgID.String(), "untag")
 				daemon.LogImageEvent(imgID.String(), imgID.String(), "untag")
 				records = append(records, untaggedRecord)
 				records = append(records, untaggedRecord)
@@ -232,7 +232,8 @@ func (daemon *Daemon) getContainerUsingImage(imageID image.ID) *container.Contai
 // optional tag or digest reference. If tag or digest is omitted, the default
 // optional tag or digest reference. If tag or digest is omitted, the default
 // tag is used. Returns the resolved image reference and an error.
 // tag is used. Returns the resolved image reference and an error.
 func (daemon *Daemon) removeImageRef(ref reference.Named) (reference.Named, error) {
 func (daemon *Daemon) removeImageRef(ref reference.Named) (reference.Named, error) {
-	ref = reference.WithDefaultTag(ref)
+	ref = reference.TagNameOnly(ref)
+
 	// Ignore the boolean value returned, as far as we're concerned, this
 	// Ignore the boolean value returned, as far as we're concerned, this
 	// is an idempotent operation and it's okay if the reference didn't
 	// is an idempotent operation and it's okay if the reference didn't
 	// exist in the first place.
 	// exist in the first place.
@@ -255,7 +256,7 @@ func (daemon *Daemon) removeAllReferencesToImageID(imgID image.ID, records *[]ty
 			return err
 			return err
 		}
 		}
 
 
-		untaggedRecord := types.ImageDeleteResponseItem{Untagged: parsedRef.String()}
+		untaggedRecord := types.ImageDeleteResponseItem{Untagged: reference.FamiliarString(parsedRef)}
 
 
 		daemon.LogImageEvent(imgID.String(), imgID.String(), "untag")
 		daemon.LogImageEvent(imgID.String(), imgID.String(), "untag")
 		*records = append(*records, untaggedRecord)
 		*records = append(*records, untaggedRecord)

+ 2 - 2
daemon/image_history.go

@@ -4,9 +4,9 @@ import (
 	"fmt"
 	"fmt"
 	"time"
 	"time"
 
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types/image"
 	"github.com/docker/docker/api/types/image"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/layer"
-	"github.com/docker/docker/reference"
 )
 )
 
 
 // ImageHistory returns a slice of ImageHistory structures for the specified image
 // ImageHistory returns a slice of ImageHistory structures for the specified image
@@ -64,7 +64,7 @@ func (daemon *Daemon) ImageHistory(name string) ([]*image.HistoryResponseItem, e
 		var tags []string
 		var tags []string
 		for _, r := range daemon.referenceStore.References(id.Digest()) {
 		for _, r := range daemon.referenceStore.References(id.Digest()) {
 			if _, ok := r.(reference.NamedTagged); ok {
 			if _, ok := r.(reference.NamedTagged); ok {
-				tags = append(tags, r.String())
+				tags = append(tags, reference.FamiliarString(r))
 			}
 			}
 		}
 		}
 
 

+ 3 - 3
daemon/image_inspect.go

@@ -3,9 +3,9 @@ package daemon
 import (
 import (
 	"time"
 	"time"
 
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/layer"
-	"github.com/docker/docker/reference"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
 )
 )
 
 
@@ -23,9 +23,9 @@ func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) {
 	for _, ref := range refs {
 	for _, ref := range refs {
 		switch ref.(type) {
 		switch ref.(type) {
 		case reference.NamedTagged:
 		case reference.NamedTagged:
-			repoTags = append(repoTags, ref.String())
+			repoTags = append(repoTags, reference.FamiliarString(ref))
 		case reference.Canonical:
 		case reference.Canonical:
-			repoDigests = append(repoDigests, ref.String())
+			repoDigests = append(repoDigests, reference.FamiliarString(ref))
 		}
 		}
 	}
 	}
 
 

+ 6 - 6
daemon/image_pull.go

@@ -5,12 +5,12 @@ import (
 	"strings"
 	"strings"
 
 
 	dist "github.com/docker/distribution"
 	dist "github.com/docker/distribution"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/builder"
 	"github.com/docker/docker/builder"
 	"github.com/docker/docker/distribution"
 	"github.com/docker/docker/distribution"
 	progressutils "github.com/docker/docker/distribution/utils"
 	progressutils "github.com/docker/docker/distribution/utils"
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/pkg/progress"
-	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/registry"
 	"github.com/opencontainers/go-digest"
 	"github.com/opencontainers/go-digest"
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
@@ -24,7 +24,7 @@ func (daemon *Daemon) PullImage(ctx context.Context, image, tag string, metaHead
 	// compatibility.
 	// compatibility.
 	image = strings.TrimSuffix(image, ":")
 	image = strings.TrimSuffix(image, ":")
 
 
-	ref, err := reference.ParseNamed(image)
+	ref, err := reference.ParseNormalizedNamed(image)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -48,11 +48,11 @@ func (daemon *Daemon) PullImage(ctx context.Context, image, tag string, metaHead
 
 
 // PullOnBuild tells Docker to pull image referenced by `name`.
 // PullOnBuild tells Docker to pull image referenced by `name`.
 func (daemon *Daemon) PullOnBuild(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer) (builder.Image, error) {
 func (daemon *Daemon) PullOnBuild(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer) (builder.Image, error) {
-	ref, err := reference.ParseNamed(name)
+	ref, err := reference.ParseNormalizedNamed(name)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	ref = reference.WithDefaultTag(ref)
+	ref = reference.TagNameOnly(ref)
 
 
 	pullRegistryAuth := &types.AuthConfig{}
 	pullRegistryAuth := &types.AuthConfig{}
 	if len(authConfigs) > 0 {
 	if len(authConfigs) > 0 {
@@ -118,12 +118,12 @@ func (daemon *Daemon) GetRepository(ctx context.Context, ref reference.NamedTagg
 		return nil, false, err
 		return nil, false, err
 	}
 	}
 	// makes sure name is not empty or `scratch`
 	// makes sure name is not empty or `scratch`
-	if err := distribution.ValidateRepoName(repoInfo.Name()); err != nil {
+	if err := distribution.ValidateRepoName(repoInfo.Name); err != nil {
 		return nil, false, err
 		return nil, false, err
 	}
 	}
 
 
 	// get endpoints
 	// get endpoints
-	endpoints, err := daemon.RegistryService.LookupPullEndpoints(repoInfo.Hostname())
+	endpoints, err := daemon.RegistryService.LookupPullEndpoints(reference.Domain(repoInfo.Name))
 	if err != nil {
 	if err != nil {
 		return nil, false, err
 		return nil, false, err
 	}
 	}

+ 2 - 2
daemon/image_push.go

@@ -4,17 +4,17 @@ import (
 	"io"
 	"io"
 
 
 	"github.com/docker/distribution/manifest/schema2"
 	"github.com/docker/distribution/manifest/schema2"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/distribution"
 	"github.com/docker/docker/distribution"
 	progressutils "github.com/docker/docker/distribution/utils"
 	progressutils "github.com/docker/docker/distribution/utils"
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/pkg/progress"
-	"github.com/docker/docker/reference"
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 )
 )
 
 
 // PushImage initiates a push operation on the repository named localName.
 // PushImage initiates a push operation on the repository named localName.
 func (daemon *Daemon) PushImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
 func (daemon *Daemon) PushImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
-	ref, err := reference.ParseNamed(image)
+	ref, err := reference.ParseNormalizedNamed(image)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}

+ 4 - 4
daemon/image_tag.go

@@ -1,8 +1,8 @@
 package daemon
 package daemon
 
 
 import (
 import (
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/image"
-	"github.com/docker/docker/reference"
 )
 )
 
 
 // TagImage creates the tag specified by newTag, pointing to the image named
 // 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 {
 		return err
 		return err
 	}
 	}
 
 
-	newTag, err := reference.WithName(repository)
+	newTag, err := reference.ParseNormalizedNamed(repository)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 	if tag != "" {
 	if tag != "" {
-		if newTag, err = reference.WithTag(newTag, tag); err != nil {
+		if newTag, err = reference.WithTag(reference.TrimNamed(newTag), tag); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}
@@ -32,6 +32,6 @@ func (daemon *Daemon) TagImageWithReference(imageID image.ID, newTag reference.N
 		return err
 		return err
 	}
 	}
 
 
-	daemon.LogImageEvent(imageID.String(), newTag.String(), "tag")
+	daemon.LogImageEvent(imageID.String(), reference.FamiliarString(newTag), "tag")
 	return nil
 	return nil
 }
 }

+ 3 - 3
daemon/images.go

@@ -135,7 +135,7 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs
 				var found bool
 				var found bool
 				var matchErr error
 				var matchErr error
 				for _, pattern := range imageFilters.Get("reference") {
 				for _, pattern := range imageFilters.Get("reference") {
-					found, matchErr = reference.Match(pattern, ref)
+					found, matchErr = reference.FamiliarMatch(pattern, ref)
 					if matchErr != nil {
 					if matchErr != nil {
 						return nil, matchErr
 						return nil, matchErr
 					}
 					}
@@ -145,10 +145,10 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs
 				}
 				}
 			}
 			}
 			if _, ok := ref.(reference.Canonical); ok {
 			if _, ok := ref.(reference.Canonical); ok {
-				newImage.RepoDigests = append(newImage.RepoDigests, ref.String())
+				newImage.RepoDigests = append(newImage.RepoDigests, reference.FamiliarString(ref))
 			}
 			}
 			if _, ok := ref.(reference.NamedTagged); ok {
 			if _, ok := ref.(reference.NamedTagged); ok {
-				newImage.RepoTags = append(newImage.RepoTags, ref.String())
+				newImage.RepoTags = append(newImage.RepoTags, reference.FamiliarString(ref))
 			}
 			}
 		}
 		}
 		if newImage.RepoDigests == nil && newImage.RepoTags == nil {
 		if newImage.RepoDigests == nil && newImage.RepoTags == nil {

+ 3 - 4
daemon/import.go

@@ -2,13 +2,13 @@ package daemon
 
 
 import (
 import (
 	"encoding/json"
 	"encoding/json"
-	"errors"
 	"io"
 	"io"
 	"net/http"
 	"net/http"
 	"net/url"
 	"net/url"
 	"runtime"
 	"runtime"
 	"time"
 	"time"
 
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/builder/dockerfile"
 	"github.com/docker/docker/builder/dockerfile"
 	"github.com/docker/docker/dockerversion"
 	"github.com/docker/docker/dockerversion"
@@ -18,7 +18,7 @@ import (
 	"github.com/docker/docker/pkg/httputils"
 	"github.com/docker/docker/pkg/httputils"
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/pkg/streamformatter"
 	"github.com/docker/docker/pkg/streamformatter"
-	"github.com/docker/docker/reference"
+	"github.com/pkg/errors"
 )
 )
 
 
 // ImportImage imports an image, getting the archived layer data either from
 // 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
 
 
 	if repository != "" {
 	if repository != "" {
 		var err error
 		var err error
-		newRef, err = reference.ParseNamed(repository)
+		newRef, err = reference.ParseNormalizedNamed(repository)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
-
 		if _, isCanonical := newRef.(reference.Canonical); isCanonical {
 		if _, isCanonical := newRef.(reference.Canonical); isCanonical {
 			return errors.New("cannot import digest reference")
 			return errors.New("cannot import digest reference")
 		}
 		}

+ 1 - 1
daemon/prune.go

@@ -6,13 +6,13 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/filters"
 	timetypes "github.com/docker/docker/api/types/time"
 	timetypes "github.com/docker/docker/api/types/time"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/pkg/directory"
 	"github.com/docker/docker/pkg/directory"
-	"github.com/docker/docker/reference"
 	"github.com/docker/docker/runconfig"
 	"github.com/docker/docker/runconfig"
 	"github.com/docker/docker/volume"
 	"github.com/docker/docker/volume"
 	"github.com/docker/libnetwork"
 	"github.com/docker/libnetwork"

+ 2 - 2
distribution/config.go

@@ -14,7 +14,7 @@ import (
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/pkg/progress"
-	"github.com/docker/docker/reference"
+	refstore "github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/registry"
 	"github.com/docker/libtrust"
 	"github.com/docker/libtrust"
 	"github.com/opencontainers/go-digest"
 	"github.com/opencontainers/go-digest"
@@ -44,7 +44,7 @@ type Config struct {
 	ImageStore ImageConfigStore
 	ImageStore ImageConfigStore
 	// ReferenceStore manages tags. This value is optional, when excluded
 	// ReferenceStore manages tags. This value is optional, when excluded
 	// content will not be tagged.
 	// content will not be tagged.
-	ReferenceStore reference.Store
+	ReferenceStore refstore.Store
 	// RequireSchema2 ensures that only schema2 manifests are used.
 	// RequireSchema2 ensures that only schema2 manifests are used.
 	RequireSchema2 bool
 	RequireSchema2 bool
 }
 }

+ 4 - 4
distribution/errors.go

@@ -7,12 +7,12 @@ import (
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/distribution"
 	"github.com/docker/distribution"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/registry/api/errcode"
 	"github.com/docker/distribution/registry/api/errcode"
 	"github.com/docker/distribution/registry/api/v2"
 	"github.com/docker/distribution/registry/api/v2"
 	"github.com/docker/distribution/registry/client"
 	"github.com/docker/distribution/registry/client"
 	"github.com/docker/distribution/registry/client/auth"
 	"github.com/docker/distribution/registry/client/auth"
 	"github.com/docker/docker/distribution/xfer"
 	"github.com/docker/docker/distribution/xfer"
-	"github.com/docker/docker/reference"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
 )
 )
 
 
@@ -78,11 +78,11 @@ func TranslatePullError(err error, ref reference.Named) error {
 		switch v.Code {
 		switch v.Code {
 		case errcode.ErrorCodeDenied:
 		case errcode.ErrorCodeDenied:
 			// ErrorCodeDenied is used when access to the repository was denied
 			// ErrorCodeDenied is used when access to the repository was denied
-			newErr = errors.Errorf("repository %s not found: does not exist or no pull access", ref.Name())
+			newErr = errors.Errorf("repository %s not found: does not exist or no pull access", reference.FamiliarName(ref))
 		case v2.ErrorCodeManifestUnknown:
 		case v2.ErrorCodeManifestUnknown:
-			newErr = errors.Errorf("manifest for %s not found", ref.String())
+			newErr = errors.Errorf("manifest for %s not found", reference.FamiliarString(ref))
 		case v2.ErrorCodeNameUnknown:
 		case v2.ErrorCodeNameUnknown:
-			newErr = errors.Errorf("repository %s not found", ref.Name())
+			newErr = errors.Errorf("repository %s not found", reference.FamiliarName(ref))
 		}
 		}
 		if newErr != nil {
 		if newErr != nil {
 			logrus.Infof("Translating %q to %q", err, newErr)
 			logrus.Infof("Translating %q to %q", err, newErr)

+ 12 - 15
distribution/pull.go

@@ -1,14 +1,14 @@
 package distribution
 package distribution
 
 
 import (
 import (
-	"errors"
 	"fmt"
 	"fmt"
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api"
 	"github.com/docker/docker/api"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/pkg/progress"
-	"github.com/docker/docker/reference"
+	refstore "github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/registry"
 	"github.com/opencontainers/go-digest"
 	"github.com/opencontainers/go-digest"
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
@@ -56,12 +56,12 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo
 		return err
 		return err
 	}
 	}
 
 
-	// makes sure name is not empty or `scratch`
-	if err := ValidateRepoName(repoInfo.Name()); err != nil {
+	// makes sure name is not `scratch`
+	if err := ValidateRepoName(repoInfo.Name); err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	endpoints, err := imagePullConfig.RegistryService.LookupPullEndpoints(repoInfo.Hostname())
+	endpoints, err := imagePullConfig.RegistryService.LookupPullEndpoints(reference.Domain(repoInfo.Name))
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -105,7 +105,7 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo
 			}
 			}
 		}
 		}
 
 
-		logrus.Debugf("Trying to pull %s from %s %s", repoInfo.Name(), endpoint.URL, endpoint.Version)
+		logrus.Debugf("Trying to pull %s from %s %s", reference.FamiliarName(repoInfo.Name), endpoint.URL, endpoint.Version)
 
 
 		puller, err := newPuller(endpoint, repoInfo, imagePullConfig)
 		puller, err := newPuller(endpoint, repoInfo, imagePullConfig)
 		if err != nil {
 		if err != nil {
@@ -147,12 +147,12 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo
 			return TranslatePullError(err, ref)
 			return TranslatePullError(err, ref)
 		}
 		}
 
 
-		imagePullConfig.ImageEventLogger(ref.String(), repoInfo.Name(), "pull")
+		imagePullConfig.ImageEventLogger(reference.FamiliarString(ref), reference.FamiliarName(repoInfo.Name), "pull")
 		return nil
 		return nil
 	}
 	}
 
 
 	if lastErr == nil {
 	if lastErr == nil {
-		lastErr = fmt.Errorf("no endpoints found for %s", ref.String())
+		lastErr = fmt.Errorf("no endpoints found for %s", reference.FamiliarString(ref))
 	}
 	}
 
 
 	return TranslatePullError(lastErr, ref)
 	return TranslatePullError(lastErr, ref)
@@ -171,17 +171,14 @@ func writeStatus(requestedTag string, out progress.Output, layersDownloaded bool
 }
 }
 
 
 // ValidateRepoName validates the name of a repository.
 // ValidateRepoName validates the name of a repository.
-func ValidateRepoName(name string) error {
-	if name == "" {
-		return errors.New("Repository name can't be empty")
-	}
-	if name == api.NoBaseImageSpecifier {
+func ValidateRepoName(name reference.Named) error {
+	if reference.FamiliarName(name) == api.NoBaseImageSpecifier {
 		return fmt.Errorf("'%s' is a reserved name", api.NoBaseImageSpecifier)
 		return fmt.Errorf("'%s' is a reserved name", api.NoBaseImageSpecifier)
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
-func addDigestReference(store reference.Store, ref reference.Named, dgst digest.Digest, id digest.Digest) error {
+func addDigestReference(store refstore.Store, ref reference.Named, dgst digest.Digest, id digest.Digest) error {
 	dgstRef, err := reference.WithDigest(reference.TrimNamed(ref), dgst)
 	dgstRef, err := reference.WithDigest(reference.TrimNamed(ref), dgst)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -193,7 +190,7 @@ func addDigestReference(store reference.Store, ref reference.Named, dgst digest.
 			logrus.Errorf("Image ID for digest %s changed from %s to %s, cannot update", dgst.String(), oldTagID, id)
 			logrus.Errorf("Image ID for digest %s changed from %s to %s, cannot update", dgst.String(), oldTagID, id)
 		}
 		}
 		return nil
 		return nil
-	} else if err != reference.ErrDoesNotExist {
+	} else if err != refstore.ErrDoesNotExist {
 		return err
 		return err
 	}
 	}
 
 

+ 17 - 17
distribution/pull_v1.go

@@ -12,6 +12,7 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/registry/client/transport"
 	"github.com/docker/distribution/registry/client/transport"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/distribution/xfer"
 	"github.com/docker/docker/distribution/xfer"
@@ -22,7 +23,6 @@ import (
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/stringid"
-	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/registry"
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 )
 )
@@ -67,23 +67,23 @@ func (p *v1Puller) Pull(ctx context.Context, ref reference.Named) error {
 		// TODO(dmcgowan): Check if should fallback
 		// TODO(dmcgowan): Check if should fallback
 		return err
 		return err
 	}
 	}
-	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.")
+	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.")
 
 
 	return nil
 	return nil
 }
 }
 
 
 func (p *v1Puller) pullRepository(ctx context.Context, ref reference.Named) error {
 func (p *v1Puller) pullRepository(ctx context.Context, ref reference.Named) error {
-	progress.Message(p.config.ProgressOutput, "", "Pulling repository "+p.repoInfo.FullName())
+	progress.Message(p.config.ProgressOutput, "", "Pulling repository "+p.repoInfo.Name.Name())
 
 
 	tagged, isTagged := ref.(reference.NamedTagged)
 	tagged, isTagged := ref.(reference.NamedTagged)
 
 
-	repoData, err := p.session.GetRepositoryData(p.repoInfo)
+	repoData, err := p.session.GetRepositoryData(p.repoInfo.Name)
 	if err != nil {
 	if err != nil {
 		if strings.Contains(err.Error(), "HTTP code: 404") {
 		if strings.Contains(err.Error(), "HTTP code: 404") {
 			if isTagged {
 			if isTagged {
-				return fmt.Errorf("Error: image %s:%s not found", p.repoInfo.RemoteName(), tagged.Tag())
+				return fmt.Errorf("Error: image %s:%s not found", reference.Path(p.repoInfo.Name), tagged.Tag())
 			}
 			}
-			return fmt.Errorf("Error: image %s not found", p.repoInfo.RemoteName())
+			return fmt.Errorf("Error: image %s not found", reference.Path(p.repoInfo.Name))
 		}
 		}
 		// Unexpected HTTP error
 		// Unexpected HTTP error
 		return err
 		return err
@@ -92,13 +92,13 @@ func (p *v1Puller) pullRepository(ctx context.Context, ref reference.Named) erro
 	logrus.Debug("Retrieving the tag list")
 	logrus.Debug("Retrieving the tag list")
 	var tagsList map[string]string
 	var tagsList map[string]string
 	if !isTagged {
 	if !isTagged {
-		tagsList, err = p.session.GetRemoteTags(repoData.Endpoints, p.repoInfo)
+		tagsList, err = p.session.GetRemoteTags(repoData.Endpoints, p.repoInfo.Name)
 	} else {
 	} else {
 		var tagID string
 		var tagID string
 		tagsList = make(map[string]string)
 		tagsList = make(map[string]string)
-		tagID, err = p.session.GetRemoteTag(repoData.Endpoints, p.repoInfo, tagged.Tag())
+		tagID, err = p.session.GetRemoteTag(repoData.Endpoints, p.repoInfo.Name, tagged.Tag())
 		if err == registry.ErrRepoNotFound {
 		if err == registry.ErrRepoNotFound {
-			return fmt.Errorf("Tag %s not found in repository %s", tagged.Tag(), p.repoInfo.FullName())
+			return fmt.Errorf("Tag %s not found in repository %s", tagged.Tag(), p.repoInfo.Name.Name())
 		}
 		}
 		tagsList[tagged.Tag()] = tagID
 		tagsList[tagged.Tag()] = tagID
 	}
 	}
@@ -127,7 +127,7 @@ func (p *v1Puller) pullRepository(ctx context.Context, ref reference.Named) erro
 		}
 		}
 	}
 	}
 
 
-	writeStatus(ref.String(), p.config.ProgressOutput, layersDownloaded)
+	writeStatus(reference.FamiliarString(ref), p.config.ProgressOutput, layersDownloaded)
 	return nil
 	return nil
 }
 }
 
 
@@ -137,7 +137,7 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit
 		return nil
 		return nil
 	}
 	}
 
 
-	localNameRef, err := reference.WithTag(p.repoInfo, img.Tag)
+	localNameRef, err := reference.WithTag(p.repoInfo.Name, img.Tag)
 	if err != nil {
 	if err != nil {
 		retErr := fmt.Errorf("Image (id: %s) has invalid tag: %s", img.ID, img.Tag)
 		retErr := fmt.Errorf("Image (id: %s) has invalid tag: %s", img.ID, img.Tag)
 		logrus.Debug(retErr.Error())
 		logrus.Debug(retErr.Error())
@@ -148,15 +148,15 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit
 		return err
 		return err
 	}
 	}
 
 
-	progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s", img.Tag, p.repoInfo.FullName())
+	progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s", img.Tag, p.repoInfo.Name.Name())
 	success := false
 	success := false
 	var lastErr error
 	var lastErr error
 	for _, ep := range p.repoInfo.Index.Mirrors {
 	for _, ep := range p.repoInfo.Index.Mirrors {
 		ep += "v1/"
 		ep += "v1/"
-		progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), fmt.Sprintf("Pulling image (%s) from %s, mirror: %s", img.Tag, p.repoInfo.FullName(), ep))
+		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))
 		if err = p.pullImage(ctx, img.ID, ep, localNameRef, layersDownloaded); err != nil {
 		if err = p.pullImage(ctx, img.ID, ep, localNameRef, layersDownloaded); err != nil {
 			// Don't report errors when pulling from mirrors.
 			// Don't report errors when pulling from mirrors.
-			logrus.Debugf("Error pulling image (%s) from %s, mirror: %s, %s", img.Tag, p.repoInfo.FullName(), ep, err)
+			logrus.Debugf("Error pulling image (%s) from %s, mirror: %s, %s", img.Tag, p.repoInfo.Name.Name(), ep, err)
 			continue
 			continue
 		}
 		}
 		success = true
 		success = true
@@ -164,12 +164,12 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit
 	}
 	}
 	if !success {
 	if !success {
 		for _, ep := range repoData.Endpoints {
 		for _, ep := range repoData.Endpoints {
-			progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s, endpoint: %s", img.Tag, p.repoInfo.FullName(), ep)
+			progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s, endpoint: %s", img.Tag, p.repoInfo.Name.Name(), ep)
 			if err = p.pullImage(ctx, img.ID, ep, localNameRef, layersDownloaded); err != nil {
 			if err = p.pullImage(ctx, img.ID, ep, localNameRef, layersDownloaded); err != nil {
 				// It's not ideal that only the last error is returned, it would be better to concatenate the errors.
 				// It's not ideal that only the last error is returned, it would be better to concatenate the errors.
 				// As the error is also given to the output stream the user will see the error.
 				// As the error is also given to the output stream the user will see the error.
 				lastErr = err
 				lastErr = err
-				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)
+				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)
 				continue
 				continue
 			}
 			}
 			success = true
 			success = true
@@ -177,7 +177,7 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit
 		}
 		}
 	}
 	}
 	if !success {
 	if !success {
-		err := fmt.Errorf("Error pulling image (%s) from %s, %v", img.Tag, p.repoInfo.FullName(), lastErr)
+		err := fmt.Errorf("Error pulling image (%s) from %s, %v", img.Tag, p.repoInfo.Name.Name(), lastErr)
 		progress.Update(p.config.ProgressOutput, stringid.TruncateID(img.ID), err.Error())
 		progress.Update(p.config.ProgressOutput, stringid.TruncateID(img.ID), err.Error())
 		return err
 		return err
 	}
 	}

+ 11 - 10
distribution/pull_v2.go

@@ -15,6 +15,7 @@ import (
 	"github.com/docker/distribution/manifest/manifestlist"
 	"github.com/docker/distribution/manifest/manifestlist"
 	"github.com/docker/distribution/manifest/schema1"
 	"github.com/docker/distribution/manifest/schema1"
 	"github.com/docker/distribution/manifest/schema2"
 	"github.com/docker/distribution/manifest/schema2"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/registry/api/errcode"
 	"github.com/docker/distribution/registry/api/errcode"
 	"github.com/docker/distribution/registry/client/auth"
 	"github.com/docker/distribution/registry/client/auth"
 	"github.com/docker/distribution/registry/client/transport"
 	"github.com/docker/distribution/registry/client/transport"
@@ -26,7 +27,7 @@ import (
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/stringid"
-	"github.com/docker/docker/reference"
+	refstore "github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/registry"
 	"github.com/opencontainers/go-digest"
 	"github.com/opencontainers/go-digest"
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
@@ -124,7 +125,7 @@ func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named) (e
 		}
 		}
 	}
 	}
 
 
-	writeStatus(ref.String(), p.config.ProgressOutput, layersDownloaded)
+	writeStatus(reference.FamiliarString(ref), p.config.ProgressOutput, layersDownloaded)
 
 
 	return nil
 	return nil
 }
 }
@@ -317,7 +318,7 @@ func (ld *v2LayerDescriptor) truncateDownloadFile() error {
 
 
 func (ld *v2LayerDescriptor) Registered(diffID layer.DiffID) {
 func (ld *v2LayerDescriptor) Registered(diffID layer.DiffID) {
 	// Cache mapping from this layer's DiffID to the blobsum
 	// Cache mapping from this layer's DiffID to the blobsum
-	ld.V2MetadataService.Add(diffID, metadata.V2Metadata{Digest: ld.digest, SourceRepository: ld.repoInfo.FullName()})
+	ld.V2MetadataService.Add(diffID, metadata.V2Metadata{Digest: ld.digest, SourceRepository: ld.repoInfo.Name.Name()})
 }
 }
 
 
 func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdated bool, err error) {
 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
 		}
 		}
 		tagOrDigest = digested.Digest().String()
 		tagOrDigest = digested.Digest().String()
 	} else {
 	} else {
-		return false, fmt.Errorf("internal error: reference has neither a tag nor a digest: %s", ref.String())
+		return false, fmt.Errorf("internal error: reference has neither a tag nor a digest: %s", reference.FamiliarString(ref))
 	}
 	}
 
 
 	if manifest == nil {
 	if manifest == nil {
@@ -371,8 +372,8 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdat
 	// the other side speaks the v2 protocol.
 	// the other side speaks the v2 protocol.
 	p.confirmedV2 = true
 	p.confirmedV2 = true
 
 
-	logrus.Debugf("Pulling ref from V2 registry: %s", ref.String())
-	progress.Message(p.config.ProgressOutput, tagOrDigest, "Pulling from "+p.repo.Named().Name())
+	logrus.Debugf("Pulling ref from V2 registry: %s", reference.FamiliarString(ref))
+	progress.Message(p.config.ProgressOutput, tagOrDigest, "Pulling from "+reference.FamiliarName(p.repo.Named()))
 
 
 	var (
 	var (
 		id             digest.Digest
 		id             digest.Digest
@@ -410,7 +411,7 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdat
 			if oldTagID == id {
 			if oldTagID == id {
 				return false, addDigestReference(p.config.ReferenceStore, ref, manifestDigest, id)
 				return false, addDigestReference(p.config.ReferenceStore, ref, manifestDigest, id)
 			}
 			}
-		} else if err != reference.ErrDoesNotExist {
+		} else if err != refstore.ErrDoesNotExist {
 			return false, err
 			return false, err
 		}
 		}
 
 
@@ -802,13 +803,13 @@ func verifySchema1Manifest(signedManifest *schema1.SignedManifest, ref reference
 	m = &signedManifest.Manifest
 	m = &signedManifest.Manifest
 
 
 	if m.SchemaVersion != 1 {
 	if m.SchemaVersion != 1 {
-		return nil, fmt.Errorf("unsupported schema version %d for %q", m.SchemaVersion, ref.String())
+		return nil, fmt.Errorf("unsupported schema version %d for %q", m.SchemaVersion, reference.FamiliarString(ref))
 	}
 	}
 	if len(m.FSLayers) != len(m.History) {
 	if len(m.FSLayers) != len(m.History) {
-		return nil, fmt.Errorf("length of history not equal to number of layers for %q", ref.String())
+		return nil, fmt.Errorf("length of history not equal to number of layers for %q", reference.FamiliarString(ref))
 	}
 	}
 	if len(m.FSLayers) == 0 {
 	if len(m.FSLayers) == 0 {
-		return nil, fmt.Errorf("no FSLayers in manifest for %q", ref.String())
+		return nil, fmt.Errorf("no FSLayers in manifest for %q", reference.FamiliarString(ref))
 	}
 	}
 	return m, nil
 	return m, nil
 }
 }

+ 2 - 2
distribution/pull_v2_test.go

@@ -9,7 +9,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/distribution/manifest/schema1"
 	"github.com/docker/distribution/manifest/schema1"
-	"github.com/docker/docker/reference"
+	"github.com/docker/distribution/reference"
 	"github.com/opencontainers/go-digest"
 	"github.com/opencontainers/go-digest"
 )
 )
 
 
@@ -113,7 +113,7 @@ func TestValidateManifest(t *testing.T) {
 	if runtime.GOOS == "windows" {
 	if runtime.GOOS == "windows" {
 		t.Skip("Needs fixing on Windows")
 		t.Skip("Needs fixing on Windows")
 	}
 	}
-	expectedDigest, err := reference.ParseNamed("repo@sha256:02fee8c3220ba806531f606525eceb83f4feb654f62b207191b1c9209188dedd")
+	expectedDigest, err := reference.ParseNormalizedNamed("repo@sha256:02fee8c3220ba806531f606525eceb83f4feb654f62b207191b1c9209188dedd")
 	if err != nil {
 	if err != nil {
 		t.Fatal("could not parse reference")
 		t.Fatal("could not parse reference")
 	}
 	}

+ 8 - 8
distribution/push.go

@@ -7,9 +7,9 @@ import (
 	"io"
 	"io"
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/pkg/progress"
-	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/registry"
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 )
 )
@@ -64,16 +64,16 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo
 		return err
 		return err
 	}
 	}
 
 
-	endpoints, err := imagePushConfig.RegistryService.LookupPushEndpoints(repoInfo.Hostname())
+	endpoints, err := imagePushConfig.RegistryService.LookupPushEndpoints(reference.Domain(repoInfo.Name))
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	progress.Messagef(imagePushConfig.ProgressOutput, "", "The push refers to a repository [%s]", repoInfo.FullName())
+	progress.Messagef(imagePushConfig.ProgressOutput, "", "The push refers to a repository [%s]", repoInfo.Name.Name())
 
 
-	associations := imagePushConfig.ReferenceStore.ReferencesByName(repoInfo)
+	associations := imagePushConfig.ReferenceStore.ReferencesByName(repoInfo.Name)
 	if len(associations) == 0 {
 	if len(associations) == 0 {
-		return fmt.Errorf("An image does not exist locally with the tag: %s", repoInfo.Name())
+		return fmt.Errorf("An image does not exist locally with the tag: %s", reference.FamiliarName(repoInfo.Name))
 	}
 	}
 
 
 	var (
 	var (
@@ -106,7 +106,7 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo
 			}
 			}
 		}
 		}
 
 
-		logrus.Debugf("Trying to push %s to %s %s", repoInfo.FullName(), endpoint.URL, endpoint.Version)
+		logrus.Debugf("Trying to push %s to %s %s", repoInfo.Name.Name(), endpoint.URL, endpoint.Version)
 
 
 		pusher, err := NewPusher(ref, endpoint, repoInfo, imagePushConfig)
 		pusher, err := NewPusher(ref, endpoint, repoInfo, imagePushConfig)
 		if err != nil {
 		if err != nil {
@@ -135,12 +135,12 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo
 			return err
 			return err
 		}
 		}
 
 
-		imagePushConfig.ImageEventLogger(ref.String(), repoInfo.Name(), "push")
+		imagePushConfig.ImageEventLogger(reference.FamiliarString(ref), reference.FamiliarName(repoInfo.Name), "push")
 		return nil
 		return nil
 	}
 	}
 
 
 	if lastErr == nil {
 	if lastErr == nil {
-		lastErr = fmt.Errorf("no endpoints found for %s", repoInfo.FullName())
+		lastErr = fmt.Errorf("no endpoints found for %s", repoInfo.Name.Name())
 	}
 	}
 	return lastErr
 	return lastErr
 }
 }

+ 5 - 5
distribution/push_v1.go

@@ -5,6 +5,7 @@ import (
 	"sync"
 	"sync"
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/registry/client/transport"
 	"github.com/docker/distribution/registry/client/transport"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/dockerversion"
 	"github.com/docker/docker/dockerversion"
@@ -14,7 +15,6 @@ import (
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/stringid"
-	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/registry"
 	"github.com/opencontainers/go-digest"
 	"github.com/opencontainers/go-digest"
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
@@ -356,8 +356,8 @@ func (p *v1Pusher) pushImageToEndpoint(ctx context.Context, endpoint string, ima
 		}
 		}
 		if topImage, isTopImage := img.(*v1TopImage); isTopImage {
 		if topImage, isTopImage := img.(*v1TopImage); isTopImage {
 			for _, tag := range tags[topImage.imageID] {
 			for _, tag := range tags[topImage.imageID] {
-				progress.Messagef(p.config.ProgressOutput, "", "Pushing tag for rev [%s] on {%s}", stringid.TruncateID(v1ID), endpoint+"repositories/"+p.repoInfo.RemoteName()+"/tags/"+tag)
-				if err := p.session.PushRegistryTag(p.repoInfo, v1ID, tag, endpoint); err != nil {
+				progress.Messagef(p.config.ProgressOutput, "", "Pushing tag for rev [%s] on {%s}", stringid.TruncateID(v1ID), endpoint+"repositories/"+reference.Path(p.repoInfo.Name)+"/tags/"+tag)
+				if err := p.session.PushRegistryTag(p.repoInfo.Name, v1ID, tag, endpoint); err != nil {
 					return err
 					return err
 				}
 				}
 			}
 			}
@@ -385,7 +385,7 @@ func (p *v1Pusher) pushRepository(ctx context.Context) error {
 
 
 	// Register all the images in a repository with the registry
 	// Register all the images in a repository with the registry
 	// If an image is not in this list it will not be associated with the repository
 	// If an image is not in this list it will not be associated with the repository
-	repoData, err := p.session.PushImageJSONIndex(p.repoInfo, imageIndex, false, nil)
+	repoData, err := p.session.PushImageJSONIndex(p.repoInfo.Name, imageIndex, false, nil)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -395,7 +395,7 @@ func (p *v1Pusher) pushRepository(ctx context.Context) error {
 			return err
 			return err
 		}
 		}
 	}
 	}
-	_, err = p.session.PushImageJSONIndex(p.repoInfo, imageIndex, true, repoData.Endpoints)
+	_, err = p.session.PushImageJSONIndex(p.repoInfo.Name, imageIndex, true, repoData.Endpoints)
 	return err
 	return err
 }
 }
 
 

+ 27 - 34
distribution/push_v2.go

@@ -15,7 +15,7 @@ import (
 	"github.com/docker/distribution"
 	"github.com/docker/distribution"
 	"github.com/docker/distribution/manifest/schema1"
 	"github.com/docker/distribution/manifest/schema1"
 	"github.com/docker/distribution/manifest/schema2"
 	"github.com/docker/distribution/manifest/schema2"
-	distreference "github.com/docker/distribution/reference"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/registry/client"
 	"github.com/docker/distribution/registry/client"
 	apitypes "github.com/docker/docker/api/types"
 	apitypes "github.com/docker/docker/api/types"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/distribution/metadata"
@@ -24,7 +24,6 @@ import (
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/stringid"
-	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/registry"
 	"github.com/opencontainers/go-digest"
 	"github.com/opencontainers/go-digest"
 )
 )
@@ -83,7 +82,7 @@ func (p *v2Pusher) pushV2Repository(ctx context.Context) (err error) {
 	if namedTagged, isNamedTagged := p.ref.(reference.NamedTagged); isNamedTagged {
 	if namedTagged, isNamedTagged := p.ref.(reference.NamedTagged); isNamedTagged {
 		imageID, err := p.config.ReferenceStore.Get(p.ref)
 		imageID, err := p.config.ReferenceStore.Get(p.ref)
 		if err != nil {
 		if err != nil {
-			return fmt.Errorf("tag does not exist: %s", p.ref.String())
+			return fmt.Errorf("tag does not exist: %s", reference.FamiliarString(p.ref))
 		}
 		}
 
 
 		return p.pushV2Tag(ctx, namedTagged, imageID)
 		return p.pushV2Tag(ctx, namedTagged, imageID)
@@ -105,23 +104,23 @@ func (p *v2Pusher) pushV2Repository(ctx context.Context) (err error) {
 	}
 	}
 
 
 	if pushed == 0 {
 	if pushed == 0 {
-		return fmt.Errorf("no tags to push for %s", p.repoInfo.Name())
+		return fmt.Errorf("no tags to push for %s", reference.FamiliarName(p.repoInfo.Name))
 	}
 	}
 
 
 	return nil
 	return nil
 }
 }
 
 
 func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, id digest.Digest) error {
 func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, id digest.Digest) error {
-	logrus.Debugf("Pushing repository: %s", ref.String())
+	logrus.Debugf("Pushing repository: %s", reference.FamiliarString(ref))
 
 
 	imgConfig, err := p.config.ImageStore.Get(id)
 	imgConfig, err := p.config.ImageStore.Get(id)
 	if err != nil {
 	if err != nil {
-		return fmt.Errorf("could not find image from tag %s: %v", ref.String(), err)
+		return fmt.Errorf("could not find image from tag %s: %v", reference.FamiliarString(ref), err)
 	}
 	}
 
 
 	rootfs, err := p.config.ImageStore.RootFSFromConfig(imgConfig)
 	rootfs, err := p.config.ImageStore.RootFSFromConfig(imgConfig)
 	if err != nil {
 	if err != nil {
-		return fmt.Errorf("unable to get rootfs for image %s: %s", ref.String(), err)
+		return fmt.Errorf("unable to get rootfs for image %s: %s", reference.FamiliarString(ref), err)
 	}
 	}
 
 
 	l, err := p.config.LayerStore.Get(rootfs.ChainID())
 	l, err := p.config.LayerStore.Get(rootfs.ChainID())
@@ -140,7 +139,7 @@ func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, id
 	descriptorTemplate := v2PushDescriptor{
 	descriptorTemplate := v2PushDescriptor{
 		v2MetadataService: p.v2MetadataService,
 		v2MetadataService: p.v2MetadataService,
 		hmacKey:           hmacKey,
 		hmacKey:           hmacKey,
-		repoInfo:          p.repoInfo,
+		repoInfo:          p.repoInfo.Name,
 		ref:               p.ref,
 		ref:               p.ref,
 		repo:              p.repo,
 		repo:              p.repo,
 		pushState:         &p.pushState,
 		pushState:         &p.pushState,
@@ -181,7 +180,7 @@ func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, id
 
 
 		logrus.Warnf("failed to upload schema2 manifest: %v - falling back to schema1", err)
 		logrus.Warnf("failed to upload schema2 manifest: %v - falling back to schema1", err)
 
 
-		manifestRef, err := distreference.WithTag(p.repo.Named(), ref.Tag())
+		manifestRef, err := reference.WithTag(p.repo.Named(), ref.Tag())
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
@@ -248,7 +247,7 @@ type v2PushDescriptor struct {
 }
 }
 
 
 func (pd *v2PushDescriptor) Key() string {
 func (pd *v2PushDescriptor) Key() string {
-	return "v2push:" + pd.ref.FullName() + " " + pd.layer.DiffID().String()
+	return "v2push:" + pd.ref.Name() + " " + pd.layer.DiffID().String()
 }
 }
 
 
 func (pd *v2PushDescriptor) ID() string {
 func (pd *v2PushDescriptor) ID() string {
@@ -304,23 +303,22 @@ func (pd *v2PushDescriptor) Upload(ctx context.Context, progressOutput progress.
 		createOpts := []distribution.BlobCreateOption{}
 		createOpts := []distribution.BlobCreateOption{}
 
 
 		if len(mountCandidate.SourceRepository) > 0 {
 		if len(mountCandidate.SourceRepository) > 0 {
-			namedRef, err := reference.WithName(mountCandidate.SourceRepository)
+			namedRef, err := reference.ParseNormalizedNamed(mountCandidate.SourceRepository)
 			if err != nil {
 			if err != nil {
-				logrus.Errorf("failed to parse source repository reference %v: %v", namedRef.String(), err)
+				logrus.Errorf("failed to parse source repository reference %v: %v", reference.FamiliarString(namedRef), err)
 				pd.v2MetadataService.Remove(mountCandidate)
 				pd.v2MetadataService.Remove(mountCandidate)
 				continue
 				continue
 			}
 			}
 
 
-			// TODO (brianbland): We need to construct a reference where the Name is
-			// only the full remote name, so clean this up when distribution has a
-			// richer reference package
-			remoteRef, err := distreference.WithName(namedRef.RemoteName())
+			// Candidates are always under same domain, create remote reference
+			// with only path to set mount from with
+			remoteRef, err := reference.WithName(reference.Path(namedRef))
 			if err != nil {
 			if err != nil {
-				logrus.Errorf("failed to make remote reference out of %q: %v", namedRef.RemoteName(), namedRef.RemoteName())
+				logrus.Errorf("failed to make remote reference out of %q: %v", reference.Path(namedRef), err)
 				continue
 				continue
 			}
 			}
 
 
-			canonicalRef, err := distreference.WithDigest(distreference.TrimNamed(remoteRef), mountCandidate.Digest)
+			canonicalRef, err := reference.WithDigest(reference.TrimNamed(remoteRef), mountCandidate.Digest)
 			if err != nil {
 			if err != nil {
 				logrus.Errorf("failed to make canonical reference: %v", err)
 				logrus.Errorf("failed to make canonical reference: %v", err)
 				continue
 				continue
@@ -347,7 +345,7 @@ func (pd *v2PushDescriptor) Upload(ctx context.Context, progressOutput progress.
 			// Cache mapping from this layer's DiffID to the blobsum
 			// Cache mapping from this layer's DiffID to the blobsum
 			if err := pd.v2MetadataService.TagAndAdd(diffID, pd.hmacKey, metadata.V2Metadata{
 			if err := pd.v2MetadataService.TagAndAdd(diffID, pd.hmacKey, metadata.V2Metadata{
 				Digest:           err.Descriptor.Digest,
 				Digest:           err.Descriptor.Digest,
-				SourceRepository: pd.repoInfo.FullName(),
+				SourceRepository: pd.repoInfo.Name(),
 			}); err != nil {
 			}); err != nil {
 				return distribution.Descriptor{}, xfer.DoNotRetry{Err: err}
 				return distribution.Descriptor{}, xfer.DoNotRetry{Err: err}
 			}
 			}
@@ -455,7 +453,7 @@ func (pd *v2PushDescriptor) uploadUsingSession(
 	// Cache mapping from this layer's DiffID to the blobsum
 	// Cache mapping from this layer's DiffID to the blobsum
 	if err := pd.v2MetadataService.TagAndAdd(diffID, pd.hmacKey, metadata.V2Metadata{
 	if err := pd.v2MetadataService.TagAndAdd(diffID, pd.hmacKey, metadata.V2Metadata{
 		Digest:           pushDigest,
 		Digest:           pushDigest,
-		SourceRepository: pd.repoInfo.FullName(),
+		SourceRepository: pd.repoInfo.Name(),
 	}); err != nil {
 	}); err != nil {
 		return distribution.Descriptor{}, xfer.DoNotRetry{Err: err}
 		return distribution.Descriptor{}, xfer.DoNotRetry{Err: err}
 	}
 	}
@@ -490,7 +488,7 @@ func (pd *v2PushDescriptor) layerAlreadyExists(
 	// filter the metadata
 	// filter the metadata
 	candidates := []metadata.V2Metadata{}
 	candidates := []metadata.V2Metadata{}
 	for _, meta := range v2Metadata {
 	for _, meta := range v2Metadata {
-		if len(meta.SourceRepository) > 0 && !checkOtherRepositories && meta.SourceRepository != pd.repoInfo.FullName() {
+		if len(meta.SourceRepository) > 0 && !checkOtherRepositories && meta.SourceRepository != pd.repoInfo.Name() {
 			continue
 			continue
 		}
 		}
 		candidates = append(candidates, meta)
 		candidates = append(candidates, meta)
@@ -521,16 +519,16 @@ func (pd *v2PushDescriptor) layerAlreadyExists(
 attempts:
 attempts:
 	for _, dgst := range layerDigests {
 	for _, dgst := range layerDigests {
 		meta := digestToMetadata[dgst]
 		meta := digestToMetadata[dgst]
-		logrus.Debugf("Checking for presence of layer %s (%s) in %s", diffID, dgst, pd.repoInfo.FullName())
+		logrus.Debugf("Checking for presence of layer %s (%s) in %s", diffID, dgst, pd.repoInfo.Name())
 		desc, err = pd.repo.Blobs(ctx).Stat(ctx, dgst)
 		desc, err = pd.repo.Blobs(ctx).Stat(ctx, dgst)
 		pd.checkedDigests[meta.Digest] = struct{}{}
 		pd.checkedDigests[meta.Digest] = struct{}{}
 		switch err {
 		switch err {
 		case nil:
 		case nil:
-			if m, ok := digestToMetadata[desc.Digest]; !ok || m.SourceRepository != pd.repoInfo.FullName() || !metadata.CheckV2MetadataHMAC(m, pd.hmacKey) {
+			if m, ok := digestToMetadata[desc.Digest]; !ok || m.SourceRepository != pd.repoInfo.Name() || !metadata.CheckV2MetadataHMAC(m, pd.hmacKey) {
 				// cache mapping from this layer's DiffID to the blobsum
 				// cache mapping from this layer's DiffID to the blobsum
 				if err := pd.v2MetadataService.TagAndAdd(diffID, pd.hmacKey, metadata.V2Metadata{
 				if err := pd.v2MetadataService.TagAndAdd(diffID, pd.hmacKey, metadata.V2Metadata{
 					Digest:           desc.Digest,
 					Digest:           desc.Digest,
-					SourceRepository: pd.repoInfo.FullName(),
+					SourceRepository: pd.repoInfo.Name(),
 				}); err != nil {
 				}); err != nil {
 					return distribution.Descriptor{}, false, xfer.DoNotRetry{Err: err}
 					return distribution.Descriptor{}, false, xfer.DoNotRetry{Err: err}
 				}
 				}
@@ -539,12 +537,12 @@ attempts:
 			exists = true
 			exists = true
 			break attempts
 			break attempts
 		case distribution.ErrBlobUnknown:
 		case distribution.ErrBlobUnknown:
-			if meta.SourceRepository == pd.repoInfo.FullName() {
+			if meta.SourceRepository == pd.repoInfo.Name() {
 				// remove the mapping to the target repository
 				// remove the mapping to the target repository
 				pd.v2MetadataService.Remove(*meta)
 				pd.v2MetadataService.Remove(*meta)
 			}
 			}
 		default:
 		default:
-			logrus.WithError(err).Debugf("Failed to check for presence of layer %s (%s) in %s", diffID, dgst, pd.repoInfo.FullName())
+			logrus.WithError(err).Debugf("Failed to check for presence of layer %s (%s) in %s", diffID, dgst, pd.repoInfo.Name())
 		}
 		}
 	}
 	}
 
 
@@ -598,11 +596,11 @@ func getRepositoryMountCandidates(
 	candidates := []metadata.V2Metadata{}
 	candidates := []metadata.V2Metadata{}
 	for _, meta := range v2Metadata {
 	for _, meta := range v2Metadata {
 		sourceRepo, err := reference.ParseNamed(meta.SourceRepository)
 		sourceRepo, err := reference.ParseNamed(meta.SourceRepository)
-		if err != nil || repoInfo.Hostname() != sourceRepo.Hostname() {
+		if err != nil || reference.Domain(repoInfo) != reference.Domain(sourceRepo) {
 			continue
 			continue
 		}
 		}
 		// target repository is not a viable candidate
 		// target repository is not a viable candidate
-		if meta.SourceRepository == repoInfo.FullName() {
+		if meta.SourceRepository == repoInfo.Name() {
 			continue
 			continue
 		}
 		}
 		candidates = append(candidates, meta)
 		candidates = append(candidates, meta)
@@ -653,7 +651,7 @@ func sortV2MetadataByLikenessAndAge(repoInfo reference.Named, hmacKey []byte, ma
 	sort.Stable(byLikeness{
 	sort.Stable(byLikeness{
 		arr:            marr,
 		arr:            marr,
 		hmacKey:        hmacKey,
 		hmacKey:        hmacKey,
-		pathComponents: getPathComponents(repoInfo.FullName()),
+		pathComponents: getPathComponents(repoInfo.Name()),
 	})
 	})
 }
 }
 
 
@@ -670,11 +668,6 @@ func numOfMatchingPathComponents(pth string, matchComponents []string) int {
 }
 }
 
 
 func getPathComponents(path string) []string {
 func getPathComponents(path string) []string {
-	// make sure to add docker.io/ prefix to the path
-	named, err := reference.ParseNamed(path)
-	if err == nil {
-		path = named.FullName()
-	}
 	return strings.Split(path, "/")
 	return strings.Split(path, "/")
 }
 }
 
 

+ 28 - 24
distribution/push_v2_test.go

@@ -8,11 +8,10 @@ import (
 	"github.com/docker/distribution"
 	"github.com/docker/distribution"
 	"github.com/docker/distribution/context"
 	"github.com/docker/distribution/context"
 	"github.com/docker/distribution/manifest/schema2"
 	"github.com/docker/distribution/manifest/schema2"
-	distreference "github.com/docker/distribution/reference"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/pkg/progress"
-	"github.com/docker/docker/reference"
 	"github.com/opencontainers/go-digest"
 	"github.com/opencontainers/go-digest"
 )
 )
 
 
@@ -43,8 +42,8 @@ func TestGetRepositoryMountCandidates(t *testing.T) {
 			name:          "one item matching",
 			name:          "one item matching",
 			targetRepo:    "busybox",
 			targetRepo:    "busybox",
 			maxCandidates: -1,
 			maxCandidates: -1,
-			metadata:      []metadata.V2Metadata{taggedMetadata("hash", "1", "hello-world")},
-			candidates:    []metadata.V2Metadata{taggedMetadata("hash", "1", "hello-world")},
+			metadata:      []metadata.V2Metadata{taggedMetadata("hash", "1", "docker.io/library/hello-world")},
+			candidates:    []metadata.V2Metadata{taggedMetadata("hash", "1", "docker.io/library/hello-world")},
 		},
 		},
 		{
 		{
 			name:          "allow missing SourceRepository",
 			name:          "allow missing SourceRepository",
@@ -63,13 +62,13 @@ func TestGetRepositoryMountCandidates(t *testing.T) {
 			maxCandidates: -1,
 			maxCandidates: -1,
 			metadata: []metadata.V2Metadata{
 			metadata: []metadata.V2Metadata{
 				{Digest: digest.Digest("1"), SourceRepository: "docker.io/user/foo"},
 				{Digest: digest.Digest("1"), SourceRepository: "docker.io/user/foo"},
-				{Digest: digest.Digest("3"), SourceRepository: "user/bar"},
-				{Digest: digest.Digest("2"), SourceRepository: "app"},
+				{Digest: digest.Digest("3"), SourceRepository: "docker.io/user/bar"},
+				{Digest: digest.Digest("2"), SourceRepository: "docker.io/library/app"},
 			},
 			},
 			candidates: []metadata.V2Metadata{
 			candidates: []metadata.V2Metadata{
-				{Digest: digest.Digest("3"), SourceRepository: "user/bar"},
+				{Digest: digest.Digest("3"), SourceRepository: "docker.io/user/bar"},
 				{Digest: digest.Digest("1"), SourceRepository: "docker.io/user/foo"},
 				{Digest: digest.Digest("1"), SourceRepository: "docker.io/user/foo"},
-				{Digest: digest.Digest("2"), SourceRepository: "app"},
+				{Digest: digest.Digest("2"), SourceRepository: "docker.io/library/app"},
 			},
 			},
 		},
 		},
 		{
 		{
@@ -78,10 +77,10 @@ func TestGetRepositoryMountCandidates(t *testing.T) {
 			targetRepo:    "127.0.0.1/foo/bar",
 			targetRepo:    "127.0.0.1/foo/bar",
 			maxCandidates: -1,
 			maxCandidates: -1,
 			metadata: []metadata.V2Metadata{
 			metadata: []metadata.V2Metadata{
-				taggedMetadata("hash", "1", "hello-world"),
+				taggedMetadata("hash", "1", "docker.io/library/hello-world"),
 				taggedMetadata("efgh", "2", "127.0.0.1/hello-world"),
 				taggedMetadata("efgh", "2", "127.0.0.1/hello-world"),
-				taggedMetadata("abcd", "3", "busybox"),
-				taggedMetadata("hash", "4", "busybox"),
+				taggedMetadata("abcd", "3", "docker.io/library/busybox"),
+				taggedMetadata("hash", "4", "docker.io/library/busybox"),
 				taggedMetadata("hash", "5", "127.0.0.1/foo"),
 				taggedMetadata("hash", "5", "127.0.0.1/foo"),
 				taggedMetadata("hash", "6", "127.0.0.1/bar"),
 				taggedMetadata("hash", "6", "127.0.0.1/bar"),
 				taggedMetadata("efgh", "7", "127.0.0.1/foo/bar"),
 				taggedMetadata("efgh", "7", "127.0.0.1/foo/bar"),
@@ -105,23 +104,25 @@ func TestGetRepositoryMountCandidates(t *testing.T) {
 			targetRepo:    "user/app",
 			targetRepo:    "user/app",
 			maxCandidates: 3,
 			maxCandidates: 3,
 			metadata: []metadata.V2Metadata{
 			metadata: []metadata.V2Metadata{
-				taggedMetadata("abcd", "1", "user/app1"),
-				taggedMetadata("abcd", "2", "user/app/base"),
-				taggedMetadata("hash", "3", "user/app"),
+				taggedMetadata("abcd", "1", "docker.io/user/app1"),
+				taggedMetadata("abcd", "2", "docker.io/user/app/base"),
+				taggedMetadata("hash", "3", "docker.io/user/app"),
 				taggedMetadata("abcd", "4", "127.0.0.1/user/app"),
 				taggedMetadata("abcd", "4", "127.0.0.1/user/app"),
-				taggedMetadata("hash", "5", "user/foo"),
-				taggedMetadata("hash", "6", "app/bar"),
+				taggedMetadata("hash", "5", "docker.io/user/foo"),
+				taggedMetadata("hash", "6", "docker.io/app/bar"),
 			},
 			},
 			candidates: []metadata.V2Metadata{
 			candidates: []metadata.V2Metadata{
 				// first by matching hash
 				// first by matching hash
-				taggedMetadata("abcd", "2", "user/app/base"),
-				taggedMetadata("abcd", "1", "user/app1"),
+				taggedMetadata("abcd", "2", "docker.io/user/app/base"),
+				taggedMetadata("abcd", "1", "docker.io/user/app1"),
 				// then by longest matching prefix
 				// then by longest matching prefix
-				taggedMetadata("hash", "3", "user/app"),
+				// "docker.io/usr/app" is excluded since candidates must
+				// be from a different repository
+				taggedMetadata("hash", "5", "docker.io/user/foo"),
 			},
 			},
 		},
 		},
 	} {
 	} {
-		repoInfo, err := reference.ParseNamed(tc.targetRepo)
+		repoInfo, err := reference.ParseNormalizedNamed(tc.targetRepo)
 		if err != nil {
 		if err != nil {
 			t.Fatalf("[%s] failed to parse reference name: %v", tc.name, err)
 			t.Fatalf("[%s] failed to parse reference name: %v", tc.name, err)
 		}
 		}
@@ -204,10 +205,13 @@ func TestLayerAlreadyExists(t *testing.T) {
 				{Digest: digest.Digest("apple"), SourceRepository: "docker.io/library/hello-world"},
 				{Digest: digest.Digest("apple"), SourceRepository: "docker.io/library/hello-world"},
 				{Digest: digest.Digest("orange"), SourceRepository: "docker.io/busybox/subapp"},
 				{Digest: digest.Digest("orange"), SourceRepository: "docker.io/busybox/subapp"},
 				{Digest: digest.Digest("pear"), SourceRepository: "docker.io/busybox"},
 				{Digest: digest.Digest("pear"), SourceRepository: "docker.io/busybox"},
-				{Digest: digest.Digest("plum"), SourceRepository: "busybox"},
+				{Digest: digest.Digest("plum"), SourceRepository: "docker.io/library/busybox"},
 				{Digest: digest.Digest("banana"), SourceRepository: "127.0.0.1/busybox"},
 				{Digest: digest.Digest("banana"), SourceRepository: "127.0.0.1/busybox"},
 			},
 			},
-			expectedRequests: []string{"plum", "pear", "apple", "orange", "banana"},
+			expectedRequests: []string{"plum", "apple", "pear", "orange", "banana"},
+			expectedRemovals: []metadata.V2Metadata{
+				{Digest: digest.Digest("plum"), SourceRepository: "docker.io/library/busybox"},
+			},
 		},
 		},
 		{
 		{
 			name:               "find existing blob",
 			name:               "find existing blob",
@@ -374,7 +378,7 @@ func TestLayerAlreadyExists(t *testing.T) {
 			},
 			},
 		},
 		},
 	} {
 	} {
-		repoInfo, err := reference.ParseNamed(tc.targetRepo)
+		repoInfo, err := reference.ParseNormalizedNamed(tc.targetRepo)
 		if err != nil {
 		if err != nil {
 			t.Fatalf("[%s] failed to parse reference name: %v", tc.name, err)
 			t.Fatalf("[%s] failed to parse reference name: %v", tc.name, err)
 		}
 		}
@@ -476,7 +480,7 @@ type mockRepo struct {
 
 
 var _ distribution.Repository = &mockRepo{}
 var _ distribution.Repository = &mockRepo{}
 
 
-func (m *mockRepo) Named() distreference.Named {
+func (m *mockRepo) Named() reference.Named {
 	m.t.Fatalf("Named() not implemented")
 	m.t.Fatalf("Named() not implemented")
 	return nil
 	return nil
 }
 }

+ 4 - 4
distribution/registry.go

@@ -8,7 +8,7 @@ import (
 
 
 	"github.com/docker/distribution"
 	"github.com/docker/distribution"
 	"github.com/docker/distribution/manifest/schema2"
 	"github.com/docker/distribution/manifest/schema2"
-	distreference "github.com/docker/distribution/reference"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/registry/client"
 	"github.com/docker/distribution/registry/client"
 	"github.com/docker/distribution/registry/client/auth"
 	"github.com/docker/distribution/registry/client/auth"
 	"github.com/docker/distribution/registry/client/transport"
 	"github.com/docker/distribution/registry/client/transport"
@@ -55,10 +55,10 @@ func init() {
 // providing timeout settings and authentication support, and also verifies the
 // providing timeout settings and authentication support, and also verifies the
 // remote API version.
 // remote API version.
 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) {
 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) {
-	repoName := repoInfo.FullName()
+	repoName := repoInfo.Name.Name()
 	// If endpoint does not support CanonicalName, use the RemoteName instead
 	// If endpoint does not support CanonicalName, use the RemoteName instead
 	if endpoint.TrimHostname {
 	if endpoint.TrimHostname {
-		repoName = repoInfo.RemoteName()
+		repoName = reference.Path(repoInfo.Name)
 	}
 	}
 
 
 	direct := &net.Dialer{
 	direct := &net.Dialer{
@@ -122,7 +122,7 @@ func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, end
 	}
 	}
 	tr := transport.NewTransport(base, modifiers...)
 	tr := transport.NewTransport(base, modifiers...)
 
 
-	repoNameRef, err := distreference.ParseNamed(repoName)
+	repoNameRef, err := reference.WithName(repoName)
 	if err != nil {
 	if err != nil {
 		return nil, foundVersion, fallbackError{
 		return nil, foundVersion, fallbackError{
 			err:         err,
 			err:         err,

+ 3 - 3
distribution/registry_unit_test.go

@@ -12,11 +12,11 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	registrytypes "github.com/docker/docker/api/types/registry"
 	registrytypes "github.com/docker/docker/api/types/registry"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/stringid"
-	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/registry"
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 )
 )
@@ -61,9 +61,9 @@ func testTokenPassThru(t *testing.T, ts *httptest.Server) {
 		TrimHostname: false,
 		TrimHostname: false,
 		TLSConfig:    nil,
 		TLSConfig:    nil,
 	}
 	}
-	n, _ := reference.ParseNamed("testremotename")
+	n, _ := reference.ParseNormalizedNamed("testremotename")
 	repoInfo := &registry.RepositoryInfo{
 	repoInfo := &registry.RepositoryInfo{
-		Named: n,
+		Name: n,
 		Index: &registrytypes.IndexInfo{
 		Index: &registrytypes.IndexInfo{
 			Name:     "testrepo",
 			Name:     "testrepo",
 			Mirrors:  nil,
 			Mirrors:  nil,

+ 5 - 5
image/tarexport/load.go

@@ -11,6 +11,7 @@ import (
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/distribution"
 	"github.com/docker/distribution"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/image/v1"
 	"github.com/docker/docker/image/v1"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/layer"
@@ -21,7 +22,6 @@ import (
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/symlink"
 	"github.com/docker/docker/pkg/symlink"
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/pkg/system"
-	"github.com/docker/docker/reference"
 	"github.com/opencontainers/go-digest"
 	"github.com/opencontainers/go-digest"
 )
 )
 
 
@@ -117,7 +117,7 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool)
 
 
 		imageRefCount = 0
 		imageRefCount = 0
 		for _, repoTag := range m.RepoTags {
 		for _, repoTag := range m.RepoTags {
-			named, err := reference.ParseNamed(repoTag)
+			named, err := reference.ParseNormalizedNamed(repoTag)
 			if err != nil {
 			if err != nil {
 				return err
 				return err
 			}
 			}
@@ -126,7 +126,7 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool)
 				return fmt.Errorf("invalid tag %q", repoTag)
 				return fmt.Errorf("invalid tag %q", repoTag)
 			}
 			}
 			l.setLoadedTag(ref, imgID.Digest(), outStream)
 			l.setLoadedTag(ref, imgID.Digest(), outStream)
-			outStream.Write([]byte(fmt.Sprintf("Loaded image: %s\n", ref)))
+			outStream.Write([]byte(fmt.Sprintf("Loaded image: %s\n", reference.FamiliarString(ref))))
 			imageRefCount++
 			imageRefCount++
 		}
 		}
 
 
@@ -201,7 +201,7 @@ func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string,
 
 
 func (l *tarexporter) setLoadedTag(ref reference.NamedTagged, imgID digest.Digest, outStream io.Writer) error {
 func (l *tarexporter) setLoadedTag(ref reference.NamedTagged, imgID digest.Digest, outStream io.Writer) error {
 	if prevID, err := l.rs.Get(ref); err == nil && prevID != imgID {
 	if prevID, err := l.rs.Get(ref); err == nil && prevID != imgID {
-		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
+		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
 	}
 	}
 
 
 	if err := l.rs.AddTag(ref, imgID, true); err != nil {
 	if err := l.rs.AddTag(ref, imgID, true); err != nil {
@@ -249,7 +249,7 @@ func (l *tarexporter) legacyLoad(tmpDir string, outStream io.Writer, progressOut
 			if !ok {
 			if !ok {
 				return fmt.Errorf("invalid target ID: %v", oldID)
 				return fmt.Errorf("invalid target ID: %v", oldID)
 			}
 			}
-			named, err := reference.WithName(name)
+			named, err := reference.ParseNormalizedNamed(name)
 			if err != nil {
 			if err != nil {
 				return err
 				return err
 			}
 			}

+ 28 - 24
image/tarexport/save.go

@@ -10,12 +10,12 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/docker/distribution"
 	"github.com/docker/distribution"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/image/v1"
 	"github.com/docker/docker/image/v1"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/pkg/system"
-	"github.com/docker/docker/reference"
 	"github.com/opencontainers/go-digest"
 	"github.com/opencontainers/go-digest"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
 )
 )
@@ -51,16 +51,12 @@ func (l *tarexporter) parseNames(names []string) (map[image.ID]*imageDescriptor,
 		}
 		}
 
 
 		if ref != nil {
 		if ref != nil {
-			var tagged reference.NamedTagged
 			if _, ok := ref.(reference.Canonical); ok {
 			if _, ok := ref.(reference.Canonical); ok {
 				return
 				return
 			}
 			}
-			var ok bool
-			if tagged, ok = ref.(reference.NamedTagged); !ok {
-				var err error
-				if tagged, err = reference.WithTag(ref, reference.DefaultTag); err != nil {
-					return
-				}
+			tagged, ok := reference.TagNameOnly(ref).(reference.NamedTagged)
+			if !ok {
+				return
 			}
 			}
 
 
 			for _, t := range imgDescr[id].refs {
 			for _, t := range imgDescr[id].refs {
@@ -73,19 +69,26 @@ func (l *tarexporter) parseNames(names []string) (map[image.ID]*imageDescriptor,
 	}
 	}
 
 
 	for _, name := range names {
 	for _, name := range names {
-		id, ref, err := reference.ParseIDOrReference(name)
+		ref, err := reference.ParseAnyReference(name)
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
-		if id != "" {
-			_, err := l.is.Get(image.IDFromDigest(id))
-			if err != nil {
-				return nil, err
+		namedRef, ok := ref.(reference.Named)
+		if !ok {
+			// Check if digest ID reference
+			if digested, ok := ref.(reference.Digested); ok {
+				id := image.IDFromDigest(digested.Digest())
+				_, err := l.is.Get(id)
+				if err != nil {
+					return nil, err
+				}
+				addAssoc(id, nil)
+				continue
 			}
 			}
-			addAssoc(image.IDFromDigest(id), nil)
-			continue
+			return nil, errors.Errorf("invalid reference: %v", name)
 		}
 		}
-		if ref.Name() == string(digest.Canonical) {
+
+		if reference.FamiliarName(namedRef) == string(digest.Canonical) {
 			imgID, err := l.is.Search(name)
 			imgID, err := l.is.Search(name)
 			if err != nil {
 			if err != nil {
 				return nil, err
 				return nil, err
@@ -93,8 +96,8 @@ func (l *tarexporter) parseNames(names []string) (map[image.ID]*imageDescriptor,
 			addAssoc(imgID, nil)
 			addAssoc(imgID, nil)
 			continue
 			continue
 		}
 		}
-		if reference.IsNameOnly(ref) {
-			assocs := l.rs.ReferencesByName(ref)
+		if reference.IsNameOnly(namedRef) {
+			assocs := l.rs.ReferencesByName(namedRef)
 			for _, assoc := range assocs {
 			for _, assoc := range assocs {
 				addAssoc(image.IDFromDigest(assoc.ID), assoc.Ref)
 				addAssoc(image.IDFromDigest(assoc.ID), assoc.Ref)
 			}
 			}
@@ -107,11 +110,11 @@ func (l *tarexporter) parseNames(names []string) (map[image.ID]*imageDescriptor,
 			}
 			}
 			continue
 			continue
 		}
 		}
-		id, err = l.rs.Get(ref)
+		id, err := l.rs.Get(namedRef)
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
-		addAssoc(image.IDFromDigest(id), ref)
+		addAssoc(image.IDFromDigest(id), namedRef)
 
 
 	}
 	}
 	return imgDescr, nil
 	return imgDescr, nil
@@ -144,11 +147,12 @@ func (s *saveSession) save(outStream io.Writer) error {
 		var layers []string
 		var layers []string
 
 
 		for _, ref := range imageDescr.refs {
 		for _, ref := range imageDescr.refs {
-			if _, ok := reposLegacy[ref.Name()]; !ok {
-				reposLegacy[ref.Name()] = make(map[string]string)
+			familiarName := reference.FamiliarName(ref)
+			if _, ok := reposLegacy[familiarName]; !ok {
+				reposLegacy[familiarName] = make(map[string]string)
 			}
 			}
-			reposLegacy[ref.Name()][ref.Tag()] = imageDescr.layers[len(imageDescr.layers)-1]
-			repoTags = append(repoTags, ref.String())
+			reposLegacy[familiarName][ref.Tag()] = imageDescr.layers[len(imageDescr.layers)-1]
+			repoTags = append(repoTags, reference.FamiliarString(ref))
 		}
 		}
 
 
 		for _, l := range imageDescr.layers {
 		for _, l := range imageDescr.layers {

+ 3 - 3
image/tarexport/tarexport.go

@@ -4,7 +4,7 @@ import (
 	"github.com/docker/distribution"
 	"github.com/docker/distribution"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/layer"
-	"github.com/docker/docker/reference"
+	refstore "github.com/docker/docker/reference"
 )
 )
 
 
 const (
 const (
@@ -26,7 +26,7 @@ type manifestItem struct {
 type tarexporter struct {
 type tarexporter struct {
 	is             image.Store
 	is             image.Store
 	ls             layer.Store
 	ls             layer.Store
-	rs             reference.Store
+	rs             refstore.Store
 	loggerImgEvent LogImageEvent
 	loggerImgEvent LogImageEvent
 }
 }
 
 
@@ -37,7 +37,7 @@ type LogImageEvent interface {
 }
 }
 
 
 // NewTarExporter returns new Exporter for tar packages
 // NewTarExporter returns new Exporter for tar packages
-func NewTarExporter(is image.Store, ls layer.Store, rs reference.Store, loggerImgEvent LogImageEvent) image.Exporter {
+func NewTarExporter(is image.Store, ls layer.Store, rs refstore.Store, loggerImgEvent LogImageEvent) image.Exporter {
 	return &tarexporter{
 	return &tarexporter{
 		is:             is,
 		is:             is,
 		ls:             ls,
 		ls:             ls,

+ 1 - 1
integration-cli/docker_cli_images_test.go

@@ -335,7 +335,7 @@ func (s *DockerSuite) TestImagesFormat(c *check.C) {
 	expected := []string{"myimage", "myimage"}
 	expected := []string{"myimage", "myimage"}
 	var names []string
 	var names []string
 	names = append(names, lines...)
 	names = append(names, lines...)
-	c.Assert(expected, checker.DeepEquals, names, check.Commentf("Expected array with truncated names: %v, got: %v", expected, names))
+	c.Assert(names, checker.DeepEquals, expected, check.Commentf("Expected array with truncated names: %v, got: %v", expected, names))
 }
 }
 
 
 // ImagesDefaultFormatAndQuiet
 // ImagesDefaultFormatAndQuiet

+ 10 - 5
migrate/v1/migratev1.go

@@ -14,12 +14,13 @@ import (
 	"encoding/json"
 	"encoding/json"
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/image"
 	imagev1 "github.com/docker/docker/image/v1"
 	imagev1 "github.com/docker/docker/image/v1"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/ioutils"
-	"github.com/docker/docker/reference"
+	refstore "github.com/docker/docker/reference"
 	"github.com/opencontainers/go-digest"
 	"github.com/opencontainers/go-digest"
 )
 )
 
 
@@ -56,7 +57,7 @@ var (
 
 
 // Migrate takes an old graph directory and transforms the metadata into the
 // Migrate takes an old graph directory and transforms the metadata into the
 // new format.
 // new format.
-func Migrate(root, driverName string, ls layer.Store, is image.Store, rs reference.Store, ms metadata.Store) error {
+func Migrate(root, driverName string, ls layer.Store, is image.Store, rs refstore.Store, ms metadata.Store) error {
 	graphDir := filepath.Join(root, graphDirName)
 	graphDir := filepath.Join(root, graphDirName)
 	if _, err := os.Lstat(graphDir); os.IsNotExist(err) {
 	if _, err := os.Lstat(graphDir); os.IsNotExist(err) {
 		return nil
 		return nil
@@ -322,11 +323,15 @@ func migrateRefs(root, driverName string, rs refAdder, mappings map[string]image
 	for name, repo := range repos.Repositories {
 	for name, repo := range repos.Repositories {
 		for tag, id := range repo {
 		for tag, id := range repo {
 			if strongID, exists := mappings[id]; exists {
 			if strongID, exists := mappings[id]; exists {
-				ref, err := reference.WithName(name)
+				ref, err := reference.ParseNormalizedNamed(name)
 				if err != nil {
 				if err != nil {
 					logrus.Errorf("migrate tags: invalid name %q, %q", name, err)
 					logrus.Errorf("migrate tags: invalid name %q, %q", name, err)
 					continue
 					continue
 				}
 				}
+				if !reference.IsNameOnly(ref) {
+					logrus.Errorf("migrate tags: invalid name %q, unexpected tag or digest", name)
+					continue
+				}
 				if dgst, err := digest.Parse(tag); err == nil {
 				if dgst, err := digest.Parse(tag); err == nil {
 					canonical, err := reference.WithDigest(reference.TrimNamed(ref), dgst)
 					canonical, err := reference.WithDigest(reference.TrimNamed(ref), dgst)
 					if err != nil {
 					if err != nil {
@@ -334,7 +339,7 @@ func migrateRefs(root, driverName string, rs refAdder, mappings map[string]image
 						continue
 						continue
 					}
 					}
 					if err := rs.AddDigest(canonical, strongID.Digest(), false); err != nil {
 					if err := rs.AddDigest(canonical, strongID.Digest(), false); err != nil {
-						logrus.Errorf("can't migrate digest %q for %q, err: %q", ref.String(), strongID, err)
+						logrus.Errorf("can't migrate digest %q for %q, err: %q", reference.FamiliarString(ref), strongID, err)
 					}
 					}
 				} else {
 				} else {
 					tagRef, err := reference.WithTag(ref, tag)
 					tagRef, err := reference.WithTag(ref, tag)
@@ -343,7 +348,7 @@ func migrateRefs(root, driverName string, rs refAdder, mappings map[string]image
 						continue
 						continue
 					}
 					}
 					if err := rs.AddTag(tagRef, strongID.Digest(), false); err != nil {
 					if err := rs.AddTag(tagRef, strongID.Digest(), false); err != nil {
-						logrus.Errorf("can't migrate tag %q for %q, err: %q", ref.String(), strongID, err)
+						logrus.Errorf("can't migrate tag %q for %q, err: %q", reference.FamiliarString(ref), strongID, err)
 					}
 					}
 				}
 				}
 				logrus.Infof("migrated tag %s:%s to point to %s", name, tag, strongID)
 				logrus.Infof("migrated tag %s:%s to point to %s", name, tag, strongID)

+ 4 - 4
migrate/v1/migratev1_test.go

@@ -13,10 +13,10 @@ import (
 	"runtime"
 	"runtime"
 	"testing"
 	"testing"
 
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/layer"
-	"github.com/docker/docker/reference"
 	"github.com/opencontainers/go-digest"
 	"github.com/opencontainers/go-digest"
 )
 )
 
 
@@ -40,9 +40,9 @@ func TestMigrateRefs(t *testing.T) {
 	}
 	}
 
 
 	expected := map[string]string{
 	expected := map[string]string{
-		"busybox:latest": "sha256:fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9",
-		"busybox@sha256:16a2a52884c2a9481ed267c2d46483eac7693b813a63132368ab098a71303f8a": "sha256:fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9",
-		"registry:2": "sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae",
+		"docker.io/library/busybox:latest":                                                                  "sha256:fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9",
+		"docker.io/library/busybox@sha256:16a2a52884c2a9481ed267c2d46483eac7693b813a63132368ab098a71303f8a": "sha256:fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9",
+		"docker.io/library/registry:2":                                                                      "sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae",
 	}
 	}
 
 
 	if !reflect.DeepEqual(expected, ta.refs) {
 	if !reflect.DeepEqual(expected, ta.refs) {

+ 13 - 13
plugin/backend_linux.go

@@ -18,6 +18,7 @@ import (
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/distribution/manifest/schema2"
 	"github.com/docker/distribution/manifest/schema2"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/distribution"
 	"github.com/docker/docker/distribution"
@@ -30,7 +31,7 @@ import (
 	"github.com/docker/docker/pkg/pools"
 	"github.com/docker/docker/pkg/pools"
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/plugin/v2"
 	"github.com/docker/docker/plugin/v2"
-	"github.com/docker/docker/reference"
+	refstore "github.com/docker/docker/reference"
 	"github.com/opencontainers/go-digest"
 	"github.com/opencontainers/go-digest"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
@@ -232,11 +233,11 @@ func (pm *Manager) Upgrade(ctx context.Context, ref reference.Named, name string
 	defer pm.muGC.RUnlock()
 	defer pm.muGC.RUnlock()
 
 
 	// revalidate because Pull is public
 	// revalidate because Pull is public
-	nameref, err := reference.ParseNamed(name)
+	nameref, err := reference.ParseNormalizedNamed(name)
 	if err != nil {
 	if err != nil {
 		return errors.Wrapf(err, "failed to parse %q", name)
 		return errors.Wrapf(err, "failed to parse %q", name)
 	}
 	}
-	name = reference.WithDefaultTag(nameref).String()
+	name = reference.FamiliarString(reference.TagNameOnly(nameref))
 
 
 	tmpRootFSDir, err := ioutil.TempDir(pm.tmpDir(), ".rootfs")
 	tmpRootFSDir, err := ioutil.TempDir(pm.tmpDir(), ".rootfs")
 	defer os.RemoveAll(tmpRootFSDir)
 	defer os.RemoveAll(tmpRootFSDir)
@@ -277,11 +278,11 @@ func (pm *Manager) Pull(ctx context.Context, ref reference.Named, name string, m
 	defer pm.muGC.RUnlock()
 	defer pm.muGC.RUnlock()
 
 
 	// revalidate because Pull is public
 	// revalidate because Pull is public
-	nameref, err := reference.ParseNamed(name)
+	nameref, err := reference.ParseNormalizedNamed(name)
 	if err != nil {
 	if err != nil {
 		return errors.Wrapf(err, "failed to parse %q", name)
 		return errors.Wrapf(err, "failed to parse %q", name)
 	}
 	}
-	name = reference.WithDefaultTag(nameref).String()
+	name = reference.FamiliarString(reference.TagNameOnly(nameref))
 
 
 	if err := pm.config.Store.validateName(name); err != nil {
 	if err := pm.config.Store.validateName(name); err != nil {
 		return err
 		return err
@@ -370,7 +371,7 @@ func (pm *Manager) Push(ctx context.Context, name string, metaHeader http.Header
 		return err
 		return err
 	}
 	}
 
 
-	ref, err := reference.ParseNamed(p.Name())
+	ref, err := reference.ParseNormalizedNamed(p.Name())
 	if err != nil {
 	if err != nil {
 		return errors.Wrapf(err, "plugin has invalid name %v for push", p.Name())
 		return errors.Wrapf(err, "plugin has invalid name %v for push", p.Name())
 	}
 	}
@@ -448,8 +449,8 @@ func (r *pluginReference) References(id digest.Digest) []reference.Named {
 	return []reference.Named{r.name}
 	return []reference.Named{r.name}
 }
 }
 
 
-func (r *pluginReference) ReferencesByName(ref reference.Named) []reference.Association {
-	return []reference.Association{
+func (r *pluginReference) ReferencesByName(ref reference.Named) []refstore.Association {
+	return []refstore.Association{
 		{
 		{
 			Ref: r.name,
 			Ref: r.name,
 			ID:  r.pluginID,
 			ID:  r.pluginID,
@@ -459,7 +460,7 @@ func (r *pluginReference) ReferencesByName(ref reference.Named) []reference.Asso
 
 
 func (r *pluginReference) Get(ref reference.Named) (digest.Digest, error) {
 func (r *pluginReference) Get(ref reference.Named) (digest.Digest, error) {
 	if r.name.String() != ref.String() {
 	if r.name.String() != ref.String() {
-		return digest.Digest(""), reference.ErrDoesNotExist
+		return digest.Digest(""), refstore.ErrDoesNotExist
 	}
 	}
 	return r.pluginID, nil
 	return r.pluginID, nil
 }
 }
@@ -664,15 +665,14 @@ func (pm *Manager) CreateFromContext(ctx context.Context, tarCtx io.ReadCloser,
 	pm.muGC.RLock()
 	pm.muGC.RLock()
 	defer pm.muGC.RUnlock()
 	defer pm.muGC.RUnlock()
 
 
-	ref, err := reference.ParseNamed(options.RepoName)
+	ref, err := reference.ParseNormalizedNamed(options.RepoName)
 	if err != nil {
 	if err != nil {
 		return errors.Wrapf(err, "failed to parse reference %v", options.RepoName)
 		return errors.Wrapf(err, "failed to parse reference %v", options.RepoName)
 	}
 	}
 	if _, ok := ref.(reference.Canonical); ok {
 	if _, ok := ref.(reference.Canonical); ok {
 		return errors.Errorf("canonical references are not permitted")
 		return errors.Errorf("canonical references are not permitted")
 	}
 	}
-	taggedRef := reference.WithDefaultTag(ref)
-	name := taggedRef.String()
+	name := reference.FamiliarString(reference.TagNameOnly(ref))
 
 
 	if err := pm.config.Store.validateName(name); err != nil { // fast check, real check is in createPlugin()
 	if err := pm.config.Store.validateName(name); err != nil { // fast check, real check is in createPlugin()
 		return err
 		return err
@@ -754,7 +754,7 @@ func (pm *Manager) CreateFromContext(ctx context.Context, tarCtx io.ReadCloser,
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	p.PluginObj.PluginReference = taggedRef.String()
+	p.PluginObj.PluginReference = name
 
 
 	pm.config.LogPluginEvent(p.PluginObj.ID, name, "create")
 	pm.config.LogPluginEvent(p.PluginObj.ID, name, "create")
 
 

+ 1 - 1
plugin/backend_unsupported.go

@@ -7,9 +7,9 @@ import (
 	"io"
 	"io"
 	"net/http"
 	"net/http"
 
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/filters"
-	"github.com/docker/docker/reference"
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 )
 )
 
 

+ 5 - 5
plugin/store.go

@@ -5,10 +5,10 @@ import (
 	"strings"
 	"strings"
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/pkg/plugingetter"
 	"github.com/docker/docker/pkg/plugingetter"
 	"github.com/docker/docker/pkg/plugins"
 	"github.com/docker/docker/pkg/plugins"
 	"github.com/docker/docker/plugin/v2"
 	"github.com/docker/docker/plugin/v2"
-	"github.com/docker/docker/reference"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
 )
 )
 
 
@@ -230,19 +230,19 @@ func (ps *Store) resolvePluginID(idOrName string) (string, error) {
 		return idOrName, nil
 		return idOrName, nil
 	}
 	}
 
 
-	ref, err := reference.ParseNamed(idOrName)
+	ref, err := reference.ParseNormalizedNamed(idOrName)
 	if err != nil {
 	if err != nil {
 		return "", errors.WithStack(ErrNotFound(idOrName))
 		return "", errors.WithStack(ErrNotFound(idOrName))
 	}
 	}
 	if _, ok := ref.(reference.Canonical); ok {
 	if _, ok := ref.(reference.Canonical); ok {
-		logrus.Warnf("canonical references cannot be resolved: %v", ref.String())
+		logrus.Warnf("canonical references cannot be resolved: %v", reference.FamiliarString(ref))
 		return "", errors.WithStack(ErrNotFound(idOrName))
 		return "", errors.WithStack(ErrNotFound(idOrName))
 	}
 	}
 
 
-	fullRef := reference.WithDefaultTag(ref)
+	ref = reference.TagNameOnly(ref)
 
 
 	for _, p := range ps.plugins {
 	for _, p := range ps.plugins {
-		if p.PluginObj.Name == fullRef.String() {
+		if p.PluginObj.Name == reference.FamiliarString(ref) {
 			return p.PluginObj.ID, nil
 			return p.PluginObj.ID, nil
 		}
 		}
 	}
 	}

+ 0 - 194
reference/reference.go

@@ -1,194 +0,0 @@
-package reference
-
-import (
-	"fmt"
-
-	distreference "github.com/docker/distribution/reference"
-	"github.com/docker/docker/pkg/stringid"
-	"github.com/opencontainers/go-digest"
-	"github.com/pkg/errors"
-)
-
-const (
-	// DefaultTag defines the default tag used when performing images related actions and no tag or digest is specified
-	DefaultTag = "latest"
-	// DefaultHostname is the default built-in hostname
-	DefaultHostname = "docker.io"
-	// LegacyDefaultHostname is automatically converted to DefaultHostname
-	LegacyDefaultHostname = "index.docker.io"
-	// DefaultRepoPrefix is the prefix used for default repositories in default host
-	DefaultRepoPrefix = "library/"
-)
-
-// Named is an object with a full name
-type Named interface {
-	// Name returns normalized repository name, like "ubuntu".
-	Name() string
-	// String returns full reference, like "ubuntu@sha256:abcdef..."
-	String() string
-	// FullName returns full repository name with hostname, like "docker.io/library/ubuntu"
-	FullName() string
-	// Hostname returns hostname for the reference, like "docker.io"
-	Hostname() string
-	// RemoteName returns the repository component of the full name, like "library/ubuntu"
-	RemoteName() string
-}
-
-// NamedTagged is an object including a name and tag.
-type NamedTagged interface {
-	Named
-	Tag() string
-}
-
-// Canonical reference is an object with a fully unique
-// name including a name with hostname and digest
-type Canonical interface {
-	Named
-	Digest() digest.Digest
-}
-
-// ParseNamed parses s and returns a syntactically valid reference implementing
-// the Named interface. The reference must have a name, otherwise an error is
-// returned.
-// If an error was encountered it is returned, along with a nil Reference.
-func ParseNamed(s string) (Named, error) {
-	named, err := distreference.ParseNormalizedNamed(s)
-	if err != nil {
-		return nil, errors.Wrapf(err, "failed to parse reference %q", s)
-	}
-	if err := validateName(distreference.FamiliarName(named)); err != nil {
-		return nil, err
-	}
-
-	// Ensure returned reference cannot have tag and digest
-	if canonical, isCanonical := named.(distreference.Canonical); isCanonical {
-		r, err := distreference.WithDigest(distreference.TrimNamed(named), canonical.Digest())
-		if err != nil {
-			return nil, err
-		}
-		return &canonicalRef{namedRef{r}}, nil
-	}
-	if tagged, isTagged := named.(distreference.NamedTagged); isTagged {
-		r, err := distreference.WithTag(distreference.TrimNamed(named), tagged.Tag())
-		if err != nil {
-			return nil, err
-		}
-		return &taggedRef{namedRef{r}}, nil
-	}
-
-	return &namedRef{named}, nil
-}
-
-// TrimNamed removes any tag or digest from the named reference
-func TrimNamed(ref Named) Named {
-	return &namedRef{distreference.TrimNamed(ref)}
-}
-
-// WithName returns a named object representing the given string. If the input
-// is invalid ErrReferenceInvalidFormat will be returned.
-func WithName(name string) (Named, error) {
-	r, err := distreference.ParseNormalizedNamed(name)
-	if err != nil {
-		return nil, err
-	}
-	if err := validateName(distreference.FamiliarName(r)); err != nil {
-		return nil, err
-	}
-	if !distreference.IsNameOnly(r) {
-		return nil, distreference.ErrReferenceInvalidFormat
-	}
-	return &namedRef{r}, nil
-}
-
-// WithTag combines the name from "name" and the tag from "tag" to form a
-// reference incorporating both the name and the tag.
-func WithTag(name Named, tag string) (NamedTagged, error) {
-	r, err := distreference.WithTag(name, tag)
-	if err != nil {
-		return nil, err
-	}
-	return &taggedRef{namedRef{r}}, nil
-}
-
-// WithDigest combines the name from "name" and the digest from "digest" to form
-// a reference incorporating both the name and the digest.
-func WithDigest(name Named, digest digest.Digest) (Canonical, error) {
-	r, err := distreference.WithDigest(name, digest)
-	if err != nil {
-		return nil, err
-	}
-	return &canonicalRef{namedRef{r}}, nil
-}
-
-type namedRef struct {
-	distreference.Named
-}
-type taggedRef struct {
-	namedRef
-}
-type canonicalRef struct {
-	namedRef
-}
-
-func (r *namedRef) Name() string {
-	return distreference.FamiliarName(r.Named)
-}
-
-func (r *namedRef) String() string {
-	return distreference.FamiliarString(r.Named)
-}
-
-func (r *namedRef) FullName() string {
-	return r.Named.Name()
-}
-func (r *namedRef) Hostname() string {
-	return distreference.Domain(r.Named)
-}
-func (r *namedRef) RemoteName() string {
-	return distreference.Path(r.Named)
-}
-func (r *taggedRef) Tag() string {
-	return r.namedRef.Named.(distreference.NamedTagged).Tag()
-}
-func (r *canonicalRef) Digest() digest.Digest {
-	return r.namedRef.Named.(distreference.Canonical).Digest()
-}
-
-// WithDefaultTag adds a default tag to a reference if it only has a repo name.
-func WithDefaultTag(ref Named) Named {
-	if IsNameOnly(ref) {
-		ref, _ = WithTag(ref, DefaultTag)
-	}
-	return ref
-}
-
-// IsNameOnly returns true if reference only contains a repo name.
-func IsNameOnly(ref Named) bool {
-	if _, ok := ref.(NamedTagged); ok {
-		return false
-	}
-	if _, ok := ref.(Canonical); ok {
-		return false
-	}
-	return true
-}
-
-// ParseIDOrReference parses string for an image ID or a reference. ID can be
-// without a default prefix.
-func ParseIDOrReference(idOrRef string) (digest.Digest, Named, error) {
-	if err := stringid.ValidateID(idOrRef); err == nil {
-		idOrRef = "sha256:" + idOrRef
-	}
-	if dgst, err := digest.Parse(idOrRef); err == nil {
-		return dgst, nil, nil
-	}
-	ref, err := ParseNamed(idOrRef)
-	return "", ref, err
-}
-
-func validateName(name string) error {
-	if err := stringid.ValidateID(name); err == nil {
-		return fmt.Errorf("Invalid repository name (%s), cannot specify 64-byte hexadecimal strings", name)
-	}
-	return nil
-}

+ 0 - 275
reference/reference_test.go

@@ -1,275 +0,0 @@
-package reference
-
-import (
-	"testing"
-
-	"github.com/opencontainers/go-digest"
-)
-
-func TestValidateReferenceName(t *testing.T) {
-	validRepoNames := []string{
-		"docker/docker",
-		"library/debian",
-		"debian",
-		"docker.io/docker/docker",
-		"docker.io/library/debian",
-		"docker.io/debian",
-		"index.docker.io/docker/docker",
-		"index.docker.io/library/debian",
-		"index.docker.io/debian",
-		"127.0.0.1:5000/docker/docker",
-		"127.0.0.1:5000/library/debian",
-		"127.0.0.1:5000/debian",
-		"thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev",
-	}
-	invalidRepoNames := []string{
-		"https://github.com/docker/docker",
-		"docker/Docker",
-		"-docker",
-		"-docker/docker",
-		"-docker.io/docker/docker",
-		"docker///docker",
-		"docker.io/docker/Docker",
-		"docker.io/docker///docker",
-		"1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
-		"docker.io/1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
-	}
-
-	for _, name := range invalidRepoNames {
-		_, err := ParseNamed(name)
-		if err == nil {
-			t.Fatalf("Expected invalid repo name for %q", name)
-		}
-	}
-
-	for _, name := range validRepoNames {
-		_, err := ParseNamed(name)
-		if err != nil {
-			t.Fatalf("Error parsing repo name %s, got: %q", name, err)
-		}
-	}
-}
-
-func TestValidateRemoteName(t *testing.T) {
-	validRepositoryNames := []string{
-		// Sanity check.
-		"docker/docker",
-
-		// Allow 64-character non-hexadecimal names (hexadecimal names are forbidden).
-		"thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev",
-
-		// Allow embedded hyphens.
-		"docker-rules/docker",
-
-		// Allow multiple hyphens as well.
-		"docker---rules/docker",
-
-		//Username doc and image name docker being tested.
-		"doc/docker",
-
-		// single character names are now allowed.
-		"d/docker",
-		"jess/t",
-
-		// Consecutive underscores.
-		"dock__er/docker",
-	}
-	for _, repositoryName := range validRepositoryNames {
-		_, err := ParseNamed(repositoryName)
-		if err != nil {
-			t.Errorf("Repository name should be valid: %v. Error: %v", repositoryName, err)
-		}
-	}
-
-	invalidRepositoryNames := []string{
-		// Disallow capital letters.
-		"docker/Docker",
-
-		// Only allow one slash.
-		"docker///docker",
-
-		// Disallow 64-character hexadecimal.
-		"1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
-
-		// Disallow leading and trailing hyphens in namespace.
-		"-docker/docker",
-		"docker-/docker",
-		"-docker-/docker",
-
-		// Don't allow underscores everywhere (as opposed to hyphens).
-		"____/____",
-
-		"_docker/_docker",
-
-		// Disallow consecutive periods.
-		"dock..er/docker",
-		"dock_.er/docker",
-		"dock-.er/docker",
-
-		// No repository.
-		"docker/",
-
-		//namespace too long
-		"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",
-	}
-	for _, repositoryName := range invalidRepositoryNames {
-		if _, err := ParseNamed(repositoryName); err == nil {
-			t.Errorf("Repository name should be invalid: %v", repositoryName)
-		}
-	}
-}
-
-func TestParseRepositoryInfo(t *testing.T) {
-	type tcase struct {
-		RemoteName, NormalizedName, FullName, AmbiguousName, Hostname string
-	}
-
-	tcases := []tcase{
-		{
-			RemoteName:     "fooo/bar",
-			NormalizedName: "fooo/bar",
-			FullName:       "docker.io/fooo/bar",
-			AmbiguousName:  "index.docker.io/fooo/bar",
-			Hostname:       "docker.io",
-		},
-		{
-			RemoteName:     "library/ubuntu",
-			NormalizedName: "ubuntu",
-			FullName:       "docker.io/library/ubuntu",
-			AmbiguousName:  "library/ubuntu",
-			Hostname:       "docker.io",
-		},
-		{
-			RemoteName:     "nonlibrary/ubuntu",
-			NormalizedName: "nonlibrary/ubuntu",
-			FullName:       "docker.io/nonlibrary/ubuntu",
-			AmbiguousName:  "",
-			Hostname:       "docker.io",
-		},
-		{
-			RemoteName:     "other/library",
-			NormalizedName: "other/library",
-			FullName:       "docker.io/other/library",
-			AmbiguousName:  "",
-			Hostname:       "docker.io",
-		},
-		{
-			RemoteName:     "private/moonbase",
-			NormalizedName: "127.0.0.1:8000/private/moonbase",
-			FullName:       "127.0.0.1:8000/private/moonbase",
-			AmbiguousName:  "",
-			Hostname:       "127.0.0.1:8000",
-		},
-		{
-			RemoteName:     "privatebase",
-			NormalizedName: "127.0.0.1:8000/privatebase",
-			FullName:       "127.0.0.1:8000/privatebase",
-			AmbiguousName:  "",
-			Hostname:       "127.0.0.1:8000",
-		},
-		{
-			RemoteName:     "private/moonbase",
-			NormalizedName: "example.com/private/moonbase",
-			FullName:       "example.com/private/moonbase",
-			AmbiguousName:  "",
-			Hostname:       "example.com",
-		},
-		{
-			RemoteName:     "privatebase",
-			NormalizedName: "example.com/privatebase",
-			FullName:       "example.com/privatebase",
-			AmbiguousName:  "",
-			Hostname:       "example.com",
-		},
-		{
-			RemoteName:     "private/moonbase",
-			NormalizedName: "example.com:8000/private/moonbase",
-			FullName:       "example.com:8000/private/moonbase",
-			AmbiguousName:  "",
-			Hostname:       "example.com:8000",
-		},
-		{
-			RemoteName:     "privatebasee",
-			NormalizedName: "example.com:8000/privatebasee",
-			FullName:       "example.com:8000/privatebasee",
-			AmbiguousName:  "",
-			Hostname:       "example.com:8000",
-		},
-		{
-			RemoteName:     "library/ubuntu-12.04-base",
-			NormalizedName: "ubuntu-12.04-base",
-			FullName:       "docker.io/library/ubuntu-12.04-base",
-			AmbiguousName:  "index.docker.io/library/ubuntu-12.04-base",
-			Hostname:       "docker.io",
-		},
-	}
-
-	for _, tcase := range tcases {
-		refStrings := []string{tcase.NormalizedName, tcase.FullName}
-		if tcase.AmbiguousName != "" {
-			refStrings = append(refStrings, tcase.AmbiguousName)
-		}
-
-		var refs []Named
-		for _, r := range refStrings {
-			named, err := ParseNamed(r)
-			if err != nil {
-				t.Fatal(err)
-			}
-			refs = append(refs, named)
-			named, err = WithName(r)
-			if err != nil {
-				t.Fatal(err)
-			}
-			refs = append(refs, named)
-		}
-
-		for _, r := range refs {
-			if expected, actual := tcase.NormalizedName, r.Name(); expected != actual {
-				t.Fatalf("Invalid normalized reference for %q. Expected %q, got %q", r, expected, actual)
-			}
-			if expected, actual := tcase.FullName, r.FullName(); expected != actual {
-				t.Fatalf("Invalid fullName for %q. Expected %q, got %q", r, expected, actual)
-			}
-			if expected, actual := tcase.Hostname, r.Hostname(); expected != actual {
-				t.Fatalf("Invalid hostname for %q. Expected %q, got %q", r, expected, actual)
-			}
-			if expected, actual := tcase.RemoteName, r.RemoteName(); expected != actual {
-				t.Fatalf("Invalid remoteName for %q. Expected %q, got %q", r, expected, actual)
-			}
-
-		}
-	}
-}
-
-func TestParseReferenceWithTagAndDigest(t *testing.T) {
-	ref, err := ParseNamed("busybox:latest@sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa")
-	if err != nil {
-		t.Fatal(err)
-	}
-	if _, isTagged := ref.(NamedTagged); isTagged {
-		t.Fatalf("Reference from %q should not support tag", ref)
-	}
-	if _, isCanonical := ref.(Canonical); !isCanonical {
-		t.Fatalf("Reference from %q should not support digest", ref)
-	}
-	if expected, actual := "busybox@sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa", ref.String(); actual != expected {
-		t.Fatalf("Invalid parsed reference for %q: expected %q, got %q", ref, expected, actual)
-	}
-}
-
-func TestInvalidReferenceComponents(t *testing.T) {
-	if _, err := WithName("-foo"); err == nil {
-		t.Fatal("Expected WithName to detect invalid name")
-	}
-	ref, err := WithName("busybox")
-	if err != nil {
-		t.Fatal(err)
-	}
-	if _, err := WithTag(ref, "-foo"); err == nil {
-		t.Fatal("Expected WithName to detect invalid tag")
-	}
-	if _, err := WithDigest(ref, digest.Digest("foo")); err == nil {
-		t.Fatal("Expected WithName to detect invalid digest")
-	}
-}

+ 71 - 46
reference/store.go

@@ -9,6 +9,7 @@ import (
 	"sort"
 	"sort"
 	"sync"
 	"sync"
 
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/opencontainers/go-digest"
 	"github.com/opencontainers/go-digest"
 )
 )
@@ -21,18 +22,18 @@ var (
 
 
 // An Association is a tuple associating a reference with an image ID.
 // An Association is a tuple associating a reference with an image ID.
 type Association struct {
 type Association struct {
-	Ref Named
+	Ref reference.Named
 	ID  digest.Digest
 	ID  digest.Digest
 }
 }
 
 
 // Store provides the set of methods which can operate on a tag store.
 // Store provides the set of methods which can operate on a tag store.
 type Store interface {
 type Store interface {
-	References(id digest.Digest) []Named
-	ReferencesByName(ref Named) []Association
-	AddTag(ref Named, id digest.Digest, force bool) error
-	AddDigest(ref Canonical, id digest.Digest, force bool) error
-	Delete(ref Named) (bool, error)
-	Get(ref Named) (digest.Digest, error)
+	References(id digest.Digest) []reference.Named
+	ReferencesByName(ref reference.Named) []Association
+	AddTag(ref reference.Named, id digest.Digest, force bool) error
+	AddDigest(ref reference.Canonical, id digest.Digest, force bool) error
+	Delete(ref reference.Named) (bool, error)
+	Get(ref reference.Named) (digest.Digest, error)
 }
 }
 
 
 type store struct {
 type store struct {
@@ -44,24 +45,28 @@ type store struct {
 	Repositories map[string]repository
 	Repositories map[string]repository
 	// referencesByIDCache is a cache of references indexed by ID, to speed
 	// referencesByIDCache is a cache of references indexed by ID, to speed
 	// up References.
 	// up References.
-	referencesByIDCache map[digest.Digest]map[string]Named
+	referencesByIDCache map[digest.Digest]map[string]reference.Named
 }
 }
 
 
 // Repository maps tags to digests. The key is a stringified Reference,
 // Repository maps tags to digests. The key is a stringified Reference,
 // including the repository name.
 // including the repository name.
 type repository map[string]digest.Digest
 type repository map[string]digest.Digest
 
 
-type lexicalRefs []Named
+type lexicalRefs []reference.Named
 
 
-func (a lexicalRefs) Len() int           { return len(a) }
-func (a lexicalRefs) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
-func (a lexicalRefs) Less(i, j int) bool { return a[i].String() < a[j].String() }
+func (a lexicalRefs) Len() int      { return len(a) }
+func (a lexicalRefs) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a lexicalRefs) Less(i, j int) bool {
+	return a[i].String() < a[j].String()
+}
 
 
 type lexicalAssociations []Association
 type lexicalAssociations []Association
 
 
-func (a lexicalAssociations) Len() int           { return len(a) }
-func (a lexicalAssociations) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
-func (a lexicalAssociations) Less(i, j int) bool { return a[i].Ref.String() < a[j].Ref.String() }
+func (a lexicalAssociations) Len() int      { return len(a) }
+func (a lexicalAssociations) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a lexicalAssociations) Less(i, j int) bool {
+	return a[i].Ref.String() < a[j].Ref.String()
+}
 
 
 // NewReferenceStore creates a new reference store, tied to a file path where
 // NewReferenceStore creates a new reference store, tied to a file path where
 // the set of references are serialized in JSON format.
 // the set of references are serialized in JSON format.
@@ -74,7 +79,7 @@ func NewReferenceStore(jsonPath string) (Store, error) {
 	store := &store{
 	store := &store{
 		jsonPath:            abspath,
 		jsonPath:            abspath,
 		Repositories:        make(map[string]repository),
 		Repositories:        make(map[string]repository),
-		referencesByIDCache: make(map[digest.Digest]map[string]Named),
+		referencesByIDCache: make(map[digest.Digest]map[string]reference.Named),
 	}
 	}
 	// Load the json file if it exists, otherwise create it.
 	// Load the json file if it exists, otherwise create it.
 	if err := store.reload(); os.IsNotExist(err) {
 	if err := store.reload(); os.IsNotExist(err) {
@@ -89,43 +94,45 @@ func NewReferenceStore(jsonPath string) (Store, error) {
 
 
 // AddTag adds a tag reference to the store. If force is set to true, existing
 // AddTag adds a tag reference to the store. If force is set to true, existing
 // references can be overwritten. This only works for tags, not digests.
 // references can be overwritten. This only works for tags, not digests.
-func (store *store) AddTag(ref Named, id digest.Digest, force bool) error {
-	if _, isCanonical := ref.(Canonical); isCanonical {
+func (store *store) AddTag(ref reference.Named, id digest.Digest, force bool) error {
+	if _, isCanonical := ref.(reference.Canonical); isCanonical {
 		return errors.New("refusing to create a tag with a digest reference")
 		return errors.New("refusing to create a tag with a digest reference")
 	}
 	}
-	return store.addReference(WithDefaultTag(ref), id, force)
+	return store.addReference(reference.TagNameOnly(ref), id, force)
 }
 }
 
 
 // AddDigest adds a digest reference to the store.
 // AddDigest adds a digest reference to the store.
-func (store *store) AddDigest(ref Canonical, id digest.Digest, force bool) error {
+func (store *store) AddDigest(ref reference.Canonical, id digest.Digest, force bool) error {
 	return store.addReference(ref, id, force)
 	return store.addReference(ref, id, force)
 }
 }
 
 
-func (store *store) addReference(ref Named, id digest.Digest, force bool) error {
-	if ref.Name() == string(digest.Canonical) {
+func (store *store) addReference(ref reference.Named, id digest.Digest, force bool) error {
+	refName := reference.FamiliarName(ref)
+	refStr := reference.FamiliarString(ref)
+
+	if refName == string(digest.Canonical) {
 		return errors.New("refusing to create an ambiguous tag using digest algorithm as name")
 		return errors.New("refusing to create an ambiguous tag using digest algorithm as name")
 	}
 	}
 
 
 	store.mu.Lock()
 	store.mu.Lock()
 	defer store.mu.Unlock()
 	defer store.mu.Unlock()
 
 
-	repository, exists := store.Repositories[ref.Name()]
+	repository, exists := store.Repositories[refName]
 	if !exists || repository == nil {
 	if !exists || repository == nil {
 		repository = make(map[string]digest.Digest)
 		repository = make(map[string]digest.Digest)
-		store.Repositories[ref.Name()] = repository
+		store.Repositories[refName] = repository
 	}
 	}
 
 
-	refStr := ref.String()
 	oldID, exists := repository[refStr]
 	oldID, exists := repository[refStr]
 
 
 	if exists {
 	if exists {
 		// force only works for tags
 		// force only works for tags
-		if digested, isDigest := ref.(Canonical); isDigest {
+		if digested, isDigest := ref.(reference.Canonical); isDigest {
 			return fmt.Errorf("Cannot overwrite digest %s", digested.Digest().String())
 			return fmt.Errorf("Cannot overwrite digest %s", digested.Digest().String())
 		}
 		}
 
 
 		if !force {
 		if !force {
-			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())
+			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())
 		}
 		}
 
 
 		if store.referencesByIDCache[oldID] != nil {
 		if store.referencesByIDCache[oldID] != nil {
@@ -138,7 +145,7 @@ func (store *store) addReference(ref Named, id digest.Digest, force bool) error
 
 
 	repository[refStr] = id
 	repository[refStr] = id
 	if store.referencesByIDCache[id] == nil {
 	if store.referencesByIDCache[id] == nil {
-		store.referencesByIDCache[id] = make(map[string]Named)
+		store.referencesByIDCache[id] = make(map[string]reference.Named)
 	}
 	}
 	store.referencesByIDCache[id][refStr] = ref
 	store.referencesByIDCache[id][refStr] = ref
 
 
@@ -147,24 +154,24 @@ func (store *store) addReference(ref Named, id digest.Digest, force bool) error
 
 
 // Delete deletes a reference from the store. It returns true if a deletion
 // Delete deletes a reference from the store. It returns true if a deletion
 // happened, or false otherwise.
 // happened, or false otherwise.
-func (store *store) Delete(ref Named) (bool, error) {
-	ref = WithDefaultTag(ref)
+func (store *store) Delete(ref reference.Named) (bool, error) {
+	ref = reference.TagNameOnly(ref)
+
+	refName := reference.FamiliarName(ref)
+	refStr := reference.FamiliarString(ref)
 
 
 	store.mu.Lock()
 	store.mu.Lock()
 	defer store.mu.Unlock()
 	defer store.mu.Unlock()
 
 
-	repoName := ref.Name()
-
-	repository, exists := store.Repositories[repoName]
+	repository, exists := store.Repositories[refName]
 	if !exists {
 	if !exists {
 		return false, ErrDoesNotExist
 		return false, ErrDoesNotExist
 	}
 	}
 
 
-	refStr := ref.String()
 	if id, exists := repository[refStr]; exists {
 	if id, exists := repository[refStr]; exists {
 		delete(repository, refStr)
 		delete(repository, refStr)
 		if len(repository) == 0 {
 		if len(repository) == 0 {
-			delete(store.Repositories, repoName)
+			delete(store.Repositories, refName)
 		}
 		}
 		if store.referencesByIDCache[id] != nil {
 		if store.referencesByIDCache[id] != nil {
 			delete(store.referencesByIDCache[id], refStr)
 			delete(store.referencesByIDCache[id], refStr)
@@ -179,18 +186,34 @@ func (store *store) Delete(ref Named) (bool, error) {
 }
 }
 
 
 // Get retrieves an item from the store by reference
 // Get retrieves an item from the store by reference
-func (store *store) Get(ref Named) (digest.Digest, error) {
-	ref = WithDefaultTag(ref)
+func (store *store) Get(ref reference.Named) (digest.Digest, error) {
+	if canonical, ok := ref.(reference.Canonical); ok {
+		// If reference contains both tag and digest, only
+		// lookup by digest as it takes precendent over
+		// tag, until tag/digest combos are stored.
+		if _, ok := ref.(reference.Tagged); ok {
+			var err error
+			ref, err = reference.WithDigest(reference.TrimNamed(canonical), canonical.Digest())
+			if err != nil {
+				return "", err
+			}
+		}
+	} else {
+		ref = reference.TagNameOnly(ref)
+	}
+
+	refName := reference.FamiliarName(ref)
+	refStr := reference.FamiliarString(ref)
 
 
 	store.mu.RLock()
 	store.mu.RLock()
 	defer store.mu.RUnlock()
 	defer store.mu.RUnlock()
 
 
-	repository, exists := store.Repositories[ref.Name()]
+	repository, exists := store.Repositories[refName]
 	if !exists || repository == nil {
 	if !exists || repository == nil {
 		return "", ErrDoesNotExist
 		return "", ErrDoesNotExist
 	}
 	}
 
 
-	id, exists := repository[ref.String()]
+	id, exists := repository[refStr]
 	if !exists {
 	if !exists {
 		return "", ErrDoesNotExist
 		return "", ErrDoesNotExist
 	}
 	}
@@ -200,7 +223,7 @@ func (store *store) Get(ref Named) (digest.Digest, error) {
 
 
 // References returns a slice of references to the given ID. The slice
 // References returns a slice of references to the given ID. The slice
 // will be nil if there are no references to this ID.
 // will be nil if there are no references to this ID.
-func (store *store) References(id digest.Digest) []Named {
+func (store *store) References(id digest.Digest) []reference.Named {
 	store.mu.RLock()
 	store.mu.RLock()
 	defer store.mu.RUnlock()
 	defer store.mu.RUnlock()
 
 
@@ -208,7 +231,7 @@ func (store *store) References(id digest.Digest) []Named {
 	// 1) We must not return a mutable
 	// 1) We must not return a mutable
 	// 2) It would be ugly to expose the extraneous map keys to callers.
 	// 2) It would be ugly to expose the extraneous map keys to callers.
 
 
-	var references []Named
+	var references []reference.Named
 	for _, ref := range store.referencesByIDCache[id] {
 	for _, ref := range store.referencesByIDCache[id] {
 		references = append(references, ref)
 		references = append(references, ref)
 	}
 	}
@@ -221,18 +244,20 @@ func (store *store) References(id digest.Digest) []Named {
 // ReferencesByName returns the references for a given repository name.
 // ReferencesByName returns the references for a given repository name.
 // If there are no references known for this repository name,
 // If there are no references known for this repository name,
 // ReferencesByName returns nil.
 // ReferencesByName returns nil.
-func (store *store) ReferencesByName(ref Named) []Association {
+func (store *store) ReferencesByName(ref reference.Named) []Association {
+	refName := reference.FamiliarName(ref)
+
 	store.mu.RLock()
 	store.mu.RLock()
 	defer store.mu.RUnlock()
 	defer store.mu.RUnlock()
 
 
-	repository, exists := store.Repositories[ref.Name()]
+	repository, exists := store.Repositories[refName]
 	if !exists {
 	if !exists {
 		return nil
 		return nil
 	}
 	}
 
 
 	var associations []Association
 	var associations []Association
 	for refStr, refID := range repository {
 	for refStr, refID := range repository {
-		ref, err := ParseNamed(refStr)
+		ref, err := reference.ParseNormalizedNamed(refStr)
 		if err != nil {
 		if err != nil {
 			// Should never happen
 			// Should never happen
 			return nil
 			return nil
@@ -270,13 +295,13 @@ func (store *store) reload() error {
 
 
 	for _, repository := range store.Repositories {
 	for _, repository := range store.Repositories {
 		for refStr, refID := range repository {
 		for refStr, refID := range repository {
-			ref, err := ParseNamed(refStr)
+			ref, err := reference.ParseNormalizedNamed(refStr)
 			if err != nil {
 			if err != nil {
 				// Should never happen
 				// Should never happen
 				continue
 				continue
 			}
 			}
 			if store.referencesByIDCache[refID] == nil {
 			if store.referencesByIDCache[refID] == nil {
-				store.referencesByIDCache[refID] = make(map[string]Named)
+				store.referencesByIDCache[refID] = make(map[string]reference.Named)
 			}
 			}
 			store.referencesByIDCache[refID][refStr] = ref
 			store.referencesByIDCache[refID][refStr] = ref
 		}
 		}

+ 16 - 15
reference/store_test.go

@@ -8,6 +8,7 @@ import (
 	"strings"
 	"strings"
 	"testing"
 	"testing"
 
 
+	"github.com/docker/distribution/reference"
 	"github.com/opencontainers/go-digest"
 	"github.com/opencontainers/go-digest"
 )
 )
 
 
@@ -45,7 +46,7 @@ func TestLoad(t *testing.T) {
 	}
 	}
 
 
 	for refStr, expectedID := range saveLoadTestCases {
 	for refStr, expectedID := range saveLoadTestCases {
-		ref, err := ParseNamed(refStr)
+		ref, err := reference.ParseNormalizedNamed(refStr)
 		if err != nil {
 		if err != nil {
 			t.Fatalf("failed to parse reference: %v", err)
 			t.Fatalf("failed to parse reference: %v", err)
 		}
 		}
@@ -74,11 +75,11 @@ func TestSave(t *testing.T) {
 	}
 	}
 
 
 	for refStr, id := range saveLoadTestCases {
 	for refStr, id := range saveLoadTestCases {
-		ref, err := ParseNamed(refStr)
+		ref, err := reference.ParseNormalizedNamed(refStr)
 		if err != nil {
 		if err != nil {
 			t.Fatalf("failed to parse reference: %v", err)
 			t.Fatalf("failed to parse reference: %v", err)
 		}
 		}
-		if canonical, ok := ref.(Canonical); ok {
+		if canonical, ok := ref.(reference.Canonical); ok {
 			err = store.AddDigest(canonical, id, false)
 			err = store.AddDigest(canonical, id, false)
 			if err != nil {
 			if err != nil {
 				t.Fatalf("could not add digest reference %s: %v", refStr, err)
 				t.Fatalf("could not add digest reference %s: %v", refStr, err)
@@ -120,7 +121,7 @@ func TestAddDeleteGet(t *testing.T) {
 	testImageID3 := digest.Digest("sha256:9655aef5fd742a1b4e1b7b163aa9f1c76c186304bf39102283d80927c916ca9e")
 	testImageID3 := digest.Digest("sha256:9655aef5fd742a1b4e1b7b163aa9f1c76c186304bf39102283d80927c916ca9e")
 
 
 	// Try adding a reference with no tag or digest
 	// Try adding a reference with no tag or digest
-	nameOnly, err := WithName("username/repo")
+	nameOnly, err := reference.ParseNormalizedNamed("username/repo")
 	if err != nil {
 	if err != nil {
 		t.Fatalf("could not parse reference: %v", err)
 		t.Fatalf("could not parse reference: %v", err)
 	}
 	}
@@ -129,7 +130,7 @@ func TestAddDeleteGet(t *testing.T) {
 	}
 	}
 
 
 	// Add a few references
 	// Add a few references
-	ref1, err := ParseNamed("username/repo1:latest")
+	ref1, err := reference.ParseNormalizedNamed("username/repo1:latest")
 	if err != nil {
 	if err != nil {
 		t.Fatalf("could not parse reference: %v", err)
 		t.Fatalf("could not parse reference: %v", err)
 	}
 	}
@@ -137,7 +138,7 @@ func TestAddDeleteGet(t *testing.T) {
 		t.Fatalf("error adding to store: %v", err)
 		t.Fatalf("error adding to store: %v", err)
 	}
 	}
 
 
-	ref2, err := ParseNamed("username/repo1:old")
+	ref2, err := reference.ParseNormalizedNamed("username/repo1:old")
 	if err != nil {
 	if err != nil {
 		t.Fatalf("could not parse reference: %v", err)
 		t.Fatalf("could not parse reference: %v", err)
 	}
 	}
@@ -145,7 +146,7 @@ func TestAddDeleteGet(t *testing.T) {
 		t.Fatalf("error adding to store: %v", err)
 		t.Fatalf("error adding to store: %v", err)
 	}
 	}
 
 
-	ref3, err := ParseNamed("username/repo1:alias")
+	ref3, err := reference.ParseNormalizedNamed("username/repo1:alias")
 	if err != nil {
 	if err != nil {
 		t.Fatalf("could not parse reference: %v", err)
 		t.Fatalf("could not parse reference: %v", err)
 	}
 	}
@@ -153,7 +154,7 @@ func TestAddDeleteGet(t *testing.T) {
 		t.Fatalf("error adding to store: %v", err)
 		t.Fatalf("error adding to store: %v", err)
 	}
 	}
 
 
-	ref4, err := ParseNamed("username/repo2:latest")
+	ref4, err := reference.ParseNormalizedNamed("username/repo2:latest")
 	if err != nil {
 	if err != nil {
 		t.Fatalf("could not parse reference: %v", err)
 		t.Fatalf("could not parse reference: %v", err)
 	}
 	}
@@ -161,11 +162,11 @@ func TestAddDeleteGet(t *testing.T) {
 		t.Fatalf("error adding to store: %v", err)
 		t.Fatalf("error adding to store: %v", err)
 	}
 	}
 
 
-	ref5, err := ParseNamed("username/repo3@sha256:58153dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c")
+	ref5, err := reference.ParseNormalizedNamed("username/repo3@sha256:58153dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c")
 	if err != nil {
 	if err != nil {
 		t.Fatalf("could not parse reference: %v", err)
 		t.Fatalf("could not parse reference: %v", err)
 	}
 	}
-	if err = store.AddDigest(ref5.(Canonical), testImageID2, false); err != nil {
+	if err = store.AddDigest(ref5.(reference.Canonical), testImageID2, false); err != nil {
 		t.Fatalf("error adding to store: %v", err)
 		t.Fatalf("error adding to store: %v", err)
 	}
 	}
 
 
@@ -228,7 +229,7 @@ func TestAddDeleteGet(t *testing.T) {
 	}
 	}
 
 
 	// Get should return ErrDoesNotExist for a nonexistent repo
 	// Get should return ErrDoesNotExist for a nonexistent repo
-	nonExistRepo, err := ParseNamed("username/nonexistrepo:latest")
+	nonExistRepo, err := reference.ParseNormalizedNamed("username/nonexistrepo:latest")
 	if err != nil {
 	if err != nil {
 		t.Fatalf("could not parse reference: %v", err)
 		t.Fatalf("could not parse reference: %v", err)
 	}
 	}
@@ -237,7 +238,7 @@ func TestAddDeleteGet(t *testing.T) {
 	}
 	}
 
 
 	// Get should return ErrDoesNotExist for a nonexistent tag
 	// Get should return ErrDoesNotExist for a nonexistent tag
-	nonExistTag, err := ParseNamed("username/repo1:nonexist")
+	nonExistTag, err := reference.ParseNormalizedNamed("username/repo1:nonexist")
 	if err != nil {
 	if err != nil {
 		t.Fatalf("could not parse reference: %v", err)
 		t.Fatalf("could not parse reference: %v", err)
 	}
 	}
@@ -263,7 +264,7 @@ func TestAddDeleteGet(t *testing.T) {
 	}
 	}
 
 
 	// Check ReferencesByName
 	// Check ReferencesByName
-	repoName, err := WithName("username/repo1")
+	repoName, err := reference.ParseNormalizedNamed("username/repo1")
 	if err != nil {
 	if err != nil {
 		t.Fatalf("could not parse reference: %v", err)
 		t.Fatalf("could not parse reference: %v", err)
 	}
 	}
@@ -334,7 +335,7 @@ func TestInvalidTags(t *testing.T) {
 	id := digest.Digest("sha256:470022b8af682154f57a2163d030eb369549549cba00edc69e1b99b46bb924d6")
 	id := digest.Digest("sha256:470022b8af682154f57a2163d030eb369549549cba00edc69e1b99b46bb924d6")
 
 
 	// sha256 as repo name
 	// sha256 as repo name
-	ref, err := ParseNamed("sha256:abc")
+	ref, err := reference.ParseNormalizedNamed("sha256:abc")
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -344,7 +345,7 @@ func TestInvalidTags(t *testing.T) {
 	}
 	}
 
 
 	// setting digest as a tag
 	// setting digest as a tag
-	ref, err = ParseNamed("registry@sha256:367eb40fd0330a7e464777121e39d2f5b3e8e23a1e159342e53ab05c9e4d94e6")
+	ref, err = reference.ParseNormalizedNamed("registry@sha256:367eb40fd0330a7e464777121e39d2f5b3e8e23a1e159342e53ab05c9e4d94e6")
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}

+ 4 - 9
registry/config.go

@@ -10,7 +10,6 @@ import (
 	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/reference"
 	registrytypes "github.com/docker/docker/api/types/registry"
 	registrytypes "github.com/docker/docker/api/types/registry"
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/opts"
-	forkedref "github.com/docker/docker/reference"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
 	"github.com/spf13/pflag"
 	"github.com/spf13/pflag"
 )
 )
@@ -271,8 +270,9 @@ func ValidateMirror(val string) (string, error) {
 
 
 // ValidateIndexName validates an index name.
 // ValidateIndexName validates an index name.
 func ValidateIndexName(val string) (string, error) {
 func ValidateIndexName(val string) (string, error) {
-	if val == forkedref.LegacyDefaultHostname {
-		val = forkedref.DefaultHostname
+	// TODO: upstream this to check to reference package
+	if val == "index.docker.io" {
+		val = "docker.io"
 	}
 	}
 	if strings.HasPrefix(val, "-") || strings.HasSuffix(val, "-") {
 	if strings.HasPrefix(val, "-") || strings.HasSuffix(val, "-") {
 		return "", fmt.Errorf("Invalid index name (%s). Cannot begin or end with a hyphen.", val)
 		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
 	}
 	}
 	official := !strings.ContainsRune(reference.FamiliarName(name), '/')
 	official := !strings.ContainsRune(reference.FamiliarName(name), '/')
 
 
-	// TODO: remove used of forked reference package
-	nameref, err := forkedref.ParseNamed(name.String())
-	if err != nil {
-		return nil, err
-	}
 	return &RepositoryInfo{
 	return &RepositoryInfo{
-		Named:    nameref,
+		Name:     reference.TrimNamed(name),
 		Index:    index,
 		Index:    index,
 		Official: official,
 		Official: official,
 	}, nil
 	}, nil

+ 1 - 1
registry/registry_mock_test.go

@@ -17,8 +17,8 @@ import (
 	"testing"
 	"testing"
 	"time"
 	"time"
 
 
+	"github.com/docker/distribution/reference"
 	registrytypes "github.com/docker/docker/api/types/registry"
 	registrytypes "github.com/docker/docker/api/types/registry"
-	"github.com/docker/docker/reference"
 	"github.com/gorilla/mux"
 	"github.com/gorilla/mux"
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"

+ 10 - 11
registry/registry_test.go

@@ -14,7 +14,6 @@ import (
 	"github.com/docker/distribution/registry/client/transport"
 	"github.com/docker/distribution/registry/client/transport"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	registrytypes "github.com/docker/docker/api/types/registry"
 	registrytypes "github.com/docker/docker/api/types/registry"
-	forkedref "github.com/docker/docker/reference"
 )
 )
 
 
 var (
 var (
@@ -202,7 +201,7 @@ func TestGetRemoteImageLayer(t *testing.T) {
 
 
 func TestGetRemoteTag(t *testing.T) {
 func TestGetRemoteTag(t *testing.T) {
 	r := spawnTestRegistrySession(t)
 	r := spawnTestRegistrySession(t)
-	repoRef, err := forkedref.ParseNamed(REPO)
+	repoRef, err := reference.ParseNormalizedNamed(REPO)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -212,7 +211,7 @@ func TestGetRemoteTag(t *testing.T) {
 	}
 	}
 	assertEqual(t, tag, imageID, "Expected tag test to map to "+imageID)
 	assertEqual(t, tag, imageID, "Expected tag test to map to "+imageID)
 
 
-	bazRef, err := forkedref.ParseNamed("foo42/baz")
+	bazRef, err := reference.ParseNormalizedNamed("foo42/baz")
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -224,7 +223,7 @@ func TestGetRemoteTag(t *testing.T) {
 
 
 func TestGetRemoteTags(t *testing.T) {
 func TestGetRemoteTags(t *testing.T) {
 	r := spawnTestRegistrySession(t)
 	r := spawnTestRegistrySession(t)
-	repoRef, err := forkedref.ParseNamed(REPO)
+	repoRef, err := reference.ParseNormalizedNamed(REPO)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -236,7 +235,7 @@ func TestGetRemoteTags(t *testing.T) {
 	assertEqual(t, tags["latest"], imageID, "Expected tag latest to map to "+imageID)
 	assertEqual(t, tags["latest"], imageID, "Expected tag latest to map to "+imageID)
 	assertEqual(t, tags["test"], imageID, "Expected tag test to map to "+imageID)
 	assertEqual(t, tags["test"], imageID, "Expected tag test to map to "+imageID)
 
 
-	bazRef, err := forkedref.ParseNamed("foo42/baz")
+	bazRef, err := reference.ParseNormalizedNamed("foo42/baz")
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -253,7 +252,7 @@ func TestGetRepositoryData(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	host := "http://" + parsedURL.Host + "/v1/"
 	host := "http://" + parsedURL.Host + "/v1/"
-	repoRef, err := forkedref.ParseNamed(REPO)
+	repoRef, err := reference.ParseNormalizedNamed(REPO)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -516,9 +515,9 @@ func TestParseRepositoryInfo(t *testing.T) {
 			t.Error(err)
 			t.Error(err)
 		} else {
 		} else {
 			checkEqual(t, repoInfo.Index.Name, expectedRepoInfo.Index.Name, reposName)
 			checkEqual(t, repoInfo.Index.Name, expectedRepoInfo.Index.Name, reposName)
-			checkEqual(t, repoInfo.RemoteName(), expectedRepoInfo.RemoteName, reposName)
-			checkEqual(t, repoInfo.Name(), expectedRepoInfo.LocalName, reposName)
-			checkEqual(t, repoInfo.FullName(), expectedRepoInfo.CanonicalName, reposName)
+			checkEqual(t, reference.Path(repoInfo.Name), expectedRepoInfo.RemoteName, reposName)
+			checkEqual(t, reference.FamiliarName(repoInfo.Name), expectedRepoInfo.LocalName, reposName)
+			checkEqual(t, repoInfo.Name.Name(), expectedRepoInfo.CanonicalName, reposName)
 			checkEqual(t, repoInfo.Index.Official, expectedRepoInfo.Index.Official, reposName)
 			checkEqual(t, repoInfo.Index.Official, expectedRepoInfo.Index.Official, reposName)
 			checkEqual(t, repoInfo.Official, expectedRepoInfo.Official, reposName)
 			checkEqual(t, repoInfo.Official, expectedRepoInfo.Official, reposName)
 		}
 		}
@@ -689,7 +688,7 @@ func TestMirrorEndpointLookup(t *testing.T) {
 
 
 func TestPushRegistryTag(t *testing.T) {
 func TestPushRegistryTag(t *testing.T) {
 	r := spawnTestRegistrySession(t)
 	r := spawnTestRegistrySession(t)
-	repoRef, err := forkedref.ParseNamed(REPO)
+	repoRef, err := reference.ParseNormalizedNamed(REPO)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -711,7 +710,7 @@ func TestPushImageJSONIndex(t *testing.T) {
 			Checksum: "sha256:bea7bf2e4bacd479344b737328db47b18880d09096e6674165533aa994f5e9f2",
 			Checksum: "sha256:bea7bf2e4bacd479344b737328db47b18880d09096e6674165533aa994f5e9f2",
 		},
 		},
 	}
 	}
-	repoRef, err := forkedref.ParseNamed(REPO)
+	repoRef, err := reference.ParseNormalizedNamed(REPO)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}

+ 10 - 10
registry/session.go

@@ -19,6 +19,7 @@ import (
 	"strings"
 	"strings"
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/registry/api/errcode"
 	"github.com/docker/distribution/registry/api/errcode"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	registrytypes "github.com/docker/docker/api/types/registry"
 	registrytypes "github.com/docker/docker/api/types/registry"
@@ -26,7 +27,6 @@ import (
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/tarsum"
 	"github.com/docker/docker/pkg/tarsum"
-	"github.com/docker/docker/reference"
 )
 )
 
 
 var (
 var (
@@ -324,7 +324,7 @@ func (r *Session) GetRemoteImageLayer(imgID, registry string, imgSize int64) (io
 // argument, and returns data from the first one that answers the query
 // argument, and returns data from the first one that answers the query
 // successfully.
 // successfully.
 func (r *Session) GetRemoteTag(registries []string, repositoryRef reference.Named, askedTag string) (string, error) {
 func (r *Session) GetRemoteTag(registries []string, repositoryRef reference.Named, askedTag string) (string, error) {
-	repository := repositoryRef.RemoteName()
+	repository := reference.Path(repositoryRef)
 
 
 	if strings.Count(repository, "/") == 0 {
 	if strings.Count(repository, "/") == 0 {
 		// This will be removed once the registry supports auto-resolution on
 		// This will be removed once the registry supports auto-resolution on
@@ -362,7 +362,7 @@ func (r *Session) GetRemoteTag(registries []string, repositoryRef reference.Name
 // the first one that answers the query successfully. It returns a map with
 // the first one that answers the query successfully. It returns a map with
 // tag names as the keys and image IDs as the values.
 // tag names as the keys and image IDs as the values.
 func (r *Session) GetRemoteTags(registries []string, repositoryRef reference.Named) (map[string]string, error) {
 func (r *Session) GetRemoteTags(registries []string, repositoryRef reference.Named) (map[string]string, error) {
-	repository := repositoryRef.RemoteName()
+	repository := reference.Path(repositoryRef)
 
 
 	if strings.Count(repository, "/") == 0 {
 	if strings.Count(repository, "/") == 0 {
 		// This will be removed once the registry supports auto-resolution on
 		// This will be removed once the registry supports auto-resolution on
@@ -416,7 +416,7 @@ func buildEndpointsList(headers []string, indexEp string) ([]string, error) {
 
 
 // GetRepositoryData returns lists of images and endpoints for the repository
 // GetRepositoryData returns lists of images and endpoints for the repository
 func (r *Session) GetRepositoryData(name reference.Named) (*RepositoryData, error) {
 func (r *Session) GetRepositoryData(name reference.Named) (*RepositoryData, error) {
-	repositoryTarget := fmt.Sprintf("%srepositories/%s/images", r.indexEndpoint.String(), name.RemoteName())
+	repositoryTarget := fmt.Sprintf("%srepositories/%s/images", r.indexEndpoint.String(), reference.Path(name))
 
 
 	logrus.Debugf("[registry] Calling GET %s", repositoryTarget)
 	logrus.Debugf("[registry] Calling GET %s", repositoryTarget)
 
 
@@ -450,7 +450,7 @@ func (r *Session) GetRepositoryData(name reference.Named) (*RepositoryData, erro
 		if err != nil {
 		if err != nil {
 			logrus.Debugf("Error reading response body: %s", err)
 			logrus.Debugf("Error reading response body: %s", err)
 		}
 		}
-		return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to pull repository %s: %q", res.StatusCode, name.RemoteName(), errBody), res)
+		return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to pull repository %s: %q", res.StatusCode, reference.Path(name), errBody), res)
 	}
 	}
 
 
 	var endpoints []string
 	var endpoints []string
@@ -605,7 +605,7 @@ func (r *Session) PushImageLayerRegistry(imgID string, layer io.Reader, registry
 func (r *Session) PushRegistryTag(remote reference.Named, revision, tag, registry string) error {
 func (r *Session) PushRegistryTag(remote reference.Named, revision, tag, registry string) error {
 	// "jsonify" the string
 	// "jsonify" the string
 	revision = "\"" + revision + "\""
 	revision = "\"" + revision + "\""
-	path := fmt.Sprintf("repositories/%s/tags/%s", remote.RemoteName(), tag)
+	path := fmt.Sprintf("repositories/%s/tags/%s", reference.Path(remote), tag)
 
 
 	req, err := http.NewRequest("PUT", registry+path, strings.NewReader(revision))
 	req, err := http.NewRequest("PUT", registry+path, strings.NewReader(revision))
 	if err != nil {
 	if err != nil {
@@ -619,7 +619,7 @@ func (r *Session) PushRegistryTag(remote reference.Named, revision, tag, registr
 	}
 	}
 	res.Body.Close()
 	res.Body.Close()
 	if res.StatusCode != 200 && res.StatusCode != 201 {
 	if res.StatusCode != 200 && res.StatusCode != 201 {
-		return httputils.NewHTTPRequestError(fmt.Sprintf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, remote.RemoteName()), res)
+		return httputils.NewHTTPRequestError(fmt.Sprintf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, reference.Path(remote)), res)
 	}
 	}
 	return nil
 	return nil
 }
 }
@@ -645,7 +645,7 @@ func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData,
 	if validate {
 	if validate {
 		suffix = "images"
 		suffix = "images"
 	}
 	}
-	u := fmt.Sprintf("%srepositories/%s/%s", r.indexEndpoint.String(), remote.RemoteName(), suffix)
+	u := fmt.Sprintf("%srepositories/%s/%s", r.indexEndpoint.String(), reference.Path(remote), suffix)
 	logrus.Debugf("[registry] PUT %s", u)
 	logrus.Debugf("[registry] PUT %s", u)
 	logrus.Debugf("Image list pushed to index:\n%s", imgListJSON)
 	logrus.Debugf("Image list pushed to index:\n%s", imgListJSON)
 	headers := map[string][]string{
 	headers := map[string][]string{
@@ -683,7 +683,7 @@ func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData,
 			if err != nil {
 			if err != nil {
 				logrus.Debugf("Error reading response body: %s", err)
 				logrus.Debugf("Error reading response body: %s", err)
 			}
 			}
-			return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push repository %s: %q", res.StatusCode, remote.RemoteName(), errBody), res)
+			return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push repository %s: %q", res.StatusCode, reference.Path(remote), errBody), res)
 		}
 		}
 		tokens = res.Header["X-Docker-Token"]
 		tokens = res.Header["X-Docker-Token"]
 		logrus.Debugf("Auth token: %v", tokens)
 		logrus.Debugf("Auth token: %v", tokens)
@@ -701,7 +701,7 @@ func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData,
 			if err != nil {
 			if err != nil {
 				logrus.Debugf("Error reading response body: %s", err)
 				logrus.Debugf("Error reading response body: %s", err)
 			}
 			}
-			return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push checksums %s: %q", res.StatusCode, remote.RemoteName(), errBody), res)
+			return nil, httputils.NewHTTPRequestError(fmt.Sprintf("Error: Status %d trying to push checksums %s: %q", res.StatusCode, reference.Path(remote), errBody), res)
 		}
 		}
 	}
 	}
 
 

+ 2 - 2
registry/types.go

@@ -1,8 +1,8 @@
 package registry
 package registry
 
 
 import (
 import (
+	"github.com/docker/distribution/reference"
 	registrytypes "github.com/docker/docker/api/types/registry"
 	registrytypes "github.com/docker/docker/api/types/registry"
-	"github.com/docker/docker/reference"
 )
 )
 
 
 // RepositoryData tracks the image list, list of endpoints for a repository
 // RepositoryData tracks the image list, list of endpoints for a repository
@@ -57,7 +57,7 @@ var apiVersions = map[APIVersion]string{
 
 
 // RepositoryInfo describes a repository
 // RepositoryInfo describes a repository
 type RepositoryInfo struct {
 type RepositoryInfo struct {
-	reference.Named
+	Name reference.Named
 	// Index points to registry information
 	// Index points to registry information
 	Index *registrytypes.IndexInfo
 	Index *registrytypes.IndexInfo
 	// Official indicates whether the repository is considered official.
 	// Official indicates whether the repository is considered official.