github.com/reds/docker@v1.11.2-rc1/layer/layer_test.go (about) 1 package layer 2 3 import ( 4 "bytes" 5 "io" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "runtime" 10 "strings" 11 "testing" 12 13 "github.com/docker/distribution/digest" 14 "github.com/docker/docker/daemon/graphdriver" 15 "github.com/docker/docker/daemon/graphdriver/vfs" 16 "github.com/docker/docker/pkg/archive" 17 "github.com/docker/docker/pkg/idtools" 18 "github.com/docker/docker/pkg/stringid" 19 ) 20 21 func init() { 22 graphdriver.ApplyUncompressedLayer = archive.UnpackLayer 23 vfs.CopyWithTar = archive.CopyWithTar 24 } 25 26 func newVFSGraphDriver(td string) (graphdriver.Driver, error) { 27 uidMap := []idtools.IDMap{ 28 { 29 ContainerID: 0, 30 HostID: os.Getuid(), 31 Size: 1, 32 }, 33 } 34 gidMap := []idtools.IDMap{ 35 { 36 ContainerID: 0, 37 HostID: os.Getgid(), 38 Size: 1, 39 }, 40 } 41 42 return graphdriver.GetDriver("vfs", td, nil, uidMap, gidMap) 43 } 44 45 func newTestGraphDriver(t *testing.T) (graphdriver.Driver, func()) { 46 td, err := ioutil.TempDir("", "graph-") 47 if err != nil { 48 t.Fatal(err) 49 } 50 51 driver, err := newVFSGraphDriver(td) 52 if err != nil { 53 t.Fatal(err) 54 } 55 56 return driver, func() { 57 os.RemoveAll(td) 58 } 59 } 60 61 func newTestStore(t *testing.T) (Store, string, func()) { 62 td, err := ioutil.TempDir("", "layerstore-") 63 if err != nil { 64 t.Fatal(err) 65 } 66 67 graph, graphcleanup := newTestGraphDriver(t) 68 fms, err := NewFSMetadataStore(td) 69 if err != nil { 70 t.Fatal(err) 71 } 72 ls, err := NewStoreFromGraphDriver(fms, graph) 73 if err != nil { 74 t.Fatal(err) 75 } 76 77 return ls, td, func() { 78 graphcleanup() 79 os.RemoveAll(td) 80 } 81 } 82 83 type layerInit func(root string) error 84 85 func createLayer(ls Store, parent ChainID, layerFunc layerInit) (Layer, error) { 86 containerID := stringid.GenerateRandomID() 87 mount, err := ls.CreateRWLayer(containerID, parent, "", nil) 88 if err != nil { 89 return nil, err 90 } 91 92 path, err := mount.Mount("") 93 if err != nil { 94 return nil, err 95 } 96 97 if err := layerFunc(path); err != nil { 98 return nil, err 99 } 100 101 ts, err := mount.TarStream() 102 if err != nil { 103 return nil, err 104 } 105 defer ts.Close() 106 107 layer, err := ls.Register(ts, parent) 108 if err != nil { 109 return nil, err 110 } 111 112 if err := mount.Unmount(); err != nil { 113 return nil, err 114 } 115 116 if _, err := ls.ReleaseRWLayer(mount); err != nil { 117 return nil, err 118 } 119 120 return layer, nil 121 } 122 123 type FileApplier interface { 124 ApplyFile(root string) error 125 } 126 127 type testFile struct { 128 name string 129 content []byte 130 permission os.FileMode 131 } 132 133 func newTestFile(name string, content []byte, perm os.FileMode) FileApplier { 134 return &testFile{ 135 name: name, 136 content: content, 137 permission: perm, 138 } 139 } 140 141 func (tf *testFile) ApplyFile(root string) error { 142 fullPath := filepath.Join(root, tf.name) 143 if err := os.MkdirAll(filepath.Dir(fullPath), 0755); err != nil { 144 return err 145 } 146 // Check if already exists 147 if stat, err := os.Stat(fullPath); err == nil && stat.Mode().Perm() != tf.permission { 148 if err := os.Chmod(fullPath, tf.permission); err != nil { 149 return err 150 } 151 } 152 if err := ioutil.WriteFile(fullPath, tf.content, tf.permission); err != nil { 153 return err 154 } 155 return nil 156 } 157 158 func initWithFiles(files ...FileApplier) layerInit { 159 return func(root string) error { 160 for _, f := range files { 161 if err := f.ApplyFile(root); err != nil { 162 return err 163 } 164 } 165 return nil 166 } 167 } 168 169 func getCachedLayer(l Layer) *roLayer { 170 if rl, ok := l.(*referencedCacheLayer); ok { 171 return rl.roLayer 172 } 173 return l.(*roLayer) 174 } 175 176 func getMountLayer(l RWLayer) *mountedLayer { 177 if rl, ok := l.(*referencedRWLayer); ok { 178 return rl.mountedLayer 179 } 180 return l.(*mountedLayer) 181 } 182 183 func createMetadata(layers ...Layer) []Metadata { 184 metadata := make([]Metadata, len(layers)) 185 for i := range layers { 186 size, err := layers[i].Size() 187 if err != nil { 188 panic(err) 189 } 190 191 metadata[i].ChainID = layers[i].ChainID() 192 metadata[i].DiffID = layers[i].DiffID() 193 metadata[i].Size = size 194 metadata[i].DiffSize = getCachedLayer(layers[i]).size 195 } 196 197 return metadata 198 } 199 200 func assertMetadata(t *testing.T, metadata, expectedMetadata []Metadata) { 201 if len(metadata) != len(expectedMetadata) { 202 t.Fatalf("Unexpected number of deletes %d, expected %d", len(metadata), len(expectedMetadata)) 203 } 204 205 for i := range metadata { 206 if metadata[i] != expectedMetadata[i] { 207 t.Errorf("Unexpected metadata\n\tExpected: %#v\n\tActual: %#v", expectedMetadata[i], metadata[i]) 208 } 209 } 210 if t.Failed() { 211 t.FailNow() 212 } 213 } 214 215 func releaseAndCheckDeleted(t *testing.T, ls Store, layer Layer, removed ...Layer) { 216 layerCount := len(ls.(*layerStore).layerMap) 217 expectedMetadata := createMetadata(removed...) 218 metadata, err := ls.Release(layer) 219 if err != nil { 220 t.Fatal(err) 221 } 222 223 assertMetadata(t, metadata, expectedMetadata) 224 225 if expected := layerCount - len(removed); len(ls.(*layerStore).layerMap) != expected { 226 t.Fatalf("Unexpected number of layers %d, expected %d", len(ls.(*layerStore).layerMap), expected) 227 } 228 } 229 230 func cacheID(l Layer) string { 231 return getCachedLayer(l).cacheID 232 } 233 234 func assertLayerEqual(t *testing.T, l1, l2 Layer) { 235 if l1.ChainID() != l2.ChainID() { 236 t.Fatalf("Mismatched ID: %s vs %s", l1.ChainID(), l2.ChainID()) 237 } 238 if l1.DiffID() != l2.DiffID() { 239 t.Fatalf("Mismatched DiffID: %s vs %s", l1.DiffID(), l2.DiffID()) 240 } 241 242 size1, err := l1.Size() 243 if err != nil { 244 t.Fatal(err) 245 } 246 247 size2, err := l2.Size() 248 if err != nil { 249 t.Fatal(err) 250 } 251 252 if size1 != size2 { 253 t.Fatalf("Mismatched size: %d vs %d", size1, size2) 254 } 255 256 if cacheID(l1) != cacheID(l2) { 257 t.Fatalf("Mismatched cache id: %s vs %s", cacheID(l1), cacheID(l2)) 258 } 259 260 p1 := l1.Parent() 261 p2 := l2.Parent() 262 if p1 != nil && p2 != nil { 263 assertLayerEqual(t, p1, p2) 264 } else if p1 != nil || p2 != nil { 265 t.Fatalf("Mismatched parents: %v vs %v", p1, p2) 266 } 267 } 268 269 func TestMountAndRegister(t *testing.T) { 270 ls, _, cleanup := newTestStore(t) 271 defer cleanup() 272 273 li := initWithFiles(newTestFile("testfile.txt", []byte("some test data"), 0644)) 274 layer, err := createLayer(ls, "", li) 275 if err != nil { 276 t.Fatal(err) 277 } 278 279 size, _ := layer.Size() 280 t.Logf("Layer size: %d", size) 281 282 mount2, err := ls.CreateRWLayer("new-test-mount", layer.ChainID(), "", nil) 283 if err != nil { 284 t.Fatal(err) 285 } 286 287 path2, err := mount2.Mount("") 288 if err != nil { 289 t.Fatal(err) 290 } 291 292 b, err := ioutil.ReadFile(filepath.Join(path2, "testfile.txt")) 293 if err != nil { 294 t.Fatal(err) 295 } 296 297 if expected := "some test data"; string(b) != expected { 298 t.Fatalf("Wrong file data, expected %q, got %q", expected, string(b)) 299 } 300 301 if err := mount2.Unmount(); err != nil { 302 t.Fatal(err) 303 } 304 305 if _, err := ls.ReleaseRWLayer(mount2); err != nil { 306 t.Fatal(err) 307 } 308 } 309 310 func TestLayerRelease(t *testing.T) { 311 // TODO Windows: Figure out why this is failing 312 if runtime.GOOS == "windows" { 313 t.Skip("Failing on Windows") 314 } 315 ls, _, cleanup := newTestStore(t) 316 defer cleanup() 317 318 layer1, err := createLayer(ls, "", initWithFiles(newTestFile("layer1.txt", []byte("layer 1 file"), 0644))) 319 if err != nil { 320 t.Fatal(err) 321 } 322 323 layer2, err := createLayer(ls, layer1.ChainID(), initWithFiles(newTestFile("layer2.txt", []byte("layer 2 file"), 0644))) 324 if err != nil { 325 t.Fatal(err) 326 } 327 328 if _, err := ls.Release(layer1); err != nil { 329 t.Fatal(err) 330 } 331 332 layer3a, err := createLayer(ls, layer2.ChainID(), initWithFiles(newTestFile("layer3.txt", []byte("layer 3a file"), 0644))) 333 if err != nil { 334 t.Fatal(err) 335 } 336 337 layer3b, err := createLayer(ls, layer2.ChainID(), initWithFiles(newTestFile("layer3.txt", []byte("layer 3b file"), 0644))) 338 if err != nil { 339 t.Fatal(err) 340 } 341 342 if _, err := ls.Release(layer2); err != nil { 343 t.Fatal(err) 344 } 345 346 t.Logf("Layer1: %s", layer1.ChainID()) 347 t.Logf("Layer2: %s", layer2.ChainID()) 348 t.Logf("Layer3a: %s", layer3a.ChainID()) 349 t.Logf("Layer3b: %s", layer3b.ChainID()) 350 351 if expected := 4; len(ls.(*layerStore).layerMap) != expected { 352 t.Fatalf("Unexpected number of layers %d, expected %d", len(ls.(*layerStore).layerMap), expected) 353 } 354 355 releaseAndCheckDeleted(t, ls, layer3b, layer3b) 356 releaseAndCheckDeleted(t, ls, layer3a, layer3a, layer2, layer1) 357 } 358 359 func TestStoreRestore(t *testing.T) { 360 // TODO Windows: Figure out why this is failing 361 if runtime.GOOS == "windows" { 362 t.Skip("Failing on Windows") 363 } 364 ls, _, cleanup := newTestStore(t) 365 defer cleanup() 366 367 layer1, err := createLayer(ls, "", initWithFiles(newTestFile("layer1.txt", []byte("layer 1 file"), 0644))) 368 if err != nil { 369 t.Fatal(err) 370 } 371 372 layer2, err := createLayer(ls, layer1.ChainID(), initWithFiles(newTestFile("layer2.txt", []byte("layer 2 file"), 0644))) 373 if err != nil { 374 t.Fatal(err) 375 } 376 377 if _, err := ls.Release(layer1); err != nil { 378 t.Fatal(err) 379 } 380 381 layer3, err := createLayer(ls, layer2.ChainID(), initWithFiles(newTestFile("layer3.txt", []byte("layer 3 file"), 0644))) 382 if err != nil { 383 t.Fatal(err) 384 } 385 386 if _, err := ls.Release(layer2); err != nil { 387 t.Fatal(err) 388 } 389 390 m, err := ls.CreateRWLayer("some-mount_name", layer3.ChainID(), "", nil) 391 if err != nil { 392 t.Fatal(err) 393 } 394 395 path, err := m.Mount("") 396 if err != nil { 397 t.Fatal(err) 398 } 399 400 if err := ioutil.WriteFile(filepath.Join(path, "testfile.txt"), []byte("nothing here"), 0644); err != nil { 401 t.Fatal(err) 402 } 403 assertActivityCount(t, m, 1) 404 405 if err := m.Unmount(); err != nil { 406 t.Fatal(err) 407 } 408 409 assertActivityCount(t, m, 0) 410 411 ls2, err := NewStoreFromGraphDriver(ls.(*layerStore).store, ls.(*layerStore).driver) 412 if err != nil { 413 t.Fatal(err) 414 } 415 416 layer3b, err := ls2.Get(layer3.ChainID()) 417 if err != nil { 418 t.Fatal(err) 419 } 420 421 assertLayerEqual(t, layer3b, layer3) 422 423 // Create again with same name, should return error 424 if _, err := ls2.CreateRWLayer("some-mount_name", layer3b.ChainID(), "", nil); err == nil { 425 t.Fatal("Expected error creating mount with same name") 426 } else if err != ErrMountNameConflict { 427 t.Fatal(err) 428 } 429 430 m2, err := ls2.GetRWLayer("some-mount_name") 431 if err != nil { 432 t.Fatal(err) 433 } 434 435 if mountPath, err := m2.Mount(""); err != nil { 436 t.Fatal(err) 437 } else if path != mountPath { 438 t.Fatalf("Unexpected path %s, expected %s", mountPath, path) 439 } 440 441 assertActivityCount(t, m2, 1) 442 443 if mountPath, err := m2.Mount(""); err != nil { 444 t.Fatal(err) 445 } else if path != mountPath { 446 t.Fatalf("Unexpected path %s, expected %s", mountPath, path) 447 } 448 assertActivityCount(t, m2, 2) 449 if err := m2.Unmount(); err != nil { 450 t.Fatal(err) 451 } 452 453 assertActivityCount(t, m2, 1) 454 455 b, err := ioutil.ReadFile(filepath.Join(path, "testfile.txt")) 456 if err != nil { 457 t.Fatal(err) 458 } 459 if expected := "nothing here"; string(b) != expected { 460 t.Fatalf("Unexpected content %q, expected %q", string(b), expected) 461 } 462 463 if err := m2.Unmount(); err != nil { 464 t.Fatal(err) 465 } 466 467 assertActivityCount(t, m2, 0) 468 469 if metadata, err := ls2.ReleaseRWLayer(m2); err != nil { 470 t.Fatal(err) 471 } else if len(metadata) != 0 { 472 t.Fatalf("Unexpectedly deleted layers: %#v", metadata) 473 } 474 475 if metadata, err := ls2.ReleaseRWLayer(m2); err != nil { 476 t.Fatal(err) 477 } else if len(metadata) != 0 { 478 t.Fatalf("Unexpectedly deleted layers: %#v", metadata) 479 } 480 481 releaseAndCheckDeleted(t, ls2, layer3b, layer3, layer2, layer1) 482 } 483 484 func TestTarStreamStability(t *testing.T) { 485 // TODO Windows: Figure out why this is failing 486 if runtime.GOOS == "windows" { 487 t.Skip("Failing on Windows") 488 } 489 ls, _, cleanup := newTestStore(t) 490 defer cleanup() 491 492 files1 := []FileApplier{ 493 newTestFile("/etc/hosts", []byte("mydomain 10.0.0.1"), 0644), 494 newTestFile("/etc/profile", []byte("PATH=/usr/bin"), 0644), 495 } 496 addedFile := newTestFile("/etc/shadow", []byte("root:::::::"), 0644) 497 files2 := []FileApplier{ 498 newTestFile("/etc/hosts", []byte("mydomain 10.0.0.2"), 0644), 499 newTestFile("/etc/profile", []byte("PATH=/usr/bin"), 0664), 500 newTestFile("/root/.bashrc", []byte("PATH=/usr/sbin:/usr/bin"), 0644), 501 } 502 503 tar1, err := tarFromFiles(files1...) 504 if err != nil { 505 t.Fatal(err) 506 } 507 508 tar2, err := tarFromFiles(files2...) 509 if err != nil { 510 t.Fatal(err) 511 } 512 513 layer1, err := ls.Register(bytes.NewReader(tar1), "") 514 if err != nil { 515 t.Fatal(err) 516 } 517 518 // hack layer to add file 519 p, err := ls.(*layerStore).driver.Get(layer1.(*referencedCacheLayer).cacheID, "") 520 if err != nil { 521 t.Fatal(err) 522 } 523 524 if err := addedFile.ApplyFile(p); err != nil { 525 t.Fatal(err) 526 } 527 528 if err := ls.(*layerStore).driver.Put(layer1.(*referencedCacheLayer).cacheID); err != nil { 529 t.Fatal(err) 530 } 531 532 layer2, err := ls.Register(bytes.NewReader(tar2), layer1.ChainID()) 533 if err != nil { 534 t.Fatal(err) 535 } 536 537 id1 := layer1.ChainID() 538 t.Logf("Layer 1: %s", layer1.ChainID()) 539 t.Logf("Layer 2: %s", layer2.ChainID()) 540 541 if _, err := ls.Release(layer1); err != nil { 542 t.Fatal(err) 543 } 544 545 assertLayerDiff(t, tar2, layer2) 546 547 layer1b, err := ls.Get(id1) 548 if err != nil { 549 t.Logf("Content of layer map: %#v", ls.(*layerStore).layerMap) 550 t.Fatal(err) 551 } 552 553 if _, err := ls.Release(layer2); err != nil { 554 t.Fatal(err) 555 } 556 557 assertLayerDiff(t, tar1, layer1b) 558 559 if _, err := ls.Release(layer1b); err != nil { 560 t.Fatal(err) 561 } 562 } 563 564 func assertLayerDiff(t *testing.T, expected []byte, layer Layer) { 565 expectedDigest := digest.FromBytes(expected) 566 567 if digest.Digest(layer.DiffID()) != expectedDigest { 568 t.Fatalf("Mismatched diff id for %s, got %s, expected %s", layer.ChainID(), layer.DiffID(), expected) 569 } 570 571 ts, err := layer.TarStream() 572 if err != nil { 573 t.Fatal(err) 574 } 575 defer ts.Close() 576 577 actual, err := ioutil.ReadAll(ts) 578 if err != nil { 579 t.Fatal(err) 580 } 581 582 if len(actual) != len(expected) { 583 logByteDiff(t, actual, expected) 584 t.Fatalf("Mismatched tar stream size for %s, got %d, expected %d", layer.ChainID(), len(actual), len(expected)) 585 } 586 587 actualDigest := digest.FromBytes(actual) 588 589 if actualDigest != expectedDigest { 590 logByteDiff(t, actual, expected) 591 t.Fatalf("Wrong digest of tar stream, got %s, expected %s", actualDigest, expectedDigest) 592 } 593 } 594 595 const maxByteLog = 4 * 1024 596 597 func logByteDiff(t *testing.T, actual, expected []byte) { 598 d1, d2 := byteDiff(actual, expected) 599 if len(d1) == 0 && len(d2) == 0 { 600 return 601 } 602 603 prefix := len(actual) - len(d1) 604 if len(d1) > maxByteLog || len(d2) > maxByteLog { 605 t.Logf("Byte diff after %d matching bytes", prefix) 606 } else { 607 t.Logf("Byte diff after %d matching bytes\nActual bytes after prefix:\n%x\nExpected bytes after prefix:\n%x", prefix, d1, d2) 608 } 609 } 610 611 // byteDiff returns the differing bytes after the matching prefix 612 func byteDiff(b1, b2 []byte) ([]byte, []byte) { 613 i := 0 614 for i < len(b1) && i < len(b2) { 615 if b1[i] != b2[i] { 616 break 617 } 618 i++ 619 } 620 621 return b1[i:], b2[i:] 622 } 623 624 func tarFromFiles(files ...FileApplier) ([]byte, error) { 625 td, err := ioutil.TempDir("", "tar-") 626 if err != nil { 627 return nil, err 628 } 629 defer os.RemoveAll(td) 630 631 for _, f := range files { 632 if err := f.ApplyFile(td); err != nil { 633 return nil, err 634 } 635 } 636 637 r, err := archive.Tar(td, archive.Uncompressed) 638 if err != nil { 639 return nil, err 640 } 641 642 buf := bytes.NewBuffer(nil) 643 if _, err := io.Copy(buf, r); err != nil { 644 return nil, err 645 } 646 647 return buf.Bytes(), nil 648 } 649 650 // assertReferences asserts that all the references are to the same 651 // image and represent the full set of references to that image. 652 func assertReferences(t *testing.T, references ...Layer) { 653 if len(references) == 0 { 654 return 655 } 656 base := references[0].(*referencedCacheLayer).roLayer 657 seenReferences := map[Layer]struct{}{ 658 references[0]: {}, 659 } 660 for i := 1; i < len(references); i++ { 661 other := references[i].(*referencedCacheLayer).roLayer 662 if base != other { 663 t.Fatalf("Unexpected referenced cache layer %s, expecting %s", other.ChainID(), base.ChainID()) 664 } 665 if _, ok := base.references[references[i]]; !ok { 666 t.Fatalf("Reference not part of reference list: %v", references[i]) 667 } 668 if _, ok := seenReferences[references[i]]; ok { 669 t.Fatalf("Duplicated reference %v", references[i]) 670 } 671 } 672 if rc := len(base.references); rc != len(references) { 673 t.Fatalf("Unexpected number of references %d, expecting %d", rc, len(references)) 674 } 675 } 676 677 func assertActivityCount(t *testing.T, l RWLayer, expected int) { 678 rl := l.(*referencedRWLayer) 679 if rl.activityCount != expected { 680 t.Fatalf("Unexpected activity count %d, expected %d", rl.activityCount, expected) 681 } 682 } 683 684 func TestRegisterExistingLayer(t *testing.T) { 685 ls, _, cleanup := newTestStore(t) 686 defer cleanup() 687 688 baseFiles := []FileApplier{ 689 newTestFile("/etc/profile", []byte("# Base configuration"), 0644), 690 } 691 692 layerFiles := []FileApplier{ 693 newTestFile("/root/.bashrc", []byte("# Root configuration"), 0644), 694 } 695 696 li := initWithFiles(baseFiles...) 697 layer1, err := createLayer(ls, "", li) 698 if err != nil { 699 t.Fatal(err) 700 } 701 702 tar1, err := tarFromFiles(layerFiles...) 703 if err != nil { 704 t.Fatal(err) 705 } 706 707 layer2a, err := ls.Register(bytes.NewReader(tar1), layer1.ChainID()) 708 if err != nil { 709 t.Fatal(err) 710 } 711 712 layer2b, err := ls.Register(bytes.NewReader(tar1), layer1.ChainID()) 713 if err != nil { 714 t.Fatal(err) 715 } 716 717 assertReferences(t, layer2a, layer2b) 718 } 719 720 func TestTarStreamVerification(t *testing.T) { 721 // TODO Windows: Figure out why this is failing 722 if runtime.GOOS == "windows" { 723 t.Skip("Failing on Windows") 724 } 725 ls, tmpdir, cleanup := newTestStore(t) 726 defer cleanup() 727 728 files1 := []FileApplier{ 729 newTestFile("/foo", []byte("abc"), 0644), 730 newTestFile("/bar", []byte("def"), 0644), 731 } 732 files2 := []FileApplier{ 733 newTestFile("/foo", []byte("abc"), 0644), 734 newTestFile("/bar", []byte("def"), 0600), // different perm 735 } 736 737 tar1, err := tarFromFiles(files1...) 738 if err != nil { 739 t.Fatal(err) 740 } 741 742 tar2, err := tarFromFiles(files2...) 743 if err != nil { 744 t.Fatal(err) 745 } 746 747 layer1, err := ls.Register(bytes.NewReader(tar1), "") 748 if err != nil { 749 t.Fatal(err) 750 } 751 752 layer2, err := ls.Register(bytes.NewReader(tar2), "") 753 if err != nil { 754 t.Fatal(err) 755 } 756 id1 := digest.Digest(layer1.ChainID()) 757 id2 := digest.Digest(layer2.ChainID()) 758 759 // Replace tar data files 760 src, err := os.Open(filepath.Join(tmpdir, id1.Algorithm().String(), id1.Hex(), "tar-split.json.gz")) 761 if err != nil { 762 t.Fatal(err) 763 } 764 765 dst, err := os.Create(filepath.Join(tmpdir, id2.Algorithm().String(), id2.Hex(), "tar-split.json.gz")) 766 if err != nil { 767 t.Fatal(err) 768 } 769 770 if _, err := io.Copy(dst, src); err != nil { 771 t.Fatal(err) 772 } 773 774 src.Close() 775 dst.Close() 776 777 ts, err := layer2.TarStream() 778 if err != nil { 779 t.Fatal(err) 780 } 781 _, err = io.Copy(ioutil.Discard, ts) 782 if err == nil { 783 t.Fatal("expected data verification to fail") 784 } 785 if !strings.Contains(err.Error(), "could not verify layer data") { 786 t.Fatalf("wrong error returned from tarstream: %q", err) 787 } 788 }