github.com/jwhonce/docker@v0.6.7-0.20190327063223-da823cf3a5a3/integration-cli/docker_cli_by_digest_test.go (about)

     1  package main
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"regexp"
     7  	"strings"
     8  
     9  	"github.com/docker/distribution/manifest/schema2"
    10  	"github.com/docker/docker/api/types"
    11  	"github.com/docker/docker/integration-cli/checker"
    12  	"github.com/docker/docker/integration-cli/cli"
    13  	"github.com/docker/docker/integration-cli/cli/build"
    14  	"github.com/go-check/check"
    15  	"github.com/opencontainers/go-digest"
    16  	"gotest.tools/assert"
    17  	is "gotest.tools/assert/cmp"
    18  )
    19  
    20  var (
    21  	remoteRepoName  = "dockercli/busybox-by-dgst"
    22  	repoName        = fmt.Sprintf("%s/%s", privateRegistryURL, remoteRepoName)
    23  	pushDigestRegex = regexp.MustCompile("[\\S]+: digest: ([\\S]+) size: [0-9]+")
    24  	digestRegex     = regexp.MustCompile("Digest: ([\\S]+)")
    25  )
    26  
    27  func setupImage(c *check.C) (digest.Digest, error) {
    28  	return setupImageWithTag(c, "latest")
    29  }
    30  
    31  func setupImageWithTag(c *check.C, tag string) (digest.Digest, error) {
    32  	containerName := "busyboxbydigest"
    33  
    34  	// new file is committed because this layer is used for detecting malicious
    35  	// changes. if this was committed as empty layer it would be skipped on pull
    36  	// and malicious changes would never be detected.
    37  	cli.DockerCmd(c, "run", "-e", "digest=1", "--name", containerName, "busybox", "touch", "anewfile")
    38  
    39  	// tag the image to upload it to the private registry
    40  	repoAndTag := repoName + ":" + tag
    41  	cli.DockerCmd(c, "commit", containerName, repoAndTag)
    42  
    43  	// delete the container as we don't need it any more
    44  	cli.DockerCmd(c, "rm", "-fv", containerName)
    45  
    46  	// push the image
    47  	out := cli.DockerCmd(c, "push", repoAndTag).Combined()
    48  
    49  	// delete our local repo that we previously tagged
    50  	cli.DockerCmd(c, "rmi", repoAndTag)
    51  
    52  	matches := pushDigestRegex.FindStringSubmatch(out)
    53  	c.Assert(matches, checker.HasLen, 2, check.Commentf("unable to parse digest from push output: %s", out))
    54  	pushDigest := matches[1]
    55  
    56  	return digest.Digest(pushDigest), nil
    57  }
    58  
    59  func testPullByTagDisplaysDigest(c *check.C) {
    60  	testRequires(c, DaemonIsLinux)
    61  	pushDigest, err := setupImage(c)
    62  	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
    63  
    64  	// pull from the registry using the tag
    65  	out, _ := dockerCmd(c, "pull", repoName)
    66  
    67  	// the pull output includes "Digest: <digest>", so find that
    68  	matches := digestRegex.FindStringSubmatch(out)
    69  	c.Assert(matches, checker.HasLen, 2, check.Commentf("unable to parse digest from pull output: %s", out))
    70  	pullDigest := matches[1]
    71  
    72  	// make sure the pushed and pull digests match
    73  	c.Assert(pushDigest.String(), checker.Equals, pullDigest)
    74  }
    75  
    76  func (s *DockerRegistrySuite) TestPullByTagDisplaysDigest(c *check.C) {
    77  	testPullByTagDisplaysDigest(c)
    78  }
    79  
    80  func testPullByDigest(c *check.C) {
    81  	testRequires(c, DaemonIsLinux)
    82  	pushDigest, err := setupImage(c)
    83  	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
    84  
    85  	// pull from the registry using the <name>@<digest> reference
    86  	imageReference := fmt.Sprintf("%s@%s", repoName, pushDigest)
    87  	out, _ := dockerCmd(c, "pull", imageReference)
    88  
    89  	// the pull output includes "Digest: <digest>", so find that
    90  	matches := digestRegex.FindStringSubmatch(out)
    91  	c.Assert(matches, checker.HasLen, 2, check.Commentf("unable to parse digest from pull output: %s", out))
    92  	pullDigest := matches[1]
    93  
    94  	// make sure the pushed and pull digests match
    95  	c.Assert(pushDigest.String(), checker.Equals, pullDigest)
    96  }
    97  
    98  func (s *DockerRegistrySuite) TestPullByDigest(c *check.C) {
    99  	testPullByDigest(c)
   100  }
   101  
   102  func testPullByDigestNoFallback(c *check.C) {
   103  	testRequires(c, DaemonIsLinux)
   104  	// pull from the registry using the <name>@<digest> reference
   105  	imageReference := fmt.Sprintf("%s@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", repoName)
   106  	out, _, err := dockerCmdWithError("pull", imageReference)
   107  	c.Assert(err, checker.NotNil, check.Commentf("expected non-zero exit status and correct error message when pulling non-existing image"))
   108  	c.Assert(out, checker.Contains, fmt.Sprintf("manifest for %s not found", imageReference), check.Commentf("expected non-zero exit status and correct error message when pulling non-existing image"))
   109  }
   110  
   111  func (s *DockerRegistrySuite) TestPullByDigestNoFallback(c *check.C) {
   112  	testPullByDigestNoFallback(c)
   113  }
   114  
   115  func (s *DockerRegistrySuite) TestCreateByDigest(c *check.C) {
   116  	pushDigest, err := setupImage(c)
   117  	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
   118  
   119  	imageReference := fmt.Sprintf("%s@%s", repoName, pushDigest)
   120  
   121  	containerName := "createByDigest"
   122  	dockerCmd(c, "create", "--name", containerName, imageReference)
   123  
   124  	res := inspectField(c, containerName, "Config.Image")
   125  	c.Assert(res, checker.Equals, imageReference)
   126  }
   127  
   128  func (s *DockerRegistrySuite) TestRunByDigest(c *check.C) {
   129  	pushDigest, err := setupImage(c)
   130  	c.Assert(err, checker.IsNil)
   131  
   132  	imageReference := fmt.Sprintf("%s@%s", repoName, pushDigest)
   133  
   134  	containerName := "runByDigest"
   135  	out, _ := dockerCmd(c, "run", "--name", containerName, imageReference, "sh", "-c", "echo found=$digest")
   136  
   137  	foundRegex := regexp.MustCompile("found=([^\n]+)")
   138  	matches := foundRegex.FindStringSubmatch(out)
   139  	c.Assert(matches, checker.HasLen, 2, check.Commentf("unable to parse digest from pull output: %s", out))
   140  	c.Assert(matches[1], checker.Equals, "1", check.Commentf("Expected %q, got %q", "1", matches[1]))
   141  
   142  	res := inspectField(c, containerName, "Config.Image")
   143  	c.Assert(res, checker.Equals, imageReference)
   144  }
   145  
   146  func (s *DockerRegistrySuite) TestRemoveImageByDigest(c *check.C) {
   147  	digest, err := setupImage(c)
   148  	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
   149  
   150  	imageReference := fmt.Sprintf("%s@%s", repoName, digest)
   151  
   152  	// pull from the registry using the <name>@<digest> reference
   153  	dockerCmd(c, "pull", imageReference)
   154  
   155  	// make sure inspect runs ok
   156  	inspectField(c, imageReference, "Id")
   157  
   158  	// do the delete
   159  	err = deleteImages(imageReference)
   160  	c.Assert(err, checker.IsNil, check.Commentf("unexpected error deleting image"))
   161  
   162  	// try to inspect again - it should error this time
   163  	_, err = inspectFieldWithError(imageReference, "Id")
   164  	//unexpected nil err trying to inspect what should be a non-existent image
   165  	c.Assert(err, checker.NotNil)
   166  	c.Assert(err.Error(), checker.Contains, "No such object")
   167  }
   168  
   169  func (s *DockerRegistrySuite) TestBuildByDigest(c *check.C) {
   170  	digest, err := setupImage(c)
   171  	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
   172  
   173  	imageReference := fmt.Sprintf("%s@%s", repoName, digest)
   174  
   175  	// pull from the registry using the <name>@<digest> reference
   176  	dockerCmd(c, "pull", imageReference)
   177  
   178  	// get the image id
   179  	imageID := inspectField(c, imageReference, "Id")
   180  
   181  	// do the build
   182  	name := "buildbydigest"
   183  	buildImageSuccessfully(c, name, build.WithDockerfile(fmt.Sprintf(
   184  		`FROM %s
   185       CMD ["/bin/echo", "Hello World"]`, imageReference)))
   186  	c.Assert(err, checker.IsNil)
   187  
   188  	// get the build's image id
   189  	res := inspectField(c, name, "Config.Image")
   190  	// make sure they match
   191  	c.Assert(res, checker.Equals, imageID)
   192  }
   193  
   194  func (s *DockerRegistrySuite) TestTagByDigest(c *check.C) {
   195  	digest, err := setupImage(c)
   196  	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
   197  
   198  	imageReference := fmt.Sprintf("%s@%s", repoName, digest)
   199  
   200  	// pull from the registry using the <name>@<digest> reference
   201  	dockerCmd(c, "pull", imageReference)
   202  
   203  	// tag it
   204  	tag := "tagbydigest"
   205  	dockerCmd(c, "tag", imageReference, tag)
   206  
   207  	expectedID := inspectField(c, imageReference, "Id")
   208  
   209  	tagID := inspectField(c, tag, "Id")
   210  	c.Assert(tagID, checker.Equals, expectedID)
   211  }
   212  
   213  func (s *DockerRegistrySuite) TestListImagesWithoutDigests(c *check.C) {
   214  	digest, err := setupImage(c)
   215  	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
   216  
   217  	imageReference := fmt.Sprintf("%s@%s", repoName, digest)
   218  
   219  	// pull from the registry using the <name>@<digest> reference
   220  	dockerCmd(c, "pull", imageReference)
   221  
   222  	out, _ := dockerCmd(c, "images")
   223  	c.Assert(out, checker.Not(checker.Contains), "DIGEST", check.Commentf("list output should not have contained DIGEST header"))
   224  }
   225  
   226  func (s *DockerRegistrySuite) TestListImagesWithDigests(c *check.C) {
   227  
   228  	// setup image1
   229  	digest1, err := setupImageWithTag(c, "tag1")
   230  	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
   231  	imageReference1 := fmt.Sprintf("%s@%s", repoName, digest1)
   232  	c.Logf("imageReference1 = %s", imageReference1)
   233  
   234  	// pull image1 by digest
   235  	dockerCmd(c, "pull", imageReference1)
   236  
   237  	// list images
   238  	out, _ := dockerCmd(c, "images", "--digests")
   239  
   240  	// make sure repo shown, tag=<none>, digest = $digest1
   241  	re1 := regexp.MustCompile(`\s*` + repoName + `\s*<none>\s*` + digest1.String() + `\s`)
   242  	c.Assert(re1.MatchString(out), checker.True, check.Commentf("expected %q: %s", re1.String(), out))
   243  	// setup image2
   244  	digest2, err := setupImageWithTag(c, "tag2")
   245  	//error setting up image
   246  	c.Assert(err, checker.IsNil)
   247  	imageReference2 := fmt.Sprintf("%s@%s", repoName, digest2)
   248  	c.Logf("imageReference2 = %s", imageReference2)
   249  
   250  	// pull image1 by digest
   251  	dockerCmd(c, "pull", imageReference1)
   252  
   253  	// pull image2 by digest
   254  	dockerCmd(c, "pull", imageReference2)
   255  
   256  	// list images
   257  	out, _ = dockerCmd(c, "images", "--digests")
   258  
   259  	// make sure repo shown, tag=<none>, digest = $digest1
   260  	c.Assert(re1.MatchString(out), checker.True, check.Commentf("expected %q: %s", re1.String(), out))
   261  
   262  	// make sure repo shown, tag=<none>, digest = $digest2
   263  	re2 := regexp.MustCompile(`\s*` + repoName + `\s*<none>\s*` + digest2.String() + `\s`)
   264  	c.Assert(re2.MatchString(out), checker.True, check.Commentf("expected %q: %s", re2.String(), out))
   265  
   266  	// pull tag1
   267  	dockerCmd(c, "pull", repoName+":tag1")
   268  
   269  	// list images
   270  	out, _ = dockerCmd(c, "images", "--digests")
   271  
   272  	// make sure image 1 has repo, tag, <none> AND repo, <none>, digest
   273  	reWithDigest1 := regexp.MustCompile(`\s*` + repoName + `\s*tag1\s*` + digest1.String() + `\s`)
   274  	c.Assert(reWithDigest1.MatchString(out), checker.True, check.Commentf("expected %q: %s", reWithDigest1.String(), out))
   275  	// make sure image 2 has repo, <none>, digest
   276  	c.Assert(re2.MatchString(out), checker.True, check.Commentf("expected %q: %s", re2.String(), out))
   277  
   278  	// pull tag 2
   279  	dockerCmd(c, "pull", repoName+":tag2")
   280  
   281  	// list images
   282  	out, _ = dockerCmd(c, "images", "--digests")
   283  
   284  	// make sure image 1 has repo, tag, digest
   285  	c.Assert(reWithDigest1.MatchString(out), checker.True, check.Commentf("expected %q: %s", reWithDigest1.String(), out))
   286  
   287  	// make sure image 2 has repo, tag, digest
   288  	reWithDigest2 := regexp.MustCompile(`\s*` + repoName + `\s*tag2\s*` + digest2.String() + `\s`)
   289  	c.Assert(reWithDigest2.MatchString(out), checker.True, check.Commentf("expected %q: %s", reWithDigest2.String(), out))
   290  
   291  	// list images
   292  	out, _ = dockerCmd(c, "images", "--digests")
   293  
   294  	// make sure image 1 has repo, tag, digest
   295  	c.Assert(reWithDigest1.MatchString(out), checker.True, check.Commentf("expected %q: %s", reWithDigest1.String(), out))
   296  	// make sure image 2 has repo, tag, digest
   297  	c.Assert(reWithDigest2.MatchString(out), checker.True, check.Commentf("expected %q: %s", reWithDigest2.String(), out))
   298  	// make sure busybox has tag, but not digest
   299  	busyboxRe := regexp.MustCompile(`\s*busybox\s*latest\s*<none>\s`)
   300  	c.Assert(busyboxRe.MatchString(out), checker.True, check.Commentf("expected %q: %s", busyboxRe.String(), out))
   301  }
   302  
   303  func (s *DockerRegistrySuite) TestListDanglingImagesWithDigests(c *check.C) {
   304  	// setup image1
   305  	digest1, err := setupImageWithTag(c, "dangle1")
   306  	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
   307  	imageReference1 := fmt.Sprintf("%s@%s", repoName, digest1)
   308  	c.Logf("imageReference1 = %s", imageReference1)
   309  
   310  	// pull image1 by digest
   311  	dockerCmd(c, "pull", imageReference1)
   312  
   313  	// list images
   314  	out, _ := dockerCmd(c, "images", "--digests")
   315  
   316  	// make sure repo shown, tag=<none>, digest = $digest1
   317  	re1 := regexp.MustCompile(`\s*` + repoName + `\s*<none>\s*` + digest1.String() + `\s`)
   318  	c.Assert(re1.MatchString(out), checker.True, check.Commentf("expected %q: %s", re1.String(), out))
   319  	// setup image2
   320  	digest2, err := setupImageWithTag(c, "dangle2")
   321  	//error setting up image
   322  	c.Assert(err, checker.IsNil)
   323  	imageReference2 := fmt.Sprintf("%s@%s", repoName, digest2)
   324  	c.Logf("imageReference2 = %s", imageReference2)
   325  
   326  	// pull image1 by digest
   327  	dockerCmd(c, "pull", imageReference1)
   328  
   329  	// pull image2 by digest
   330  	dockerCmd(c, "pull", imageReference2)
   331  
   332  	// list images
   333  	out, _ = dockerCmd(c, "images", "--digests", "--filter=dangling=true")
   334  
   335  	// make sure repo shown, tag=<none>, digest = $digest1
   336  	c.Assert(re1.MatchString(out), checker.True, check.Commentf("expected %q: %s", re1.String(), out))
   337  
   338  	// make sure repo shown, tag=<none>, digest = $digest2
   339  	re2 := regexp.MustCompile(`\s*` + repoName + `\s*<none>\s*` + digest2.String() + `\s`)
   340  	c.Assert(re2.MatchString(out), checker.True, check.Commentf("expected %q: %s", re2.String(), out))
   341  
   342  	// pull dangle1 tag
   343  	dockerCmd(c, "pull", repoName+":dangle1")
   344  
   345  	// list images
   346  	out, _ = dockerCmd(c, "images", "--digests", "--filter=dangling=true")
   347  
   348  	// make sure image 1 has repo, tag, <none> AND repo, <none>, digest
   349  	reWithDigest1 := regexp.MustCompile(`\s*` + repoName + `\s*dangle1\s*` + digest1.String() + `\s`)
   350  	c.Assert(reWithDigest1.MatchString(out), checker.False, check.Commentf("unexpected %q: %s", reWithDigest1.String(), out))
   351  	// make sure image 2 has repo, <none>, digest
   352  	c.Assert(re2.MatchString(out), checker.True, check.Commentf("expected %q: %s", re2.String(), out))
   353  
   354  	// pull dangle2 tag
   355  	dockerCmd(c, "pull", repoName+":dangle2")
   356  
   357  	// list images, show tagged images
   358  	out, _ = dockerCmd(c, "images", "--digests")
   359  
   360  	// make sure image 1 has repo, tag, digest
   361  	c.Assert(reWithDigest1.MatchString(out), checker.True, check.Commentf("expected %q: %s", reWithDigest1.String(), out))
   362  
   363  	// make sure image 2 has repo, tag, digest
   364  	reWithDigest2 := regexp.MustCompile(`\s*` + repoName + `\s*dangle2\s*` + digest2.String() + `\s`)
   365  	c.Assert(reWithDigest2.MatchString(out), checker.True, check.Commentf("expected %q: %s", reWithDigest2.String(), out))
   366  
   367  	// list images, no longer dangling, should not match
   368  	out, _ = dockerCmd(c, "images", "--digests", "--filter=dangling=true")
   369  
   370  	// make sure image 1 has repo, tag, digest
   371  	c.Assert(reWithDigest1.MatchString(out), checker.False, check.Commentf("unexpected %q: %s", reWithDigest1.String(), out))
   372  	// make sure image 2 has repo, tag, digest
   373  	c.Assert(reWithDigest2.MatchString(out), checker.False, check.Commentf("unexpected %q: %s", reWithDigest2.String(), out))
   374  }
   375  
   376  func (s *DockerRegistrySuite) TestInspectImageWithDigests(c *check.C) {
   377  	digest, err := setupImage(c)
   378  	c.Assert(err, check.IsNil, check.Commentf("error setting up image"))
   379  
   380  	imageReference := fmt.Sprintf("%s@%s", repoName, digest)
   381  
   382  	// pull from the registry using the <name>@<digest> reference
   383  	dockerCmd(c, "pull", imageReference)
   384  
   385  	out, _ := dockerCmd(c, "inspect", imageReference)
   386  
   387  	var imageJSON []types.ImageInspect
   388  	err = json.Unmarshal([]byte(out), &imageJSON)
   389  	c.Assert(err, checker.IsNil)
   390  	c.Assert(imageJSON, checker.HasLen, 1)
   391  	c.Assert(imageJSON[0].RepoDigests, checker.HasLen, 1)
   392  	assert.Check(c, is.Contains(imageJSON[0].RepoDigests, imageReference))
   393  }
   394  
   395  func (s *DockerRegistrySuite) TestPsListContainersFilterAncestorImageByDigest(c *check.C) {
   396  	existingContainers := ExistingContainerIDs(c)
   397  
   398  	digest, err := setupImage(c)
   399  	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
   400  
   401  	imageReference := fmt.Sprintf("%s@%s", repoName, digest)
   402  
   403  	// pull from the registry using the <name>@<digest> reference
   404  	dockerCmd(c, "pull", imageReference)
   405  
   406  	// build an image from it
   407  	imageName1 := "images_ps_filter_test"
   408  	buildImageSuccessfully(c, imageName1, build.WithDockerfile(fmt.Sprintf(
   409  		`FROM %s
   410  		 LABEL match me 1`, imageReference)))
   411  
   412  	// run a container based on that
   413  	dockerCmd(c, "run", "--name=test1", imageReference, "echo", "hello")
   414  	expectedID := getIDByName(c, "test1")
   415  
   416  	// run a container based on the a descendant of that too
   417  	dockerCmd(c, "run", "--name=test2", imageName1, "echo", "hello")
   418  	expectedID1 := getIDByName(c, "test2")
   419  
   420  	expectedIDs := []string{expectedID, expectedID1}
   421  
   422  	// Invalid imageReference
   423  	out, _ := dockerCmd(c, "ps", "-a", "-q", "--no-trunc", fmt.Sprintf("--filter=ancestor=busybox@%s", digest))
   424  	// Filter container for ancestor filter should be empty
   425  	c.Assert(strings.TrimSpace(out), checker.Equals, "")
   426  
   427  	// Valid imageReference
   428  	out, _ = dockerCmd(c, "ps", "-a", "-q", "--no-trunc", "--filter=ancestor="+imageReference)
   429  	checkPsAncestorFilterOutput(c, RemoveOutputForExistingElements(out, existingContainers), imageReference, expectedIDs)
   430  }
   431  
   432  func (s *DockerRegistrySuite) TestDeleteImageByIDOnlyPulledByDigest(c *check.C) {
   433  	pushDigest, err := setupImage(c)
   434  	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
   435  
   436  	// pull from the registry using the <name>@<digest> reference
   437  	imageReference := fmt.Sprintf("%s@%s", repoName, pushDigest)
   438  	dockerCmd(c, "pull", imageReference)
   439  	// just in case...
   440  
   441  	dockerCmd(c, "tag", imageReference, repoName+":sometag")
   442  
   443  	imageID := inspectField(c, imageReference, "Id")
   444  
   445  	dockerCmd(c, "rmi", imageID)
   446  
   447  	_, err = inspectFieldWithError(imageID, "Id")
   448  	c.Assert(err, checker.NotNil, check.Commentf("image should have been deleted"))
   449  }
   450  
   451  func (s *DockerRegistrySuite) TestDeleteImageWithDigestAndTag(c *check.C) {
   452  	pushDigest, err := setupImage(c)
   453  	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
   454  
   455  	// pull from the registry using the <name>@<digest> reference
   456  	imageReference := fmt.Sprintf("%s@%s", repoName, pushDigest)
   457  	dockerCmd(c, "pull", imageReference)
   458  
   459  	imageID := inspectField(c, imageReference, "Id")
   460  
   461  	repoTag := repoName + ":sometag"
   462  	repoTag2 := repoName + ":othertag"
   463  	dockerCmd(c, "tag", imageReference, repoTag)
   464  	dockerCmd(c, "tag", imageReference, repoTag2)
   465  
   466  	dockerCmd(c, "rmi", repoTag2)
   467  
   468  	// rmi should have deleted only repoTag2, because there's another tag
   469  	inspectField(c, repoTag, "Id")
   470  
   471  	dockerCmd(c, "rmi", repoTag)
   472  
   473  	// rmi should have deleted the tag, the digest reference, and the image itself
   474  	_, err = inspectFieldWithError(imageID, "Id")
   475  	c.Assert(err, checker.NotNil, check.Commentf("image should have been deleted"))
   476  }
   477  
   478  func (s *DockerRegistrySuite) TestDeleteImageWithDigestAndMultiRepoTag(c *check.C) {
   479  	pushDigest, err := setupImage(c)
   480  	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
   481  
   482  	repo2 := fmt.Sprintf("%s/%s", repoName, "repo2")
   483  
   484  	// pull from the registry using the <name>@<digest> reference
   485  	imageReference := fmt.Sprintf("%s@%s", repoName, pushDigest)
   486  	dockerCmd(c, "pull", imageReference)
   487  
   488  	imageID := inspectField(c, imageReference, "Id")
   489  
   490  	repoTag := repoName + ":sometag"
   491  	repoTag2 := repo2 + ":othertag"
   492  	dockerCmd(c, "tag", imageReference, repoTag)
   493  	dockerCmd(c, "tag", imageReference, repoTag2)
   494  
   495  	dockerCmd(c, "rmi", repoTag)
   496  
   497  	// rmi should have deleted repoTag and image reference, but left repoTag2
   498  	inspectField(c, repoTag2, "Id")
   499  	_, err = inspectFieldWithError(imageReference, "Id")
   500  	c.Assert(err, checker.NotNil, check.Commentf("image digest reference should have been removed"))
   501  
   502  	_, err = inspectFieldWithError(repoTag, "Id")
   503  	c.Assert(err, checker.NotNil, check.Commentf("image tag reference should have been removed"))
   504  
   505  	dockerCmd(c, "rmi", repoTag2)
   506  
   507  	// rmi should have deleted the tag, the digest reference, and the image itself
   508  	_, err = inspectFieldWithError(imageID, "Id")
   509  	c.Assert(err, checker.NotNil, check.Commentf("image should have been deleted"))
   510  }
   511  
   512  // TestPullFailsWithAlteredManifest tests that a `docker pull` fails when
   513  // we have modified a manifest blob and its digest cannot be verified.
   514  // This is the schema2 version of the test.
   515  func (s *DockerRegistrySuite) TestPullFailsWithAlteredManifest(c *check.C) {
   516  	testRequires(c, DaemonIsLinux)
   517  	manifestDigest, err := setupImage(c)
   518  	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
   519  
   520  	// Load the target manifest blob.
   521  	manifestBlob := s.reg.ReadBlobContents(c, manifestDigest)
   522  
   523  	var imgManifest schema2.Manifest
   524  	err = json.Unmarshal(manifestBlob, &imgManifest)
   525  	c.Assert(err, checker.IsNil, check.Commentf("unable to decode image manifest from blob"))
   526  
   527  	// Change a layer in the manifest.
   528  	imgManifest.Layers[0].Digest = digest.Digest("sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
   529  
   530  	// Move the existing data file aside, so that we can replace it with a
   531  	// malicious blob of data. NOTE: we defer the returned undo func.
   532  	undo := s.reg.TempMoveBlobData(c, manifestDigest)
   533  	defer undo()
   534  
   535  	alteredManifestBlob, err := json.MarshalIndent(imgManifest, "", "   ")
   536  	c.Assert(err, checker.IsNil, check.Commentf("unable to encode altered image manifest to JSON"))
   537  
   538  	s.reg.WriteBlobContents(c, manifestDigest, alteredManifestBlob)
   539  
   540  	// Now try pulling that image by digest. We should get an error about
   541  	// digest verification for the manifest digest.
   542  
   543  	// Pull from the registry using the <name>@<digest> reference.
   544  	imageReference := fmt.Sprintf("%s@%s", repoName, manifestDigest)
   545  	out, exitStatus, _ := dockerCmdWithError("pull", imageReference)
   546  	c.Assert(exitStatus, checker.Not(check.Equals), 0)
   547  
   548  	expectedErrorMsg := fmt.Sprintf("manifest verification failed for digest %s", manifestDigest)
   549  	c.Assert(out, checker.Contains, expectedErrorMsg)
   550  }