github.com/docker/Engine@v17.12.1-ce-rc2+incompatible/integration-cli/docker_cli_by_digest_test.go (about)

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