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