@@ -227,7 +227,10 @@ def test_external_dir_mapping(self):
227227backup_dir ,'node' ,node ,backup_type = "delta" ,
228228options = [
229229"-j" ,"4" ,"--stream" ,
230- "-E" ,"{0}:{1}" .format (external_dir1_old ,external_dir2_old )])
230+ "-E" ,"{0}{1}{2}" .format (
231+ external_dir1_old ,
232+ self .EXTERNAL_DIRECTORY_DELIMITER ,
233+ external_dir2_old )])
231234
232235pgdata = self .pgdata_content (
233236node .base_dir ,exclude_dirs = ['logs' ])
@@ -383,8 +386,9 @@ def test_external_backward_compatibility(self):
383386backup_dir ,'node' ,node ,backup_type = "delta" ,
384387options = [
385388"-j" ,"4" ,"--stream" ,
386- "-E" ,"{0}:{1 }" .format (
389+ "-E" ,"{0}{1}{2 }" .format (
387390external_dir1_old ,
391+ self .EXTERNAL_DIRECTORY_DELIMITER ,
388392external_dir2_old )])
389393
390394pgdata = self .pgdata_content (
@@ -472,8 +476,9 @@ def test_external_backward_compatibility_merge_1(self):
472476backup_dir ,'node' ,node ,backup_type = "delta" ,
473477options = [
474478"-j" ,"4" ,"--stream" ,
475- "-E" ,"{0}:{1 }" .format (
479+ "-E" ,"{0}{1}{2 }" .format (
476480external_dir1_old ,
481+ self .EXTERNAL_DIRECTORY_DELIMITER ,
477482external_dir2_old )])
478483
479484pgdata = self .pgdata_content (
@@ -565,8 +570,9 @@ def test_external_backward_compatibility_merge_2(self):
565570backup_type = "delta" ,
566571options = [
567572"-j" ,"4" ,"--stream" ,
568- "-E" ,"{0}:{1 }" .format (
573+ "-E" ,"{0}{1}{2 }" .format (
569574external_dir1_old ,
575+ self .EXTERNAL_DIRECTORY_DELIMITER ,
570576external_dir2_old )])
571577
572578pgbench = node .pgbench (options = ['-T' ,'30' ,'-c' ,'1' ])
@@ -592,8 +598,9 @@ def test_external_backward_compatibility_merge_2(self):
592598backup_type = "delta" ,
593599options = [
594600"-j" ,"4" ,"--stream" ,
595- "-E" ,"{0}:{1 }" .format (
601+ "-E" ,"{0}{1}{2 }" .format (
596602external_dir1_old ,
603+ self .EXTERNAL_DIRECTORY_DELIMITER ,
597604external_dir2_old )])
598605
599606pgdata = self .pgdata_content (
@@ -687,8 +694,9 @@ def test_external_merge(self):
687694backup_dir ,'node' ,node ,backup_type = "delta" ,
688695options = [
689696"-j" ,"4" ,"--stream" ,
690- "-E" ,"{0}:{1 }" .format (
697+ "-E" ,"{0}{1}{2 }" .format (
691698external_dir1_old ,
699+ self .EXTERNAL_DIRECTORY_DELIMITER ,
692700external_dir2_old )])
693701
694702self .merge_backup (backup_dir ,'node' ,backup_id = backup_id )
@@ -767,8 +775,9 @@ def test_external_merge_1(self):
767775backup_dir ,'node' ,node ,
768776options = [
769777"-j" ,"4" ,"--stream" ,
770- "-E" ,"{0}:{1 }" .format (
778+ "-E" ,"{0}{1}{2 }" .format (
771779external_dir1_old ,
780+ self .EXTERNAL_DIRECTORY_DELIMITER ,
772781external_dir2_old )])
773782
774783# drop old external data
@@ -791,8 +800,9 @@ def test_external_merge_1(self):
791800backup_dir ,'node' ,node ,backup_type = "delta" ,
792801options = [
793802"-j" ,"4" ,"--stream" ,
794- "-E" ,"{0}:{1 }" .format (
803+ "-E" ,"{0}{1}{2 }" .format (
795804external_dir1_old ,
805+ self .EXTERNAL_DIRECTORY_DELIMITER ,
796806external_dir2_old )])
797807
798808pgdata = self .pgdata_content (
@@ -877,8 +887,9 @@ def test_external_merge_single(self):
877887backup_dir ,'node' ,node ,backup_type = "delta" ,
878888options = [
879889"-j" ,"4" ,"--stream" ,
880- "-E" ,"{0}:{1 }" .format (
890+ "-E" ,"{0}{1}{2 }" .format (
881891external_dir1_old ,
892+ self .EXTERNAL_DIRECTORY_DELIMITER ,
882893external_dir2_old )])
883894
884895self .merge_backup (backup_dir ,'node' ,backup_id = backup_id )
@@ -959,17 +970,19 @@ def test_external_merge_double(self):
959970backup_dir ,'node' ,node ,backup_type = "delta" ,
960971options = [
961972"-j" ,"4" ,"--stream" ,
962- "-E" ,"{0}:{1 }" .format (
973+ "-E" ,"{0}{1}{2 }" .format (
963974external_dir1_old ,
975+ self .EXTERNAL_DIRECTORY_DELIMITER ,
964976external_dir2_old )])
965977
966978# delta backup with external directories
967979backup_id = self .backup_node (
968980backup_dir ,'node' ,node ,backup_type = "delta" ,
969981options = [
970982"-j" ,"4" ,"--stream" ,
971- "-E" ,"{0}:{1 }" .format (
983+ "-E" ,"{0}{1}{2 }" .format (
972984external_dir1_old ,
985+ self .EXTERNAL_DIRECTORY_DELIMITER ,
973986external_dir2_old )])
974987
975988pgdata = self .pgdata_content (
@@ -1049,8 +1062,9 @@ def test_restore_skip_external(self):
10491062backup_dir ,'node' ,node ,
10501063options = [
10511064"-j" ,"4" ,"--stream" ,
1052- "-E" ,"{0}:{1 }" .format (
1065+ "-E" ,"{0}{1}{2 }" .format (
10531066external_dir1 ,
1067+ self .EXTERNAL_DIRECTORY_DELIMITER ,
10541068external_dir2 )])
10551069
10561070# delete first externals, so pgdata_compare
@@ -1476,9 +1490,95 @@ def test_restore_external_dir_not_empty(self):
14761490def test_restore_external_dir_is_missing (self ):
14771491"""
14781492 take FULL backup with not empty external directory
1479- drop external directory
1480- take DELTA backup
1481- restore page backup, check that restored
1493+ delete external directory
1494+ take DELTA backup with external directory, which
1495+ should fail
1496+ """
1497+ fname = self .id ().split ('.' )[3 ]
1498+ backup_dir = os .path .join (self .tmp_path ,module_name ,fname ,'backup' )
1499+ core_dir = os .path .join (self .tmp_path ,module_name ,fname )
1500+ shutil .rmtree (core_dir ,ignore_errors = True )
1501+ node = self .make_simple_node (
1502+ base_dir = os .path .join (module_name ,fname ,'node' ),
1503+ set_replication = True ,
1504+ initdb_params = ['--data-checksums' ],
1505+ pg_options = {
1506+ 'max_wal_senders' :'2' ,
1507+ 'autovacuum' :'off' })
1508+
1509+ self .init_pb (backup_dir )
1510+ self .add_instance (backup_dir ,'node' ,node )
1511+ node .slow_start ()
1512+
1513+ external_dir = self .get_tblspace_path (node ,'external_dir' )
1514+
1515+ # create empty file in external directory
1516+ # open(os.path.join(external_dir, 'file'), 'a').close()
1517+ os .mkdir (external_dir )
1518+ with open (os .path .join (external_dir ,'file' ),'w+' )as f :
1519+ f .close ()
1520+
1521+ # FULL backup with external directory
1522+ self .backup_node (
1523+ backup_dir ,'node' ,node ,
1524+ options = [
1525+ "-j" ,"4" ,"--stream" ,
1526+ "-E" ,"{0}" .format (
1527+ external_dir )])
1528+
1529+ # drop external directory
1530+ shutil .rmtree (external_dir ,ignore_errors = True )
1531+
1532+ try :
1533+ self .backup_node (
1534+ backup_dir ,'node' ,node ,
1535+ backup_type = 'delta' ,
1536+ options = [
1537+ "-j" ,"4" ,"--stream" ,
1538+ "-E" ,"{0}" .format (
1539+ external_dir )])
1540+ # we should die here because exception is what we expect to happen
1541+ self .assertEqual (
1542+ 1 ,0 ,
1543+ "Expecting Error because external dir is missing"
1544+ "\n Output: {0}\n CMD: {1}" .format (
1545+ repr (self .output ),self .cmd ))
1546+ except ProbackupException as e :
1547+ self .assertTrue (
1548+ 'ERROR: External directory is not found: "{0}"' .format (external_dir )in e .message ,
1549+ '\n Unexpected Error Message: {0}\n CMD: {1}' .format (
1550+ repr (e .message ),self .cmd ))
1551+
1552+ sleep (1 )
1553+
1554+ # take DELTA without external directories
1555+ self .backup_node (
1556+ backup_dir ,'node' ,node ,
1557+ backup_type = 'delta' ,
1558+ options = ["-j" ,"4" ,"--stream" ])
1559+
1560+ pgdata = self .pgdata_content (
1561+ node .base_dir ,exclude_dirs = ['logs' ])
1562+
1563+ # Restore Delta backup
1564+ node .cleanup ()
1565+ shutil .rmtree (node .base_dir ,ignore_errors = True )
1566+
1567+ self .restore_node (backup_dir ,'node' ,node )
1568+
1569+ pgdata_restored = self .pgdata_content (
1570+ node .base_dir ,exclude_dirs = ['logs' ])
1571+ self .compare_pgdata (pgdata ,pgdata_restored )
1572+
1573+ # Clean after yourself
1574+ self .del_test_dir (module_name ,fname )
1575+
1576+ def test_restore_external_dir_is_empty (self ):
1577+ """
1578+ take FULL backup with not empty external directory
1579+ drop external directory content
1580+ take DELTA backup with the same external directory
1581+ restore DELRA backup, check that restored
14821582 external directory is empty
14831583 """
14841584fname = self .id ().split ('.' )[3 ]
@@ -1513,9 +1613,10 @@ def test_restore_external_dir_is_missing(self):
15131613"-E" ,"{0}" .format (
15141614external_dir )])
15151615
1516- #drop externalfile only
1616+ #make externaldirectory empty
15171617os .remove (os .path .join (external_dir ,'file' ))
15181618
1619+ # take DELTA backup with empty external directory
15191620self .backup_node (
15201621backup_dir ,'node' ,node ,
15211622backup_type = 'delta' ,
@@ -1530,18 +1631,101 @@ def test_restore_external_dir_is_missing(self):
15301631# Restore Delta backup
15311632node_restored = self .make_simple_node (
15321633base_dir = os .path .join (module_name ,fname ,'node_restored' ))
1634+
15331635node_restored .cleanup ()
15341636
1535- external_dir_new = self .get_tblspace_path (node_restored ,'external_dir_new' )
1637+ external_dir_new = self .get_tblspace_path (
1638+ node_restored ,'external_dir' )
15361639
15371640self .restore_node (
15381641backup_dir ,'node' ,node_restored ,
1539- options = [" --external-mapping={0}={1}" .format (
1540- external_dir ,external_dir_new )])
1642+ options = [' --external-mapping={0}={1}' .format (
1643+ external_dir ,external_dir_new )])
15411644
15421645pgdata_restored = self .pgdata_content (
15431646node_restored .base_dir ,exclude_dirs = ['logs' ])
15441647self .compare_pgdata (pgdata ,pgdata_restored )
15451648
15461649# Clean after yourself
15471650self .del_test_dir (module_name ,fname )
1651+
1652+ def test_restore_external_dir_string_order (self ):
1653+ """
1654+ take FULL backup with not empty external directory
1655+ drop external directory content
1656+ take DELTA backup with the same external directory
1657+ restore DELRA backup, check that restored
1658+ external directory is empty
1659+ """
1660+ fname = self .id ().split ('.' )[3 ]
1661+ backup_dir = os .path .join (self .tmp_path ,module_name ,fname ,'backup' )
1662+ core_dir = os .path .join (self .tmp_path ,module_name ,fname )
1663+ shutil .rmtree (core_dir ,ignore_errors = True )
1664+ node = self .make_simple_node (
1665+ base_dir = os .path .join (module_name ,fname ,'node' ),
1666+ set_replication = True ,
1667+ initdb_params = ['--data-checksums' ],
1668+ pg_options = {
1669+ 'max_wal_senders' :'2' ,
1670+ 'autovacuum' :'off' })
1671+
1672+ self .init_pb (backup_dir )
1673+ self .add_instance (backup_dir ,'node' ,node )
1674+ node .slow_start ()
1675+
1676+ external_dir_1 = self .get_tblspace_path (node ,'external_dir_1' )
1677+ external_dir_2 = self .get_tblspace_path (node ,'external_dir_2' )
1678+
1679+ # create empty file in external directory
1680+ os .mkdir (external_dir_1 )
1681+ with open (os .path .join (external_dir_1 ,'fileA' ),'w+' )as f :
1682+ f .close ()
1683+
1684+ os .mkdir (external_dir_2 )
1685+ with open (os .path .join (external_dir_2 ,'fileZ' ),'w+' )as f :
1686+ f .close ()
1687+
1688+ # FULL backup with external directory
1689+ self .backup_node (
1690+ backup_dir ,'node' ,node ,
1691+ options = [
1692+ "-j" ,"4" ,"--stream" ,
1693+ "-E" ,"{0}{1}{2}" .format (
1694+ external_dir_1 ,
1695+ self .EXTERNAL_DIRECTORY_DELIMITER ,
1696+ external_dir_2 )])
1697+
1698+ with open (os .path .join (external_dir_1 ,'fileB' ),'w+' )as f :
1699+ f .close ()
1700+
1701+ with open (os .path .join (external_dir_2 ,'fileY' ),'w+' )as f :
1702+ f .close ()
1703+
1704+ # take DELTA backup and swap external_dir_2 and external_dir_1
1705+ # in external_dir_str
1706+ self .backup_node (
1707+ backup_dir ,'node' ,node ,
1708+ backup_type = 'delta' ,
1709+ options = [
1710+ "-j" ,"4" ,"--stream" ,
1711+ "-E" ,"{0}{1}{2}" .format (
1712+ external_dir_2 ,
1713+ self .EXTERNAL_DIRECTORY_DELIMITER ,
1714+ external_dir_1 )])
1715+
1716+ pgdata = self .pgdata_content (
1717+ node .base_dir ,exclude_dirs = ['logs' ])
1718+
1719+ # Restore Delta backup
1720+ node .cleanup ()
1721+ shutil .rmtree (node .base_dir ,ignore_errors = True )
1722+
1723+ self .restore_node (backup_dir ,'node' ,node )
1724+
1725+ pgdata_restored = self .pgdata_content (
1726+ node .base_dir ,exclude_dirs = ['logs' ])
1727+
1728+ self .compare_pgdata (pgdata ,pgdata_restored )
1729+
1730+ # Clean after yourself
1731+ self .del_test_dir (module_name ,fname )