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