Parcourir la 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 il y a 8 ans
Parent
commit
3a1279393f
78 fichiers modifiés avec 538 ajouts et 1117 suppressions
  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"
 	"net/http"
 
+	"github.com/docker/distribution/reference"
 	enginetypes "github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
-	"github.com/docker/docker/reference"
 	"golang.org/x/net/context"
 )
 

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

@@ -7,13 +7,12 @@ import (
 	"strconv"
 	"strings"
 
-	distreference "github.com/docker/distribution/reference"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/server/httputils"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/streamformatter"
-	"github.com/docker/docker/reference"
 	"github.com/pkg/errors"
 	"golang.org/x/net/context"
 )
@@ -47,39 +46,27 @@ func parseHeaders(headers http.Header) (map[string][]string, *types.AuthConfig)
 // be returned.
 func parseRemoteRef(remote string) (reference.Named, string, error) {
 	// 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 {
 		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 {
 			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 {
@@ -188,24 +175,24 @@ func getName(ref reference.Named, tag, name string) (string, error) {
 				if err != nil {
 					return "", err
 				}
-				name = nt.String()
+				name = reference.FamiliarString(nt)
 			} else {
-				name = reference.WithDefaultTag(trimmed).String()
+				name = reference.FamiliarString(reference.TagNameOnly(trimmed))
 			}
 		} else {
-			name = ref.String()
+			name = reference.FamiliarString(ref)
 		}
 	} else {
-		localRef, err := reference.ParseNamed(name)
+		localRef, err := reference.ParseNormalizedNamed(name)
 		if err != nil {
 			return "", err
 		}
 		if _, ok := localRef.(reference.Canonical); ok {
 			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
-			name = reference.WithDefaultTag(localRef).String()
+			name = reference.FamiliarString(reference.TagNameOnly(localRef))
 		}
 	}
 	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"
 	"time"
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/backend"
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/image"
-	"github.com/docker/docker/reference"
 	"golang.org/x/net/context"
 )
 

+ 3 - 10
builder/dockerfile/builder.go

