@@ -565,7 +565,7 @@ def test_external_backward_compatibility_merge_2(self):
565565"""
566566 take backup with old binary without external dirs support
567567 take delta backup with new binary and 2 external directories
568- merge delta backupajd restore it
568+ merge delta backupand restore it
569569 """
570570fname = self .id ().split ('.' )[3 ]
571571backup_dir = os .path .join (self .tmp_path ,module_name ,fname ,'backup' )
@@ -654,7 +654,7 @@ def test_external_backward_compatibility_merge_2(self):
654654pgdata = self .pgdata_content (
655655node .base_dir ,exclude_dirs = ['logs' ])
656656
657- # Merge chainchain with new binary
657+ # Merge chainusing new binary
658658self .merge_backup (backup_dir ,'node' ,backup_id = backup_id )
659659
660660# Restore merged backup
@@ -663,15 +663,19 @@ def test_external_backward_compatibility_merge_2(self):
663663
664664node_restored .cleanup ()
665665
666- external_dir1_new = self .get_tblspace_path (node_restored ,'external_dir1' )
667- external_dir2_new = self .get_tblspace_path (node_restored ,'external_dir2' )
666+ external_dir1_new = self .get_tblspace_path (
667+ node_restored ,'external_dir1' )
668+ external_dir2_new = self .get_tblspace_path (
669+ node_restored ,'external_dir2' )
668670
669671self .restore_node (
670672backup_dir ,'node' ,node_restored ,
671673options = [
672674"-j" ,"4" ,
673- "--external-mapping={0}={1}" .format (external_dir1 ,external_dir1_new ),
674- "--external-mapping={0}={1}" .format (external_dir2 ,external_dir2_new )])
675+ "--external-mapping={0}={1}" .format (
676+ external_dir1 ,external_dir1_new ),
677+ "--external-mapping={0}={1}" .format (
678+ external_dir2 ,external_dir2_new )])
675679
676680pgdata_restored = self .pgdata_content (
677681node_restored .base_dir ,exclude_dirs = ['logs' ])
@@ -699,7 +703,7 @@ def test_external_merge(self):
699703
700704node .pgbench_init (scale = 3 )
701705
702- #FULL backup with old binary without external dirs support
706+ #take temp FULL backup
703707tmp_id = self .backup_node (
704708backup_dir ,'node' ,node ,options = ["-j" ,"4" ,"--stream" ])
705709
@@ -753,8 +757,10 @@ def test_external_merge(self):
753757backup_dir ,'node' ,node ,
754758options = [
755759"-j" ,"4" ,
756- "--external-mapping={0}={1}" .format (external_dir1 ,external_dir1_new ),
757- "--external-mapping={0}={1}" .format (external_dir2 ,external_dir2_new )])
760+ "--external-mapping={0}={1}" .format (
761+ external_dir1 ,external_dir1_new ),
762+ "--external-mapping={0}={1}" .format (
763+ external_dir2 ,external_dir2_new )])
758764
759765pgdata_restored = self .pgdata_content (
760766node .base_dir ,exclude_dirs = ['logs' ])
@@ -2459,3 +2465,75 @@ def test_smart_restore_externals(self):
24592465
24602466# Clean after yourself
24612467self .del_test_dir (module_name ,fname )
2468+
2469+ # @unittest.skip("skip")
2470+ def test_external_validation (self ):
2471+ """
2472+ make node, create database,
2473+ take full backup with external directory,
2474+ corrupt external file in backup,
2475+ run validate which should fail
2476+ """
2477+ fname = self .id ().split ('.' )[3 ]
2478+ node = self .make_simple_node (
2479+ base_dir = os .path .join (module_name ,fname ,'node' ),
2480+ set_replication = True ,
2481+ initdb_params = ['--data-checksums' ])
2482+
2483+ backup_dir = os .path .join (self .tmp_path ,module_name ,fname ,'backup' )
2484+ self .init_pb (backup_dir )
2485+ self .add_instance (backup_dir ,'node' ,node )
2486+ node .slow_start ()
2487+
2488+ # take temp FULL backup
2489+ tmp_id = self .backup_node (
2490+ backup_dir ,'node' ,node ,options = ['--stream' ])
2491+
2492+ external_dir = self .get_tblspace_path (node ,'external_dir' )
2493+
2494+ # fill external directories with data
2495+ self .restore_node (
2496+ backup_dir ,'node' ,node ,backup_id = tmp_id ,
2497+ data_dir = external_dir ,options = ["-j" ,"4" ])
2498+
2499+ self .delete_pb (backup_dir ,'node' ,backup_id = tmp_id )
2500+
2501+ # take FULL backup
2502+ full_id = self .backup_node (
2503+ backup_dir ,'node' ,node ,
2504+ options = [
2505+ '--stream' ,'-E' ,"{0}" .format (external_dir )])
2506+
2507+ # Corrupt file
2508+ file = os .path .join (
2509+ backup_dir ,'backups' ,'node' ,full_id ,
2510+ 'external_directories' ,'externaldir1' ,'postgresql.auto.conf' )
2511+
2512+ with open (file ,"r+b" ,0 )as f :
2513+ f .seek (42 )
2514+ f .write (b"blah" )
2515+ f .flush ()
2516+ f .close
2517+
2518+ try :
2519+ self .validate_pb (backup_dir )
2520+ # we should die here because exception is what we expect to happen
2521+ self .assertEqual (
2522+ 1 ,0 ,
2523+ "Expecting Error because file in external dir is corrupted"
2524+ "\n Output: {0}\n CMD: {1}" .format (
2525+ repr (self .output ),self .cmd ))
2526+ except ProbackupException as e :
2527+ self .assertIn (
2528+ 'WARNING: Invalid CRC of backup file' ,
2529+ e .message ,
2530+ '\n Unexpected Error Message: {0}\n CMD: {1}' .format (
2531+ repr (e .message ),self .cmd ))
2532+
2533+ self .assertEqual (
2534+ 'CORRUPT' ,
2535+ self .show_pb (backup_dir ,'node' ,full_id )['status' ],
2536+ 'Backup STATUS should be "CORRUPT"' )
2537+
2538+ # Clean after yourself
2539+ self .del_test_dir (module_name ,fname )