diff --git a/command/container/create.go b/command/container/create.go index 13890d9ef5..01d7815c92 100644 --- a/command/container/create.go +++ b/command/container/create.go @@ -5,6 +5,7 @@ import ( "io" "os" + "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" networktypes "github.com/docker/docker/api/types/network" @@ -13,8 +14,6 @@ import ( "github.com/docker/docker/cli/command/image" apiclient "github.com/docker/docker/client" "github.com/docker/docker/pkg/jsonmessage" - // FIXME migrate to docker/distribution/reference - "github.com/docker/docker/reference" "github.com/docker/docker/registry" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -72,7 +71,7 @@ func runCreate(dockerCli *command.DockerCli, flags *pflag.FlagSet, opts *createO } func pullImage(ctx context.Context, dockerCli *command.DockerCli, image string, out io.Writer) error { - ref, err := reference.ParseNamed(image) + ref, err := reference.ParseNormalizedNamed(image) if err != nil { return err } @@ -150,7 +149,12 @@ func newCIDFile(path string) (*cidFile, error) { func createContainer(ctx context.Context, dockerCli *command.DockerCli, config *container.Config, hostConfig *container.HostConfig, networkingConfig *networktypes.NetworkingConfig, cidfile, name string) (*container.ContainerCreateCreatedBody, error) { stderr := dockerCli.Err() - var containerIDFile *cidFile + var ( + containerIDFile *cidFile + trustedRef reference.Canonical + namedRef reference.Named + ) + if cidfile != "" { var err error if containerIDFile, err = newCIDFile(cidfile); err != nil { @@ -159,21 +163,24 @@ func createContainer(ctx context.Context, dockerCli *command.DockerCli, config * defer containerIDFile.Close() } - var trustedRef reference.Canonical - _, ref, err := reference.ParseIDOrReference(config.Image) + ref, err := reference.ParseAnyReference(config.Image) if err != nil { return nil, err } - if ref != nil { - ref = reference.WithDefaultTag(ref) + if named, ok := ref.(reference.Named); ok { + if reference.IsNameOnly(named) { + namedRef = reference.EnsureTagged(named) + } else { + namedRef = named + } - if ref, ok := ref.(reference.NamedTagged); ok && command.IsTrusted() { + if taggedRef, ok := namedRef.(reference.NamedTagged); ok && command.IsTrusted() { var err error - trustedRef, err = image.TrustedReference(ctx, dockerCli, ref, nil) + trustedRef, err = image.TrustedReference(ctx, dockerCli, taggedRef, nil) if err != nil { return nil, err } - config.Image = trustedRef.String() + config.Image = reference.FamiliarString(trustedRef) } } @@ -182,15 +189,15 @@ func createContainer(ctx context.Context, dockerCli *command.DockerCli, config * //if image not found try to pull it if err != nil { - if apiclient.IsErrImageNotFound(err) && ref != nil { - fmt.Fprintf(stderr, "Unable to find image '%s' locally\n", ref.String()) + if apiclient.IsErrImageNotFound(err) && namedRef != nil { + fmt.Fprintf(stderr, "Unable to find image '%s' locally\n", reference.FamiliarString(namedRef)) // we don't want to write to stdout anything apart from container.ID if err = pullImage(ctx, dockerCli, config.Image, stderr); err != nil { return nil, err } - if ref, ok := ref.(reference.NamedTagged); ok && trustedRef != nil { - if err := image.TagTrusted(ctx, dockerCli, trustedRef, ref); err != nil { + if taggedRef, ok := namedRef.(reference.NamedTagged); ok && trustedRef != nil { + if err := image.TagTrusted(ctx, dockerCli, trustedRef, taggedRef); err != nil { return nil, err } } diff --git a/command/formatter/image.go b/command/formatter/image.go index 9187dfb2e2..fc0168cf72 100644 --- a/command/formatter/image.go +++ b/command/formatter/image.go @@ -4,9 +4,9 @@ import ( "fmt" "time" + "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" "github.com/docker/docker/pkg/stringid" - "github.com/docker/docker/reference" units "github.com/docker/go-units" ) @@ -95,21 +95,23 @@ func imageFormat(ctx ImageContext, images []types.ImageSummary, format func(subC repoDigests := map[string][]string{} for _, refString := range append(image.RepoTags) { - ref, err := reference.ParseNamed(refString) + ref, err := reference.ParseNormalizedNamed(refString) if err != nil { continue } if nt, ok := ref.(reference.NamedTagged); ok { - repoTags[ref.Name()] = append(repoTags[ref.Name()], nt.Tag()) + familiarRef := reference.FamiliarName(ref) + repoTags[familiarRef] = append(repoTags[familiarRef], nt.Tag()) } } for _, refString := range append(image.RepoDigests) { - ref, err := reference.ParseNamed(refString) + ref, err := reference.ParseNormalizedNamed(refString) if err != nil { continue } if c, ok := ref.(reference.Canonical); ok { - repoDigests[ref.Name()] = append(repoDigests[ref.Name()], c.Digest().String()) + familiarRef := reference.FamiliarName(ref) + repoDigests[familiarRef] = append(repoDigests[familiarRef], c.Digest().String()) } } diff --git a/command/image/build.go b/command/image/build.go index 5d6e611406..ecc6861701 100644 --- a/command/image/build.go +++ b/command/image/build.go @@ -11,6 +11,7 @@ import ( "regexp" "runtime" + "github.com/docker/distribution/reference" "github.com/docker/docker/api" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" @@ -25,7 +26,6 @@ import ( "github.com/docker/docker/pkg/progress" "github.com/docker/docker/pkg/streamformatter" "github.com/docker/docker/pkg/urlutil" - "github.com/docker/docker/reference" runconfigopts "github.com/docker/docker/runconfig/opts" units "github.com/docker/go-units" "github.com/spf13/cobra" @@ -360,7 +360,7 @@ type translatorFunc func(context.Context, reference.NamedTagged) (reference.Cano // validateTag checks if the given image name can be resolved. func validateTag(rawRepo string) (string, error) { - _, err := reference.ParseNamed(rawRepo) + _, err := reference.ParseNormalizedNamed(rawRepo) if err != nil { return "", err } @@ -392,18 +392,21 @@ func rewriteDockerfileFrom(ctx context.Context, dockerfile io.Reader, translator matches := dockerfileFromLinePattern.FindStringSubmatch(line) if matches != nil && matches[1] != api.NoBaseImageSpecifier { // Replace the line with a resolved "FROM repo@digest" - ref, err := reference.ParseNamed(matches[1]) + var ref reference.Named + ref, err = reference.ParseNormalizedNamed(matches[1]) if err != nil { return nil, nil, err } - ref = reference.WithDefaultTag(ref) + if reference.IsNameOnly(ref) { + ref = reference.EnsureTagged(ref) + } if ref, ok := ref.(reference.NamedTagged); ok && command.IsTrusted() { trustedRef, err := translator(ctx, ref) if err != nil { return nil, nil, err } - line = dockerfileFromLinePattern.ReplaceAllLiteralString(line, fmt.Sprintf("FROM %s", trustedRef.String())) + line = dockerfileFromLinePattern.ReplaceAllLiteralString(line, fmt.Sprintf("FROM %s", reference.FamiliarString(trustedRef))) resolvedTags = append(resolvedTags, &resolvedTag{ digestRef: trustedRef, tagRef: ref, diff --git a/command/image/pull.go b/command/image/pull.go index 24933fe846..d5aa3eefbd 100644 --- a/command/image/pull.go +++ b/command/image/pull.go @@ -7,9 +7,9 @@ import ( "golang.org/x/net/context" + "github.com/docker/distribution/reference" "github.com/docker/docker/cli" "github.com/docker/docker/cli/command" - "github.com/docker/docker/reference" "github.com/docker/docker/registry" "github.com/spf13/cobra" ) @@ -42,7 +42,8 @@ func NewPullCommand(dockerCli *command.DockerCli) *cobra.Command { } func runPull(dockerCli *command.DockerCli, opts pullOptions) error { - distributionRef, err := reference.ParseNamed(opts.remote) + var distributionRef reference.Named + distributionRef, err := reference.ParseNormalizedNamed(opts.remote) if err != nil { return err } @@ -51,8 +52,9 @@ func runPull(dockerCli *command.DockerCli, opts pullOptions) error { } if !opts.all && reference.IsNameOnly(distributionRef) { - distributionRef = reference.WithDefaultTag(distributionRef) - fmt.Fprintf(dockerCli.Out(), "Using default tag: %s\n", reference.DefaultTag) + taggedRef := reference.EnsureTagged(distributionRef) + fmt.Fprintf(dockerCli.Out(), "Using default tag: %s\n", taggedRef.Tag()) + distributionRef = taggedRef } // Resolve the Repository name from fqn to RepositoryInfo @@ -71,7 +73,7 @@ func runPull(dockerCli *command.DockerCli, opts pullOptions) error { if command.IsTrusted() && !isCanonical { err = trustedPull(ctx, dockerCli, repoInfo, distributionRef, authConfig, requestPrivilege) } else { - err = imagePullPrivileged(ctx, dockerCli, authConfig, distributionRef.String(), requestPrivilege, opts.all) + err = imagePullPrivileged(ctx, dockerCli, authConfig, reference.FamiliarString(distributionRef), requestPrivilege, opts.all) } if err != nil { if strings.Contains(err.Error(), "target is plugin") { diff --git a/command/image/push.go b/command/image/push.go index a8ce4945ec..7972718e61 100644 --- a/command/image/push.go +++ b/command/image/push.go @@ -3,10 +3,10 @@ package image import ( "golang.org/x/net/context" + "github.com/docker/distribution/reference" "github.com/docker/docker/cli" "github.com/docker/docker/cli/command" "github.com/docker/docker/pkg/jsonmessage" - "github.com/docker/docker/reference" "github.com/docker/docker/registry" "github.com/spf13/cobra" ) @@ -30,7 +30,7 @@ func NewPushCommand(dockerCli *command.DockerCli) *cobra.Command { } func runPush(dockerCli *command.DockerCli, remote string) error { - ref, err := reference.ParseNamed(remote) + ref, err := reference.ParseNormalizedNamed(remote) if err != nil { return err } @@ -51,7 +51,7 @@ func runPush(dockerCli *command.DockerCli, remote string) error { return trustedPush(ctx, dockerCli, repoInfo, ref, authConfig, requestPrivilege) } - responseBody, err := imagePushPrivileged(ctx, dockerCli, authConfig, ref.String(), requestPrivilege) + responseBody, err := imagePushPrivileged(ctx, dockerCli, authConfig, ref, requestPrivilege) if err != nil { return err } diff --git a/command/image/trust.go b/command/image/trust.go index 58e0574396..2ff9b463d5 100644 --- a/command/image/trust.go +++ b/command/image/trust.go @@ -10,11 +10,11 @@ import ( "sort" "github.com/Sirupsen/logrus" + "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" "github.com/docker/docker/cli/command" "github.com/docker/docker/cli/trust" "github.com/docker/docker/pkg/jsonmessage" - "github.com/docker/docker/reference" "github.com/docker/docker/registry" "github.com/docker/notary/client" "github.com/docker/notary/tuf/data" @@ -30,7 +30,7 @@ type target struct { // trustedPush handles content trust pushing of an image func trustedPush(ctx context.Context, cli *command.DockerCli, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error { - responseBody, err := imagePushPrivileged(ctx, cli, authConfig, ref.String(), requestPrivilege) + responseBody, err := imagePushPrivileged(ctx, cli, authConfig, ref, requestPrivilege) if err != nil { return err } @@ -202,7 +202,7 @@ func addTargetToAllSignableRoles(repo *client.NotaryRepository, target *client.T } // imagePushPrivileged push the image -func imagePushPrivileged(ctx context.Context, cli *command.DockerCli, authConfig types.AuthConfig, ref string, requestPrivilege types.RequestPrivilegeFunc) (io.ReadCloser, error) { +func imagePushPrivileged(ctx context.Context, cli *command.DockerCli, authConfig types.AuthConfig, ref reference.Named, requestPrivilege types.RequestPrivilegeFunc) (io.ReadCloser, error) { encodedAuth, err := command.EncodeAuthToBase64(authConfig) if err != nil { return nil, err @@ -212,7 +212,7 @@ func imagePushPrivileged(ctx context.Context, cli *command.DockerCli, authConfig PrivilegeFunc: requestPrivilege, } - return cli.Client().ImagePush(ctx, ref, options) + return cli.Client().ImagePush(ctx, reference.FamiliarString(ref), options) } // trustedPull handles content trust pulling of an image @@ -229,12 +229,12 @@ func trustedPull(ctx context.Context, cli *command.DockerCli, repoInfo *registry // List all targets targets, err := notaryRepo.ListTargets(trust.ReleasesRole, data.CanonicalTargetsRole) if err != nil { - return trust.NotaryError(repoInfo.FullName(), err) + return trust.NotaryError(ref.Name(), err) } for _, tgt := range targets { t, err := convertTarget(tgt.Target) if err != nil { - fmt.Fprintf(cli.Out(), "Skipping target for %q\n", repoInfo.Name()) + fmt.Fprintf(cli.Out(), "Skipping target for %q\n", reference.FamiliarName(ref)) continue } // Only list tags in the top level targets role or the releases delegation role - ignore @@ -245,17 +245,17 @@ func trustedPull(ctx context.Context, cli *command.DockerCli, repoInfo *registry refs = append(refs, t) } if len(refs) == 0 { - return trust.NotaryError(repoInfo.FullName(), fmt.Errorf("No trusted tags for %s", repoInfo.FullName())) + return trust.NotaryError(ref.Name(), fmt.Errorf("No trusted tags for %s", ref.Name())) } } else { t, err := notaryRepo.GetTargetByName(tagged.Tag(), trust.ReleasesRole, data.CanonicalTargetsRole) if err != nil { - return trust.NotaryError(repoInfo.FullName(), err) + return trust.NotaryError(ref.Name(), err) } // Only get the tag if it's in the top level targets role or the releases delegation role // ignore it if it's in any other delegation roles if t.Role != trust.ReleasesRole && t.Role != data.CanonicalTargetsRole { - return trust.NotaryError(repoInfo.FullName(), fmt.Errorf("No trust data for %s", tagged.Tag())) + return trust.NotaryError(ref.Name(), fmt.Errorf("No trust data for %s", tagged.Tag())) } logrus.Debugf("retrieving target for %s role\n", t.Role) @@ -272,24 +272,21 @@ func trustedPull(ctx context.Context, cli *command.DockerCli, repoInfo *registry if displayTag != "" { displayTag = ":" + displayTag } - fmt.Fprintf(cli.Out(), "Pull (%d of %d): %s%s@%s\n", i+1, len(refs), repoInfo.Name(), displayTag, r.digest) + fmt.Fprintf(cli.Out(), "Pull (%d of %d): %s%s@%s\n", i+1, len(refs), reference.FamiliarName(ref), displayTag, r.digest) - ref, err := reference.WithDigest(reference.TrimNamed(repoInfo), r.digest) + trustedRef, err := reference.WithDigest(reference.TrimNamed(ref), r.digest) if err != nil { return err } - if err := imagePullPrivileged(ctx, cli, authConfig, ref.String(), requestPrivilege, false); err != nil { + if err := imagePullPrivileged(ctx, cli, authConfig, reference.FamiliarString(trustedRef), requestPrivilege, false); err != nil { return err } - tagged, err := reference.WithTag(repoInfo, r.name) - if err != nil { - return err - } - trustedRef, err := reference.WithDigest(reference.TrimNamed(repoInfo), r.digest) + tagged, err := reference.WithTag(reference.TrimNamed(ref), r.name) if err != nil { return err } + if err := TagTrusted(ctx, cli, trustedRef, tagged); err != nil { return err } @@ -375,7 +372,11 @@ func convertTarget(t client.Target) (target, error) { // TagTrusted tags a trusted ref func TagTrusted(ctx context.Context, cli *command.DockerCli, trustedRef reference.Canonical, ref reference.NamedTagged) error { - fmt.Fprintf(cli.Out(), "Tagging %s as %s\n", trustedRef.String(), ref.String()) + // Use familiar references when interacting with client and output + familiarRef := reference.FamiliarString(ref) + trustedFamiliarRef := reference.FamiliarString(trustedRef) - return cli.Client().ImageTag(ctx, trustedRef.String(), ref.String()) + fmt.Fprintf(cli.Out(), "Tagging %s as %s\n", trustedFamiliarRef, familiarRef) + + return cli.Client().ImageTag(ctx, trustedFamiliarRef, familiarRef) } diff --git a/command/plugin/create.go b/command/plugin/create.go index 82d17af48c..e1e6f74ee3 100644 --- a/command/plugin/create.go +++ b/command/plugin/create.go @@ -8,18 +8,18 @@ import ( "path/filepath" "github.com/Sirupsen/logrus" + "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" "github.com/docker/docker/cli" "github.com/docker/docker/cli/command" "github.com/docker/docker/pkg/archive" - "github.com/docker/docker/reference" "github.com/spf13/cobra" "golang.org/x/net/context" ) // validateTag checks if the given repoName can be resolved. func validateTag(rawRepo string) error { - _, err := reference.ParseNamed(rawRepo) + _, err := reference.ParseNormalizedNamed(rawRepo) return err } diff --git a/command/plugin/install.go b/command/plugin/install.go index a64dc2525a..39b8c15ec7 100644 --- a/command/plugin/install.go +++ b/command/plugin/install.go @@ -6,14 +6,13 @@ import ( "fmt" "strings" - distreference "github.com/docker/distribution/reference" + "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" registrytypes "github.com/docker/docker/api/types/registry" "github.com/docker/docker/cli" "github.com/docker/docker/cli/command" "github.com/docker/docker/cli/command/image" "github.com/docker/docker/pkg/jsonmessage" - "github.com/docker/docker/reference" "github.com/docker/docker/registry" "github.com/spf13/cobra" "golang.org/x/net/context" @@ -52,8 +51,8 @@ func newInstallCommand(dockerCli *command.DockerCli) *cobra.Command { return cmd } -func getRepoIndexFromUnnormalizedRef(ref distreference.Named) (*registrytypes.IndexInfo, error) { - named, err := reference.ParseNamed(ref.Name()) +func getRepoIndexFromUnnormalizedRef(ref reference.Named) (*registrytypes.IndexInfo, error) { + named, err := reference.ParseNormalizedNamed(ref.Name()) if err != nil { return nil, err } @@ -85,71 +84,60 @@ func newRegistryService() registry.Service { } func runInstall(dockerCli *command.DockerCli, opts pluginOptions) error { - // Parse name using distribution reference package to support name - // containing both tag and digest. Names with both tag and digest - // will be treated by the daemon as a pull by digest with - // an alias for the tag (if no alias is provided). - ref, err := distreference.ParseNamed(opts.name) + // Names with both tag and digest will be treated by the daemon + // as a pull by digest with an alias for the tag + // (if no alias is provided). + ref, err := reference.ParseNormalizedNamed(opts.name) if err != nil { return err } alias := "" if opts.alias != "" { - aref, err := reference.ParseNamed(opts.alias) + aref, err := reference.ParseNormalizedNamed(opts.alias) if err != nil { return err } - aref = reference.WithDefaultTag(aref) - if _, ok := aref.(reference.NamedTagged); !ok { + if _, ok := aref.(reference.Canonical); ok { return fmt.Errorf("invalid name: %s", opts.alias) } - alias = aref.String() + alias = reference.FamiliarString(reference.EnsureTagged(aref)) } ctx := context.Background() - index, err := getRepoIndexFromUnnormalizedRef(ref) + repoInfo, err := registry.ParseRepositoryInfo(ref) if err != nil { return err } remote := ref.String() - _, isCanonical := ref.(distreference.Canonical) + _, isCanonical := ref.(reference.Canonical) if command.IsTrusted() && !isCanonical { if alias == "" { - alias = ref.String() + alias = reference.FamiliarString(ref) } - var nt reference.NamedTagged - named, err := reference.ParseNamed(ref.Name()) - if err != nil { - return err - } - if tagged, ok := ref.(distreference.Tagged); ok { - nt, err = reference.WithTag(named, tagged.Tag()) - if err != nil { - return err - } - } else { - named = reference.WithDefaultTag(named) - nt = named.(reference.NamedTagged) + + nt, ok := ref.(reference.NamedTagged) + if !ok { + nt = reference.EnsureTagged(ref) } trusted, err := image.TrustedReference(ctx, dockerCli, nt, newRegistryService()) if err != nil { return err } - remote = trusted.String() + remote = reference.FamiliarString(trusted) } - authConfig := command.ResolveAuthConfig(ctx, dockerCli, index) + authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index) encodedAuth, err := command.EncodeAuthToBase64(authConfig) if err != nil { return err } - registryAuthFunc := command.RegistryAuthenticationPrivilegedFunc(dockerCli, index, "plugin install") + registryAuthFunc := command.RegistryAuthenticationPrivilegedFunc(dockerCli, repoInfo.Index, "plugin install") options := types.PluginInstallOptions{ RegistryAuth: encodedAuth, diff --git a/command/plugin/push.go b/command/plugin/push.go index b0766307f3..b0ddad939e 100644 --- a/command/plugin/push.go +++ b/command/plugin/push.go @@ -5,11 +5,11 @@ import ( "golang.org/x/net/context" + "github.com/docker/distribution/reference" "github.com/docker/docker/cli" "github.com/docker/docker/cli/command" "github.com/docker/docker/cli/command/image" "github.com/docker/docker/pkg/jsonmessage" - "github.com/docker/docker/reference" "github.com/docker/docker/registry" "github.com/spf13/cobra" ) @@ -32,16 +32,17 @@ func newPushCommand(dockerCli *command.DockerCli) *cobra.Command { } func runPush(dockerCli *command.DockerCli, name string) error { - named, err := reference.ParseNamed(name) // FIXME: validate + named, err := reference.ParseNormalizedNamed(name) if err != nil { return err } - if reference.IsNameOnly(named) { - named = reference.WithDefaultTag(named) + if _, ok := named.(reference.Canonical); ok { + return fmt.Errorf("invalid name: %s", name) } - ref, ok := named.(reference.NamedTagged) + + taggedRef, ok := named.(reference.NamedTagged) if !ok { - return fmt.Errorf("invalid name: %s", named.String()) + taggedRef = reference.EnsureTagged(named) } ctx := context.Background() @@ -56,7 +57,8 @@ func runPush(dockerCli *command.DockerCli, name string) error { if err != nil { return err } - responseBody, err := dockerCli.Client().PluginPush(ctx, ref.String(), encodedAuth) + + responseBody, err := dockerCli.Client().PluginPush(ctx, reference.FamiliarString(taggedRef), encodedAuth) if err != nil { return err } diff --git a/command/registry.go b/command/registry.go index 65f6b3309e..411310fa34 100644 --- a/command/registry.go +++ b/command/registry.go @@ -12,10 +12,10 @@ import ( "golang.org/x/net/context" + "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" registrytypes "github.com/docker/docker/api/types/registry" "github.com/docker/docker/pkg/term" - "github.com/docker/docker/reference" "github.com/docker/docker/registry" ) @@ -174,7 +174,7 @@ func RetrieveAuthTokenFromImage(ctx context.Context, cli *DockerCli, image strin // resolveAuthConfigFromImage retrieves that AuthConfig using the image string func resolveAuthConfigFromImage(ctx context.Context, cli *DockerCli, image string) (types.AuthConfig, error) { - registryRef, err := reference.ParseNamed(image) + registryRef, err := reference.ParseNormalizedNamed(image) if err != nil { return types.AuthConfig{}, err } diff --git a/command/service/trust.go b/command/service/trust.go index 15f8a708f0..d466f3b648 100644 --- a/command/service/trust.go +++ b/command/service/trust.go @@ -5,11 +5,10 @@ import ( "fmt" "github.com/Sirupsen/logrus" - distreference "github.com/docker/distribution/reference" + "github.com/docker/distribution/reference" "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/cli/command" "github.com/docker/docker/cli/trust" - "github.com/docker/docker/reference" "github.com/docker/docker/registry" "github.com/docker/notary/tuf/data" "github.com/opencontainers/go-digest" @@ -24,41 +23,34 @@ func resolveServiceImageDigest(dockerCli *command.DockerCli, service *swarm.Serv return nil } - image := service.TaskTemplate.ContainerSpec.Image - - // We only attempt to resolve the digest if the reference - // could be parsed as a digest reference. Specifying an image ID - // is valid but not resolvable. There is no warning message for - // an image ID because it's valid to use one. - if _, err := digest.Parse(image); err == nil { - return nil - } - - ref, err := reference.ParseNamed(image) + ref, err := reference.ParseAnyReference(service.TaskTemplate.ContainerSpec.Image) if err != nil { - return fmt.Errorf("Could not parse image reference %s", service.TaskTemplate.ContainerSpec.Image) + return errors.Wrapf(err, "invalid reference %s", service.TaskTemplate.ContainerSpec.Image) } - if _, ok := ref.(reference.Canonical); !ok { - ref = reference.WithDefaultTag(ref) - taggedRef, ok := ref.(reference.NamedTagged) + // If reference does not have digest (is not canonical nor image id) + if _, ok := ref.(reference.Digested); !ok { + namedRef, ok := ref.(reference.Named) if !ok { - // This should never happen because a reference either - // has a digest, or WithDefaultTag would give it a tag. - return errors.New("Failed to resolve image digest using content trust: reference is missing a tag") + return errors.New("failed to resolve image digest using content trust: reference is not named") + } + taggedRef := reference.EnsureTagged(namedRef) + resolvedImage, err := trustedResolveDigest(context.Background(), dockerCli, taggedRef) if err != nil { - return fmt.Errorf("Failed to resolve image digest using content trust: %v", err) + return errors.Wrap(err, "failed to resolve image digest using content trust") } - logrus.Debugf("resolved image tag to %s using content trust", resolvedImage.String()) - service.TaskTemplate.ContainerSpec.Image = resolvedImage.String() + resolvedFamiliar := reference.FamiliarString(resolvedImage) + logrus.Debugf("resolved image tag to %s using content trust", resolvedFamiliar) + service.TaskTemplate.ContainerSpec.Image = resolvedFamiliar } + return nil } -func trustedResolveDigest(ctx context.Context, cli *command.DockerCli, ref reference.NamedTagged) (distreference.Canonical, error) { +func trustedResolveDigest(ctx context.Context, cli *command.DockerCli, ref reference.NamedTagged) (reference.Canonical, error) { repoInfo, err := registry.ParseRepositoryInfo(ref) if err != nil { return nil, err @@ -78,7 +70,7 @@ func trustedResolveDigest(ctx context.Context, cli *command.DockerCli, ref refer // Only get the tag if it's in the top level targets role or the releases delegation role // ignore it if it's in any other delegation roles if t.Role != trust.ReleasesRole && t.Role != data.CanonicalTargetsRole { - return nil, trust.NotaryError(repoInfo.FullName(), fmt.Errorf("No trust data for %s", ref.String())) + return nil, trust.NotaryError(repoInfo.FullName(), fmt.Errorf("No trust data for %s", reference.FamiliarString(ref))) } logrus.Debugf("retrieving target for %s role\n", t.Role) @@ -89,8 +81,6 @@ func trustedResolveDigest(ctx context.Context, cli *command.DockerCli, ref refer dgst := digest.NewDigestFromHex("sha256", hex.EncodeToString(h)) - // Using distribution reference package to make sure that adding a - // digest does not erase the tag. When the two reference packages - // are unified, this will no longer be an issue. - return distreference.WithDigest(ref, dgst) + // Allow returning canonical reference with tag + return reference.WithDigest(ref, dgst) }