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