github.com/torfuzx/docker@v1.8.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/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 }