github.com/akashshinde/docker@v1.9.1/integration-cli/docker_cli_by_digest_test.go (about)

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