diff --git a/cli/command/image/pull.go b/cli/command/image/pull.go index 7485d21a6880..33b6e34aca0b 100644 --- a/cli/command/image/pull.go +++ b/cli/command/image/pull.go @@ -27,6 +27,15 @@ type pullOptions struct { quiet bool } +// hasTagAndDigest reports whether ref specifies both a tag and a digest. +// Those are valid reference forms, but docker pull treats them as pull-by-digest +// and does not associate the tag with the local image, which is surprising. +func hasTagAndDigest(ref reference.Named) bool { + _, tagged := ref.(reference.Tagged) + _, digested := ref.(reference.Digested) + return tagged && digested +} + // newPullCommand creates a new `docker pull` command func newPullCommand(dockerCLI command.Cli) *cobra.Command { var opts pullOptions @@ -73,6 +82,10 @@ func runPull(ctx context.Context, dockerCLI command.Cli, opts pullOptions) error return err case opts.all && !reference.IsNameOnly(distributionRef): return errors.New("tag can't be used with --all-tags/-a") + case hasTagAndDigest(distributionRef): + // Use line is NAME[:TAG|@DIGEST]; combining both is ambiguous (tag is + // dropped on pull and the image is not tagged locally). + return errors.New("reference cannot contain both a tag and a digest") case !opts.all && reference.IsNameOnly(distributionRef): distributionRef = reference.TagNameOnly(distributionRef) if tagged, ok := distributionRef.(reference.Tagged); ok && !opts.quiet { diff --git a/cli/command/image/pull_test.go b/cli/command/image/pull_test.go index eb8edfbca699..03f4cdb0a1b8 100644 --- a/cli/command/image/pull_test.go +++ b/cli/command/image/pull_test.go @@ -34,6 +34,11 @@ func TestNewPullCommandErrors(t *testing.T) { expectedError: "tag can't be used with --all-tags/-a", args: []string{"--all-tags", "image:tag"}, }, + { + name: "tag-and-digest", + expectedError: "reference cannot contain both a tag and a digest", + args: []string{"image:tag@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { diff --git a/docs/reference/commandline/image_pull.md b/docs/reference/commandline/image_pull.md index beb270660aa6..21e6a701d3ea 100644 --- a/docs/reference/commandline/image_pull.md +++ b/docs/reference/commandline/image_pull.md @@ -150,6 +150,10 @@ Status: Image is up to date for ubuntu@sha256:2e863c44b718727c860746568e1d54afd1 docker.io/library/ubuntu@sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc436217b30 ``` +Do not combine a tag and a digest in the same reference (for example, +`ubuntu:24.04@sha256:…`). `docker pull` rejects that form; use either a tag or +a digest, not both. + Digest can also be used in the `FROM` of a Dockerfile, for example: ```dockerfile