diff options
78 files changed, 538 insertions, 1117 deletions
diff --git a/api/server/router/plugin/backend.go b/api/server/router/plugin/backend.go index f4ce9a5af5..a5f3c9790a 100644 --- a/api/server/router/plugin/backend.go +++ b/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" ) diff --git a/api/server/router/plugin/plugin_routes.go b/api/server/router/plugin/plugin_routes.go index 07cd1f64ef..0d743a4a95 100644 --- a/api/server/router/plugin/plugin_routes.go +++ b/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 diff --git a/api/types/reference/image_reference.go b/api/types/reference/image_reference.go deleted file mode 100644 index be9cf8ebed..0000000000 --- a/api/types/reference/image_reference.go +++ /dev/null @@ -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 -} diff --git a/api/types/reference/image_reference_test.go b/api/types/reference/image_reference_test.go deleted file mode 100644 index 2f6268f5bc..0000000000 --- a/api/types/reference/image_reference_test.go +++ /dev/null @@ -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) - } - } -} diff --git a/builder/builder.go b/builder/builder.go index 42b601e8d8..a98ca29f67 100644 --- a/builder/builder.go +++ b/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" ) diff --git a/builder/dockerfile/builder.go b/builder/dockerfile/builder.go index 9464c52ca4..9b98c678cd 100644 --- a/builder/dockerfile/builder.go +++ b/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() diff --git a/cli/command/container/create.go b/cli/command/container/create.go index cfd672e77a..9559ba0c05 100644 --- a/cli/command/container/create.go +++ b/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 diff --git a/cli/command/formatter/disk_usage.go b/cli/command/formatter/disk_usage.go index dc5eec41d7..fd7aabc7c2 100644 --- a/cli/command/formatter/disk_usage.go +++ b/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() } } diff --git a/cli/command/formatter/image.go b/cli/command/formatter/image.go index 06319b9355..b6508224a3 100644 --- a/cli/command/formatter/image.go +++ b/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 diff --git a/cli/command/formatter/service.go b/cli/command/formatter/service.go index 9d9241b224..8e38cb3a11 100644 --- a/cli/command/formatter/service.go +++ b/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) + } } } diff --git a/cli/command/image/build.go b/cli/command/image/build.go index 34e0a39500..96d90cf585 100644 --- a/cli/command/image/build.go +++ b/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 { diff --git a/cli/command/image/pull.go b/cli/command/image/pull.go index 967beca86f..515273d43c 100644 --- a/cli/command/image/pull.go +++ b/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 diff --git a/cli/command/image/trust.go b/cli/command/image/trust.go index 2ff9b463d5..8332dd7deb 100644 --- a/cli/command/image/trust.go +++ b/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 { diff --git a/cli/command/plugin/install.go b/cli/command/plugin/install.go index 15877761af..9e9ea40e2a 100644 --- a/cli/command/plugin/install.go +++ b/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() diff --git a/cli/command/plugin/push.go b/cli/command/plugin/push.go index 6b826dce68..f3643b7f1b 100644 --- a/cli/command/plugin/push.go +++ b/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 } diff --git a/cli/command/plugin/upgrade.go b/cli/command/plugin/upgrade.go index 6861aa1b32..07f0c7bb91 100644 --- a/cli/command/plugin/upgrade.go +++ b/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") diff --git a/cli/command/service/trust.go b/cli/command/service/trust.go index d466f3b648..3fd80ae879 100644 --- a/cli/command/service/trust.go +++ b/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) diff --git a/cli/command/task/print.go b/cli/command/task/print.go index 60a2bca85b..d7e20bb59a 100644 --- a/cli/command/task/print.go +++ b/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) + } } + } } diff --git a/cli/trust/trust.go b/cli/trust/trust.go index 44f8197ba2..777a611181 100644 --- a/cli/trust/trust.go +++ b/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), diff --git a/client/container_commit.go b/client/container_commit.go index c766d62e40..531d796ee7 100644 --- a/client/container_commit.go +++ b/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{} diff --git a/client/image_create.go b/client/image_create.go index cf023a7186..4436abb0dd 100644 --- a/client/image_create.go +++ b/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 diff --git a/client/image_import.go b/client/image_import.go index c6f154b249..d7dedd8232 100644 --- a/client/image_import.go +++ b/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 } } diff --git a/client/image_pull.go b/client/image_pull.go index 3bffdb70e8..a72b9bf7fc 100644 --- a/client/image_pull.go +++ b/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 "" +} diff --git a/client/image_pull_test.go b/client/image_pull_test.go index fe6bafed97..ab49d2d349 100644 --- a/client/image_pull_test.go +++ b/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) } } diff --git a/client/image_push.go b/client/image_push.go index 8e73d28f56..410d2fb91d 100644 --- a/client/image_push.go +++ b/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 diff --git a/client/image_push_test.go b/client/image_push_test.go index b52da8b8dc..f93debf5bb 100644 --- a/client/image_push_test.go +++ b/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 diff --git a/client/image_tag.go b/client/image_tag.go index dbcd078e1c..35abe332bf 100644 --- a/client/image_tag.go +++ b/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) diff --git a/client/plugin_install.go b/client/plugin_install.go index 3217c4cf39..33876cc101 100644 --- a/client/plugin_install.go +++ b/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) diff --git a/client/plugin_upgrade.go b/client/plugin_upgrade.go index 95a4356b97..24293c5073 100644 --- a/client/plugin_upgrade.go +++ b/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) diff --git a/daemon/cluster/cluster.go b/daemon/cluster/cluster.go index 5f32ef85e2..18888d53b6 100644 --- a/daemon/cluster/cluster.go +++ b/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. diff --git a/daemon/cluster/executor/backend.go b/daemon/cluster/executor/backend.go index 0f1da38558..6612929cc1 100644 --- a/daemon/cluster/executor/backend.go +++ b/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" diff --git a/daemon/cluster/executor/container/adapter.go b/daemon/cluster/executor/container/adapter.go index 565af80f2a..76cfc19766 100644 --- a/daemon/cluster/executor/container/adapter.go +++ b/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) diff --git a/daemon/cluster/executor/container/container.go b/daemon/cluster/executor/container/container.go index fdb06c25e1..0b733904d5 100644 --- a/daemon/cluster/executor/container/container.go +++ b/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 { diff --git a/daemon/commit.go b/daemon/commit.go index 1e7bffb1dc..070bf18dcf 100644 --- a/daemon/commit.go +++ b/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{ diff --git a/daemon/daemon.go b/daemon/daemon.go index 492dd0252d..560b89a419 100644 --- a/daemon/daemon.go +++ b/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) } diff --git a/daemon/daemon_solaris.go b/daemon/daemon_solaris.go index 17a593f56b..0f514e4305 100644 --- a/daemon/daemon_solaris.go +++ b/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 } diff --git a/daemon/errors.go b/daemon/errors.go index 566a32f175..96c30df2ea 100644 --- a/daemon/errors.go +++ b/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 } diff --git a/daemon/events/filter.go b/daemon/events/filter.go index 5c9c527692..d10051600e 100644 --- a/daemon/events/filter.go +++ b/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) } diff --git a/daemon/image.go b/daemon/image.go index 32a8d77432..43ee483ff0 100644 --- a/daemon/image.go +++ b/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. diff --git a/daemon/image_delete.go b/daemon/image_delete.go index 30854e3f90..b7dbd249eb 100644 --- a/daemon/image_delete.go +++ b/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) diff --git a/daemon/image_history.go b/daemon/image_history.go index 9e544963b1..b763c86c03 100644 --- a/daemon/image_history.go +++ b/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)) } } diff --git a/daemon/image_inspect.go b/daemon/image_inspect.go index 5a05247099..267a41946a 100644 --- a/daemon/image_inspect.go +++ b/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)) } } diff --git a/daemon/image_pull.go b/daemon/image_pull.go index f1b5f83c79..5cbd7ba42a 100644 --- a/daemon/image_pull.go +++ b/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 } diff --git a/daemon/image_push.go b/daemon/image_push.go index e6382c7f27..0f060d117f 100644 --- a/daemon/image_push.go +++ b/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 } diff --git a/daemon/image_tag.go b/daemon/image_tag.go index 36fa3b462e..10a584b361 100644 --- a/daemon/image_tag.go +++ b/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 } diff --git a/daemon/images.go b/daemon/images.go index a8571f6dc9..e6e1945092 100644 --- a/daemon/images.go +++ b/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 { diff --git a/daemon/import.go b/daemon/import.go index c93322b92e..adc82550de 100644 --- a/daemon/import.go +++ b/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") } diff --git a/daemon/prune.go b/daemon/prune.go index 0c0e554274..2964983137 100644 --- a/daemon/prune.go +++ b/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" diff --git a/distribution/config.go b/distribution/config.go index ab1ab6c003..3490631cf2 100644 --- a/distribution/config.go +++ b/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 } diff --git a/distribution/errors.go b/distribution/errors.go index b8cf9fb9e8..adf272a5cf 100644 --- a/distribution/errors.go +++ b/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) diff --git a/distribution/pull.go b/distribution/pull.go index ac52ba1fcb..571beaa7ad 100644 --- a/distribution/pull.go +++ b/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 } diff --git a/distribution/pull_v1.go b/distribution/pull_v1.go index f44ed4f371..d873d338cd 100644 --- a/distribution/pull_v1.go +++ b/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 } diff --git a/distribution/pull_v2.go b/distribution/pull_v2.go index e707e7d3b6..14c88fd33b 100644 --- a/distribution/pull_v2.go +++ b/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 } diff --git a/distribution/pull_v2_test.go b/distribution/pull_v2_test.go index d65653311c..df93c1ef83 100644 --- a/distribution/pull_v2_test.go +++ b/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") } diff --git a/distribution/push.go b/distribution/push.go index d35bdb103e..9d5328035c 100644 --- a/distribution/push.go +++ b/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 } diff --git a/distribution/push_v1.go b/distribution/push_v1.go index 67ed3554c0..431faaf28c 100644 --- a/distribution/push_v1.go +++ b/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 } diff --git a/distribution/push_v2.go b/distribution/push_v2.go index f2115b39bc..d6fa8b1c88 100644 --- a/distribution/push_v2.go +++ b/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, "/") } diff --git a/distribution/push_v2_test.go b/distribution/push_v2_test.go index 7e9f4166f1..e8e2b1af25 100644 --- a/distribution/push_v2_test.go +++ b/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 } diff --git a/distribution/registry.go b/distribution/registry.go index 95e181ded8..bce270a5ed 100644 --- a/distribution/registry.go +++ b/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, diff --git a/distribution/registry_unit_test.go b/distribution/registry_unit_test.go index 75d08f4866..ebe6ecad97 100644 --- a/distribution/registry_unit_test.go +++ b/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 := ®istry.RepositoryInfo{ - Named: n, + Name: n, Index: ®istrytypes.IndexInfo{ Name: "testrepo", Mirrors: nil, diff --git a/image/tarexport/load.go b/image/tarexport/load.go index 0ef07f8e16..cdd377ab90 100644 --- a/image/tarexport/load.go +++ b/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 } diff --git a/image/tarexport/save.go b/image/tarexport/save.go index bab7a9611f..8d33a58724 100644 --- a/image/tarexport/save.go +++ b/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 { diff --git a/image/tarexport/tarexport.go b/image/tarexport/tarexport.go index a5229e555b..f7fab74f53 100644 --- a/image/tarexport/tarexport.go +++ b/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, diff --git a/integration-cli/docker_cli_images_test.go b/integration-cli/docker_cli_images_test.go index 61c700362e..4bcc04d67d 100644 --- a/integration-cli/docker_cli_images_test.go +++ b/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 diff --git a/migrate/v1/migratev1.go b/migrate/v1/migratev1.go index c9d8108a25..3cb8828915 100644 --- a/migrate/v1/migratev1.go +++ b/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) diff --git a/migrate/v1/migratev1_test.go b/migrate/v1/migratev1_test.go index 796c07814a..55898f12b4 100644 --- a/migrate/v1/migratev1_test.go +++ b/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) { diff --git a/plugin/backend_linux.go b/plugin/backend_linux.go index e88aad86f3..ddd0dd5986 100644 --- a/plugin/backend_linux.go +++ b/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") diff --git a/plugin/backend_unsupported.go b/plugin/backend_unsupported.go index 35feb5cdca..2d4850eeba 100644 --- a/plugin/backend_unsupported.go +++ b/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" ) diff --git a/plugin/store.go b/plugin/store.go index ab60bff547..244522e10a 100644 --- a/plugin/store.go +++ b/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 } } diff --git a/reference/reference.go b/reference/reference.go deleted file mode 100644 index e6e070cb8c..0000000000 --- a/reference/reference.go +++ /dev/null @@ -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 -} diff --git a/reference/reference_test.go b/reference/reference_test.go deleted file mode 100644 index 481a2dd97b..0000000000 --- a/reference/reference_test.go +++ /dev/null @@ -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") - } -} diff --git a/reference/store.go b/reference/store.go index 35ae33660a..844ebf5319 100644 --- a/reference/store.go +++ b/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 } diff --git a/reference/store_test.go b/reference/store_test.go index 0d96e6b694..8f0ff6304e 100644 --- a/reference/store_test.go +++ b/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) } diff --git a/registry/config.go b/registry/config.go index ce98e85c7e..ff72381dc5 100644 --- a/registry/config.go +++ b/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 diff --git a/registry/registry_mock_test.go b/registry/registry_mock_test.go index 21fc1fdcc7..58b05d3849 100644 --- a/registry/registry_mock_test.go +++ b/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" diff --git a/registry/registry_test.go b/registry/registry_test.go index 7ab10721e0..1cbaaf4d46 100644 --- a/registry/registry_test.go +++ b/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) } diff --git a/registry/session.go b/registry/session.go index ade111bb6c..c71e778037 100644 --- a/registry/session.go +++ b/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) } } diff --git a/registry/types.go b/registry/types.go index 8dbcb961f1..0c3cbd6913 100644 --- a/registry/types.go +++ b/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. |