@@ -11,6 +11,7 @@ import (
 	"strings"
 
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/distribution/reference"
 	apierrors "github.com/docker/docker/api/errors"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/backend"
@@ -19,7 +20,6 @@ import (
 	"github.com/docker/docker/builder/dockerfile/parser"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/stringid"
-	"github.com/docker/docker/reference"
 	perrors "github.com/pkg/errors"
 	"golang.org/x/net/context"
 )
@@ -176,23 +176,16 @@ func sanitizeRepoAndTags(names []string) ([]reference.Named, error) {
 			continue
 		}
 
-		ref, err := reference.ParseNamed(repo)
+		ref, err := reference.ParseNormalizedNamed(repo)
 		if err != nil {
 			return nil, err
 		}
 
-		ref = reference.WithDefaultTag(ref)
-
 		if _, isCanonical := ref.(reference.Canonical); isCanonical {
 			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()
 

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

@@ -168,11 +168,7 @@ func createContainer(ctx context.Context, dockerCli *command.DockerCli, config *
 		return nil, err
 	}
 	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() {
 			var err error

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

@@ -94,12 +94,12 @@ func (ctx *DiskUsageContext) Write() {
 		tag := "<none>"
 		if len(i.RepoTags) > 0 && !isDangling(*i) {
 			// Only show the first tag
-			ref, err := reference.ParseNamed(i.RepoTags[0])
+			ref, err := reference.ParseNormalizedNamed(i.RepoTags[0])
 			if err != nil {
 				continue
 			}
 			if nt, ok := ref.(reference.NamedTagged); ok {
-				repo = ref.Name()
+				repo = reference.FamiliarName(ref)
 				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{}
 			repoDigests := map[string][]string{}
 
-			for _, refString := range append(image.RepoTags) {
+			for _, refString := range image.RepoTags {
 				ref, err := reference.ParseNormalizedNamed(refString)
 				if err != nil {
 					continue
@@ -104,7 +104,7 @@ func imageFormat(ctx ImageContext, images []types.ImageSummary, format func(subC
 					repoTags[familiarRef] = append(repoTags[familiarRef], nt.Tag())
 				}
 			}
-			for _, refString := range append(image.RepoDigests) {
+			for _, refString := range image.RepoDigests {
 				ref, err := reference.ParseNormalizedNamed(refString)
 				if err != nil {
 					continue

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

@@ -5,7 +5,7 @@ import (
 	"strings"
 	"time"
 
-	distreference "github.com/docker/distribution/reference"
+	"github.com/docker/distribution/reference"
 	mounttypes "github.com/docker/docker/api/types/mount"
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/cli/command/inspect"
@@ -409,11 +409,12 @@ func (c *serviceContext) Replicas() string {
 func (c *serviceContext) Image() string {
 	c.AddHeader(imageHeader)
 	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 {
 				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() {
 				trustedRef, err := translator(ctx, ref)
 				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 {
-	var distributionRef reference.Named
 	distributionRef, err := reference.ParseNormalizedNamed(opts.remote)
 	if err != nil {
 		return err
@@ -52,9 +51,10 @@ func runPull(dockerCli *command.DockerCli, opts pullOptions) error {
 	}
 
 	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

+ 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
 		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)
 	case nil:
 		// already initialized and we have successfully downloaded the latest metadata
 		err = addTargetToAllSignableRoles(repo, target)
 	default:
-		return trust.NotaryError(repoInfo.FullName(), err)
+		return trust.NotaryError(repoInfo.Name.Name(), err)
 	}
 
 	if err == nil {
@@ -145,11 +145,11 @@ func PushTrustedReference(cli *command.DockerCli, repoInfo *registry.RepositoryI
 	}
 
 	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
 }
 
@@ -342,12 +342,12 @@ func TrustedReference(ctx context.Context, cli *command.DockerCli, ref reference
 
 	t, err := notaryRepo.GetTargetByName(ref.Tag(), trust.ReleasesRole, data.CanonicalTargetsRole)
 	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
 	// all other delegation roles
 	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)
 	if err != nil {

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

@@ -7,7 +7,6 @@ import (
 
 	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
-	registrytypes "github.com/docker/docker/api/types/registry"
 	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cli/command"
 	"github.com/docker/docker/cli/command/image"
@@ -54,20 +53,6 @@ func newInstallCommand(dockerCli *command.DockerCli) *cobra.Command {
 	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 {
 	registry.Service
 }
@@ -104,9 +89,10 @@ func buildPullConfig(ctx context.Context, dockerCli *command.DockerCli, opts plu
 
 	_, isCanonical := ref.(reference.Canonical)
 	if command.IsTrusted() && !isCanonical {
+		ref = reference.TagNameOnly(ref)
 		nt, ok := ref.(reference.NamedTagged)
 		if !ok {
-			nt = reference.EnsureTagged(ref)
+			return types.PluginInstallOptions{}, fmt.Errorf("invalid name: %s", ref.String())
 		}
 
 		ctx := context.Background()
@@ -148,7 +134,7 @@ func runInstall(dockerCli *command.DockerCli, opts pluginOptions) error {
 		if _, ok := aref.(reference.Canonical); ok {
 			return fmt.Errorf("invalid name: %s", opts.localName)
 		}
-		localName = reference.FamiliarString(reference.EnsureTagged(aref))
+		localName = reference.FamiliarString(reference.TagNameOnly(aref))
 	}
 
 	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)
 	}
 
-	taggedRef, ok := named.(reference.NamedTagged)
-	if !ok {
-		taggedRef = reference.EnsureTagged(named)
-	}
+	named = reference.TagNameOnly(named)
 
 	ctx := context.Background()
 
@@ -58,7 +55,7 @@ func runPush(dockerCli *command.DockerCli, name string) error {
 		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 {
 		return err
 	}

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

@@ -5,10 +5,10 @@ import (
 	"fmt"
 	"strings"
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cli/command"
 	"github.com/docker/docker/pkg/jsonmessage"
-	"github.com/docker/docker/reference"
 	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 )
@@ -49,19 +49,19 @@ func runUpgrade(dockerCli *command.DockerCli, opts pluginOptions) error {
 	if opts.remote == "" {
 		opts.remote = p.PluginReference
 	}
-	remote, err := reference.ParseNamed(opts.remote)
+	remote, err := reference.ParseNormalizedNamed(opts.remote)
 	if err != nil {
 		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 {
 		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 !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), "Plugin images do not match, are you sure?") {
 			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)
 		if !ok {
 			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)
 		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)
 	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
 	// ignore it if it's in any other delegation roles
 	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)

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

@@ -10,7 +10,7 @@ import (
 
 	"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/cli/command"
 	"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
 		if !noTrunc {
-			ref, err := distreference.ParseNamed(image)
+			ref, err := reference.ParseNormalizedNamed(image)
 			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{
-		Repository: repoInfo.FullName(),
+		Repository: repoInfo.Name.Name(),
 		Actions:    actions,
 		Class:      repoInfo.Class,
 	}
@@ -166,7 +166,7 @@ func GetNotaryRepository(streams command.Streams, repoInfo *registry.RepositoryI
 
 	return client.NewNotaryRepository(
 		trustDirectory(),
-		repoInfo.FullName(),
+		repoInfo.Name.Name(),
 		server,
 		tr,
 		getPassphraseRetriever(streams),

+ 8 - 6
client/container_commit.go

@@ -5,9 +5,8 @@ import (
 	"errors"
 	"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/reference"
 	"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) {
 	var repository, tag string
 	if options.Reference != "" {
-		distributionRef, err := distreference.ParseNamed(options.Reference)
+		ref, err := reference.ParseNormalizedNamed(options.Reference)
 		if err != nil {
 			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")
 		}
+		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{}

+ 4 - 4
client/image_create.go

@@ -6,21 +6,21 @@ import (
 
 	"golang.org/x/net/context"
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/api/types/reference"
 )
 
 // ImageCreate creates a new image based in the parent options.
 // It returns the JSON content in the response body.
 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 {
 		return nil, err
 	}
 
 	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)
 	if err != nil {
 		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) {
 	if ref != "" {
 		//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
 		}
 	}

+ 21 - 6
client/image_pull.go

@@ -7,8 +7,8 @@ import (
 
 	"golang.org/x/net/context"
 
+	"github.com/docker/distribution/reference"
 	"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.
@@ -19,16 +19,16 @@ import (
 // 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 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 {
 		return nil, err
 	}
 
 	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)
@@ -44,3 +44,18 @@ func (cli *Client) ImagePull(ctx context.Context, ref string, options types.Imag
 	}
 	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
 	_, 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)
 	}
 }

+ 10 - 8
client/image_push.go

@@ -8,7 +8,7 @@ import (
 
 	"golang.org/x/net/context"
 
-	distreference "github.com/docker/distribution/reference"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 )
 
@@ -16,31 +16,33 @@ import (
 // It executes the privileged function if the operation is unauthorized
 // and it tries one more time.
 // 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 {
 		return nil, err
 	}
 
-	if _, isCanonical := distributionRef.(distreference.Canonical); isCanonical {
+	if _, isCanonical := ref.(reference.Canonical); isCanonical {
 		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()
 	}
 
 	query := url.Values{}
 	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 {
 		newAuthHeader, privilegeErr := options.PrivilegeFunc()
 		if privilegeErr != nil {
 			return nil, privilegeErr
 		}
-		resp, err = cli.tryImagePush(ctx, distributionRef.Name(), query, newAuthHeader)
+		resp, err = cli.tryImagePush(ctx, name, query, newAuthHeader)
 	}
 	if err != nil {
 		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
 	_, 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)
 	}
 	// An canonical reference cannot be pushed

+ 9 - 8
client/image_tag.go

@@ -3,32 +3,33 @@ package client
 import (
 	"net/url"
 
-	distreference "github.com/docker/distribution/reference"
-	"github.com/docker/docker/api/types/reference"
+	"github.com/docker/distribution/reference"
 	"github.com/pkg/errors"
 	"golang.org/x/net/context"
 )
 
 // ImageTag tags an image in the docker host
 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)
 	}
 
-	distributionRef, err := distreference.ParseNamed(target)
+	ref, err := reference.ParseNormalizedNamed(target)
 	if err != nil {
 		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")
 	}
 
-	tag := reference.GetTagFromNamedRef(distributionRef)
+	ref = reference.TagNameOnly(ref)
 
 	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)
 	ensureReaderClosed(resp)

+ 1 - 1
client/plugin_install.go

@@ -15,7 +15,7 @@ import (
 // PluginInstall installs a plugin
 func (cli *Client) PluginInstall(ctx context.Context, name string, options types.PluginInstallOptions) (rc io.ReadCloser, err error) {
 	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")
 	}
 	query.Set("remote", options.RemoteRef)

+ 1 - 1
client/plugin_upgrade.go

@@ -14,7 +14,7 @@ import (
 // PluginUpgrade upgrades a plugin
 func (cli *Client) PluginUpgrade(ctx context.Context, name string, options types.PluginInstallOptions) (rc io.ReadCloser, err error) {
 	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")
 	}
 	query.Set("remote", options.RemoteRef)

+ 21 - 27
daemon/cluster/cluster.go

@@ -52,7 +52,7 @@ import (
 	"time"
 
 	"github.com/Sirupsen/logrus"
-	distreference "github.com/docker/distribution/reference"
+	"github.com/docker/distribution/reference"
 	apierrors "github.com/docker/docker/api/errors"
 	apitypes "github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/backend"
@@ -66,13 +66,11 @@ import (
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/signal"
 	"github.com/docker/docker/pkg/stdcopy"
-	"github.com/docker/docker/reference"
 	"github.com/docker/docker/runconfig"
 	swarmapi "github.com/docker/swarmkit/api"
 	"github.com/docker/swarmkit/manager/encryption"
 	swarmnode "github.com/docker/swarmkit/node"
 	gogotypes "github.com/gogo/protobuf/types"
-	"github.com/opencontainers/go-digest"
 	"github.com/pkg/errors"
 	"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
-// 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) {
-	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 {
 		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 {
-			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 {
 			return "", err
 		}
-		dscrptr, err := repo.Tags(ctx).Get(ctx, namedTaggedRef.Tag())
+		dscrptr, err := repo.Tags(ctx).Get(ctx, taggedRef.Tag())
 		if err != nil {
 			return "", err
 		}
 
-		namedDigestedRef, err := distreference.WithDigest(distreference.EnsureTagged(ref), dscrptr.Digest)
+		namedDigestedRef, err := reference.WithDigest(taggedRef, dscrptr.Digest)
 		if err != nil {
 			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
-	return ref.String(), nil
+	return reference.FamiliarString(ref), nil
 }
 
 // CreateService creates a new service in a managed swarm cluster.

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

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

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

@@ -11,13 +11,13 @@ import (
 	"time"
 
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/backend"
 	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/daemon/cluster/convert"
 	executorpkg "github.com/docker/docker/daemon/cluster/executor"
-	"github.com/docker/docker/reference"
 	"github.com/docker/libnetwork"
 	"github.com/docker/swarmkit/agent/exec"
 	"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
 	// exists locally.
-	named, err := reference.ParseNamed(spec.Image)
+	named, err := reference.ParseNormalizedNamed(spec.Image)
 	if err == nil {
 		if _, ok := named.(reference.Canonical); ok {
 			_, 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/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	enginecontainer "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/events"
@@ -18,7 +19,6 @@ import (
 	"github.com/docker/docker/api/types/network"
 	volumetypes "github.com/docker/docker/api/types/volume"
 	clustertypes "github.com/docker/docker/daemon/cluster/provider"
-	"github.com/docker/docker/reference"
 	"github.com/docker/go-connections/nat"
 	"github.com/docker/swarmkit/agent/exec"
 	"github.com/docker/swarmkit/api"
@@ -132,11 +132,11 @@ func (c *containerConfig) name() string {
 
 func (c *containerConfig) image() string {
 	raw := c.spec().Image
-	ref, err := reference.ParseNamed(raw)
+	ref, err := reference.ParseNormalizedNamed(raw)
 	if err != nil {
 		return raw
 	}
-	return reference.WithDefaultTag(ref).String()
+	return reference.FamiliarString(reference.TagNameOnly(ref))
 }
 
 func (c *containerConfig) portBindings() nat.PortMap {

+ 8 - 5
daemon/commit.go

@@ -2,12 +2,12 @@ package daemon
 
 import (
 	"encoding/json"
-	"fmt"
 	"io"
 	"runtime"
 	"strings"
 	"time"
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types/backend"
 	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/builder/dockerfile"
@@ -16,7 +16,7 @@ import (
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/layer"
 	"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),
@@ -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.
 	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() {
@@ -228,10 +228,13 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str
 
 	imageRef := ""
 	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 {
 			return "", err
 		}
+		if !reference.IsNameOnly(newTag) {
+			return "", errors.Errorf("unexpected repository name: %s", c.Repo)
+		}
 		if c.Tag != "" {
 			if newTag, err = reference.WithTag(newTag, c.Tag); err != nil {
 				return "", err
@@ -240,7 +243,7 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str
 		if err := daemon.TagImageWithReference(id, newTag); err != nil {
 			return "", err
 		}
-		imageRef = newTag.String()
+		imageRef = reference.FamiliarString(newTag)
 	}
 
 	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/truncindex"
 	"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/runconfig"
 	volumedrivers "github.com/docker/docker/volume/drivers"
@@ -76,7 +76,7 @@ type Daemon struct {
 	repository                string
 	containers                container.Store
 	execCommands              *exec.Store
-	referenceStore            reference.Store
+	referenceStore            refstore.Store
 	downloadManager           *xfer.LayerDownloadManager
 	uploadManager             *xfer.LayerUploadManager
 	distributionMetadataStore dmetadata.Store
@@ -637,7 +637,7 @@ func NewDaemon(config *Config, registryService registry.Service, containerdRemot
 
 	eventsService := events.New()
 
-	referenceStore, err := reference.NewReferenceStore(filepath.Join(imageRoot, "repositories.json"))
+	referenceStore, err := refstore.NewReferenceStore(filepath.Join(imageRoot, "repositories.json"))
 	if err != nil {
 		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/parsers/kernel"
 	"github.com/docker/docker/pkg/sysinfo"
-	"github.com/docker/docker/reference"
+	refstore "github.com/docker/docker/reference"
 	"github.com/docker/libnetwork"
 	nwconfig "github.com/docker/libnetwork/config"
 	"github.com/docker/libnetwork/drivers/solaris/bridge"
@@ -491,7 +491,7 @@ func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.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
 	return nil
 }

+ 1 - 17
daemon/errors.go

@@ -2,29 +2,13 @@ package daemon
 
 import (
 	"fmt"
-	"strings"
 
 	"github.com/docker/docker/api/errors"
-	"github.com/docker/docker/reference"
 )
 
 func (d *Daemon) imageNotExistToErrcode(err error) error {
 	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
 }

+ 3 - 3
daemon/events/filter.go

@@ -1,9 +1,9 @@
 package events
 
 import (
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/api/types/filters"
-	"github.com/docker/docker/reference"
 )
 
 // 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 {
-	ref, err := reference.ParseNamed(image)
+	ref, err := reference.ParseNormalizedNamed(image)
 	if err != nil {
 		return image
 	}
-	return ref.Name()
+	return reference.FamiliarName(ref)
 }

+ 23 - 13
daemon/image.go

@@ -3,45 +3,55 @@ package daemon
 import (
 	"fmt"
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/builder"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/stringid"
-	"github.com/docker/docker/reference"
 )
 
 // ErrImageDoesNotExist is error returned when no image can be found for a reference.
 type ErrImageDoesNotExist struct {
-	RefOrID string
+	ref reference.Reference
 }
 
 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
 // refOrID.
 func (daemon *Daemon) GetImageID(refOrID string) (image.ID, error) {
-	id, ref, err := reference.ParseIDOrReference(refOrID)
+	ref, err := reference.ParseAnyReference(refOrID)
 	if err != nil {
 		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
 	}
 
 	// 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 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
 					}
 				}
@@ -54,7 +64,7 @@ func (daemon *Daemon) GetImageID(refOrID string) (image.ID, error) {
 		return id, nil
 	}
 
-	return "", ErrImageDoesNotExist{refOrID}
+	return "", ErrImageDoesNotExist{ref}
 }
 
 // GetImage returns an image corresponding to the image referred to by refOrID.

+ 8 - 7
daemon/image_delete.go

@@ -5,12 +5,12 @@ import (
 	"strings"
 	"time"
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/errors"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/stringid"
-	"github.com/docker/docker/reference"
 )
 
 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 {
 			return nil, err
 		}
@@ -99,7 +99,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
 			return nil, err
 		}
 
-		untaggedRecord := types.ImageDeleteResponseItem{Untagged: parsedRef.String()}
+		untaggedRecord := types.ImageDeleteResponseItem{Untagged: reference.FamiliarString(parsedRef)}
 
 		daemon.LogImageEvent(imgID.String(), imgID.String(), "untag")
 		records = append(records, untaggedRecord)
@@ -126,7 +126,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
 							return records, err
 						}
 
-						untaggedRecord := types.ImageDeleteResponseItem{Untagged: repoRef.String()}
+						untaggedRecord := types.ImageDeleteResponseItem{Untagged: reference.FamiliarString(repoRef)}
 						records = append(records, untaggedRecord)
 					} else {
 						remainingRefs = append(remainingRefs, repoRef)
@@ -162,7 +162,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
 					return nil, err
 				}
 
-				untaggedRecord := types.ImageDeleteResponseItem{Untagged: parsedRef.String()}
+				untaggedRecord := types.ImageDeleteResponseItem{Untagged: reference.FamiliarString(parsedRef)}
 
 				daemon.LogImageEvent(imgID.String(), imgID.String(), "untag")
 				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
 // tag is used. Returns the resolved image reference and an 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
 	// is an idempotent operation and it's okay if the reference didn't
 	// exist in the first place.
@@ -255,7 +256,7 @@ func (daemon *Daemon) removeAllReferencesToImageID(imgID image.ID, records *[]ty
 			return err
 		}
 
-		untaggedRecord := types.ImageDeleteResponseItem{Untagged: parsedRef.String()}
+		untaggedRecord := types.ImageDeleteResponseItem{Untagged: reference.FamiliarString(parsedRef)}
 
 		daemon.LogImageEvent(imgID.String(), imgID.String(), "untag")
 		*records = append(*records, untaggedRecord)

+ 2 - 2
daemon/image_history.go

@@ -4,9 +4,9 @@ import (
 	"fmt"
 	"time"
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types/image"
 	"github.com/docker/docker/layer"
-	"github.com/docker/docker/reference"
 )
 
 // 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
 		for _, r := range daemon.referenceStore.References(id.Digest()) {
 			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 (
 	"time"
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/layer"
-	"github.com/docker/docker/reference"
 	"github.com/pkg/errors"
 )
 
@@ -23,9 +23,9 @@ func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) {
 	for _, ref := range refs {
 		switch ref.(type) {
 		case reference.NamedTagged:
-			repoTags = append(repoTags, ref.String())
+			repoTags = append(repoTags, reference.FamiliarString(ref))
 		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"
 
 	dist "github.com/docker/distribution"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/builder"
 	"github.com/docker/docker/distribution"
 	progressutils "github.com/docker/docker/distribution/utils"
 	"github.com/docker/docker/pkg/progress"
-	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
 	"github.com/opencontainers/go-digest"
 	"golang.org/x/net/context"
@@ -24,7 +24,7 @@ func (daemon *Daemon) PullImage(ctx context.Context, image, tag string, metaHead
 	// compatibility.
 	image = strings.TrimSuffix(image, ":")
 
-	ref, err := reference.ParseNamed(image)
+	ref, err := reference.ParseNormalizedNamed(image)
 	if err != nil {
 		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`.
 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 {
 		return nil, err
 	}
-	ref = reference.WithDefaultTag(ref)
+	ref = reference.TagNameOnly(ref)
 
 	pullRegistryAuth := &types.AuthConfig{}
 	if len(authConfigs) > 0 {
@@ -118,12 +118,12 @@ func (daemon *Daemon) GetRepository(ctx context.Context, ref reference.NamedTagg
 		return nil, false, err
 	}
 	// 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
 	}
 
 	// get endpoints
-	endpoints, err := daemon.RegistryService.LookupPullEndpoints(repoInfo.Hostname())
+	endpoints, err := daemon.RegistryService.LookupPullEndpoints(reference.Domain(repoInfo.Name))
 	if err != nil {
 		return nil, false, err
 	}

+ 2 - 2
daemon/image_push.go

@@ -4,17 +4,17 @@ import (
 	"io"
 
 	"github.com/docker/distribution/manifest/schema2"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/distribution"
 	progressutils "github.com/docker/docker/distribution/utils"
 	"github.com/docker/docker/pkg/progress"
-	"github.com/docker/docker/reference"
 	"golang.org/x/net/context"
 )
 
 // 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 {
-	ref, err := reference.ParseNamed(image)
+	ref, err := reference.ParseNormalizedNamed(image)
 	if err != nil {
 		return err
 	}

+ 4 - 4
daemon/image_tag.go

@@ -1,8 +1,8 @@
 package daemon
 
 import (
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/image"
-	"github.com/docker/docker/reference"
 )
 
 // 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
 	}
 
-	newTag, err := reference.WithName(repository)
+	newTag, err := reference.ParseNormalizedNamed(repository)
 	if err != nil {
 		return err
 	}
 	if tag != "" {
-		if newTag, err = reference.WithTag(newTag, tag); err != nil {
+		if newTag, err = reference.WithTag(reference.TrimNamed(newTag), tag); err != nil {
 			return err
 		}
 	}
@@ -32,6 +32,6 @@ func (daemon *Daemon) TagImageWithReference(imageID image.ID, newTag reference.N
 		return err
 	}
 
-	daemon.LogImageEvent(imageID.String(), newTag.String(), "tag")
+	daemon.LogImageEvent(imageID.String(), reference.FamiliarString(newTag), "tag")
 	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 matchErr error
 				for _, pattern := range imageFilters.Get("reference") {
-					found, matchErr = reference.Match(pattern, ref)
+					found, matchErr = reference.FamiliarMatch(pattern, ref)
 					if matchErr != nil {
 						return nil, matchErr
 					}
@@ -145,10 +145,10 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs
 				}
 			}
 			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 {
-				newImage.RepoTags = append(newImage.RepoTags, ref.String())
+				newImage.RepoTags = append(newImage.RepoTags, reference.FamiliarString(ref))
 			}
 		}
 		if newImage.RepoDigests == nil && newImage.RepoTags == nil {

+ 3 - 4
daemon/import.go

@@ -2,13 +2,13 @@ package daemon
 
 import (
 	"encoding/json"
-	"errors"
 	"io"
 	"net/http"
 	"net/url"
 	"runtime"
 	"time"
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/builder/dockerfile"
 	"github.com/docker/docker/dockerversion"
@@ -18,7 +18,7 @@ import (
 	"github.com/docker/docker/pkg/httputils"
 	"github.com/docker/docker/pkg/progress"
 	"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
@@ -35,11 +35,10 @@ func (daemon *Daemon) ImportImage(src string, repository, tag string, msg string
 
 	if repository != "" {
 		var err error
-		newRef, err = reference.ParseNamed(repository)
+		newRef, err = reference.ParseNormalizedNamed(repository)
 		if err != nil {
 			return err
 		}
-
 		if _, isCanonical := newRef.(reference.Canonical); isCanonical {
 			return errors.New("cannot import digest reference")
 		}

+ 1 - 1
daemon/prune.go

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

+ 2 - 2
distribution/config.go

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

+ 4 - 4
distribution/errors.go

@@ -7,12 +7,12 @@ import (
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/distribution"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/registry/api/errcode"
 	"github.com/docker/distribution/registry/api/v2"
 	"github.com/docker/distribution/registry/client"
 	"github.com/docker/distribution/registry/client/auth"
 	"github.com/docker/docker/distribution/xfer"
-	"github.com/docker/docker/reference"
 	"github.com/pkg/errors"
 )
 
@@ -78,11 +78,11 @@ func TranslatePullError(err error, ref reference.Named) error {
 		switch v.Code {
 		case errcode.ErrorCodeDenied:
 			// 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:
-			newErr = errors.Errorf("manifest for %s not found", ref.String())
+			newErr = errors.Errorf("manifest for %s not found", reference.FamiliarString(ref))
 		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 {
 			logrus.Infof("Translating %q to %q", err, newErr)

+ 12 - 15
distribution/pull.go

@@ -1,14 +1,14 @@
 package distribution
 
 import (
-	"errors"
 	"fmt"
 
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/pkg/progress"
-	"github.com/docker/docker/reference"
+	refstore "github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
 	"github.com/opencontainers/go-digest"
 	"golang.org/x/net/context"
@@ -56,12 +56,12 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo
 		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
 	}
 
-	endpoints, err := imagePullConfig.RegistryService.LookupPullEndpoints(repoInfo.Hostname())
+	endpoints, err := imagePullConfig.RegistryService.LookupPullEndpoints(reference.Domain(repoInfo.Name))
 	if err != nil {
 		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)
 		if err != nil {
@@ -147,12 +147,12 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo
 			return TranslatePullError(err, ref)
 		}
 
-		imagePullConfig.ImageEventLogger(ref.String(), repoInfo.Name(), "pull")
+		imagePullConfig.ImageEventLogger(reference.FamiliarString(ref), reference.FamiliarName(repoInfo.Name), "pull")
 		return 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)
@@ -171,17 +171,14 @@ func writeStatus(requestedTag string, out progress.Output, layersDownloaded bool
 }
 
 // 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 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)
 	if err != nil {
 		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)
 		}
 		return nil
-	} else if err != reference.ErrDoesNotExist {
+	} else if err != refstore.ErrDoesNotExist {
 		return err
 	}
 

+ 17 - 17
distribution/pull_v1.go

@@ -12,6 +12,7 @@ import (
 	"time"
 
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/registry/client/transport"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/distribution/xfer"
@@ -22,7 +23,6 @@ import (
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/pkg/stringid"
-	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
 	"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
 		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
 }
 
 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)
 
-	repoData, err := p.session.GetRepositoryData(p.repoInfo)
+	repoData, err := p.session.GetRepositoryData(p.repoInfo.Name)
 	if err != nil {
 		if strings.Contains(err.Error(), "HTTP code: 404") {
 			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
 		return err
@@ -92,13 +92,13 @@ func (p *v1Puller) pullRepository(ctx context.Context, ref reference.Named) erro
 	logrus.Debug("Retrieving the tag list")
 	var tagsList map[string]string
 	if !isTagged {
-		tagsList, err = p.session.GetRemoteTags(repoData.Endpoints, p.repoInfo)
+		tagsList, err = p.session.GetRemoteTags(repoData.Endpoints, p.repoInfo.Name)
 	} else {
 		var tagID 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 {
-			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
 	}
@@ -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
 }
 
@@ -137,7 +137,7 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit
 		return nil
 	}
 
-	localNameRef, err := reference.WithTag(p.repoInfo, img.Tag)
+	localNameRef, err := reference.WithTag(p.repoInfo.Name, img.Tag)
 	if err != nil {
 		retErr := fmt.Errorf("Image (id: %s) has invalid tag: %s", img.ID, img.Tag)
 		logrus.Debug(retErr.Error())
@@ -148,15 +148,15 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit
 		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
 	var lastErr error
 	for _, ep := range p.repoInfo.Index.Mirrors {
 		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 {
 			// 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
 		}
 		success = true
@@ -164,12 +164,12 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit
 	}
 	if !success {
 		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 {
 				// 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.
 				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
 			}
 			success = true
@@ -177,7 +177,7 @@ func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.Reposit
 		}
 	}
 	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())
 		return err
 	}

+ 11 - 10
distribution/pull_v2.go

@@ -15,6 +15,7 @@ import (
 	"github.com/docker/distribution/manifest/manifestlist"
 	"github.com/docker/distribution/manifest/schema1"
 	"github.com/docker/distribution/manifest/schema2"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/registry/api/errcode"
 	"github.com/docker/distribution/registry/client/auth"
 	"github.com/docker/distribution/registry/client/transport"
@@ -26,7 +27,7 @@ import (
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/pkg/stringid"
-	"github.com/docker/docker/reference"
+	refstore "github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
 	"github.com/opencontainers/go-digest"
 	"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
 }
@@ -317,7 +318,7 @@ func (ld *v2LayerDescriptor) truncateDownloadFile() error {
 
 func (ld *v2LayerDescriptor) Registered(diffID layer.DiffID) {
 	// 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) {
@@ -343,7 +344,7 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdat
 		}
 		tagOrDigest = digested.Digest().String()
 	} 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 {
@@ -371,8 +372,8 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdat
 	// the other side speaks the v2 protocol.
 	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 (
 		id             digest.Digest
@@ -410,7 +411,7 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdat
 			if oldTagID == id {
 				return false, addDigestReference(p.config.ReferenceStore, ref, manifestDigest, id)
 			}
-		} else if err != reference.ErrDoesNotExist {
+		} else if err != refstore.ErrDoesNotExist {
 			return false, err
 		}
 
@@ -802,13 +803,13 @@ func verifySchema1Manifest(signedManifest *schema1.SignedManifest, ref reference
 	m = &signedManifest.Manifest
 
 	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) {
-		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 {
-		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
 }

+ 2 - 2
distribution/pull_v2_test.go

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

+ 8 - 8
distribution/push.go

@@ -7,9 +7,9 @@ import (
 	"io"
 
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/pkg/progress"
-	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
 	"golang.org/x/net/context"
 )
@@ -64,16 +64,16 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo
 		return err
 	}
 
-	endpoints, err := imagePushConfig.RegistryService.LookupPushEndpoints(repoInfo.Hostname())
+	endpoints, err := imagePushConfig.RegistryService.LookupPushEndpoints(reference.Domain(repoInfo.Name))
 	if err != nil {
 		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 {
-		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 (
@@ -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)
 		if err != nil {
@@ -135,12 +135,12 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo
 			return err
 		}
 
-		imagePushConfig.ImageEventLogger(ref.String(), repoInfo.Name(), "push")
+		imagePushConfig.ImageEventLogger(reference.FamiliarString(ref), reference.FamiliarName(repoInfo.Name), "push")
 		return 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
 }

+ 5 - 5
distribution/push_v1.go

@@ -5,6 +5,7 @@ import (
 	"sync"
 
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/registry/client/transport"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/dockerversion"
@@ -14,7 +15,6 @@ import (
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/pkg/stringid"
-	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
 	"github.com/opencontainers/go-digest"
 	"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 {
 			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
 				}
 			}
@@ -385,7 +385,7 @@ func (p *v1Pusher) pushRepository(ctx context.Context) error {
 
 	// 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
-	repoData, err := p.session.PushImageJSONIndex(p.repoInfo, imageIndex, false, nil)
+	repoData, err := p.session.PushImageJSONIndex(p.repoInfo.Name, imageIndex, false, nil)
 	if err != nil {
 		return err
 	}
@@ -395,7 +395,7 @@ func (p *v1Pusher) pushRepository(ctx context.Context) error {
 			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
 }
 

+ 27 - 34
distribution/push_v2.go

@@ -15,7 +15,7 @@ import (
 	"github.com/docker/distribution"
 	"github.com/docker/distribution/manifest/schema1"
 	"github.com/docker/distribution/manifest/schema2"
-	distreference "github.com/docker/distribution/reference"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/registry/client"
 	apitypes "github.com/docker/docker/api/types"
 	"github.com/docker/docker/distribution/metadata"
@@ -24,7 +24,6 @@ import (
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/pkg/stringid"
-	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
 	"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 {
 		imageID, err := p.config.ReferenceStore.Get(p.ref)
 		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)
@@ -105,23 +104,23 @@ func (p *v2Pusher) pushV2Repository(ctx context.Context) (err error) {
 	}
 
 	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
 }
 
 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)
 	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)
 	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())
@@ -140,7 +139,7 @@ func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, id
 	descriptorTemplate := v2PushDescriptor{
 		v2MetadataService: p.v2MetadataService,
 		hmacKey:           hmacKey,
-		repoInfo:          p.repoInfo,
+		repoInfo:          p.repoInfo.Name,
 		ref:               p.ref,
 		repo:              p.repo,
 		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)
 
-		manifestRef, err := distreference.WithTag(p.repo.Named(), ref.Tag())
+		manifestRef, err := reference.WithTag(p.repo.Named(), ref.Tag())
 		if err != nil {
 			return err
 		}
@@ -248,7 +247,7 @@ type v2PushDescriptor struct {
 }
 
 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 {
@@ -304,23 +303,22 @@ func (pd *v2PushDescriptor) Upload(ctx context.Context, progressOutput progress.
 		createOpts := []distribution.BlobCreateOption{}
 
 		if len(mountCandidate.SourceRepository) > 0 {
-			namedRef, err := reference.WithName(mountCandidate.SourceRepository)
+			namedRef, err := reference.ParseNormalizedNamed(mountCandidate.SourceRepository)
 			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)
 				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 {
-				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
 			}
 
-			canonicalRef, err := distreference.WithDigest(distreference.TrimNamed(remoteRef), mountCandidate.Digest)
+			canonicalRef, err := reference.WithDigest(reference.TrimNamed(remoteRef), mountCandidate.Digest)
 			if err != nil {
 				logrus.Errorf("failed to make canonical reference: %v", err)
 				continue
@@ -347,7 +345,7 @@ func (pd *v2PushDescriptor) Upload(ctx context.Context, progressOutput progress.
 			// Cache mapping from this layer's DiffID to the blobsum
 			if err := pd.v2MetadataService.TagAndAdd(diffID, pd.hmacKey, metadata.V2Metadata{
 				Digest:           err.Descriptor.Digest,
-				SourceRepository: pd.repoInfo.FullName(),
+				SourceRepository: pd.repoInfo.Name(),
 			}); err != nil {
 				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
 	if err := pd.v2MetadataService.TagAndAdd(diffID, pd.hmacKey, metadata.V2Metadata{
 		Digest:           pushDigest,
-		SourceRepository: pd.repoInfo.FullName(),
+		SourceRepository: pd.repoInfo.Name(),
 	}); err != nil {
 		return distribution.Descriptor{}, xfer.DoNotRetry{Err: err}
 	}
@@ -490,7 +488,7 @@ func (pd *v2PushDescriptor) layerAlreadyExists(
 	// filter the metadata
 	candidates := []metadata.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
 		}
 		candidates = append(candidates, meta)
@@ -521,16 +519,16 @@ func (pd *v2PushDescriptor) layerAlreadyExists(
 attempts:
 	for _, dgst := range layerDigests {
 		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)
 		pd.checkedDigests[meta.Digest] = struct{}{}
 		switch err {
 		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
 				if err := pd.v2MetadataService.TagAndAdd(diffID, pd.hmacKey, metadata.V2Metadata{
 					Digest:           desc.Digest,
-					SourceRepository: pd.repoInfo.FullName(),
+					SourceRepository: pd.repoInfo.Name(),
 				}); err != nil {
 					return distribution.Descriptor{}, false, xfer.DoNotRetry{Err: err}
 				}
@@ -539,12 +537,12 @@ attempts:
 			exists = true
 			break attempts
 		case distribution.ErrBlobUnknown:
-			if meta.SourceRepository == pd.repoInfo.FullName() {
+			if meta.SourceRepository == pd.repoInfo.Name() {
 				// remove the mapping to the target repository
 				pd.v2MetadataService.Remove(*meta)
 			}
 		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{}
 	for _, meta := range v2Metadata {
 		sourceRepo, err := reference.ParseNamed(meta.SourceRepository)
-		if err != nil || repoInfo.Hostname() != sourceRepo.Hostname() {
+		if err != nil || reference.Domain(repoInfo) != reference.Domain(sourceRepo) {
 			continue
 		}
 		// target repository is not a viable candidate
-		if meta.SourceRepository == repoInfo.FullName() {
+		if meta.SourceRepository == repoInfo.Name() {
 			continue
 		}
 		candidates = append(candidates, meta)
@@ -653,7 +651,7 @@ func sortV2MetadataByLikenessAndAge(repoInfo reference.Named, hmacKey []byte, ma
 	sort.Stable(byLikeness{
 		arr:            marr,
 		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 {
-	// 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, "/")
 }
 

+ 28 - 24
distribution/push_v2_test.go

@@ -8,11 +8,10 @@ import (
 	"github.com/docker/distribution"
 	"github.com/docker/distribution/context"
 	"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/layer"
 	"github.com/docker/docker/pkg/progress"
-	"github.com/docker/docker/reference"
 	"github.com/opencontainers/go-digest"
 )
 
@@ -43,8 +42,8 @@ func TestGetRepositoryMountCandidates(t *testing.T) {
 			name:          "one item matching",
 			targetRepo:    "busybox",
 			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",
@@ -63,13 +62,13 @@ func TestGetRepositoryMountCandidates(t *testing.T) {
 			maxCandidates: -1,
 			metadata: []metadata.V2Metadata{
 				{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{
-				{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("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",
 			maxCandidates: -1,
 			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("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", "6", "127.0.0.1/bar"),
 				taggedMetadata("efgh", "7", "127.0.0.1/foo/bar"),
@@ -105,23 +104,25 @@ func TestGetRepositoryMountCandidates(t *testing.T) {
 			targetRepo:    "user/app",
 			maxCandidates: 3,
 			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("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{
 				// 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
-				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 {
 			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("orange"), SourceRepository: "docker.io/busybox/subapp"},
 				{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"},
 			},
-			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",
@@ -374,7 +378,7 @@ func TestLayerAlreadyExists(t *testing.T) {
 			},
 		},
 	} {
-		repoInfo, err := reference.ParseNamed(tc.targetRepo)
+		repoInfo, err := reference.ParseNormalizedNamed(tc.targetRepo)
 		if err != nil {
 			t.Fatalf("[%s] failed to parse reference name: %v", tc.name, err)
 		}
@@ -476,7 +480,7 @@ type mockRepo struct {
 
 var _ distribution.Repository = &mockRepo{}
 
-func (m *mockRepo) Named() distreference.Named {
+func (m *mockRepo) Named() reference.Named {
 	m.t.Fatalf("Named() not implemented")
 	return nil
 }

+ 4 - 4
distribution/registry.go

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

+ 3 - 3
distribution/registry_unit_test.go

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

+ 5 - 5
image/tarexport/load.go

@@ -11,6 +11,7 @@ import (
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/distribution"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/image/v1"
 	"github.com/docker/docker/layer"
@@ -21,7 +22,6 @@ import (
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/symlink"
 	"github.com/docker/docker/pkg/system"
-	"github.com/docker/docker/reference"
 	"github.com/opencontainers/go-digest"
 )
 
@@ -117,7 +117,7 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool)
 
 		imageRefCount = 0
 		for _, repoTag := range m.RepoTags {
-			named, err := reference.ParseNamed(repoTag)
+			named, err := reference.ParseNormalizedNamed(repoTag)
 			if err != nil {
 				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)
 			}
 			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++
 		}
 
@@ -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 {
 	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 {
@@ -249,7 +249,7 @@ func (l *tarexporter) legacyLoad(tmpDir string, outStream io.Writer, progressOut
 			if !ok {
 				return fmt.Errorf("invalid target ID: %v", oldID)
 			}
-			named, err := reference.WithName(name)
+			named, err := reference.ParseNormalizedNamed(name)
 			if err != nil {
 				return err
 			}

+ 28 - 24
image/tarexport/save.go

@@ -10,12 +10,12 @@ import (
 	"time"
 
 	"github.com/docker/distribution"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/image/v1"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/system"
-	"github.com/docker/docker/reference"
 	"github.com/opencontainers/go-digest"
 	"github.com/pkg/errors"
 )
@@ -51,16 +51,12 @@ func (l *tarexporter) parseNames(names []string) (map[image.ID]*imageDescriptor,
 		}
 
 		if ref != nil {
-			var tagged reference.NamedTagged
 			if _, ok := ref.(reference.Canonical); ok {
 				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 {
@@ -73,19 +69,26 @@ func (l *tarexporter) parseNames(names []string) (map[image.ID]*imageDescriptor,
 	}
 
 	for _, name := range names {
-		id, ref, err := reference.ParseIDOrReference(name)
+		ref, err := reference.ParseAnyReference(name)
 		if err != nil {
 			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)
 			if err != nil {
 				return nil, err
@@ -93,8 +96,8 @@ func (l *tarexporter) parseNames(names []string) (map[image.ID]*imageDescriptor,
 			addAssoc(imgID, nil)
 			continue
 		}
-		if reference.IsNameOnly(ref) {
-			assocs := l.rs.ReferencesByName(ref)
+		if reference.IsNameOnly(namedRef) {
+			assocs := l.rs.ReferencesByName(namedRef)
 			for _, assoc := range assocs {
 				addAssoc(image.IDFromDigest(assoc.ID), assoc.Ref)
 			}
@@ -107,11 +110,11 @@ func (l *tarexporter) parseNames(names []string) (map[image.ID]*imageDescriptor,
 			}
 			continue
 		}
-		id, err = l.rs.Get(ref)
+		id, err := l.rs.Get(namedRef)
 		if err != nil {
 			return nil, err
 		}
-		addAssoc(image.IDFromDigest(id), ref)
+		addAssoc(image.IDFromDigest(id), namedRef)
 
 	}
 	return imgDescr, nil
@@ -144,11 +147,12 @@ func (s *saveSession) save(outStream io.Writer) error {
 		var layers []string
 
 		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 {

+ 3 - 3
image/tarexport/tarexport.go

@@ -4,7 +4,7 @@ import (
 	"github.com/docker/distribution"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/layer"
-	"github.com/docker/docker/reference"
+	refstore "github.com/docker/docker/reference"
 )
 
 const (
@@ -26,7 +26,7 @@ type manifestItem struct {
 type tarexporter struct {
 	is             image.Store
 	ls             layer.Store
-	rs             reference.Store
+	rs             refstore.Store
 	loggerImgEvent LogImageEvent
 }
 
@@ -37,7 +37,7 @@ type LogImageEvent interface {
 }
 
 // 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{
 		is:             is,
 		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"}
 	var names []string
 	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

+ 10 - 5
migrate/v1/migratev1.go

@@ -14,12 +14,13 @@ import (
 	"encoding/json"
 
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/image"
 	imagev1 "github.com/docker/docker/image/v1"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/pkg/ioutils"
-	"github.com/docker/docker/reference"
+	refstore "github.com/docker/docker/reference"
 	"github.com/opencontainers/go-digest"
 )
 
@@ -56,7 +57,7 @@ var (
 
 // Migrate takes an old graph directory and transforms the metadata into the
 // 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)
 	if _, err := os.Lstat(graphDir); os.IsNotExist(err) {
 		return nil
@@ -322,11 +323,15 @@ func migrateRefs(root, driverName string, rs refAdder, mappings map[string]image
 	for name, repo := range repos.Repositories {
 		for tag, id := range repo {
 			if strongID, exists := mappings[id]; exists {
-				ref, err := reference.WithName(name)
+				ref, err := reference.ParseNormalizedNamed(name)
 				if err != nil {
 					logrus.Errorf("migrate tags: invalid name %q, %q", name, err)
 					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 {
 					canonical, err := reference.WithDigest(reference.TrimNamed(ref), dgst)
 					if err != nil {
@@ -334,7 +339,7 @@ func migrateRefs(root, driverName string, rs refAdder, mappings map[string]image
 						continue
 					}
 					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 {
 					tagRef, err := reference.WithTag(ref, tag)
@@ -343,7 +348,7 @@ func migrateRefs(root, driverName string, rs refAdder, mappings map[string]image
 						continue
 					}
 					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)

+ 4 - 4
migrate/v1/migratev1_test.go

@@ -13,10 +13,10 @@ import (
 	"runtime"
 	"testing"
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/layer"
-	"github.com/docker/docker/reference"
 	"github.com/opencontainers/go-digest"
 )
 
@@ -40,9 +40,9 @@ func TestMigrateRefs(t *testing.T) {
 	}
 
 	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) {

+ 13 - 13
plugin/backend_linux.go

@@ -18,6 +18,7 @@ import (
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/distribution/manifest/schema2"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/distribution"
@@ -30,7 +31,7 @@ import (
 	"github.com/docker/docker/pkg/pools"
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/plugin/v2"
-	"github.com/docker/docker/reference"
+	refstore "github.com/docker/docker/reference"
 	"github.com/opencontainers/go-digest"
 	"github.com/pkg/errors"
 	"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()
 
 	// revalidate because Pull is public
-	nameref, err := reference.ParseNamed(name)
+	nameref, err := reference.ParseNormalizedNamed(name)
 	if err != nil {
 		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")
 	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()
 
 	// revalidate because Pull is public
-	nameref, err := reference.ParseNamed(name)
+	nameref, err := reference.ParseNormalizedNamed(name)
 	if err != nil {
 		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 {
 		return err
@@ -370,7 +371,7 @@ func (pm *Manager) Push(ctx context.Context, name string, metaHeader http.Header
 		return err
 	}
 
-	ref, err := reference.ParseNamed(p.Name())
+	ref, err := reference.ParseNormalizedNamed(p.Name())
 	if err != nil {
 		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}
 }
 
-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,
 			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) {
 	if r.name.String() != ref.String() {
-		return digest.Digest(""), reference.ErrDoesNotExist
+		return digest.Digest(""), refstore.ErrDoesNotExist
 	}
 	return r.pluginID, nil
 }
@@ -664,15 +665,14 @@ func (pm *Manager) CreateFromContext(ctx context.Context, tarCtx io.ReadCloser,
 	pm.muGC.RLock()
 	defer pm.muGC.RUnlock()
 
-	ref, err := reference.ParseNamed(options.RepoName)
+	ref, err := reference.ParseNormalizedNamed(options.RepoName)
 	if err != nil {
 		return errors.Wrapf(err, "failed to parse reference %v", options.RepoName)
 	}
 	if _, ok := ref.(reference.Canonical); ok {
 		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()
 		return err
@@ -754,7 +754,7 @@ func (pm *Manager) CreateFromContext(ctx context.Context, tarCtx io.ReadCloser,
 	if err != nil {
 		return err
 	}
-	p.PluginObj.PluginReference = taggedRef.String()
+	p.PluginObj.PluginReference = name
 
 	pm.config.LogPluginEvent(p.PluginObj.ID, name, "create")
 

+ 1 - 1
plugin/backend_unsupported.go

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

+ 5 - 5
plugin/store.go

@@ -5,10 +5,10 @@ import (
 	"strings"
 
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/pkg/plugingetter"
 	"github.com/docker/docker/pkg/plugins"
 	"github.com/docker/docker/plugin/v2"
-	"github.com/docker/docker/reference"
 	"github.com/pkg/errors"
 )
 
@@ -230,19 +230,19 @@ func (ps *Store) resolvePluginID(idOrName string) (string, error) {
 		return idOrName, nil
 	}
 
-	ref, err := reference.ParseNamed(idOrName)
+	ref, err := reference.ParseNormalizedNamed(idOrName)
 	if err != nil {
 		return "", errors.WithStack(ErrNotFound(idOrName))
 	}
 	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))
 	}
 
-	fullRef := reference.WithDefaultTag(ref)
+	ref = reference.TagNameOnly(ref)
 
 	for _, p := range ps.plugins {
-		if p.PluginObj.Name == fullRef.String() {
+		if p.PluginObj.Name == reference.FamiliarString(ref) {
 			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"
 	"sync"
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/opencontainers/go-digest"
 )
@@ -21,18 +22,18 @@ var (
 
 // An Association is a tuple associating a reference with an image ID.
 type Association struct {
-	Ref Named
+	Ref reference.Named
 	ID  digest.Digest
 }
 
 // Store provides the set of methods which can operate on a tag store.
 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 {
@@ -44,24 +45,28 @@ type store struct {
 	Repositories map[string]repository
 	// referencesByIDCache is a cache of references indexed by ID, to speed
 	// 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,
 // including the repository name.
 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
 
-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
 // the set of references are serialized in JSON format.
@@ -74,7 +79,7 @@ func NewReferenceStore(jsonPath string) (Store, error) {
 	store := &store{
 		jsonPath:            abspath,
 		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.
 	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
 // 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 store.addReference(WithDefaultTag(ref), id, force)
+	return store.addReference(reference.TagNameOnly(ref), id, force)
 }
 
 // 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)
 }
 
-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")
 	}
 
 	store.mu.Lock()
 	defer store.mu.Unlock()
 
-	repository, exists := store.Repositories[ref.Name()]
+	repository, exists := store.Repositories[refName]
 	if !exists || repository == nil {
 		repository = make(map[string]digest.Digest)
-		store.Repositories[ref.Name()] = repository
+		store.Repositories[refName] = repository
 	}
 
-	refStr := ref.String()
 	oldID, exists := repository[refStr]
 
 	if exists {
 		// 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())
 		}
 
 		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 {
@@ -138,7 +145,7 @@ func (store *store) addReference(ref Named, id digest.Digest, force bool) error
 
 	repository[refStr] = id
 	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
 
@@ -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
 // 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()
 	defer store.mu.Unlock()
 
-	repoName := ref.Name()
-
-	repository, exists := store.Repositories[repoName]
+	repository, exists := store.Repositories[refName]
 	if !exists {
 		return false, ErrDoesNotExist
 	}
 
-	refStr := ref.String()
 	if id, exists := repository[refStr]; exists {
 		delete(repository, refStr)
 		if len(repository) == 0 {
-			delete(store.Repositories, repoName)
+			delete(store.Repositories, refName)
 		}
 		if store.referencesByIDCache[id] != nil {
 			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
-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()
 	defer store.mu.RUnlock()
 
-	repository, exists := store.Repositories[ref.Name()]
+	repository, exists := store.Repositories[refName]
 	if !exists || repository == nil {
 		return "", ErrDoesNotExist
 	}
 
-	id, exists := repository[ref.String()]
+	id, exists := repository[refStr]
 	if !exists {
 		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
 // 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()
 	defer store.mu.RUnlock()
 
@@ -208,7 +231,7 @@ func (store *store) References(id digest.Digest) []Named {
 	// 1) We must not return a mutable
 	// 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] {
 		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.
 // If there are no references known for this repository name,
 // 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()
 	defer store.mu.RUnlock()
 
-	repository, exists := store.Repositories[ref.Name()]
+	repository, exists := store.Repositories[refName]
 	if !exists {
 		return nil
 	}
 
 	var associations []Association
 	for refStr, refID := range repository {
-		ref, err := ParseNamed(refStr)
+		ref, err := reference.ParseNormalizedNamed(refStr)
 		if err != nil {
 			// Should never happen
 			return nil
@@ -270,13 +295,13 @@ func (store *store) reload() error {
 
 	for _, repository := range store.Repositories {
 		for refStr, refID := range repository {
-			ref, err := ParseNamed(refStr)
+			ref, err := reference.ParseNormalizedNamed(refStr)
 			if err != nil {
 				// Should never happen
 				continue
 			}
 			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
 		}

+ 16 - 15
reference/store_test.go

@@ -8,6 +8,7 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/docker/distribution/reference"
 	"github.com/opencontainers/go-digest"
 )
 
@@ -45,7 +46,7 @@ func TestLoad(t *testing.T) {
 	}
 
 	for refStr, expectedID := range saveLoadTestCases {
-		ref, err := ParseNamed(refStr)
+		ref, err := reference.ParseNormalizedNamed(refStr)
 		if err != nil {
 			t.Fatalf("failed to parse reference: %v", err)
 		}
@@ -74,11 +75,11 @@ func TestSave(t *testing.T) {
 	}
 
 	for refStr, id := range saveLoadTestCases {
-		ref, err := ParseNamed(refStr)
+		ref, err := reference.ParseNormalizedNamed(refStr)
 		if err != nil {
 			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)
 			if err != nil {
 				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")
 
 	// Try adding a reference with no tag or digest
-	nameOnly, err := WithName("username/repo")
+	nameOnly, err := reference.ParseNormalizedNamed("username/repo")
 	if err != nil {
 		t.Fatalf("could not parse reference: %v", err)
 	}
@@ -129,7 +130,7 @@ func TestAddDeleteGet(t *testing.T) {
 	}
 
 	// Add a few references
-	ref1, err := ParseNamed("username/repo1:latest")
+	ref1, err := reference.ParseNormalizedNamed("username/repo1:latest")
 	if err != nil {
 		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)
 	}
 
-	ref2, err := ParseNamed("username/repo1:old")
+	ref2, err := reference.ParseNormalizedNamed("username/repo1:old")
 	if err != nil {
 		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)
 	}
 
-	ref3, err := ParseNamed("username/repo1:alias")
+	ref3, err := reference.ParseNormalizedNamed("username/repo1:alias")
 	if err != nil {
 		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)
 	}
 
-	ref4, err := ParseNamed("username/repo2:latest")
+	ref4, err := reference.ParseNormalizedNamed("username/repo2:latest")
 	if err != nil {
 		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)
 	}
 
-	ref5, err := ParseNamed("username/repo3@sha256:58153dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c")
+	ref5, err := reference.ParseNormalizedNamed("username/repo3@sha256:58153dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c")
 	if err != nil {
 		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)
 	}
 
@@ -228,7 +229,7 @@ func TestAddDeleteGet(t *testing.T) {
 	}
 
 	// Get should return ErrDoesNotExist for a nonexistent repo
-	nonExistRepo, err := ParseNamed("username/nonexistrepo:latest")
+	nonExistRepo, err := reference.ParseNormalizedNamed("username/nonexistrepo:latest")
 	if err != nil {
 		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
-	nonExistTag, err := ParseNamed("username/repo1:nonexist")
+	nonExistTag, err := reference.ParseNormalizedNamed("username/repo1:nonexist")
 	if err != nil {
 		t.Fatalf("could not parse reference: %v", err)
 	}
@@ -263,7 +264,7 @@ func TestAddDeleteGet(t *testing.T) {
 	}
 
 	// Check ReferencesByName
-	repoName, err := WithName("username/repo1")
+	repoName, err := reference.ParseNormalizedNamed("username/repo1")
 	if err != nil {
 		t.Fatalf("could not parse reference: %v", err)
 	}
@@ -334,7 +335,7 @@ func TestInvalidTags(t *testing.T) {
 	id := digest.Digest("sha256:470022b8af682154f57a2163d030eb369549549cba00edc69e1b99b46bb924d6")
 
 	// sha256 as repo name
-	ref, err := ParseNamed("sha256:abc")
+	ref, err := reference.ParseNormalizedNamed("sha256:abc")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -344,7 +345,7 @@ func TestInvalidTags(t *testing.T) {
 	}
 
 	// setting digest as a tag
-	ref, err = ParseNamed("registry@sha256:367eb40fd0330a7e464777121e39d2f5b3e8e23a1e159342e53ab05c9e4d94e6")
+	ref, err = reference.ParseNormalizedNamed("registry@sha256:367eb40fd0330a7e464777121e39d2f5b3e8e23a1e159342e53ab05c9e4d94e6")
 	if err != nil {
 		t.Fatal(err)
 	}

+ 4 - 9
registry/config.go

@@ -10,7 +10,6 @@ import (
 	"github.com/docker/distribution/reference"
 	registrytypes "github.com/docker/docker/api/types/registry"
 	"github.com/docker/docker/opts"
-	forkedref "github.com/docker/docker/reference"
 	"github.com/pkg/errors"
 	"github.com/spf13/pflag"
 )
@@ -271,8 +270,9 @@ func ValidateMirror(val string) (string, error) {
 
 // ValidateIndexName validates an index name.
 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, "-") {
 		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), '/')
 
-	// TODO: remove used of forked reference package
-	nameref, err := forkedref.ParseNamed(name.String())
-	if err != nil {
-		return nil, err
-	}
 	return &RepositoryInfo{
-		Named:    nameref,
+		Name:     reference.TrimNamed(name),
 		Index:    index,
 		Official: official,
 	}, nil

+ 1 - 1
registry/registry_mock_test.go

@@ -17,8 +17,8 @@ import (
 	"testing"
 	"time"
 
+	"github.com/docker/distribution/reference"
 	registrytypes "github.com/docker/docker/api/types/registry"
-	"github.com/docker/docker/reference"
 	"github.com/gorilla/mux"
 
 	"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/docker/api/types"
 	registrytypes "github.com/docker/docker/api/types/registry"
-	forkedref "github.com/docker/docker/reference"
 )
 
 var (
@@ -202,7 +201,7 @@ func TestGetRemoteImageLayer(t *testing.T) {
 
 func TestGetRemoteTag(t *testing.T) {
 	r := spawnTestRegistrySession(t)
-	repoRef, err := forkedref.ParseNamed(REPO)
+	repoRef, err := reference.ParseNormalizedNamed(REPO)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -212,7 +211,7 @@ func TestGetRemoteTag(t *testing.T) {
 	}
 	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 {
 		t.Fatal(err)
 	}
@@ -224,7 +223,7 @@ func TestGetRemoteTag(t *testing.T) {
 
 func TestGetRemoteTags(t *testing.T) {
 	r := spawnTestRegistrySession(t)
-	repoRef, err := forkedref.ParseNamed(REPO)
+	repoRef, err := reference.ParseNormalizedNamed(REPO)
 	if err != nil {
 		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["test"], imageID, "Expected tag test to map to "+imageID)
 
-	bazRef, err := forkedref.ParseNamed("foo42/baz")
+	bazRef, err := reference.ParseNormalizedNamed("foo42/baz")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -253,7 +252,7 @@ func TestGetRepositoryData(t *testing.T) {
 		t.Fatal(err)
 	}
 	host := "http://" + parsedURL.Host + "/v1/"
-	repoRef, err := forkedref.ParseNamed(REPO)
+	repoRef, err := reference.ParseNormalizedNamed(REPO)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -516,9 +515,9 @@ func TestParseRepositoryInfo(t *testing.T) {
 			t.Error(err)
 		} else {
 			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.Official, expectedRepoInfo.Official, reposName)
 		}
@@ -689,7 +688,7 @@ func TestMirrorEndpointLookup(t *testing.T) {
 
 func TestPushRegistryTag(t *testing.T) {
 	r := spawnTestRegistrySession(t)
-	repoRef, err := forkedref.ParseNamed(REPO)
+	repoRef, err := reference.ParseNormalizedNamed(REPO)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -711,7 +710,7 @@ func TestPushImageJSONIndex(t *testing.T) {
 			Checksum: "sha256:bea7bf2e4bacd479344b737328db47b18880d09096e6674165533aa994f5e9f2",
 		},
 	}
-	repoRef, err := forkedref.ParseNamed(REPO)
+	repoRef, err := reference.ParseNormalizedNamed(REPO)
 	if err != nil {
 		t.Fatal(err)
 	}

+ 10 - 10
registry/session.go

@@ -19,6 +19,7 @@ import (
 	"strings"
 
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/registry/api/errcode"
 	"github.com/docker/docker/api/types"
 	registrytypes "github.com/docker/docker/api/types/registry"
@@ -26,7 +27,6 @@ import (
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/tarsum"
-	"github.com/docker/docker/reference"
 )
 
 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
 // successfully.
 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 {
 		// 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
 // tag names as the keys and image IDs as the values.
 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 {
 		// 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
 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)
 
@@ -450,7 +450,7 @@ func (r *Session) GetRepositoryData(name reference.Named) (*RepositoryData, erro
 		if err != nil {
 			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
@@ -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 {
 	// "jsonify" the string
 	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))
 	if err != nil {
@@ -619,7 +619,7 @@ func (r *Session) PushRegistryTag(remote reference.Named, revision, tag, registr
 	}
 	res.Body.Close()
 	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
 }
@@ -645,7 +645,7 @@ func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData,
 	if validate {
 		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("Image list pushed to index:\n%s", imgListJSON)
 	headers := map[string][]string{
@@ -683,7 +683,7 @@ func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData,
 			if err != nil {
 				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"]
 		logrus.Debugf("Auth token: %v", tokens)
@@ -701,7 +701,7 @@ func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData,
 			if err != nil {
 				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
 
 import (
+	"github.com/docker/distribution/reference"
 	registrytypes "github.com/docker/docker/api/types/registry"
-	"github.com/docker/docker/reference"
 )
 
 // RepositoryData tracks the image list, list of endpoints for a repository
@@ -57,7 +57,7 @@ var apiVersions = map[APIVersion]string{
 
 // RepositoryInfo describes a repository
 type RepositoryInfo struct {
-	reference.Named
+	Name reference.Named
 	// Index points to registry information
 	Index *registrytypes.IndexInfo
 	// Official indicates whether the repository is considered official.