@@ -1471,11 +1471,251 @@ func TestCopyGraph_WithOptions(t *testing.T) {
1471
1471
t .Errorf ("count(Push()) = %d, want %d" , got , expected )
1472
1472
}
1473
1473
})
1474
+
1475
+ t .Run ("MountFrom_Mounted" , func (t * testing.T ) {
1476
+ root = descs [6 ]
1477
+ dst := & countingStorage {storage : cas .NewMemory ()}
1478
+ var numMount atomic.Int64
1479
+ dst .mount = func (ctx context.Context ,
1480
+ desc ocispec.Descriptor ,
1481
+ fromRepo string ,
1482
+ getContent func () (io.ReadCloser , error ),
1483
+ ) error {
1484
+ numMount .Add (1 )
1485
+ if expected := "source" ; fromRepo != expected {
1486
+ t .Fatalf ("fromRepo = %v, want %v" , fromRepo , expected )
1487
+ }
1488
+ rc , err := src .Fetch (ctx , desc )
1489
+ if err != nil {
1490
+ t .Fatalf ("Failed to fetch content: %v" , err )
1491
+ }
1492
+ defer rc .Close ()
1493
+ err = dst .storage .Push (ctx , desc , rc ) // bypass the counters
1494
+ if err != nil {
1495
+ t .Fatalf ("Failed to push content: %v" , err )
1496
+ }
1497
+ return nil
1498
+ }
1499
+ opts = oras.CopyGraphOptions {}
1500
+ var numPreCopy , numPostCopy , numOnMounted , numMountFrom atomic.Int64
1501
+ opts .PreCopy = func (ctx context.Context , desc ocispec.Descriptor ) error {
1502
+ numPreCopy .Add (1 )
1503
+ return nil
1504
+ }
1505
+ opts .PostCopy = func (ctx context.Context , desc ocispec.Descriptor ) error {
1506
+ numPostCopy .Add (1 )
1507
+ return nil
1508
+ }
1509
+ opts .OnMounted = func (ctx context.Context , d ocispec.Descriptor ) error {
1510
+ numOnMounted .Add (1 )
1511
+ return nil
1512
+ }
1513
+ opts .MountFrom = func (ctx context.Context , desc ocispec.Descriptor ) ([]string , error ) {
1514
+ numMountFrom .Add (1 )
1515
+ return []string {"source" }, nil
1516
+ }
1517
+ if err := oras .CopyGraph (ctx , src , dst , root , opts ); err != nil {
1518
+ t .Fatalf ("CopyGraph() error = %v, wantErr %v" , err , errdef .ErrSizeExceedsLimit )
1519
+ }
1520
+
1521
+ if got , expected := dst .numExists .Load (), int64 (7 ); got != expected {
1522
+ t .Errorf ("count(Exists()) = %d, want %d" , got , expected )
1523
+ }
1524
+ if got , expected := dst .numFetch .Load (), int64 (0 ); got != expected {
1525
+ t .Errorf ("count(Fetch()) = %d, want %d" , got , expected )
1526
+ }
1527
+ // 7 (exists) - 1 (skipped) = 6 pushes expected
1528
+ if got , expected := dst .numPush .Load (), int64 (3 ); got != expected {
1529
+ // If we get >=7 then ErrSkipDesc did not short circuit the push like it is supposed to do.
1530
+ t .Errorf ("count(Push()) = %d, want %d" , got , expected )
1531
+ }
1532
+ if got , expected := numMount .Load (), int64 (4 ); got != expected {
1533
+ t .Errorf ("count(Mount()) = %d, want %d" , got , expected )
1534
+ }
1535
+ if got , expected := numOnMounted .Load (), int64 (4 ); got != expected {
1536
+ t .Errorf ("count(OnMounted()) = %d, want %d" , got , expected )
1537
+ }
1538
+ if got , expected := numMountFrom .Load (), int64 (4 ); got != expected {
1539
+ t .Errorf ("count(MountFrom()) = %d, want %d" , got , expected )
1540
+ }
1541
+ if got , expected := numPreCopy .Load (), int64 (3 ); got != expected {
1542
+ t .Errorf ("count(PreCopy()) = %d, want %d" , got , expected )
1543
+ }
1544
+ if got , expected := numPostCopy .Load (), int64 (3 ); got != expected {
1545
+ t .Errorf ("count(PostCopy()) = %d, want %d" , got , expected )
1546
+ }
1547
+ })
1548
+
1549
+ t .Run ("MountFrom_Copied" , func (t * testing.T ) {
1550
+ root = descs [6 ]
1551
+ dst := & countingStorage {storage : cas .NewMemory ()}
1552
+ var numMount atomic.Int64
1553
+ dst .mount = func (ctx context.Context ,
1554
+ desc ocispec.Descriptor ,
1555
+ fromRepo string ,
1556
+ getContent func () (io.ReadCloser , error ),
1557
+ ) error {
1558
+ numMount .Add (1 )
1559
+ if expected := "source" ; fromRepo != expected {
1560
+ t .Fatalf ("fromRepo = %v, want %v" , fromRepo , expected )
1561
+ }
1562
+
1563
+ rc , err := getContent ()
1564
+ if err != nil {
1565
+ t .Fatalf ("Failed to fetch content: %v" , err )
1566
+ }
1567
+ defer rc .Close ()
1568
+ err = dst .storage .Push (ctx , desc , rc ) // bypass the counters
1569
+ if err != nil {
1570
+ t .Fatalf ("Failed to push content: %v" , err )
1571
+ }
1572
+ return nil
1573
+ }
1574
+ opts = oras.CopyGraphOptions {}
1575
+ var numPreCopy , numPostCopy , numOnMounted , numMountFrom atomic.Int64
1576
+ opts .PreCopy = func (ctx context.Context , desc ocispec.Descriptor ) error {
1577
+ numPreCopy .Add (1 )
1578
+ return nil
1579
+ }
1580
+ opts .PostCopy = func (ctx context.Context , desc ocispec.Descriptor ) error {
1581
+ numPostCopy .Add (1 )
1582
+ return nil
1583
+ }
1584
+ opts .OnMounted = func (ctx context.Context , d ocispec.Descriptor ) error {
1585
+ numOnMounted .Add (1 )
1586
+ return nil
1587
+ }
1588
+ opts .MountFrom = func (ctx context.Context , desc ocispec.Descriptor ) ([]string , error ) {
1589
+ numMountFrom .Add (1 )
1590
+ return []string {"source" }, nil
1591
+ }
1592
+ if err := oras .CopyGraph (ctx , src , dst , root , opts ); err != nil {
1593
+ t .Fatalf ("CopyGraph() error = %v, wantErr %v" , err , errdef .ErrSizeExceedsLimit )
1594
+ }
1595
+
1596
+ if got , expected := dst .numExists .Load (), int64 (7 ); got != expected {
1597
+ t .Errorf ("count(Exists()) = %d, want %d" , got , expected )
1598
+ }
1599
+ if got , expected := dst .numFetch .Load (), int64 (0 ); got != expected {
1600
+ t .Errorf ("count(Fetch()) = %d, want %d" , got , expected )
1601
+ }
1602
+ // 7 (exists) - 1 (skipped) = 6 pushes expected
1603
+ if got , expected := dst .numPush .Load (), int64 (3 ); got != expected {
1604
+ // If we get >=7 then ErrSkipDesc did not short circuit the push like it is supposed to do.
1605
+ t .Errorf ("count(Push()) = %d, want %d" , got , expected )
1606
+ }
1607
+ if got , expected := numMount .Load (), int64 (4 ); got != expected {
1608
+ t .Errorf ("count(Mount()) = %d, want %d" , got , expected )
1609
+ }
1610
+ if got , expected := numOnMounted .Load (), int64 (0 ); got != expected {
1611
+ t .Errorf ("count(OnMounted()) = %d, want %d" , got , expected )
1612
+ }
1613
+ if got , expected := numMountFrom .Load (), int64 (4 ); got != expected {
1614
+ t .Errorf ("count(MountFrom()) = %d, want %d" , got , expected )
1615
+ }
1616
+ if got , expected := numPreCopy .Load (), int64 (7 ); got != expected {
1617
+ t .Errorf ("count(PreCopy()) = %d, want %d" , got , expected )
1618
+ }
1619
+ if got , expected := numPostCopy .Load (), int64 (7 ); got != expected {
1620
+ t .Errorf ("count(PostCopy()) = %d, want %d" , got , expected )
1621
+ }
1622
+ })
1623
+
1624
+ t .Run ("MountFrom_Mounted_Second_Try" , func (t * testing.T ) {
1625
+ root = descs [6 ]
1626
+ dst := & countingStorage {storage : cas .NewMemory ()}
1627
+ var numMount atomic.Int64
1628
+ dst .mount = func (ctx context.Context ,
1629
+ desc ocispec.Descriptor ,
1630
+ fromRepo string ,
1631
+ getContent func () (io.ReadCloser , error ),
1632
+ ) error {
1633
+ numMount .Add (1 )
1634
+ switch fromRepo {
1635
+ case "source" :
1636
+ rc , err := src .Fetch (ctx , desc )
1637
+ if err != nil {
1638
+ t .Fatalf ("Failed to fetch content: %v" , err )
1639
+ }
1640
+ defer rc .Close ()
1641
+ err = dst .storage .Push (ctx , desc , rc ) // bypass the counters
1642
+ if err != nil {
1643
+ t .Fatalf ("Failed to push content: %v" , err )
1644
+ }
1645
+ return nil
1646
+ case "missing/the/data" :
1647
+ // simulate a registry mount will fail, so it will request the content to start the copy.
1648
+ rc , err := getContent ()
1649
+ if err != nil {
1650
+ return fmt .Errorf ("getContent failed: %w" , err )
1651
+ }
1652
+ defer rc .Close ()
1653
+ err = dst .storage .Push (ctx , desc , rc ) // bypass the counters
1654
+ if err != nil {
1655
+ t .Fatalf ("Failed to push content: %v" , err )
1656
+ }
1657
+ return nil
1658
+ default :
1659
+ t .Fatalf ("fromRepo = %v, want either %v or %v" , fromRepo , "missing/the/data" , "source" )
1660
+ return nil
1661
+ }
1662
+ }
1663
+ opts = oras.CopyGraphOptions {}
1664
+ var numPreCopy , numPostCopy , numOnMounted , numMountFrom atomic.Int64
1665
+ opts .PreCopy = func (ctx context.Context , desc ocispec.Descriptor ) error {
1666
+ numPreCopy .Add (1 )
1667
+ return nil
1668
+ }
1669
+ opts .PostCopy = func (ctx context.Context , desc ocispec.Descriptor ) error {
1670
+ numPostCopy .Add (1 )
1671
+ return nil
1672
+ }
1673
+ opts .OnMounted = func (ctx context.Context , d ocispec.Descriptor ) error {
1674
+ numOnMounted .Add (1 )
1675
+ return nil
1676
+ }
1677
+ opts .MountFrom = func (ctx context.Context , desc ocispec.Descriptor ) ([]string , error ) {
1678
+ numMountFrom .Add (1 )
1679
+ return []string {"missing/the/data" , "source" }, nil
1680
+ }
1681
+ if err := oras .CopyGraph (ctx , src , dst , root , opts ); err != nil {
1682
+ t .Fatalf ("CopyGraph() error = %v, wantErr %v" , err , errdef .ErrSizeExceedsLimit )
1683
+ }
1684
+
1685
+ if got , expected := dst .numExists .Load (), int64 (7 ); got != expected {
1686
+ t .Errorf ("count(Exists()) = %d, want %d" , got , expected )
1687
+ }
1688
+ if got , expected := dst .numFetch .Load (), int64 (0 ); got != expected {
1689
+ t .Errorf ("count(Fetch()) = %d, want %d" , got , expected )
1690
+ }
1691
+ // 7 (exists) - 1 (skipped) = 6 pushes expected
1692
+ if got , expected := dst .numPush .Load (), int64 (3 ); got != expected {
1693
+ // If we get >=7 then ErrSkipDesc did not short circuit the push like it is supposed to do.
1694
+ t .Errorf ("count(Push()) = %d, want %d" , got , expected )
1695
+ }
1696
+ if got , expected := numMount .Load (), int64 (4 * 2 ); got != expected {
1697
+ t .Errorf ("count(Mount()) = %d, want %d" , got , expected )
1698
+ }
1699
+ if got , expected := numOnMounted .Load (), int64 (4 ); got != expected {
1700
+ t .Errorf ("count(OnMounted()) = %d, want %d" , got , expected )
1701
+ }
1702
+ if got , expected := numMountFrom .Load (), int64 (4 ); got != expected {
1703
+ t .Errorf ("count(MountFrom()) = %d, want %d" , got , expected )
1704
+ }
1705
+ if got , expected := numPreCopy .Load (), int64 (3 ); got != expected {
1706
+ t .Errorf ("count(PreCopy()) = %d, want %d" , got , expected )
1707
+ }
1708
+ if got , expected := numPostCopy .Load (), int64 (3 ); got != expected {
1709
+ t .Errorf ("count(PostCopy()) = %d, want %d" , got , expected )
1710
+ }
1711
+ })
1474
1712
}
1475
1713
1476
1714
// countingStorage counts the calls to its content.Storage methods
1477
1715
type countingStorage struct {
1478
- storage content.Storage
1716
+ storage content.Storage
1717
+ mount mountFunc
1718
+
1479
1719
numExists , numFetch , numPush atomic.Int64
1480
1720
}
1481
1721
@@ -1494,6 +1734,16 @@ func (cs *countingStorage) Push(ctx context.Context, target ocispec.Descriptor,
1494
1734
return cs .storage .Push (ctx , target , r )
1495
1735
}
1496
1736
1737
+ type mountFunc func (context.Context , ocispec.Descriptor , string , func () (io.ReadCloser , error )) error
1738
+
1739
+ func (cs * countingStorage ) Mount (ctx context.Context ,
1740
+ desc ocispec.Descriptor ,
1741
+ fromRepo string ,
1742
+ getContent func () (io.ReadCloser , error ),
1743
+ ) error {
1744
+ return cs .mount (ctx , desc , fromRepo , getContent )
1745
+ }
1746
+
1497
1747
func TestCopyGraph_WithConcurrencyLimit (t * testing.T ) {
1498
1748
src := cas .NewMemory ()
1499
1749
// generate test content
0 commit comments