Browse Source

Merge pull request #19170 from aaronlehmann/delete-prune-digests

Prune digest references when deleting by tag
David Calavera 9 năm trước cách đây
mục cha
commit
c0c66c57b2

+ 28 - 2
daemon/image_delete.go

@@ -90,8 +90,34 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
 		daemon.LogImageEvent(imgID.String(), imgID.String(), "untag")
 		records = append(records, untaggedRecord)
 
-		// If has remaining references then untag finishes the remove
-		if len(repoRefs) > 1 {
+		repoRefs = daemon.referenceStore.References(imgID)
+
+		// If this is a tag reference and all the remaining references
+		// to this image are digest references, delete the remaining
+		// references so that they don't prevent removal of the image.
+		if _, isCanonical := parsedRef.(reference.Canonical); !isCanonical {
+			foundTagRef := false
+			for _, repoRef := range repoRefs {
+				if _, repoRefIsCanonical := repoRef.(reference.Canonical); !repoRefIsCanonical {
+					foundTagRef = true
+					break
+				}
+			}
+			if !foundTagRef {
+				for _, repoRef := range repoRefs {
+					if _, err := daemon.removeImageRef(repoRef); err != nil {
+						return records, err
+					}
+
+					untaggedRecord := types.ImageDelete{Untagged: repoRef.String()}
+					records = append(records, untaggedRecord)
+				}
+				repoRefs = []reference.Named{}
+			}
+		}
+
+		// If it has remaining references then the untag finished the remove
+		if len(repoRefs) > 0 {
 			return records, nil
 		}
 

+ 3 - 2
docs/reference/commandline/rmi.md

@@ -19,8 +19,9 @@ parent = "smn_cli"
       --no-prune           Do not delete untagged parents
 
 You can remove an image using its short or long ID, its tag, or its digest. If
-an image has one or more tag or digest reference, you must remove all of them
-before the image is removed.
+an image has one or more tag referencing it, you must remove all of them before
+the image is removed. Digest references are removed automatically when an image
+is removed by tag.
 
     $ docker images
     REPOSITORY                TAG                 IMAGE ID            CREATED             SIZE

+ 29 - 0
integration-cli/docker_cli_by_digest_test.go

@@ -395,6 +395,35 @@ func (s *DockerRegistrySuite) TestDeleteImageByIDOnlyPulledByDigest(c *check.C)
 	dockerCmd(c, "rmi", imageID)
 }
 
+func (s *DockerRegistrySuite) TestDeleteImageWithDigestAndTag(c *check.C) {
+	pushDigest, err := setupImage(c)
+	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
+
+	// pull from the registry using the <name>@<digest> reference
+	imageReference := fmt.Sprintf("%s@%s", repoName, pushDigest)
+	dockerCmd(c, "pull", imageReference)
+
+	imageID, err := inspectField(imageReference, "Id")
+	c.Assert(err, checker.IsNil, check.Commentf("error inspecting image id"))
+
+	repoTag := repoName + ":sometag"
+	repoTag2 := repoName + ":othertag"
+	dockerCmd(c, "tag", imageReference, repoTag)
+	dockerCmd(c, "tag", imageReference, repoTag2)
+
+	dockerCmd(c, "rmi", repoTag2)
+
+	// rmi should have deleted only repoTag2, because there's another tag
+	_, err = inspectField(repoTag, "Id")
+	c.Assert(err, checker.IsNil, check.Commentf("repoTag should not have been removed"))
+
+	dockerCmd(c, "rmi", repoTag)
+
+	// rmi should have deleted the tag, the digest reference, and the image itself
+	_, err = inspectField(imageID, "Id")
+	c.Assert(err, checker.NotNil, check.Commentf("image should have been deleted"))
+}
+
 // TestPullFailsWithAlteredManifest tests that a `docker pull` fails when
 // we have modified a manifest blob and its digest cannot be verified.
 // This is the schema2 version of the test.

+ 53 - 0
integration-cli/docker_cli_pull_trusted_test.go

@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"io/ioutil"
 	"os/exec"
+	"strings"
 	"time"
 
 	"github.com/docker/docker/pkg/integration/checker"
@@ -200,3 +201,55 @@ func (s *DockerTrustSuite) TestTrustedOfflinePull(c *check.C) {
 	c.Assert(err, check.IsNil, check.Commentf(out))
 	c.Assert(string(out), checker.Contains, "Tagging", check.Commentf(out))
 }
+
+func (s *DockerTrustSuite) TestTrustedPullDelete(c *check.C) {
+	repoName := fmt.Sprintf("%v/dockercli/%s:latest", privateRegistryURL, "trusted-pull-delete")
+	// tag the image and upload it to the private registry
+	_, err := buildImage(repoName, `
+                    FROM busybox
+                    CMD echo trustedpulldelete
+                `, true)
+
+	pushCmd := exec.Command(dockerBinary, "push", repoName)
+	s.trustedCmd(pushCmd)
+	out, _, err := runCommandWithOutput(pushCmd)
+	if err != nil {
+		c.Fatalf("Error running trusted push: %s\n%s", err, out)
+	}
+	if !strings.Contains(string(out), "Signing and pushing trust metadata") {
+		c.Fatalf("Missing expected output on trusted push:\n%s", out)
+	}
+
+	if out, status := dockerCmd(c, "rmi", repoName); status != 0 {
+		c.Fatalf("Error removing image %q\n%s", repoName, out)
+	}
+
+	// Try pull
+	pullCmd := exec.Command(dockerBinary, "pull", repoName)
+	s.trustedCmd(pullCmd)
+	out, _, err = runCommandWithOutput(pullCmd)
+
+	c.Assert(err, check.IsNil, check.Commentf(out))
+
+	matches := digestRegex.FindStringSubmatch(out)
+	c.Assert(matches, checker.HasLen, 2, check.Commentf("unable to parse digest from pull output: %s", out))
+	pullDigest := matches[1]
+
+	imageID, err := inspectField(repoName, "Id")
+	c.Assert(err, checker.IsNil, check.Commentf("error inspecting image id"))
+
+	imageByDigest := repoName + "@" + pullDigest
+	byDigestID, err := inspectField(imageByDigest, "Id")
+	c.Assert(err, checker.IsNil, check.Commentf("error inspecting image id"))
+
+	c.Assert(byDigestID, checker.Equals, imageID)
+
+	// rmi of tag should also remove the digest reference
+	dockerCmd(c, "rmi", repoName)
+
+	_, err = inspectField(imageByDigest, "Id")
+	c.Assert(err, checker.NotNil, check.Commentf("digest reference should have been removed"))
+
+	_, err = inspectField(imageID, "Id")
+	c.Assert(err, checker.NotNil, check.Commentf("image should have been deleted"))
+}