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