Sfoglia il codice sorgente

Remove use of forked reference package for cli

Use resolving to repo info as the split point between the
legitimate reference package and forked reference package.

Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
Derek McGowan 8 anni fa
parent
commit
0421f5173d

+ 22 - 15
cli/command/container/create.go

@@ -5,6 +5,7 @@ import (
 	"io"
 	"os"
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/container"
 	networktypes "github.com/docker/docker/api/types/network"
@@ -13,8 +14,6 @@ import (
 	"github.com/docker/docker/cli/command/image"
 	apiclient "github.com/docker/docker/client"
 	"github.com/docker/docker/pkg/jsonmessage"
-	// FIXME migrate to docker/distribution/reference
-	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
 	"github.com/spf13/cobra"
 	"github.com/spf13/pflag"
@@ -72,7 +71,7 @@ func runCreate(dockerCli *command.DockerCli, flags *pflag.FlagSet, opts *createO
 }
 
 func pullImage(ctx context.Context, dockerCli *command.DockerCli, image string, out io.Writer) error {
-	ref, err := reference.ParseNamed(image)
+	ref, err := reference.ParseNormalizedNamed(image)
 	if err != nil {
 		return err
 	}
@@ -150,7 +149,12 @@ func newCIDFile(path string) (*cidFile, error) {
 func createContainer(ctx context.Context, dockerCli *command.DockerCli, config *container.Config, hostConfig *container.HostConfig, networkingConfig *networktypes.NetworkingConfig, cidfile, name string) (*container.ContainerCreateCreatedBody, error) {
 	stderr := dockerCli.Err()
 
-	var containerIDFile *cidFile
+	var (
+		containerIDFile *cidFile
+		trustedRef      reference.Canonical
+		namedRef        reference.Named
+	)
+
 	if cidfile != "" {
 		var err error
 		if containerIDFile, err = newCIDFile(cidfile); err != nil {
@@ -159,21 +163,24 @@ func createContainer(ctx context.Context, dockerCli *command.DockerCli, config *
 		defer containerIDFile.Close()
 	}
 
-	var trustedRef reference.Canonical
-	_, ref, err := reference.ParseIDOrReference(config.Image)
+	ref, err := reference.ParseAnyReference(config.Image)
 	if err != nil {
 		return nil, err
 	}
-	if ref != nil {
-		ref = reference.WithDefaultTag(ref)
+	if named, ok := ref.(reference.Named); ok {
+		if reference.IsNameOnly(named) {
+			namedRef = reference.EnsureTagged(named)
+		} else {
+			namedRef = named
+		}
 
-		if ref, ok := ref.(reference.NamedTagged); ok && command.IsTrusted() {
+		if taggedRef, ok := namedRef.(reference.NamedTagged); ok && command.IsTrusted() {
 			var err error
-			trustedRef, err = image.TrustedReference(ctx, dockerCli, ref, nil)
+			trustedRef, err = image.TrustedReference(ctx, dockerCli, taggedRef, nil)
 			if err != nil {
 				return nil, err
 			}
-			config.Image = trustedRef.String()
+			config.Image = reference.FamiliarString(trustedRef)
 		}
 	}
 
@@ -182,15 +189,15 @@ func createContainer(ctx context.Context, dockerCli *command.DockerCli, config *
 
 	//if image not found try to pull it
 	if err != nil {
-		if apiclient.IsErrImageNotFound(err) && ref != nil {
-			fmt.Fprintf(stderr, "Unable to find image '%s' locally\n", ref.String())
+		if apiclient.IsErrImageNotFound(err) && namedRef != nil {
+			fmt.Fprintf(stderr, "Unable to find image '%s' locally\n", reference.FamiliarString(namedRef))
 
 			// we don't want to write to stdout anything apart from container.ID
 			if err = pullImage(ctx, dockerCli, config.Image, stderr); err != nil {
 				return nil, err
 			}
-			if ref, ok := ref.(reference.NamedTagged); ok && trustedRef != nil {
-				if err := image.TagTrusted(ctx, dockerCli, trustedRef, ref); err != nil {
+			if taggedRef, ok := namedRef.(reference.NamedTagged); ok && trustedRef != nil {
+				if err := image.TagTrusted(ctx, dockerCli, trustedRef, taggedRef); err != nil {
 					return nil, err
 				}
 			}

+ 7 - 5
cli/command/formatter/image.go

@@ -4,9 +4,9 @@ import (
 	"fmt"
 	"time"
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/pkg/stringid"
-	"github.com/docker/docker/reference"
 	units "github.com/docker/go-units"
 )
 
@@ -95,21 +95,23 @@ func imageFormat(ctx ImageContext, images []types.ImageSummary, format func(subC
 			repoDigests := map[string][]string{}
 
 			for _, refString := range append(image.RepoTags) {
-				ref, err := reference.ParseNamed(refString)
+				ref, err := reference.ParseNormalizedNamed(refString)
 				if err != nil {
 					continue
 				}
 				if nt, ok := ref.(reference.NamedTagged); ok {
-					repoTags[ref.Name()] = append(repoTags[ref.Name()], nt.Tag())
+					familiarRef := reference.FamiliarName(ref)
+					repoTags[familiarRef] = append(repoTags[familiarRef], nt.Tag())
 				}
 			}
 			for _, refString := range append(image.RepoDigests) {
-				ref, err := reference.ParseNamed(refString)
+				ref, err := reference.ParseNormalizedNamed(refString)
 				if err != nil {
 					continue
 				}
 				if c, ok := ref.(reference.Canonical); ok {
-					repoDigests[ref.Name()] = append(repoDigests[ref.Name()], c.Digest().String())
+					familiarRef := reference.FamiliarName(ref)
+					repoDigests[familiarRef] = append(repoDigests[familiarRef], c.Digest().String())
 				}
 			}
 

+ 8 - 5
cli/command/image/build.go

@@ -11,6 +11,7 @@ import (
 	"regexp"
 	"runtime"
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/container"
@@ -25,7 +26,6 @@ import (
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/pkg/streamformatter"
 	"github.com/docker/docker/pkg/urlutil"
-	"github.com/docker/docker/reference"
 	runconfigopts "github.com/docker/docker/runconfig/opts"
 	units "github.com/docker/go-units"
 	"github.com/spf13/cobra"
@@ -360,7 +360,7 @@ type translatorFunc func(context.Context, reference.NamedTagged) (reference.Cano
 
 // validateTag checks if the given image name can be resolved.
 func validateTag(rawRepo string) (string, error) {
-	_, err := reference.ParseNamed(rawRepo)
+	_, err := reference.ParseNormalizedNamed(rawRepo)
 	if err != nil {
 		return "", err
 	}
@@ -392,18 +392,21 @@ func rewriteDockerfileFrom(ctx context.Context, dockerfile io.Reader, translator
 		matches := dockerfileFromLinePattern.FindStringSubmatch(line)
 		if matches != nil && matches[1] != api.NoBaseImageSpecifier {
 			// Replace the line with a resolved "FROM repo@digest"
-			ref, err := reference.ParseNamed(matches[1])
+			var ref reference.Named
+			ref, err = reference.ParseNormalizedNamed(matches[1])
 			if err != nil {
 				return nil, nil, err
 			}
-			ref = reference.WithDefaultTag(ref)
+			if reference.IsNameOnly(ref) {
+				ref = reference.EnsureTagged(ref)
+			}
 			if ref, ok := ref.(reference.NamedTagged); ok && command.IsTrusted() {
 				trustedRef, err := translator(ctx, ref)
 				if err != nil {
 					return nil, nil, err
 				}
 
-				line = dockerfileFromLinePattern.ReplaceAllLiteralString(line, fmt.Sprintf("FROM %s", trustedRef.String()))
+				line = dockerfileFromLinePattern.ReplaceAllLiteralString(line, fmt.Sprintf("FROM %s", reference.FamiliarString(trustedRef)))
 				resolvedTags = append(resolvedTags, &resolvedTag{
 					digestRef: trustedRef,
 					tagRef:    ref,

+ 7 - 5
cli/command/image/pull.go

@@ -7,9 +7,9 @@ import (
 
 	"golang.org/x/net/context"
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cli/command"
-	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
 	"github.com/spf13/cobra"
 )
@@ -42,7 +42,8 @@ func NewPullCommand(dockerCli *command.DockerCli) *cobra.Command {
 }
 
 func runPull(dockerCli *command.DockerCli, opts pullOptions) error {
-	distributionRef, err := reference.ParseNamed(opts.remote)
+	var distributionRef reference.Named
+	distributionRef, err := reference.ParseNormalizedNamed(opts.remote)
 	if err != nil {
 		return err
 	}
@@ -51,8 +52,9 @@ func runPull(dockerCli *command.DockerCli, opts pullOptions) error {
 	}
 
 	if !opts.all && reference.IsNameOnly(distributionRef) {
-		distributionRef = reference.WithDefaultTag(distributionRef)
-		fmt.Fprintf(dockerCli.Out(), "Using default tag: %s\n", reference.DefaultTag)
+		taggedRef := reference.EnsureTagged(distributionRef)
+		fmt.Fprintf(dockerCli.Out(), "Using default tag: %s\n", taggedRef.Tag())
+		distributionRef = taggedRef
 	}
 
 	// Resolve the Repository name from fqn to RepositoryInfo
@@ -71,7 +73,7 @@ func runPull(dockerCli *command.DockerCli, opts pullOptions) error {
 	if command.IsTrusted() && !isCanonical {
 		err = trustedPull(ctx, dockerCli, repoInfo, distributionRef, authConfig, requestPrivilege)
 	} else {
-		err = imagePullPrivileged(ctx, dockerCli, authConfig, distributionRef.String(), requestPrivilege, opts.all)
+		err = imagePullPrivileged(ctx, dockerCli, authConfig, reference.FamiliarString(distributionRef), requestPrivilege, opts.all)
 	}
 	if err != nil {
 		if strings.Contains(err.Error(), "target is plugin") {

+ 3 - 3
cli/command/image/push.go

@@ -3,10 +3,10 @@ package image
 import (
 	"golang.org/x/net/context"
 
+	"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/docker/docker/registry"
 	"github.com/spf13/cobra"
 )
@@ -30,7 +30,7 @@ func NewPushCommand(dockerCli *command.DockerCli) *cobra.Command {
 }
 
 func runPush(dockerCli *command.DockerCli, remote string) error {
-	ref, err := reference.ParseNamed(remote)
+	ref, err := reference.ParseNormalizedNamed(remote)
 	if err != nil {
 		return err
 	}
@@ -51,7 +51,7 @@ func runPush(dockerCli *command.DockerCli, remote string) error {
 		return trustedPush(ctx, dockerCli, repoInfo, ref, authConfig, requestPrivilege)
 	}
 
-	responseBody, err := imagePushPrivileged(ctx, dockerCli, authConfig, ref.String(), requestPrivilege)
+	responseBody, err := imagePushPrivileged(ctx, dockerCli, authConfig, ref, requestPrivilege)
 	if err != nil {
 		return err
 	}

+ 20 - 19
cli/command/image/trust.go

@@ -10,11 +10,11 @@ import (
 	"sort"
 
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/cli/command"
 	"github.com/docker/docker/cli/trust"
 	"github.com/docker/docker/pkg/jsonmessage"
-	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
 	"github.com/docker/notary/client"
 	"github.com/docker/notary/tuf/data"
@@ -30,7 +30,7 @@ type target struct {
 
 // trustedPush handles content trust pushing of an image
 func trustedPush(ctx context.Context, cli *command.DockerCli, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error {
-	responseBody, err := imagePushPrivileged(ctx, cli, authConfig, ref.String(), requestPrivilege)
+	responseBody, err := imagePushPrivileged(ctx, cli, authConfig, ref, requestPrivilege)
 	if err != nil {
 		return err
 	}
@@ -202,7 +202,7 @@ func addTargetToAllSignableRoles(repo *client.NotaryRepository, target *client.T
 }
 
 // imagePushPrivileged push the image
-func imagePushPrivileged(ctx context.Context, cli *command.DockerCli, authConfig types.AuthConfig, ref string, requestPrivilege types.RequestPrivilegeFunc) (io.ReadCloser, error) {
+func imagePushPrivileged(ctx context.Context, cli *command.DockerCli, authConfig types.AuthConfig, ref reference.Named, requestPrivilege types.RequestPrivilegeFunc) (io.ReadCloser, error) {
 	encodedAuth, err := command.EncodeAuthToBase64(authConfig)
 	if err != nil {
 		return nil, err
@@ -212,7 +212,7 @@ func imagePushPrivileged(ctx context.Context, cli *command.DockerCli, authConfig
 		PrivilegeFunc: requestPrivilege,
 	}
 
-	return cli.Client().ImagePush(ctx, ref, options)
+	return cli.Client().ImagePush(ctx, reference.FamiliarString(ref), options)
 }
 
 // trustedPull handles content trust pulling of an image
@@ -229,12 +229,12 @@ func trustedPull(ctx context.Context, cli *command.DockerCli, repoInfo *registry
 		// List all targets
 		targets, err := notaryRepo.ListTargets(trust.ReleasesRole, data.CanonicalTargetsRole)
 		if err != nil {
-			return trust.NotaryError(repoInfo.FullName(), err)
+			return trust.NotaryError(ref.Name(), err)
 		}
 		for _, tgt := range targets {
 			t, err := convertTarget(tgt.Target)
 			if err != nil {
-				fmt.Fprintf(cli.Out(), "Skipping target for %q\n", repoInfo.Name())
+				fmt.Fprintf(cli.Out(), "Skipping target for %q\n", reference.FamiliarName(ref))
 				continue
 			}
 			// Only list tags in the top level targets role or the releases delegation role - ignore
@@ -245,17 +245,17 @@ func trustedPull(ctx context.Context, cli *command.DockerCli, repoInfo *registry
 			refs = append(refs, t)
 		}
 		if len(refs) == 0 {
-			return trust.NotaryError(repoInfo.FullName(), fmt.Errorf("No trusted tags for %s", repoInfo.FullName()))
+			return trust.NotaryError(ref.Name(), fmt.Errorf("No trusted tags for %s", ref.Name()))
 		}
 	} else {
 		t, err := notaryRepo.GetTargetByName(tagged.Tag(), trust.ReleasesRole, data.CanonicalTargetsRole)
 		if err != nil {
-			return trust.NotaryError(repoInfo.FullName(), err)
+			return trust.NotaryError(ref.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 trust.NotaryError(repoInfo.FullName(), fmt.Errorf("No trust data for %s", tagged.Tag()))
+			return trust.NotaryError(ref.Name(), fmt.Errorf("No trust data for %s", tagged.Tag()))
 		}
 
 		logrus.Debugf("retrieving target for %s role\n", t.Role)
@@ -272,24 +272,21 @@ func trustedPull(ctx context.Context, cli *command.DockerCli, repoInfo *registry
 		if displayTag != "" {
 			displayTag = ":" + displayTag
 		}
-		fmt.Fprintf(cli.Out(), "Pull (%d of %d): %s%s@%s\n", i+1, len(refs), repoInfo.Name(), displayTag, r.digest)
+		fmt.Fprintf(cli.Out(), "Pull (%d of %d): %s%s@%s\n", i+1, len(refs), reference.FamiliarName(ref), displayTag, r.digest)
 
-		ref, err := reference.WithDigest(reference.TrimNamed(repoInfo), r.digest)
+		trustedRef, err := reference.WithDigest(reference.TrimNamed(ref), r.digest)
 		if err != nil {
 			return err
 		}
-		if err := imagePullPrivileged(ctx, cli, authConfig, ref.String(), requestPrivilege, false); err != nil {
+		if err := imagePullPrivileged(ctx, cli, authConfig, reference.FamiliarString(trustedRef), requestPrivilege, false); err != nil {
 			return err
 		}
 
-		tagged, err := reference.WithTag(repoInfo, r.name)
-		if err != nil {
-			return err
-		}
-		trustedRef, err := reference.WithDigest(reference.TrimNamed(repoInfo), r.digest)
+		tagged, err := reference.WithTag(reference.TrimNamed(ref), r.name)
 		if err != nil {
 			return err
 		}
+
 		if err := TagTrusted(ctx, cli, trustedRef, tagged); err != nil {
 			return err
 		}
@@ -375,7 +372,11 @@ func convertTarget(t client.Target) (target, error) {
 
 // TagTrusted tags a trusted ref
 func TagTrusted(ctx context.Context, cli *command.DockerCli, trustedRef reference.Canonical, ref reference.NamedTagged) error {
-	fmt.Fprintf(cli.Out(), "Tagging %s as %s\n", trustedRef.String(), ref.String())
+	// Use familiar references when interacting with client and output
+	familiarRef := reference.FamiliarString(ref)
+	trustedFamiliarRef := reference.FamiliarString(trustedRef)
+
+	fmt.Fprintf(cli.Out(), "Tagging %s as %s\n", trustedFamiliarRef, familiarRef)
 
-	return cli.Client().ImageTag(ctx, trustedRef.String(), ref.String())
+	return cli.Client().ImageTag(ctx, trustedFamiliarRef, familiarRef)
 }

+ 2 - 2
cli/command/plugin/create.go

@@ -8,18 +8,18 @@ import (
 	"path/filepath"
 
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cli/command"
 	"github.com/docker/docker/pkg/archive"
-	"github.com/docker/docker/reference"
 	"github.com/spf13/cobra"
 	"golang.org/x/net/context"
 )
 
 // validateTag checks if the given repoName can be resolved.
 func validateTag(rawRepo string) error {
-	_, err := reference.ParseNamed(rawRepo)
+	_, err := reference.ParseNormalizedNamed(rawRepo)
 
 	return err
 }

+ 20 - 32
cli/command/plugin/install.go

@@ -6,14 +6,13 @@ import (
 	"fmt"
 	"strings"
 
-	distreference "github.com/docker/distribution/reference"
+	"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"
 	"github.com/docker/docker/pkg/jsonmessage"
-	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
 	"github.com/spf13/cobra"
 	"golang.org/x/net/context"
@@ -52,8 +51,8 @@ func newInstallCommand(dockerCli *command.DockerCli) *cobra.Command {
 	return cmd
 }
 
-func getRepoIndexFromUnnormalizedRef(ref distreference.Named) (*registrytypes.IndexInfo, error) {
-	named, err := reference.ParseNamed(ref.Name())
+func getRepoIndexFromUnnormalizedRef(ref reference.Named) (*registrytypes.IndexInfo, error) {
+	named, err := reference.ParseNormalizedNamed(ref.Name())
 	if err != nil {
 		return nil, err
 	}
@@ -85,71 +84,60 @@ func newRegistryService() registry.Service {
 }
 
 func runInstall(dockerCli *command.DockerCli, opts pluginOptions) error {
-	// Parse name using distribution reference package to support name
-	// containing both tag and digest. Names with both tag and digest
-	// will be treated by the daemon as a pull by digest with
-	// an alias for the tag (if no alias is provided).
-	ref, err := distreference.ParseNamed(opts.name)
+	// Names with both tag and digest will be treated by the daemon
+	// as a pull by digest with an alias for the tag
+	// (if no alias is provided).
+	ref, err := reference.ParseNormalizedNamed(opts.name)
 	if err != nil {
 		return err
 	}
 
 	alias := ""
 	if opts.alias != "" {
-		aref, err := reference.ParseNamed(opts.alias)
+		aref, err := reference.ParseNormalizedNamed(opts.alias)
 		if err != nil {
 			return err
 		}
-		aref = reference.WithDefaultTag(aref)
-		if _, ok := aref.(reference.NamedTagged); !ok {
+		if _, ok := aref.(reference.Canonical); ok {
 			return fmt.Errorf("invalid name: %s", opts.alias)
 		}
-		alias = aref.String()
+		alias = reference.FamiliarString(reference.EnsureTagged(aref))
 	}
 	ctx := context.Background()
 
-	index, err := getRepoIndexFromUnnormalizedRef(ref)
+	repoInfo, err := registry.ParseRepositoryInfo(ref)
 	if err != nil {
 		return err
 	}
 
 	remote := ref.String()
 
-	_, isCanonical := ref.(distreference.Canonical)
+	_, isCanonical := ref.(reference.Canonical)
 	if command.IsTrusted() && !isCanonical {
 		if alias == "" {
-			alias = ref.String()
+			alias = reference.FamiliarString(ref)
 		}
-		var nt reference.NamedTagged
-		named, err := reference.ParseNamed(ref.Name())
-		if err != nil {
-			return err
-		}
-		if tagged, ok := ref.(distreference.Tagged); ok {
-			nt, err = reference.WithTag(named, tagged.Tag())
-			if err != nil {
-				return err
-			}
-		} else {
-			named = reference.WithDefaultTag(named)
-			nt = named.(reference.NamedTagged)
+
+		nt, ok := ref.(reference.NamedTagged)
+		if !ok {
+			nt = reference.EnsureTagged(ref)
 		}
 
 		trusted, err := image.TrustedReference(ctx, dockerCli, nt, newRegistryService())
 		if err != nil {
 			return err
 		}
-		remote = trusted.String()
+		remote = reference.FamiliarString(trusted)
 	}
 
-	authConfig := command.ResolveAuthConfig(ctx, dockerCli, index)
+	authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index)
 
 	encodedAuth, err := command.EncodeAuthToBase64(authConfig)
 	if err != nil {
 		return err
 	}
 
-	registryAuthFunc := command.RegistryAuthenticationPrivilegedFunc(dockerCli, index, "plugin install")
+	registryAuthFunc := command.RegistryAuthenticationPrivilegedFunc(dockerCli, repoInfo.Index, "plugin install")
 
 	options := types.PluginInstallOptions{
 		RegistryAuth:          encodedAuth,

+ 9 - 7
cli/command/plugin/push.go

@@ -5,11 +5,11 @@ import (
 
 	"golang.org/x/net/context"
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cli/command"
 	"github.com/docker/docker/cli/command/image"
 	"github.com/docker/docker/pkg/jsonmessage"
-	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
 	"github.com/spf13/cobra"
 )
@@ -32,16 +32,17 @@ func newPushCommand(dockerCli *command.DockerCli) *cobra.Command {
 }
 
 func runPush(dockerCli *command.DockerCli, name string) error {
-	named, err := reference.ParseNamed(name) // FIXME: validate
+	named, err := reference.ParseNormalizedNamed(name)
 	if err != nil {
 		return err
 	}
-	if reference.IsNameOnly(named) {
-		named = reference.WithDefaultTag(named)
+	if _, ok := named.(reference.Canonical); ok {
+		return fmt.Errorf("invalid name: %s", name)
 	}
-	ref, ok := named.(reference.NamedTagged)
+
+	taggedRef, ok := named.(reference.NamedTagged)
 	if !ok {
-		return fmt.Errorf("invalid name: %s", named.String())
+		taggedRef = reference.EnsureTagged(named)
 	}
 
 	ctx := context.Background()
@@ -56,7 +57,8 @@ func runPush(dockerCli *command.DockerCli, name string) error {
 	if err != nil {
 		return err
 	}
-	responseBody, err := dockerCli.Client().PluginPush(ctx, ref.String(), encodedAuth)
+
+	responseBody, err := dockerCli.Client().PluginPush(ctx, reference.FamiliarString(taggedRef), encodedAuth)
 	if err != nil {
 		return err
 	}

+ 2 - 2
cli/command/registry.go

@@ -12,10 +12,10 @@ import (
 
 	"golang.org/x/net/context"
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	registrytypes "github.com/docker/docker/api/types/registry"
 	"github.com/docker/docker/pkg/term"
-	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
 )
 
@@ -174,7 +174,7 @@ func RetrieveAuthTokenFromImage(ctx context.Context, cli *DockerCli, image strin
 
 // resolveAuthConfigFromImage retrieves that AuthConfig using the image string
 func resolveAuthConfigFromImage(ctx context.Context, cli *DockerCli, image string) (types.AuthConfig, error) {
-	registryRef, err := reference.ParseNamed(image)
+	registryRef, err := reference.ParseNormalizedNamed(image)
 	if err != nil {
 		return types.AuthConfig{}, err
 	}

+ 19 - 29
cli/command/service/trust.go

@@ -5,11 +5,10 @@ import (
 	"fmt"
 
 	"github.com/Sirupsen/logrus"
-	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/trust"
-	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
 	"github.com/docker/notary/tuf/data"
 	"github.com/opencontainers/go-digest"
@@ -24,41 +23,34 @@ func resolveServiceImageDigest(dockerCli *command.DockerCli, service *swarm.Serv
 		return nil
 	}
 
-	image := service.TaskTemplate.ContainerSpec.Image
-
-	// We only attempt to resolve the digest if the reference
-	// could be parsed as a digest reference. Specifying an image ID
-	// is valid but not resolvable. There is no warning message for
-	// an image ID because it's valid to use one.
-	if _, err := digest.Parse(image); err == nil {
-		return nil
-	}
-
-	ref, err := reference.ParseNamed(image)
+	ref, err := reference.ParseAnyReference(service.TaskTemplate.ContainerSpec.Image)
 	if err != nil {
-		return fmt.Errorf("Could not parse image reference %s", service.TaskTemplate.ContainerSpec.Image)
+		return errors.Wrapf(err, "invalid reference %s", service.TaskTemplate.ContainerSpec.Image)
 	}
-	if _, ok := ref.(reference.Canonical); !ok {
-		ref = reference.WithDefaultTag(ref)
 
-		taggedRef, ok := ref.(reference.NamedTagged)
+	// If reference does not have digest (is not canonical nor image id)
+	if _, ok := ref.(reference.Digested); !ok {
+		namedRef, ok := ref.(reference.Named)
 		if !ok {
-			// This should never happen because a reference either
-			// has a digest, or WithDefaultTag would give it a tag.
-			return errors.New("Failed to resolve image digest using content trust: reference is missing a tag")
+			return errors.New("failed to resolve image digest using content trust: reference is not named")
+
 		}
 
+		taggedRef := reference.EnsureTagged(namedRef)
+
 		resolvedImage, err := trustedResolveDigest(context.Background(), dockerCli, taggedRef)
 		if err != nil {
-			return fmt.Errorf("Failed to resolve image digest using content trust: %v", err)
+			return errors.Wrap(err, "failed to resolve image digest using content trust")
 		}
-		logrus.Debugf("resolved image tag to %s using content trust", resolvedImage.String())
-		service.TaskTemplate.ContainerSpec.Image = resolvedImage.String()
+		resolvedFamiliar := reference.FamiliarString(resolvedImage)
+		logrus.Debugf("resolved image tag to %s using content trust", resolvedFamiliar)
+		service.TaskTemplate.ContainerSpec.Image = resolvedFamiliar
 	}
+
 	return nil
 }
 
-func trustedResolveDigest(ctx context.Context, cli *command.DockerCli, ref reference.NamedTagged) (distreference.Canonical, error) {
+func trustedResolveDigest(ctx context.Context, cli *command.DockerCli, ref reference.NamedTagged) (reference.Canonical, error) {
 	repoInfo, err := registry.ParseRepositoryInfo(ref)
 	if err != nil {
 		return nil, err
@@ -78,7 +70,7 @@ func trustedResolveDigest(ctx context.Context, cli *command.DockerCli, ref refer
 	// 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", ref.String()))
+		return nil, trust.NotaryError(repoInfo.FullName(), fmt.Errorf("No trust data for %s", reference.FamiliarString(ref)))
 	}
 
 	logrus.Debugf("retrieving target for %s role\n", t.Role)
@@ -89,8 +81,6 @@ func trustedResolveDigest(ctx context.Context, cli *command.DockerCli, ref refer
 
 	dgst := digest.NewDigestFromHex("sha256", hex.EncodeToString(h))
 
-	// Using distribution reference package to make sure that adding a
-	// digest does not erase the tag. When the two reference packages
-	// are unified, this will no longer be an issue.
-	return distreference.WithDigest(ref, dgst)
+	// Allow returning canonical reference with tag
+	return reference.WithDigest(ref, dgst)
 }

+ 1 - 1
distribution/push_v2_test.go

@@ -202,7 +202,7 @@ func TestLayerAlreadyExists(t *testing.T) {
 			checkOtherRepositories: true,
 			metadata: []metadata.V2Metadata{
 				{Digest: digest.Digest("apple"), SourceRepository: "docker.io/library/hello-world"},
-				{Digest: digest.Digest("orange"), SourceRepository: "docker.io/library/busybox/subapp"},
+				{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("banana"), SourceRepository: "127.0.0.1/busybox"},

+ 1 - 1
integration-cli/docker_cli_build_test.go

@@ -3082,7 +3082,7 @@ func (s *DockerSuite) TestBuildInvalidTag(c *check.C) {
 	name := "abcd:" + stringutils.GenerateRandomAlphaOnlyString(200)
 	buildImage(name, withDockerfile("FROM "+minimalBaseImage()+"\nMAINTAINER quux\n")).Assert(c, icmd.Expected{
 		ExitCode: 125,
-		Err:      "Error parsing reference",
+		Err:      "invalid reference format",
 	})
 }
 

+ 1 - 1
integration-cli/docker_cli_create_test.go

@@ -274,7 +274,7 @@ func (s *DockerSuite) TestCreateByImageID(c *check.C) {
 		c.Fatalf("expected non-zero exit code; received %d", exit)
 	}
 
-	if expected := "Error parsing reference"; !strings.Contains(out, expected) {
+	if expected := "invalid reference format"; !strings.Contains(out, expected) {
 		c.Fatalf(`Expected %q in output; got: %s`, expected, out)
 	}
 

+ 2 - 2
integration-cli/docker_cli_run_test.go

@@ -3863,8 +3863,8 @@ func (s *DockerSuite) TestRunInvalidReference(c *check.C) {
 		c.Fatalf("expected non-zero exist code; received %d", exit)
 	}
 
-	if !strings.Contains(out, "Error parsing reference") {
-		c.Fatalf(`Expected "Error parsing reference" in output; got: %s`, out)
+	if !strings.Contains(out, "invalid reference format") {
+		c.Fatalf(`Expected "invalid reference format" in output; got: %s`, out)
 	}
 }
 

+ 1 - 1
plugin/manager.go

@@ -12,6 +12,7 @@ import (
 	"sync"
 
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/layer"
@@ -19,7 +20,6 @@ import (
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/mount"
 	"github.com/docker/docker/plugin/v2"
-	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
 	"github.com/opencontainers/go-digest"
 	"github.com/pkg/errors"

+ 33 - 55
reference/reference.go

@@ -1,13 +1,12 @@
 package reference
 
 import (
-	"errors"
 	"fmt"
-	"strings"
 
 	distreference "github.com/docker/distribution/reference"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/opencontainers/go-digest"
+	"github.com/pkg/errors"
 )
 
 const (
@@ -53,21 +52,31 @@ type Canonical interface {
 // returned.
 // If an error was encountered it is returned, along with a nil Reference.
 func ParseNamed(s string) (Named, error) {
-	named, err := distreference.ParseNamed(s)
+	named, err := distreference.ParseNormalizedNamed(s)
 	if err != nil {
-		return nil, fmt.Errorf("Error parsing reference: %q is not a valid repository/tag: %s", s, err)
+		return nil, errors.Wrapf(err, "failed to parse reference %q", s)
 	}
-	r, err := WithName(named.Name())
-	if err != nil {
+	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 {
-		return WithDigest(r, canonical.Digest())
+		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 {
-		return WithTag(r, tagged.Tag())
+		r, err := distreference.WithTag(distreference.TrimNamed(named), tagged.Tag())
+		if err != nil {
+			return nil, err
+		}
+		return &taggedRef{namedRef{r}}, nil
 	}
-	return r, nil
+
+	return &namedRef{named}, nil
 }
 
 // TrimNamed removes any tag or digest from the named reference
@@ -78,16 +87,15 @@ func TrimNamed(ref Named) Named {
 // WithName returns a named object representing the given string. If the input
 // is invalid ErrReferenceInvalidFormat will be returned.
 func WithName(name string) (Named, error) {
-	name, err := normalize(name)
+	r, err := distreference.ParseNormalizedNamed(name)
 	if err != nil {
 		return nil, err
 	}
-	if err := validateName(name); err != nil {
+	if err := validateName(distreference.FamiliarName(r)); err != nil {
 		return nil, err
 	}
-	r, err := distreference.WithName(name)
-	if err != nil {
-		return nil, err
+	if !distreference.IsNameOnly(r) {
+		return nil, distreference.ErrReferenceInvalidFormat
 	}
 	return &namedRef{r}, nil
 }
@@ -122,17 +130,22 @@ 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 {
-	hostname, remoteName := splitHostname(r.Name())
-	return hostname + "/" + remoteName
+	return r.Named.Name()
 }
 func (r *namedRef) Hostname() string {
-	hostname, _ := splitHostname(r.Name())
-	return hostname
+	return distreference.Domain(r.Named)
 }
 func (r *namedRef) RemoteName() string {
-	_, remoteName := splitHostname(r.Name())
-	return remoteName
+	return distreference.Path(r.Named)
 }
 func (r *taggedRef) Tag() string {
 	return r.namedRef.Named.(distreference.NamedTagged).Tag()
@@ -173,41 +186,6 @@ func ParseIDOrReference(idOrRef string) (digest.Digest, Named, error) {
 	return "", ref, err
 }
 
-// splitHostname splits a repository name to hostname and remotename string.
-// If no valid hostname is found, the default hostname is used. Repository name
-// needs to be already validated before.
-func splitHostname(name string) (hostname, remoteName string) {
-	i := strings.IndexRune(name, '/')
-	if i == -1 || (!strings.ContainsAny(name[:i], ".:") && name[:i] != "localhost") {
-		hostname, remoteName = DefaultHostname, name
-	} else {
-		hostname, remoteName = name[:i], name[i+1:]
-	}
-	if hostname == LegacyDefaultHostname {
-		hostname = DefaultHostname
-	}
-	if hostname == DefaultHostname && !strings.ContainsRune(remoteName, '/') {
-		remoteName = DefaultRepoPrefix + remoteName
-	}
-	return
-}
-
-// normalize returns a repository name in its normalized form, meaning it
-// will not contain default hostname nor library/ prefix for official images.
-func normalize(name string) (string, error) {
-	host, remoteName := splitHostname(name)
-	if strings.ToLower(remoteName) != remoteName {
-		return "", errors.New("invalid reference format: repository name must be lowercase")
-	}
-	if host == DefaultHostname {
-		if strings.HasPrefix(remoteName, DefaultRepoPrefix) {
-			return strings.TrimPrefix(remoteName, DefaultRepoPrefix), nil
-		}
-		return remoteName, nil
-	}
-	return name, nil
-}
-
 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)

+ 14 - 7
registry/config.go

@@ -1,16 +1,17 @@
 package registry
 
 import (
-	"errors"
 	"fmt"
 	"net"
 	"net/url"
 	"strings"
 
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/distribution/reference"
 	registrytypes "github.com/docker/docker/api/types/registry"
 	"github.com/docker/docker/opts"
-	"github.com/docker/docker/reference"
+	forkedref "github.com/docker/docker/reference"
+	"github.com/pkg/errors"
 	"github.com/spf13/pflag"
 )
 
@@ -270,8 +271,8 @@ func ValidateMirror(val string) (string, error) {
 
 // ValidateIndexName validates an index name.
 func ValidateIndexName(val string) (string, error) {
-	if val == reference.LegacyDefaultHostname {
-		val = reference.DefaultHostname
+	if val == forkedref.LegacyDefaultHostname {
+		val = forkedref.DefaultHostname
 	}
 	if strings.HasPrefix(val, "-") || strings.HasSuffix(val, "-") {
 		return "", fmt.Errorf("Invalid index name (%s). Cannot begin or end with a hyphen.", val)
@@ -321,13 +322,19 @@ func GetAuthConfigKey(index *registrytypes.IndexInfo) string {
 
 // newRepositoryInfo validates and breaks down a repository name into a RepositoryInfo
 func newRepositoryInfo(config *serviceConfig, name reference.Named) (*RepositoryInfo, error) {
-	index, err := newIndexInfo(config, name.Hostname())
+	index, err := newIndexInfo(config, reference.Domain(name))
+	if err != nil {
+		return nil, err
+	}
+	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
 	}
-	official := !strings.ContainsRune(name.Name(), '/')
 	return &RepositoryInfo{
-		Named:    name,
+		Named:    nameref,
 		Index:    index,
 		Official: official,
 	}, nil

+ 12 - 11
registry/registry_test.go

@@ -10,10 +10,11 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/registry/client/transport"
 	"github.com/docker/docker/api/types"
 	registrytypes "github.com/docker/docker/api/types/registry"
-	"github.com/docker/docker/reference"
+	forkedref "github.com/docker/docker/reference"
 )
 
 var (
@@ -201,7 +202,7 @@ func TestGetRemoteImageLayer(t *testing.T) {
 
 func TestGetRemoteTag(t *testing.T) {
 	r := spawnTestRegistrySession(t)
-	repoRef, err := reference.ParseNamed(REPO)
+	repoRef, err := forkedref.ParseNamed(REPO)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -211,7 +212,7 @@ func TestGetRemoteTag(t *testing.T) {
 	}
 	assertEqual(t, tag, imageID, "Expected tag test to map to "+imageID)
 
-	bazRef, err := reference.ParseNamed("foo42/baz")
+	bazRef, err := forkedref.ParseNamed("foo42/baz")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -223,7 +224,7 @@ func TestGetRemoteTag(t *testing.T) {
 
 func TestGetRemoteTags(t *testing.T) {
 	r := spawnTestRegistrySession(t)
-	repoRef, err := reference.ParseNamed(REPO)
+	repoRef, err := forkedref.ParseNamed(REPO)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -235,7 +236,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 := reference.ParseNamed("foo42/baz")
+	bazRef, err := forkedref.ParseNamed("foo42/baz")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -252,7 +253,7 @@ func TestGetRepositoryData(t *testing.T) {
 		t.Fatal(err)
 	}
 	host := "http://" + parsedURL.Host + "/v1/"
-	repoRef, err := reference.ParseNamed(REPO)
+	repoRef, err := forkedref.ParseNamed(REPO)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -505,7 +506,7 @@ func TestParseRepositoryInfo(t *testing.T) {
 	}
 
 	for reposName, expectedRepoInfo := range expectedRepoInfos {
-		named, err := reference.WithName(reposName)
+		named, err := reference.ParseNormalizedNamed(reposName)
 		if err != nil {
 			t.Error(err)
 		}
@@ -669,7 +670,7 @@ func TestMirrorEndpointLookup(t *testing.T) {
 	if err != nil {
 		t.Error(err)
 	}
-	pushAPIEndpoints, err := s.LookupPushEndpoints(imageName.Hostname())
+	pushAPIEndpoints, err := s.LookupPushEndpoints(reference.Domain(imageName))
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -677,7 +678,7 @@ func TestMirrorEndpointLookup(t *testing.T) {
 		t.Fatal("Push endpoint should not contain mirror")
 	}
 
-	pullAPIEndpoints, err := s.LookupPullEndpoints(imageName.Hostname())
+	pullAPIEndpoints, err := s.LookupPullEndpoints(reference.Domain(imageName))
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -688,7 +689,7 @@ func TestMirrorEndpointLookup(t *testing.T) {
 
 func TestPushRegistryTag(t *testing.T) {
 	r := spawnTestRegistrySession(t)
-	repoRef, err := reference.ParseNamed(REPO)
+	repoRef, err := forkedref.ParseNamed(REPO)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -710,7 +711,7 @@ func TestPushImageJSONIndex(t *testing.T) {
 			Checksum: "sha256:bea7bf2e4bacd479344b737328db47b18880d09096e6674165533aa994f5e9f2",
 		},
 	}
-	repoRef, err := reference.ParseNamed(REPO)
+	repoRef, err := forkedref.ParseNamed(REPO)
 	if err != nil {
 		t.Fatal(err)
 	}

+ 1 - 1
registry/service.go

@@ -11,10 +11,10 @@ import (
 	"golang.org/x/net/context"
 
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/distribution/reference"
 	"github.com/docker/distribution/registry/client/auth"
 	"github.com/docker/docker/api/types"
 	registrytypes "github.com/docker/docker/api/types/registry"
-	"github.com/docker/docker/reference"
 )
 
 const (