mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2024-12-16 10:20:02 +02:00
1927 lines
92 KiB
Perl
Executable File
1927 lines
92 KiB
Perl
Executable File
####################################################################################################################################
|
|
# BackupTest.pm - Unit Tests for BackRest::Backup and BackRest::Restore
|
|
####################################################################################################################################
|
|
package BackRestTest::BackupTest;
|
|
|
|
####################################################################################################################################
|
|
# Perl includes
|
|
####################################################################################################################################
|
|
use strict;
|
|
use warnings FATAL => qw(all);
|
|
use Carp qw(confess);
|
|
|
|
use DBI;
|
|
use Exporter qw(import);
|
|
use Fcntl ':mode';
|
|
use File::Basename;
|
|
use File::Copy 'cp';
|
|
use File::stat;
|
|
use Time::HiRes qw(gettimeofday);
|
|
|
|
use lib dirname($0) . '/../lib';
|
|
use BackRest::Archive;
|
|
use BackRest::ArchiveInfo;
|
|
use BackRest::Common::Exception;
|
|
use BackRest::Common::Ini;
|
|
use BackRest::Common::Log;
|
|
use BackRest::Common::Wait;
|
|
use BackRest::Db;
|
|
use BackRest::Config::Config;
|
|
use BackRest::File;
|
|
use BackRest::Manifest;
|
|
use BackRest::Protocol::Common;
|
|
use BackRest::Protocol::RemoteMaster;
|
|
|
|
use BackRestTest::BackupCommonTest;
|
|
use BackRestTest::Common::ExecuteTest;
|
|
use BackRestTest::CommonTest;
|
|
use BackRestTest::ExpireCommonTest;
|
|
|
|
####################################################################################################################################
|
|
# BackRestTestBackup_Test
|
|
####################################################################################################################################
|
|
our @EXPORT = qw(BackRestTestBackup_Test);
|
|
|
|
sub BackRestTestBackup_Test
|
|
{
|
|
my $strTest = shift;
|
|
my $iThreadMax = shift;
|
|
|
|
# If no test was specified, then run them all
|
|
if (!defined($strTest))
|
|
{
|
|
$strTest = 'all';
|
|
}
|
|
|
|
# Setup global variables
|
|
my $strTestPath = BackRestTestCommon_TestPathGet();
|
|
my $strHost = BackRestTestCommon_HostGet();
|
|
my $strUser = BackRestTestCommon_UserGet();
|
|
my $strUserBackRest = BackRestTestCommon_UserBackRestGet();
|
|
my $strGroup = BackRestTestCommon_GroupGet();
|
|
|
|
# Setup test variables
|
|
my $iRun;
|
|
my $strModule = 'backup';
|
|
my $strThisTest;
|
|
my $bCreate;
|
|
my $strStanza = BackRestTestCommon_StanzaGet();
|
|
my $oLogTest;
|
|
|
|
my $strArchiveChecksum = '1c7e00fd09b9dd11fc2966590b3e3274645dd031';
|
|
my $iArchiveMax = 3;
|
|
my $strXlogPath = BackRestTestCommon_DbCommonPathGet() . '/pg_xlog';
|
|
my $strArchiveTestFile = BackRestTestCommon_DataPathGet() . '/test.archive2.bin';
|
|
my $strArchiveTestFile2 = BackRestTestCommon_DataPathGet() . '/test.archive1.bin';
|
|
|
|
# Print test banner
|
|
&log(INFO, 'BACKUP MODULE ******************************************************************');
|
|
&log(INFO, "THREAD-MAX: ${iThreadMax}\n");
|
|
|
|
# Drop any existing cluster
|
|
BackRestTestBackup_Drop(true, true);
|
|
|
|
#-------------------------------------------------------------------------------------------------------------------------------
|
|
# Create remotes
|
|
#-------------------------------------------------------------------------------------------------------------------------------
|
|
my $oRemote = new BackRest::Protocol::RemoteMaster
|
|
(
|
|
BackRestTestCommon_CommandRemoteFullGet(), # Remote command
|
|
OPTION_DEFAULT_BUFFER_SIZE, # Buffer size
|
|
OPTION_DEFAULT_COMPRESS_LEVEL, # Compress level
|
|
OPTION_DEFAULT_COMPRESS_LEVEL_NETWORK, # Compress network level
|
|
$strHost, # Host
|
|
$strUserBackRest, # User
|
|
PROTOCOL_TIMEOUT_TEST # Protocol timeout
|
|
);
|
|
|
|
my $oLocal = new BackRest::Protocol::Common
|
|
(
|
|
OPTION_DEFAULT_BUFFER_SIZE, # Buffer size
|
|
OPTION_DEFAULT_COMPRESS_LEVEL, # Compress level
|
|
OPTION_DEFAULT_COMPRESS_LEVEL_NETWORK, # Compress network level
|
|
PROTOCOL_TIMEOUT_TEST # Protocol timeout
|
|
);
|
|
|
|
#-------------------------------------------------------------------------------------------------------------------------------
|
|
# Test archive-push
|
|
#-------------------------------------------------------------------------------------------------------------------------------
|
|
$strThisTest = 'archive-push';
|
|
|
|
if ($strTest eq 'all' || $strTest eq $strThisTest)
|
|
{
|
|
$iRun = 0;
|
|
$bCreate = true;
|
|
my $oFile;
|
|
|
|
&log(INFO, "Test ${strThisTest}\n");
|
|
|
|
for (my $bRemote = false; $bRemote <= true; $bRemote++)
|
|
{
|
|
for (my $bCompress = false; $bCompress <= true; $bCompress++)
|
|
{
|
|
for (my $bArchiveAsync = false; $bArchiveAsync <= true; $bArchiveAsync++)
|
|
{
|
|
# Increment the run, log, and decide whether this unit test should be run
|
|
if (!BackRestTestCommon_Run(++$iRun,
|
|
"rmt ${bRemote}, cmp ${bCompress}, " .
|
|
"arc_async ${bArchiveAsync}",
|
|
$iThreadMax == 1 ? $strModule : undef,
|
|
$iThreadMax == 1 ? $strThisTest: undef,
|
|
\$oLogTest)) {next}
|
|
|
|
# Create the file object
|
|
if ($bCreate)
|
|
{
|
|
$oFile = (new BackRest::File
|
|
(
|
|
$strStanza,
|
|
BackRestTestCommon_RepoPathGet(),
|
|
$bRemote ? 'backup' : undef,
|
|
$bRemote ? $oRemote : $oLocal
|
|
))->clone();
|
|
|
|
$bCreate = false;
|
|
}
|
|
|
|
# Create the test directory
|
|
BackRestTestBackup_Create($bRemote, false);
|
|
BackRestTestBackup_Init($bRemote, $oFile, true, $oLogTest);
|
|
|
|
BackRestTestCommon_ConfigCreate(DB,
|
|
($bRemote ? BACKUP : undef),
|
|
$bCompress,
|
|
undef, # checksum
|
|
undef, # hardlink
|
|
undef, # thread-max
|
|
$bArchiveAsync,
|
|
undef);
|
|
|
|
BackRestTestCommon_ConfigCreate(BACKUP,
|
|
($bRemote ? DB : undef));
|
|
|
|
my $strCommand = BackRestTestCommon_CommandMainGet() . ' --config=' . BackRestTestCommon_DbPathGet() .
|
|
'/pg_backrest.conf --no-fork --stanza=db archive-push';
|
|
|
|
# Loop through backups
|
|
for (my $iBackup = 1; $iBackup <= 3; $iBackup++)
|
|
{
|
|
my $strArchiveFile;
|
|
|
|
# Loop through archive files
|
|
for (my $iArchive = 1; $iArchive <= $iArchiveMax; $iArchive++)
|
|
{
|
|
# Construct the archive filename
|
|
my $iArchiveNo = (($iBackup - 1) * $iArchiveMax + ($iArchive - 1)) + 1;
|
|
|
|
if ($iArchiveNo > 255)
|
|
{
|
|
confess 'backup total * archive total cannot be greater than 255';
|
|
}
|
|
|
|
$strArchiveFile = uc(sprintf('0000000100000001%08x', $iArchiveNo));
|
|
|
|
&log(INFO, ' backup ' . sprintf('%02d', $iBackup) .
|
|
', archive ' .sprintf('%02x', $iArchive) .
|
|
" - ${strArchiveFile}");
|
|
|
|
my $strSourceFile = "${strXlogPath}/${strArchiveFile}";
|
|
|
|
$oFile->copy(PATH_DB_ABSOLUTE, $strArchiveTestFile, # Source file
|
|
PATH_DB_ABSOLUTE, $strSourceFile, # Destination file
|
|
false, # Source is not compressed
|
|
false, # Destination is not compressed
|
|
undef, undef, undef, # Unused params
|
|
true); # Create path if it does not exist
|
|
|
|
executeTest($strCommand . " ${strSourceFile}", {oLogTest => $oLogTest});
|
|
|
|
if ($iArchive == $iBackup)
|
|
{
|
|
# load the archive info file so it can be munged for testing
|
|
my $strInfoFile = $oFile->pathGet(PATH_BACKUP_ARCHIVE, ARCHIVE_INFO_FILE);
|
|
my %oInfo;
|
|
BackRestTestCommon_iniLoad($strInfoFile, \%oInfo, $bRemote);
|
|
my $strDbVersion = $oInfo{&INFO_ARCHIVE_SECTION_DB}{&INFO_ARCHIVE_KEY_DB_VERSION};
|
|
my $ullDbSysId = $oInfo{&INFO_ARCHIVE_SECTION_DB}{&INFO_ARCHIVE_KEY_DB_SYSTEM_ID};
|
|
|
|
# Break the database version
|
|
$oInfo{&INFO_ARCHIVE_SECTION_DB}{&INFO_ARCHIVE_KEY_DB_VERSION} = '8.0';
|
|
BackRestTestCommon_iniSave($strInfoFile, \%oInfo, $bRemote, true);
|
|
|
|
&log(INFO, ' test db version mismatch error');
|
|
|
|
executeTest($strCommand . " ${strSourceFile}",
|
|
{iExpectedExitStatus => ERROR_ARCHIVE_MISMATCH, oLogTest => $oLogTest});
|
|
|
|
# Break the system id
|
|
$oInfo{&INFO_ARCHIVE_SECTION_DB}{&INFO_ARCHIVE_KEY_DB_VERSION} = $strDbVersion;
|
|
$oInfo{&INFO_ARCHIVE_SECTION_DB}{&INFO_ARCHIVE_KEY_DB_SYSTEM_ID} = '5000900090001855000';
|
|
BackRestTestCommon_iniSave($strInfoFile, \%oInfo, $bRemote, true);
|
|
|
|
&log(INFO, ' test db system-id mismatch error');
|
|
|
|
executeTest($strCommand . " ${strSourceFile}",
|
|
{iExpectedExitStatus => ERROR_ARCHIVE_MISMATCH, oLogTest => $oLogTest});
|
|
|
|
# Move settings back to original
|
|
$oInfo{&INFO_ARCHIVE_SECTION_DB}{&INFO_ARCHIVE_KEY_DB_SYSTEM_ID} = $ullDbSysId;
|
|
BackRestTestCommon_iniSave($strInfoFile, \%oInfo, $bRemote, true);
|
|
|
|
# Fail because the process was killed
|
|
if ($iBackup == 1 && !$bCompress)
|
|
{
|
|
&log(INFO, ' test stop');
|
|
|
|
if ($bArchiveAsync)
|
|
{
|
|
my $oExecArchive = new BackRestTest::Common::ExecuteTest(
|
|
$strCommand . ' --test --test-delay=5 --test-point=' . lc(TEST_ARCHIVE_PUSH_ASYNC_START) .
|
|
"=y ${strSourceFile}",
|
|
{oLogTest => $oLogTest, iExpectedExitStatus => ERROR_TERM});
|
|
$oExecArchive->begin();
|
|
$oExecArchive->end(TEST_ARCHIVE_PUSH_ASYNC_START);
|
|
|
|
BackRestTestBackup_Stop(undef, undef, true);
|
|
|
|
$oExecArchive->end();
|
|
|
|
}
|
|
else
|
|
{
|
|
BackRestTestBackup_Stop($strStanza);
|
|
}
|
|
|
|
executeTest($strCommand . " ${strSourceFile}",
|
|
{oLogTest => $oLogTest, iExpectedExitStatus => ERROR_STOP});
|
|
|
|
|
|
BackRestTestBackup_Start($bArchiveAsync ? undef : $strStanza);
|
|
}
|
|
|
|
# Should succeed because checksum is the same
|
|
&log(INFO, ' test archive duplicate ok');
|
|
|
|
executeTest($strCommand . " ${strSourceFile}", {oLogTest => $oLogTest});
|
|
|
|
# Now it should break on archive duplication (because checksum is different
|
|
&log(INFO, ' test archive duplicate error');
|
|
|
|
$oFile->copy(PATH_DB_ABSOLUTE, $strArchiveTestFile2, # Source file
|
|
PATH_DB_ABSOLUTE, $strSourceFile, # Destination file
|
|
false, # Source is not compressed
|
|
false, # Destination is not compressed
|
|
undef, undef, undef, # Unused params
|
|
true); # Create path if it does not exist
|
|
|
|
executeTest($strCommand . " ${strSourceFile}",
|
|
{iExpectedExitStatus => ERROR_ARCHIVE_DUPLICATE, oLogTest => $oLogTest});
|
|
|
|
if ($bArchiveAsync)
|
|
{
|
|
my $strDuplicateWal =
|
|
($bRemote ? BackRestTestCommon_LocalPathGet() : BackRestTestCommon_RepoPathGet()) .
|
|
"/archive/${strStanza}/out/${strArchiveFile}-4518a0fdf41d796760b384a358270d4682589820";
|
|
|
|
unlink ($strDuplicateWal)
|
|
or confess "unable to remove duplicate WAL segment created for testing: ${strDuplicateWal}";
|
|
}
|
|
}
|
|
|
|
# Build the archive name to check for at the destination
|
|
my $strArchiveCheck = "9.3-1/${strArchiveFile}-${strArchiveChecksum}";
|
|
|
|
if ($bCompress)
|
|
{
|
|
$strArchiveCheck .= '.gz';
|
|
}
|
|
|
|
my $oWait = waitInit(5);
|
|
my $bFound = false;
|
|
|
|
do
|
|
{
|
|
$bFound = $oFile->exists(PATH_BACKUP_ARCHIVE, $strArchiveCheck);
|
|
}
|
|
while (!$bFound && waitMore($oWait));
|
|
|
|
if (!$bFound)
|
|
{
|
|
confess 'unable to find ' . $strArchiveCheck;
|
|
}
|
|
}
|
|
|
|
# Might be nice to add tests for .backup files here (but this is already tested in full backup)
|
|
}
|
|
|
|
if (defined($oLogTest))
|
|
{
|
|
$oLogTest->supplementalAdd($oFile->pathGet(PATH_BACKUP_ARCHIVE) . '/archive.info', $bRemote);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
$bCreate = true;
|
|
}
|
|
|
|
if (BackRestTestCommon_Cleanup(\$oLogTest))
|
|
{
|
|
&log(INFO, 'cleanup');
|
|
BackRestTestBackup_Drop(true);
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------------------------------------------------------------------------------
|
|
# Test archive-stop
|
|
#-------------------------------------------------------------------------------------------------------------------------------
|
|
$strThisTest = 'archive-stop';
|
|
|
|
if ($strTest eq 'all' || $strTest eq $strThisTest)
|
|
{
|
|
$iRun = 0;
|
|
$bCreate = true;
|
|
my $oFile;
|
|
|
|
&log(INFO, "Test ${strThisTest}\n");
|
|
|
|
for (my $bRemote = false; $bRemote <= true; $bRemote++)
|
|
{
|
|
for (my $bCompress = false; $bCompress <= true; $bCompress++)
|
|
{
|
|
for (my $iError = 0; $iError <= $bRemote; $iError++)
|
|
{
|
|
# Increment the run, log, and decide whether this unit test should be run
|
|
if (!BackRestTestCommon_Run(++$iRun,
|
|
"rmt ${bRemote}, cmp ${bCompress}" .
|
|
', error ' . ($iError ? 'connect' : 'version'),
|
|
$iThreadMax == 1 ? $strModule : undef,
|
|
$iThreadMax == 1 ? $strThisTest: undef,
|
|
\$oLogTest)) {next}
|
|
|
|
# Create the file object
|
|
if ($bCreate)
|
|
{
|
|
$oFile = (new BackRest::File
|
|
(
|
|
$strStanza,
|
|
BackRestTestCommon_RepoPathGet(),
|
|
$bRemote ? 'backup' : undef,
|
|
$bRemote ? $oRemote : $oLocal
|
|
))->clone();
|
|
|
|
$bCreate = false;
|
|
}
|
|
|
|
# Create the test directory
|
|
BackRestTestBackup_Create($bRemote, false);
|
|
|
|
BackRestTestCommon_ConfigCreate(DB,
|
|
($bRemote ? BACKUP : undef),
|
|
$bCompress,
|
|
undef, # checksum
|
|
undef, # hardlink
|
|
undef, # thread-max
|
|
true, # archive-async
|
|
undef);
|
|
|
|
BackRestTestCommon_ConfigCreate(BACKUP,
|
|
($bRemote ? DB : undef));
|
|
|
|
# Helper function to push archive logs
|
|
sub archivePush
|
|
{
|
|
my $oLogTest = shift;
|
|
my $oFile = shift;
|
|
my $strXlogPath = shift;
|
|
my $strArchiveTestFile = shift;
|
|
my $iArchiveNo = shift;
|
|
my $iExpectedError = shift;
|
|
|
|
my $strSourceFile = "${strXlogPath}/" . uc(sprintf('0000000100000001%08x', $iArchiveNo));
|
|
|
|
$oFile->copy(PATH_DB_ABSOLUTE, $strArchiveTestFile, # Source file
|
|
PATH_DB_ABSOLUTE, $strSourceFile, # Destination file
|
|
false, # Source is not compressed
|
|
false, # Destination is not compressed
|
|
undef, undef, undef, # Unused params
|
|
true); # Create path if it does not exist
|
|
|
|
my $strCommand = BackRestTestCommon_CommandMainGet() . ' --config=' . BackRestTestCommon_DbPathGet() .
|
|
'/pg_backrest.conf --archive-max-mb=24 --no-fork --stanza=db archive-push' .
|
|
(defined($iExpectedError) && $iExpectedError == ERROR_HOST_CONNECT ?
|
|
" --backup-host=bogus" : '');
|
|
|
|
executeTest($strCommand . " ${strSourceFile}",
|
|
{iExpectedExitStatus => $iExpectedError, oLogTest => $oLogTest});
|
|
}
|
|
|
|
# Push a WAL segment
|
|
archivePush($oLogTest, $oFile, $strXlogPath, $strArchiveTestFile, 1);
|
|
|
|
# load the archive info file so it can be munged for testing
|
|
my $strInfoFile = $oFile->pathGet(PATH_BACKUP_ARCHIVE, ARCHIVE_INFO_FILE);
|
|
my %oInfo;
|
|
BackRestTestCommon_iniLoad($strInfoFile, \%oInfo, $bRemote);
|
|
my $strDbVersion = $oInfo{&INFO_ARCHIVE_SECTION_DB}{&INFO_ARCHIVE_KEY_DB_VERSION};
|
|
my $ullDbSysId = $oInfo{&INFO_ARCHIVE_SECTION_DB}{&INFO_ARCHIVE_KEY_DB_SYSTEM_ID};
|
|
|
|
# Break the database version
|
|
if ($iError == 0)
|
|
{
|
|
$oInfo{&INFO_ARCHIVE_SECTION_DB}{&INFO_ARCHIVE_KEY_DB_VERSION} = '8.0';
|
|
BackRestTestCommon_iniSave($strInfoFile, \%oInfo, $bRemote, true);
|
|
}
|
|
|
|
# Push two more segments with errors to exceed archive-max-mb
|
|
archivePush($oLogTest, $oFile, $strXlogPath, $strArchiveTestFile, 2,
|
|
$iError ? ERROR_HOST_CONNECT : ERROR_ARCHIVE_MISMATCH);
|
|
|
|
archivePush($oLogTest, $oFile, $strXlogPath, $strArchiveTestFile, 3,
|
|
$iError ? ERROR_HOST_CONNECT : ERROR_ARCHIVE_MISMATCH);
|
|
|
|
# Now this segment will get dropped
|
|
archivePush($oLogTest, $oFile, $strXlogPath, $strArchiveTestFile, 4);
|
|
|
|
# Fix the database version
|
|
if ($iError == 0)
|
|
{
|
|
$oInfo{&INFO_ARCHIVE_SECTION_DB}{&INFO_ARCHIVE_KEY_DB_VERSION} = '9.3';
|
|
BackRestTestCommon_iniSave($strInfoFile, \%oInfo, $bRemote, true);
|
|
}
|
|
|
|
# Remove the stop file
|
|
my $strStopFile = ($bRemote ? BackRestTestCommon_LocalPathGet() : BackRestTestCommon_RepoPathGet()) .
|
|
'/lock/db-archive.stop';
|
|
|
|
unlink($strStopFile)
|
|
or die "unable to remove stop file ${strStopFile}";
|
|
|
|
# Push two more segments - only #4 should be missing from the archive at the end
|
|
archivePush($oLogTest, $oFile, $strXlogPath, $strArchiveTestFile, 5);
|
|
archivePush($oLogTest, $oFile, $strXlogPath, $strArchiveTestFile, 6);
|
|
}
|
|
}
|
|
|
|
$bCreate = true;
|
|
}
|
|
|
|
if (BackRestTestCommon_Cleanup(\$oLogTest))
|
|
{
|
|
&log(INFO, 'cleanup');
|
|
BackRestTestBackup_Drop(true);
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------------------------------------------------------------------------------
|
|
# Test archive-get
|
|
#-------------------------------------------------------------------------------------------------------------------------------
|
|
$strThisTest = 'archive-get';
|
|
|
|
if ($strTest eq 'all' || $strTest eq $strThisTest)
|
|
{
|
|
$iRun = 0;
|
|
$bCreate = true;
|
|
my $oFile;
|
|
|
|
&log(INFO, "Test ${strThisTest}\n");
|
|
|
|
for (my $bRemote = false; $bRemote <= true; $bRemote++)
|
|
{
|
|
for (my $bCompress = false; $bCompress <= true; $bCompress++)
|
|
{
|
|
for (my $bExists = false; $bExists <= true; $bExists++)
|
|
{
|
|
# Increment the run, log, and decide whether this unit test should be run
|
|
if (!BackRestTestCommon_Run(++$iRun,
|
|
"rmt ${bRemote}, cmp ${bCompress}, exists ${bExists}",
|
|
$iThreadMax == 1 ? $strModule : undef,
|
|
$iThreadMax == 1 ? $strThisTest: undef,
|
|
\$oLogTest)) {next}
|
|
|
|
# Create the test directory
|
|
if ($bCreate)
|
|
{
|
|
# Create the file object
|
|
$oFile = (BackRest::File->new
|
|
(
|
|
$strStanza,
|
|
BackRestTestCommon_RepoPathGet(),
|
|
$bRemote ? 'backup' : undef,
|
|
$bRemote ? $oRemote : $oLocal
|
|
))->clone();
|
|
|
|
BackRestTestBackup_Create($bRemote, false);
|
|
BackRestTestBackup_Init($bRemote, $oFile, true, $oLogTest);
|
|
|
|
BackRestTestCommon_ConfigCreate(DB,
|
|
($bRemote ? BACKUP : undef));
|
|
|
|
BackRestTestCommon_ConfigCreate(BACKUP,
|
|
($bRemote ? DB : undef));
|
|
|
|
# Create the db/common/pg_xlog directory
|
|
BackRestTestCommon_PathCreate($strXlogPath);
|
|
|
|
$bCreate = false;
|
|
}
|
|
|
|
BackRestTestCommon_ConfigCreate('db', # local
|
|
($bRemote ? BACKUP : undef), # remote
|
|
$bCompress, # compress
|
|
undef, # checksum
|
|
undef, # hardlink
|
|
undef, # thread-max
|
|
undef, # archive-async
|
|
undef); # compress-async
|
|
|
|
my $strCommand = BackRestTestCommon_CommandMainGet() . ' --config=' . BackRestTestCommon_DbPathGet() .
|
|
'/pg_backrest.conf --stanza=db archive-get';
|
|
|
|
|
|
executeTest($strCommand . " 000000010000000100000001 ${strXlogPath}/000000010000000100000001",
|
|
{iExpectedExitStatus => ERROR_FILE_MISSING, oLogTest => $oLogTest});
|
|
|
|
# Create the archive info file
|
|
if ($bRemote)
|
|
{
|
|
executeTest("chmod g+r,g+x " . BackRestTestCommon_RepoPathGet(),
|
|
{bRemote => true});
|
|
}
|
|
|
|
executeTest('mkdir -p -m 770 ' . $oFile->pathGet(PATH_BACKUP_ARCHIVE),
|
|
{bRemote => $bRemote});
|
|
(new BackRest::ArchiveInfo($oFile->pathGet(PATH_BACKUP_ARCHIVE)))->check('9.3', 1234567890123456789);
|
|
|
|
if (defined($oLogTest))
|
|
{
|
|
$oLogTest->supplementalAdd($oFile->pathGet(PATH_BACKUP_ARCHIVE) . '/archive.info');
|
|
}
|
|
|
|
if ($bRemote)
|
|
{
|
|
executeTest("chmod g-r,g-x " . BackRestTestCommon_RepoPathGet(),
|
|
{bRemote => true});
|
|
}
|
|
|
|
if ($bExists)
|
|
{
|
|
# Loop through archive files
|
|
my $strArchiveFile;
|
|
|
|
for (my $iArchiveNo = 1; $iArchiveNo <= $iArchiveMax; $iArchiveNo++)
|
|
{
|
|
# Construct the archive filename
|
|
if ($iArchiveNo > 255)
|
|
{
|
|
confess 'backup total * archive total cannot be greater than 255';
|
|
}
|
|
|
|
$strArchiveFile = uc(sprintf('0000000100000001%08x', $iArchiveNo));
|
|
|
|
&log(INFO, ' archive ' .sprintf('%02x', $iArchiveNo) .
|
|
" - ${strArchiveFile}");
|
|
|
|
my $strSourceFile = "${strArchiveFile}-${strArchiveChecksum}";
|
|
|
|
if ($bCompress)
|
|
{
|
|
$strSourceFile .= '.gz';
|
|
}
|
|
|
|
$oFile->copy(PATH_DB_ABSOLUTE, $strArchiveTestFile, # Source file
|
|
PATH_BACKUP_ARCHIVE, "9.3-1/${strSourceFile}", # Destination file
|
|
false, # Source is not compressed
|
|
$bCompress, # Destination compress based on test
|
|
undef, undef, undef, # Unused params
|
|
true); # Create path if it does not exist
|
|
|
|
my $strDestinationFile = "${strXlogPath}/${strArchiveFile}";
|
|
|
|
executeTest($strCommand . " ${strArchiveFile} ${strDestinationFile}", {oLogTest => $oLogTest});
|
|
|
|
# Check that the destination file exists
|
|
if ($oFile->exists(PATH_DB_ABSOLUTE, $strDestinationFile))
|
|
{
|
|
if ($oFile->hash(PATH_DB_ABSOLUTE, $strDestinationFile) ne $strArchiveChecksum)
|
|
{
|
|
confess "archive file hash does not match ${strArchiveChecksum}";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
confess 'archive file is not in destination';
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BackRestTestBackup_Stop();
|
|
|
|
executeTest($strCommand . " 000000090000000900000009 ${strXlogPath}/RECOVERYXLOG",
|
|
{iExpectedExitStatus => ERROR_STOP, oLogTest => $oLogTest});
|
|
|
|
BackRestTestBackup_Start();
|
|
|
|
executeTest($strCommand . " 000000090000000900000009 ${strXlogPath}/RECOVERYXLOG",
|
|
{iExpectedExitStatus => 1, oLogTest => $oLogTest});
|
|
}
|
|
|
|
$bCreate = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (BackRestTestCommon_Cleanup(\$oLogTest))
|
|
{
|
|
&log(INFO, 'cleanup');
|
|
BackRestTestBackup_Drop(true);
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------------------------------------------------------------------------------
|
|
# Test expire
|
|
#-------------------------------------------------------------------------------------------------------------------------------
|
|
$strThisTest = 'expire';
|
|
|
|
if ($strTest eq 'all' || $strTest eq $strThisTest)
|
|
{
|
|
$iRun = 0;
|
|
my $oFile;
|
|
|
|
&log(INFO, "Test ${strThisTest}\n");
|
|
|
|
if (BackRestTestCommon_Run(++$iRun,
|
|
"local",
|
|
$iThreadMax == 1 ? $strModule : undef,
|
|
$iThreadMax == 1 ? $strThisTest: undef,
|
|
\$oLogTest))
|
|
{
|
|
# Create the file object
|
|
$oFile = (BackRest::File->new
|
|
(
|
|
$strStanza,
|
|
BackRestTestCommon_RepoPathGet(),
|
|
'db',
|
|
$oLocal
|
|
))->clone();
|
|
|
|
# Create the repo
|
|
BackRestTestCommon_Create();
|
|
BackRestTestCommon_CreateRepo();
|
|
|
|
# Create backup config
|
|
BackRestTestCommon_ConfigCreate('backup', # local
|
|
undef, # remote
|
|
false, # compress
|
|
undef, # checksum
|
|
undef, # hardlink
|
|
$iThreadMax, # thread-max
|
|
undef, # archive-async
|
|
undef); # compress-async
|
|
|
|
# Create the test object
|
|
my $oExpireTest = new BackRestTest::ExpireCommonTest($oFile, $oLogTest);
|
|
|
|
$oExpireTest->stanzaCreate($strStanza, '9.2');
|
|
use constant SECONDS_PER_DAY => 86400;
|
|
my $lBaseTime = time() - (SECONDS_PER_DAY * 56);
|
|
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
my $strDescription = 'Nothing to expire';
|
|
|
|
$oExpireTest->backupCreate($strStanza, BACKUP_TYPE_FULL, $lBaseTime += SECONDS_PER_DAY);
|
|
$oExpireTest->backupCreate($strStanza, BACKUP_TYPE_INCR, $lBaseTime += SECONDS_PER_DAY, 246);
|
|
|
|
$oExpireTest->process($strStanza, 1, 1, BACKUP_TYPE_FULL, 1, $strDescription);
|
|
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
$strDescription = 'Expire oldest full backup, archive expire falls on segment major boundary';
|
|
|
|
$oExpireTest->backupCreate($strStanza, BACKUP_TYPE_FULL, $lBaseTime += SECONDS_PER_DAY);
|
|
$oExpireTest->process($strStanza, 1, 1, BACKUP_TYPE_FULL, 1, $strDescription);
|
|
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
$strDescription = 'Expire oldest diff backup';
|
|
|
|
$oExpireTest->backupCreate($strStanza, BACKUP_TYPE_DIFF, $lBaseTime += SECONDS_PER_DAY);
|
|
$oExpireTest->backupCreate($strStanza, BACKUP_TYPE_DIFF, $lBaseTime += SECONDS_PER_DAY, 256);
|
|
$oExpireTest->process($strStanza, 1, 1, BACKUP_TYPE_FULL, 1, $strDescription);
|
|
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
$strDescription = 'Expire oldest full backup, archive expire does not fall on major segment boundary';
|
|
|
|
$oExpireTest->backupCreate($strStanza, BACKUP_TYPE_FULL, $lBaseTime += SECONDS_PER_DAY);
|
|
$oExpireTest->backupCreate($strStanza, BACKUP_TYPE_DIFF, $lBaseTime += SECONDS_PER_DAY, undef, 0);
|
|
$oExpireTest->backupCreate($strStanza, BACKUP_TYPE_INCR, $lBaseTime += SECONDS_PER_DAY, undef, 0);
|
|
$oExpireTest->process($strStanza, 1, 1, BACKUP_TYPE_DIFF, 1, $strDescription);
|
|
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
$strDescription = 'Expire oldest diff backup (cascade to incr)';
|
|
|
|
$oExpireTest->backupCreate($strStanza, BACKUP_TYPE_DIFF, $lBaseTime += SECONDS_PER_DAY);
|
|
$oExpireTest->process($strStanza, 1, 1, BACKUP_TYPE_DIFF, 1, $strDescription);
|
|
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
$strDescription = 'Expire archive based on newest incr backup';
|
|
|
|
$oExpireTest->backupCreate($strStanza, BACKUP_TYPE_INCR, $lBaseTime += SECONDS_PER_DAY);
|
|
$oExpireTest->process($strStanza, 1, 1, BACKUP_TYPE_INCR, 1, $strDescription);
|
|
|
|
# Cleanup
|
|
if (BackRestTestCommon_Cleanup(\$oLogTest))
|
|
{
|
|
&log(INFO, 'cleanup');
|
|
BackRestTestBackup_Drop(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------------------------------------------------------------------------------
|
|
# Test synthetic
|
|
#
|
|
# Check the backup and restore functionality using synthetic data.
|
|
#-------------------------------------------------------------------------------------------------------------------------------
|
|
$strThisTest = 'synthetic';
|
|
|
|
if ($strTest eq 'all' || $strTest eq $strThisTest)
|
|
{
|
|
$iRun = 0;
|
|
|
|
&log(INFO, "Test ${strModule} - ${strThisTest}\n");
|
|
|
|
for (my $bRemote = false; $bRemote <= true; $bRemote++)
|
|
{
|
|
for (my $bCompress = false; $bCompress <= true; $bCompress++)
|
|
{
|
|
for (my $bHardlink = false; $bHardlink <= true; $bHardlink++)
|
|
{
|
|
# Increment the run, log, and decide whether this unit test should be run
|
|
if (!BackRestTestCommon_Run(++$iRun,
|
|
"rmt ${bRemote}, cmp ${bCompress}, hardlink ${bHardlink}",
|
|
$iThreadMax == 1 ? $strModule : undef,
|
|
$iThreadMax == 1 ? $strThisTest: undef,
|
|
\$oLogTest)) {next}
|
|
|
|
# Determine if this is a neutral test, i.e. we only want to do it once for local and once for remote. Neutral means
|
|
# that options such as compression and hardlinks are disabled
|
|
my $bNeutralTest = !$bCompress && !$bHardlink;
|
|
|
|
# Get base time
|
|
my $lTime = time() - 100000;
|
|
|
|
# Build the manifest
|
|
my %oManifest;
|
|
|
|
$oManifest{&INI_SECTION_BACKREST}{&INI_KEY_VERSION} = BACKREST_VERSION;
|
|
$oManifest{&MANIFEST_SECTION_BACKUP_OPTION}{&MANIFEST_KEY_ARCHIVE_CHECK} = JSON::PP::true;
|
|
$oManifest{&MANIFEST_SECTION_BACKUP_OPTION}{&MANIFEST_KEY_ARCHIVE_COPY} = JSON::PP::true;
|
|
$oManifest{&MANIFEST_SECTION_BACKUP_OPTION}{&MANIFEST_KEY_COMPRESS} = $bCompress ? JSON::PP::true : JSON::PP::false;
|
|
$oManifest{&MANIFEST_SECTION_BACKUP_OPTION}{&MANIFEST_KEY_HARDLINK} = $bHardlink ? JSON::PP::true : JSON::PP::false;
|
|
$oManifest{&MANIFEST_SECTION_BACKUP_OPTION}{&MANIFEST_KEY_START_STOP} = JSON::PP::false;
|
|
|
|
$oManifest{&MANIFEST_SECTION_BACKUP_DB}{&MANIFEST_KEY_CATALOG} = 201306121;
|
|
$oManifest{&MANIFEST_SECTION_BACKUP_DB}{&MANIFEST_KEY_CONTROL} = 937;
|
|
$oManifest{&MANIFEST_SECTION_BACKUP_DB}{&MANIFEST_KEY_SYSTEM_ID} = 6156904820763115222;
|
|
$oManifest{&MANIFEST_SECTION_BACKUP_DB}{&MANIFEST_KEY_DB_VERSION} = '9.3';
|
|
|
|
# Create the test directory
|
|
BackRestTestBackup_Create($bRemote, false);
|
|
|
|
$oManifest{'backup:path'}{base}{&MANIFEST_SUBKEY_PATH} = BackRestTestCommon_DbCommonPathGet();
|
|
|
|
BackRestTestBackup_ManifestPathCreate(\%oManifest, 'base');
|
|
|
|
# Create the file object
|
|
my $oFile = new BackRest::File
|
|
(
|
|
$strStanza,
|
|
BackRestTestCommon_RepoPathGet(),
|
|
$bRemote ? 'backup' : undef,
|
|
$bRemote ? $oRemote : $oLocal
|
|
);
|
|
|
|
BackRestTestBackup_Init($bRemote, $oFile, true, $oLogTest, $iThreadMax);
|
|
|
|
BackRestTestBackup_ManifestFileCreate(\%oManifest, 'base', 'PG_VERSION', '9.3',
|
|
'e1f7a3a299f62225cba076fc6d3d6e677f303482', $lTime);
|
|
|
|
# Create base path
|
|
BackRestTestBackup_ManifestPathCreate(\%oManifest, 'base', 'base');
|
|
|
|
BackRestTestBackup_ManifestFileCreate(\%oManifest, 'base', 'base/base1.txt', 'BASE',
|
|
'a3b357a3e395e43fcfb19bb13f3c1b5179279593', $lTime);
|
|
|
|
# Create global path
|
|
BackRestTestBackup_ManifestPathCreate(\%oManifest, 'base', 'global');
|
|
|
|
BackRestTestBackup_ManifestFileCreate(\%oManifest, 'base', 'global/pg_control', '[replaceme]',
|
|
'56fe5780b8dca9705e0c22032a83828860a21235', $lTime - 100);
|
|
executeTest('cp ' . BackRestTestCommon_DataPathGet() . '/pg_control ' .
|
|
BackRestTestCommon_DbCommonPathGet() . '/global/pg_control');
|
|
utime($lTime - 100, $lTime - 100, BackRestTestCommon_DbCommonPathGet() . '/global/pg_control')
|
|
or confess &log(ERROR, "unable to set time");
|
|
$oManifest{'base:file'}{'global/pg_control'}{'size'} = 8192;
|
|
|
|
# Create tablespace path
|
|
BackRestTestBackup_ManifestPathCreate(\%oManifest, 'base', 'pg_tblspc');
|
|
|
|
# Create db config
|
|
BackRestTestCommon_ConfigCreate('db', # local
|
|
$bRemote ? BACKUP : undef, # remote
|
|
$bCompress, # compress
|
|
true, # checksum
|
|
$bRemote ? undef : $bHardlink, # hardlink
|
|
$iThreadMax); # thread-max
|
|
|
|
# Create backup config
|
|
if ($bRemote)
|
|
{
|
|
BackRestTestCommon_ConfigCreate('backup', # local
|
|
DB, # remote
|
|
$bCompress, # compress
|
|
true, # checksum
|
|
$bHardlink, # hardlink
|
|
$iThreadMax); # thread-max
|
|
}
|
|
|
|
# Full backup
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
my $strType = 'full';
|
|
my $strOptionalParam = '--manifest-save-threshold=3';
|
|
my $strTestPoint;
|
|
|
|
if ($bNeutralTest && $bRemote)
|
|
{
|
|
$strOptionalParam .= ' --db-timeout=2';
|
|
|
|
if ($iThreadMax > 1)
|
|
{
|
|
$strTestPoint = TEST_KEEP_ALIVE;
|
|
}
|
|
}
|
|
|
|
BackRestTestBackup_ManifestLinkCreate(\%oManifest, 'base', 'link-test', '/test');
|
|
BackRestTestBackup_ManifestPathCreate(\%oManifest, 'base', 'path-test');
|
|
|
|
my $strFullBackup = BackRestTestBackup_BackupSynthetic(
|
|
$strType, $strStanza, \%oManifest, undef,
|
|
{strOptionalParam => $strOptionalParam, strTest => $strTestPoint, fTestDelay => 0});
|
|
|
|
# Test protocol timeout
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
if ($bNeutralTest && $bRemote)
|
|
{
|
|
BackRestTestBackup_BackupSynthetic(
|
|
$strType, $strStanza, \%oManifest, 'protocol timeout',
|
|
{strOptionalParam => '--db-timeout=1',
|
|
strTest => TEST_BACKUP_START,
|
|
fTestDelay => 1,
|
|
iExpectedExitStatus => ERROR_PROTOCOL_TIMEOUT});
|
|
|
|
# Remove the aborted backup so the next backup is not a resume
|
|
BackRestTestCommon_PathRemove(BackRestTestCommon_RepoPathGet() . "/temp/${strStanza}.tmp", $bRemote);
|
|
}
|
|
|
|
# Stop operations and make sure the correct error occurs
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
if ($bNeutralTest)
|
|
{
|
|
# Test a backup abort
|
|
BackRestTestBackup_BackupBegin($strType, $strStanza, 'abort backup - local',
|
|
{strTest => TEST_BACKUP_START, fTestDelay => 5, iExpectedExitStatus => ERROR_TERM});
|
|
|
|
BackRestTestBackup_Stop(undef, undef, true);
|
|
|
|
BackRestTestBackup_BackupEnd();
|
|
|
|
# Test global stop
|
|
BackRestTestBackup_BackupSynthetic($strType, $strStanza, undef, 'global stop',
|
|
{iExpectedExitStatus => ERROR_STOP});
|
|
|
|
# Test stanza stop
|
|
BackRestTestBackup_Stop($strStanza);
|
|
|
|
# This time a warning should be generated
|
|
BackRestTestBackup_Stop($strStanza);
|
|
|
|
BackRestTestBackup_BackupSynthetic($strType, $strStanza, undef, 'stanza stop',
|
|
{iExpectedExitStatus => ERROR_STOP});
|
|
|
|
BackRestTestBackup_Start($strStanza);
|
|
BackRestTestBackup_Start();
|
|
|
|
# This time a warning should be generated
|
|
BackRestTestBackup_Start();
|
|
|
|
# If the backup is remote then test remote stops
|
|
if ($bRemote)
|
|
{
|
|
BackRestTestBackup_BackupBegin($strType, $strStanza, 'abort backup - remote',
|
|
{strTest => TEST_BACKUP_START, fTestDelay => 5,
|
|
iExpectedExitStatus => ERROR_TERM});
|
|
|
|
BackRestTestBackup_Stop(undef, $bRemote, true);
|
|
|
|
BackRestTestBackup_BackupEnd();
|
|
|
|
BackRestTestBackup_BackupSynthetic($strType, $strStanza, undef, 'global stop',
|
|
{iExpectedExitStatus => ERROR_STOP});
|
|
|
|
BackRestTestBackup_Start(undef, $bRemote, true);
|
|
}
|
|
}
|
|
|
|
# Cleanup any garbage left in the temp backup path
|
|
executeTest('rm -rf ' . BackRestTestCommon_RepoPathGet() . "/temp/${strStanza}.tmp", {bRemote => $bRemote});
|
|
|
|
# Backup Info
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
BackRestTestBackup_Info($strStanza, undef, false);
|
|
BackRestTestBackup_Info($strStanza, INFO_OUTPUT_JSON, false);
|
|
|
|
# Resume Full Backup
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
$strType = 'full';
|
|
|
|
my $strTmpPath = BackRestTestCommon_RepoPathGet() . "/temp/${strStanza}.tmp";
|
|
|
|
BackRestTestCommon_PathMove(BackRestTestCommon_RepoPathGet() . "/backup/${strStanza}/${strFullBackup}",
|
|
$strTmpPath, $bRemote);
|
|
|
|
my $oMungeManifest = BackRestTestCommon_manifestLoad("$strTmpPath/backup.manifest", $bRemote);
|
|
$oMungeManifest->remove('base:file', 'PG_VERSION', 'checksum');
|
|
BackRestTestCommon_manifestSave("$strTmpPath/backup.manifest", $oMungeManifest, $bRemote);
|
|
|
|
# Create a temp file in backup temp root to be sure it's deleted correctly
|
|
executeTest("touch ${strTmpPath}/file.tmp" . ($bCompress ? '.gz' : ''),
|
|
{bRemote => $bRemote});
|
|
|
|
$strFullBackup = BackRestTestBackup_BackupSynthetic($strType, $strStanza, \%oManifest, 'resume',
|
|
{strTest => TEST_BACKUP_RESUME});
|
|
|
|
# Restore - tests various mode, extra files/paths, missing files/paths
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
my $bDelta = true;
|
|
my $bForce = false;
|
|
|
|
# Create a path and file that are not in the manifest
|
|
BackRestTestBackup_PathCreate(\%oManifest, 'base', 'deleteme');
|
|
BackRestTestBackup_FileCreate(\%oManifest, 'base', 'deleteme/deleteme.txt', 'DELETEME');
|
|
|
|
# Change path mode
|
|
BackRestTestBackup_PathMode(\%oManifest, 'base', 'base', '0777');
|
|
|
|
# Change an existing link to the wrong directory
|
|
BackRestTestBackup_FileRemove(\%oManifest, 'base', 'link-test');
|
|
BackRestTestBackup_LinkCreate(\%oManifest, 'base', 'link-test', '/wrong');
|
|
|
|
# Remove an path
|
|
BackRestTestBackup_PathRemove(\%oManifest, 'base', 'path-test');
|
|
|
|
# Remove a file
|
|
BackRestTestBackup_FileRemove(\%oManifest, 'base', 'PG_VERSION');
|
|
BackRestTestBackup_Restore($oFile, $strFullBackup, $strStanza, $bRemote, \%oManifest, undef, $bDelta, $bForce,
|
|
undef, undef, undef, undef, undef, undef,
|
|
'add and delete files');
|
|
|
|
# Various broken info tests
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
$strType = 'incr';
|
|
BackRestTestBackup_ManifestReference(\%oManifest, $strFullBackup);
|
|
|
|
my $strInfoFile = BackRestTestCommon_RepoPathGet . "/backup/${strStanza}/backup.info";
|
|
my %oInfo;
|
|
BackRestTestCommon_iniLoad($strInfoFile, \%oInfo, $bRemote);
|
|
|
|
# Break the database version
|
|
my $strDbVersion = $oInfo{'db'}{&MANIFEST_KEY_DB_VERSION};
|
|
|
|
$oInfo{db}{&MANIFEST_KEY_DB_VERSION} = '8.0';
|
|
BackRestTestCommon_iniSave($strInfoFile, \%oInfo, $bRemote, true);
|
|
|
|
BackRestTestBackup_BackupSynthetic($strType, $strStanza, undef, 'invalid database version',
|
|
{iExpectedExitStatus => ERROR_BACKUP_MISMATCH});
|
|
$oInfo{db}{&MANIFEST_KEY_DB_VERSION} = $strDbVersion;
|
|
|
|
# Break the database system id
|
|
my $ullDbSysId = $oInfo{'db'}{&MANIFEST_KEY_SYSTEM_ID};
|
|
$oInfo{db}{&MANIFEST_KEY_SYSTEM_ID} = 6999999999999999999;
|
|
BackRestTestCommon_iniSave($strInfoFile, \%oInfo, $bRemote, true);
|
|
|
|
BackRestTestBackup_BackupSynthetic($strType, $strStanza, undef, 'invalid system id',
|
|
{iExpectedExitStatus => ERROR_BACKUP_MISMATCH});
|
|
$oInfo{db}{&MANIFEST_KEY_SYSTEM_ID} = $ullDbSysId;
|
|
|
|
# Break the control version
|
|
my $iControlVersion = $oInfo{'db'}{&MANIFEST_KEY_CONTROL};
|
|
$oInfo{db}{&MANIFEST_KEY_CONTROL} = 842;
|
|
BackRestTestCommon_iniSave($strInfoFile, \%oInfo, $bRemote, true);
|
|
|
|
BackRestTestBackup_BackupSynthetic($strType, $strStanza, undef, 'invalid control version',
|
|
{iExpectedExitStatus => ERROR_BACKUP_MISMATCH});
|
|
$oInfo{db}{&MANIFEST_KEY_CONTROL} = $iControlVersion;
|
|
|
|
# Break the catalog version
|
|
my $iCatalogVersion = $oInfo{'db'}{&MANIFEST_KEY_CATALOG};
|
|
$oInfo{db}{&MANIFEST_KEY_CATALOG} = 197208141;
|
|
BackRestTestCommon_iniSave($strInfoFile, \%oInfo, $bRemote, true);
|
|
|
|
BackRestTestBackup_BackupSynthetic($strType, $strStanza, undef, 'invalid catalog version',
|
|
{iExpectedExitStatus => ERROR_BACKUP_MISMATCH});
|
|
|
|
# Fix up info file for next test
|
|
$oInfo{db}{&MANIFEST_KEY_CATALOG} = $iCatalogVersion;
|
|
BackRestTestCommon_iniSave($strInfoFile, \%oInfo, $bRemote, true);
|
|
|
|
# Incr backup
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
$strType = 'incr';
|
|
|
|
# Add tablespace 1
|
|
BackRestTestBackup_ManifestTablespaceCreate(\%oManifest, 1);
|
|
|
|
BackRestTestBackup_ManifestFileCreate(\%oManifest, "tablespace/1", 'tablespace1.txt', 'TBLSPC1',
|
|
'd85de07d6421d90aa9191c11c889bfde43680f0f', $lTime);
|
|
BackRestTestBackup_ManifestFileCreate(\%oManifest, "base", 'badchecksum.txt', 'BADCHECKSUM',
|
|
'f927212cd08d11a42a666b2f04235398e9ceeb51', $lTime);
|
|
|
|
my $strBackup = BackRestTestBackup_BackupSynthetic($strType, $strStanza, \%oManifest, 'add tablespace 1');
|
|
|
|
# Resume Incr Backup
|
|
#
|
|
# Links are removed in the resume because it's easy to recreate them.
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
$strType = 'incr';
|
|
|
|
# Move database from backup to temp
|
|
$strTmpPath = BackRestTestCommon_RepoPathGet() . "/temp/${strStanza}.tmp";
|
|
|
|
BackRestTestCommon_PathMove(BackRestTestCommon_RepoPathGet() . "/backup/${strStanza}/${strBackup}",
|
|
$strTmpPath, $bRemote);
|
|
|
|
$oMungeManifest = BackRestTestCommon_manifestLoad("$strTmpPath/backup.manifest", $bRemote);
|
|
$oMungeManifest->set('base:file', 'badchecksum.txt', 'checksum', 'bogus');
|
|
BackRestTestCommon_manifestSave("$strTmpPath/backup.manifest", $oMungeManifest, $bRemote);
|
|
|
|
# Add tablespace 2
|
|
BackRestTestBackup_ManifestTablespaceCreate(\%oManifest, 2);
|
|
|
|
BackRestTestBackup_ManifestFileCreate(\%oManifest, "tablespace/2", 'tablespace2.txt', 'TBLSPC2',
|
|
'dc7f76e43c46101b47acc55ae4d593a9e6983578', $lTime);
|
|
|
|
$strBackup = BackRestTestBackup_BackupSynthetic($strType, $strStanza, \%oManifest, 'resume and add tablespace 2',
|
|
{strTest => TEST_BACKUP_RESUME});
|
|
|
|
# Resume Diff Backup
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
$strType = 'diff';
|
|
|
|
$strTmpPath = BackRestTestCommon_RepoPathGet() . "/temp/${strStanza}.tmp";
|
|
|
|
BackRestTestCommon_PathMove(BackRestTestCommon_RepoPathGet() . "/backup/${strStanza}/${strBackup}",
|
|
$strTmpPath, $bRemote);
|
|
|
|
$strBackup = BackRestTestBackup_BackupSynthetic($strType, $strStanza, \%oManifest, 'cannot resume - new diff',
|
|
{strTest => TEST_BACKUP_NORESUME});
|
|
|
|
# Resume Diff Backup
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
$strType = 'diff';
|
|
|
|
$strTmpPath = BackRestTestCommon_RepoPathGet() . "/temp/${strStanza}.tmp";
|
|
|
|
BackRestTestCommon_PathMove(BackRestTestCommon_RepoPathGet() . "/backup/${strStanza}/${strBackup}",
|
|
$strTmpPath, $bRemote);
|
|
|
|
$strBackup = BackRestTestBackup_BackupSynthetic($strType, $strStanza, \%oManifest, 'cannot resume - disabled',
|
|
{strTest => TEST_BACKUP_NORESUME, strOptionalParam => '--no-resume'});
|
|
|
|
# Restore
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
$bDelta = false;
|
|
|
|
# Fail on used path
|
|
BackRestTestBackup_Restore($oFile, $strBackup, $strStanza, $bRemote, \%oManifest, undef, $bDelta, $bForce,
|
|
undef, undef, undef, undef, undef, undef,
|
|
'fail on used path', ERROR_RESTORE_PATH_NOT_EMPTY);
|
|
# Fail on undef format
|
|
BackRestTestBackup_ManifestMunge($oFile, $bRemote, $strBackup, INI_SECTION_BACKREST, INI_KEY_FORMAT, undef, undef);
|
|
|
|
BackRestTestBackup_Restore($oFile, $strBackup, $strStanza, $bRemote, \%oManifest, undef, $bDelta, $bForce,
|
|
undef, undef, undef, undef, undef, undef,
|
|
'fail on undef format', ERROR_FORMAT);
|
|
|
|
# Fail on mismatch format
|
|
BackRestTestBackup_ManifestMunge($oFile, $bRemote, $strBackup, INI_SECTION_BACKREST, INI_KEY_FORMAT, undef, 0);
|
|
|
|
BackRestTestBackup_Restore($oFile, $strBackup, $strStanza, $bRemote, \%oManifest, undef, $bDelta, $bForce,
|
|
undef, undef, undef, undef, undef, undef,
|
|
'fail on mismatch format', ERROR_FORMAT);
|
|
|
|
BackRestTestBackup_ManifestMunge($oFile, $bRemote, $strBackup, INI_SECTION_BACKREST, INI_KEY_FORMAT, undef,
|
|
BACKREST_FORMAT);
|
|
|
|
# Remap the base path
|
|
my %oRemapHash;
|
|
$oRemapHash{base} = BackRestTestCommon_DbCommonPathGet(2);
|
|
$oRemapHash{1} = BackRestTestCommon_DbTablespacePathGet(1, 2);
|
|
$oRemapHash{2} = BackRestTestCommon_DbTablespacePathGet(2, 2);
|
|
|
|
BackRestTestBackup_Restore($oFile, $strBackup, $strStanza, $bRemote, \%oManifest, \%oRemapHash, $bDelta, $bForce,
|
|
undef, undef, undef, undef, undef, undef,
|
|
'remap all paths');
|
|
|
|
# Incr Backup
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
$strType = 'incr';
|
|
BackRestTestBackup_ManifestReference(\%oManifest, $strBackup);
|
|
|
|
BackRestTestBackup_ManifestFileCreate(\%oManifest, 'base', 'base/base2.txt', 'BASE2',
|
|
'09b5e31766be1dba1ec27de82f975c1b6eea2a92', $lTime);
|
|
|
|
BackRestTestBackup_ManifestTablespaceDrop(\%oManifest, 1, 2);
|
|
|
|
BackRestTestBackup_ManifestFileCreate(\%oManifest, "tablespace/2", 'tablespace2b.txt', 'TBLSPC2B',
|
|
'e324463005236d83e6e54795dbddd20a74533bf3', $lTime);
|
|
|
|
# Munge the version to make sure it gets corrected on the next run
|
|
BackRestTestBackup_ManifestMunge($oFile, $bRemote, $strBackup, INI_SECTION_BACKREST, INI_KEY_VERSION, undef,
|
|
'0.00');
|
|
|
|
$strBackup = BackRestTestBackup_BackupSynthetic($strType, $strStanza, \%oManifest, 'add files and remove tablespace 2');
|
|
|
|
# Incr Backup
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
$strType = 'incr';
|
|
BackRestTestBackup_ManifestReference(\%oManifest, $strBackup);
|
|
|
|
BackRestTestBackup_ManifestFileCreate(\%oManifest, 'base', 'base/base1.txt', 'BASEUPDT',
|
|
'9a53d532e27785e681766c98516a5e93f096a501', $lTime);
|
|
|
|
$strBackup = BackRestTestBackup_BackupSynthetic($strType, $strStanza, \%oManifest, 'update files');
|
|
|
|
# Diff Backup
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
$strType = 'diff';
|
|
BackRestTestBackup_ManifestReference(\%oManifest, $strFullBackup, true);
|
|
|
|
$strBackup = BackRestTestBackup_BackupSynthetic($strType, $strStanza, \%oManifest, 'no updates');
|
|
|
|
# Incr Backup
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
$strType = 'incr';
|
|
BackRestTestBackup_ManifestReference(\%oManifest, $strBackup);
|
|
|
|
BackRestTestBackup_BackupBegin($strType, $strStanza, "remove files - but won't affect manifest",
|
|
{strTest => TEST_MANIFEST_BUILD, fTestDelay => 1});
|
|
|
|
BackRestTestBackup_FileRemove(\%oManifest, 'base', 'base/base1.txt');
|
|
|
|
$strBackup = BackRestTestBackup_BackupEnd(\%oManifest);
|
|
|
|
# Diff Backup
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
BackRestTestBackup_ManifestReference(\%oManifest, $strFullBackup, true);
|
|
|
|
$strType = 'diff';
|
|
|
|
BackRestTestBackup_ManifestFileRemove(\%oManifest, 'base', 'base/base1.txt');
|
|
|
|
BackRestTestBackup_ManifestFileRemove(\%oManifest, "tablespace/2", 'tablespace2b.txt', true);
|
|
BackRestTestBackup_ManifestFileCreate(\%oManifest, "tablespace/2", 'tablespace2c.txt', 'TBLSPC2C',
|
|
'ad7df329ab97a1e7d35f1ff0351c079319121836', $lTime);
|
|
|
|
BackRestTestBackup_BackupBegin($strType, $strStanza, 'remove files during backup',
|
|
{strTest => TEST_MANIFEST_BUILD, fTestDelay => 1});
|
|
|
|
BackRestTestBackup_ManifestFileCreate(\%oManifest, "tablespace/2", 'tablespace2c.txt', 'TBLSPCBIGGER',
|
|
'dfcb8679956b734706cf87259d50c88f83e80e66', $lTime);
|
|
|
|
BackRestTestBackup_ManifestFileRemove(\%oManifest, 'base', 'base/base2.txt', true);
|
|
|
|
$strBackup = BackRestTestBackup_BackupEnd(\%oManifest);
|
|
|
|
# Full Backup
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
$strType = 'full';
|
|
BackRestTestBackup_ManifestReference(\%oManifest);
|
|
|
|
BackRestTestBackup_ManifestFileCreate(\%oManifest, 'base', 'base/base1.txt', 'BASEUPDT2',
|
|
'7579ada0808d7f98087a0a586d0df9de009cdc33', $lTime);
|
|
|
|
$strFullBackup = BackRestTestBackup_BackupSynthetic($strType, $strStanza, \%oManifest);
|
|
|
|
# Diff Backup
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
$strType = 'diff';
|
|
BackRestTestBackup_ManifestReference(\%oManifest, $strFullBackup);
|
|
|
|
BackRestTestBackup_ManifestFileCreate(\%oManifest, 'base', 'base/base2.txt', 'BASE2UPDT',
|
|
'cafac3c59553f2cfde41ce2e62e7662295f108c0', $lTime);
|
|
|
|
$strBackup = BackRestTestBackup_BackupSynthetic($strType, $strStanza, \%oManifest, 'add files');
|
|
|
|
# Restore
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
$bDelta = true;
|
|
|
|
BackRestTestBackup_Restore($oFile, $strBackup, $strStanza, $bRemote, undef, undef, $bDelta, $bForce,
|
|
undef, undef, undef, undef, undef, undef,
|
|
'no tablespace remap', undef, '--no-tablespace', false);
|
|
|
|
# Backup Info (with an empty stanza)
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
executeTest('mkdir ' . BackRestTestCommon_RepoPathGet . '/backup/db_empty',
|
|
{bRemote => $bRemote});
|
|
|
|
BackRestTestBackup_Info(undef, undef, false);
|
|
BackRestTestBackup_Info(undef, INFO_OUTPUT_JSON, false);
|
|
BackRestTestBackup_Info('bogus', undef, false);
|
|
BackRestTestBackup_Info('bogus', INFO_OUTPUT_JSON, false);
|
|
|
|
# Backup Info (with no stanzas)
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
executeTest('rm -rf ' . BackRestTestCommon_RepoPathGet . '/backup/*',
|
|
{bRemote => $bRemote});
|
|
|
|
BackRestTestBackup_Info(undef, undef, false);
|
|
BackRestTestBackup_Info(undef, INFO_OUTPUT_JSON, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (BackRestTestCommon_Cleanup(\$oLogTest))
|
|
{
|
|
&log(INFO, 'cleanup');
|
|
BackRestTestBackup_Drop(true);
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------------------------------------------------------------------------------
|
|
# Test full
|
|
#
|
|
# Check the entire backup mechanism using actual clusters. Only the archive and start/stop mechanisms need to be tested since
|
|
# everything else was tested in the backup test.
|
|
#-------------------------------------------------------------------------------------------------------------------------------
|
|
if ($strTest eq 'all' || $strTest eq 'full')
|
|
{
|
|
$iRun = 0;
|
|
$bCreate = true;
|
|
|
|
&log(INFO, "Test Full Backup\n");
|
|
|
|
for (my $bRemote = false; $bRemote <= true; $bRemote++)
|
|
{
|
|
for (my $bArchiveAsync = false; $bArchiveAsync <= true; $bArchiveAsync++)
|
|
{
|
|
for (my $bCompress = false; $bCompress <= true; $bCompress++)
|
|
{
|
|
# Increment the run, log, and decide whether this unit test should be run
|
|
if (!BackRestTestCommon_Run(++$iRun,
|
|
"rmt ${bRemote}, arc_async ${bArchiveAsync}, cmp ${bCompress}")) {next}
|
|
|
|
# Create the file object
|
|
my $oFile = new BackRest::File
|
|
(
|
|
$strStanza,
|
|
BackRestTestCommon_RepoPathGet(),
|
|
$bRemote ? 'backup' : undef,
|
|
$bRemote ? $oRemote : $oLocal
|
|
);
|
|
|
|
# Create the test directory
|
|
if ($bCreate)
|
|
{
|
|
BackRestTestBackup_Create($bRemote, false);
|
|
}
|
|
|
|
# Create db config
|
|
BackRestTestCommon_ConfigCreate('db', # local
|
|
$bRemote ? BACKUP : undef, # remote
|
|
$bCompress, # compress
|
|
undef, # checksum
|
|
$bRemote ? undef : false, # hardlink
|
|
$iThreadMax, # thread-max
|
|
$bArchiveAsync, # archive-async
|
|
undef); # compress-async
|
|
|
|
# Create backup config
|
|
if ($bRemote)
|
|
{
|
|
BackRestTestCommon_ConfigCreate('backup', # local
|
|
$bRemote ? DB : undef, # remote
|
|
$bCompress, # compress
|
|
undef, # checksum
|
|
false, # hardlink
|
|
$iThreadMax, # thread-max
|
|
undef, # archive-async
|
|
undef); # compress-async
|
|
}
|
|
|
|
# Create the cluster
|
|
if ($bCreate)
|
|
{
|
|
BackRestTestBackup_ClusterCreate();
|
|
$bCreate = false;
|
|
}
|
|
|
|
BackRestTestBackup_Init($bRemote, $oFile, false, undef, $iThreadMax);
|
|
|
|
# Static backup parameters
|
|
# my $bSynthetic = false;
|
|
my $fTestDelay = 1;
|
|
|
|
# Variable backup parameters
|
|
my $bDelta = true;
|
|
my $bForce = false;
|
|
my $strType = undef;
|
|
my $strTarget = undef;
|
|
my $bTargetExclusive = false;
|
|
my $bTargetResume = false;
|
|
my $strTargetTimeline = undef;
|
|
my $oRecoveryHashRef = undef;
|
|
# my $strTestPoint = undef;
|
|
my $strComment = undef;
|
|
my $iExpectedExitStatus = undef;
|
|
|
|
# Restore test string
|
|
my $strDefaultMessage = 'default';
|
|
my $strFullMessage = 'full';
|
|
my $strIncrMessage = 'incr';
|
|
my $strTimeMessage = 'time';
|
|
my $strXidMessage = 'xid';
|
|
my $strNameMessage = 'name';
|
|
my $strTimelineMessage = 'timeline3';
|
|
|
|
# Full backup
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
$strType = BACKUP_TYPE_FULL;
|
|
|
|
# Create the table where test messages will be stored
|
|
BackRestTestBackup_PgExecute("create table test (message text not null)");
|
|
BackRestTestBackup_PgSwitchXlog();
|
|
BackRestTestBackup_PgExecute("insert into test values ('$strDefaultMessage')");
|
|
|
|
# Acquire the backup advisory lock so it looks like a backup is running
|
|
if (!BackRestTestBackup_PgSelectOne('select pg_try_advisory_lock(' . DB_BACKUP_ADVISORY_LOCK . ')'))
|
|
{
|
|
confess 'unable to acquire advisory lock for testing';
|
|
}
|
|
|
|
$strComment = 'fail on backup lock exists';
|
|
BackRestTestBackup_Backup($strType, $strStanza, $strComment,
|
|
{iExpectedExitStatus => ERROR_LOCK_ACQUIRE});
|
|
|
|
# Release the backup advisory lock so the next backup will succeed
|
|
if (!BackRestTestBackup_PgSelectOne('select pg_advisory_unlock(' . DB_BACKUP_ADVISORY_LOCK . ')'))
|
|
{
|
|
confess 'unable to acquire advisory lock for testing';
|
|
}
|
|
|
|
$strComment = 'update during backup';
|
|
BackRestTestBackup_BackupBegin($strType, $strStanza, $strComment,
|
|
{strTest => TEST_MANIFEST_BUILD, fTestDelay => $fTestDelay});
|
|
|
|
BackRestTestBackup_PgExecute("update test set message = '$strFullMessage'", false);
|
|
|
|
my $strFullBackup = BackRestTestBackup_BackupEnd();
|
|
|
|
# Execute stop and make sure the backup fails
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
$strComment = 'attempt backup when stopped';
|
|
|
|
# Stop the cluster to check for any errors before continuing since the stop tests will definitely create errors and
|
|
# the logs will to be deleted to avoid causing issues further down the line.
|
|
BackRestTestBackup_ClusterStop();
|
|
BackRestTestBackup_ClusterStart();
|
|
|
|
BackRestTestBackup_Stop();
|
|
|
|
BackRestTestBackup_Backup($strType, $strStanza, $strComment,
|
|
{iExpectedExitStatus => ERROR_STOP});
|
|
|
|
BackRestTestBackup_Start();
|
|
|
|
# Setup the time target
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
BackRestTestBackup_PgExecute("update test set message = '$strTimeMessage'", false);
|
|
BackRestTestBackup_PgSwitchXlog();
|
|
my $strTimeTarget = BackRestTestBackup_PgSelectOne("select to_char(current_timestamp, 'YYYY-MM-DD HH24:MI:SS.US TZ')");
|
|
&log(INFO, " time target is ${strTimeTarget}");
|
|
|
|
# Incr backup
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
$strType = BACKUP_TYPE_INCR;
|
|
|
|
BackRestTestBackup_PgExecuteNoTrans("create tablespace ts1 location '" .
|
|
BackRestTestCommon_DbTablespacePathGet(1) . "'");
|
|
BackRestTestBackup_PgExecute("alter table test set tablespace ts1", true);
|
|
|
|
BackRestTestBackup_PgExecute("create table test_remove (id int)", false);
|
|
BackRestTestBackup_PgSwitchXlog();
|
|
BackRestTestBackup_PgExecute("update test set message = '$strDefaultMessage'", false);
|
|
BackRestTestBackup_PgSwitchXlog();
|
|
|
|
# Start a backup so the next backup has to restart it
|
|
if (BackRestTestCommon_DbVersion() >= 9.3)
|
|
{
|
|
BackRestTestBackup_PgSelectOne("select pg_start_backup('test backup that will be cancelled', true)");
|
|
}
|
|
|
|
# # Can't do this test yet because it puts errors in the Postgres log
|
|
# $strComment = 'fail on backup already running';
|
|
# BackRestTestBackup_Backup($strType, $strStanza, $bRemote, $oFile, $strComment, undef, undef, ERROR_DB_QUERY);
|
|
|
|
$strComment = 'update during backup';
|
|
BackRestTestBackup_BackupBegin($strType, $strStanza, $strComment,
|
|
{strTest => TEST_MANIFEST_BUILD, fTestDelay => $fTestDelay,
|
|
strOptionalParam => '--' . OPTION_STOP_AUTO . ' --no-' . OPTION_BACKUP_ARCHIVE_CHECK});
|
|
|
|
BackRestTestBackup_PgExecute("drop table test_remove", false);
|
|
BackRestTestBackup_PgSwitchXlog();
|
|
BackRestTestBackup_PgExecute("update test set message = '$strIncrMessage'", false);
|
|
|
|
my $strIncrBackup = BackRestTestBackup_BackupEnd();
|
|
|
|
# Setup the xid target
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
BackRestTestBackup_PgExecute("update test set message = '$strXidMessage'", false, false);
|
|
BackRestTestBackup_PgSwitchXlog();
|
|
my $strXidTarget = BackRestTestBackup_PgSelectOne("select txid_current()");
|
|
BackRestTestBackup_PgCommit();
|
|
&log(INFO, " xid target is ${strXidTarget}");
|
|
|
|
# Setup the name target
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
my $strNameTarget = 'backrest';
|
|
|
|
BackRestTestBackup_PgExecute("update test set message = '$strNameMessage'", false, true);
|
|
BackRestTestBackup_PgSwitchXlog();
|
|
|
|
if (BackRestTestCommon_DbVersion() >= 9.1)
|
|
{
|
|
BackRestTestBackup_PgExecute("select pg_create_restore_point('${strNameTarget}')", false, false);
|
|
}
|
|
|
|
&log(INFO, " name target is ${strNameTarget}");
|
|
|
|
# Restore (type = default)
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
$bDelta = false;
|
|
$bForce = false;
|
|
$strType = RECOVERY_TYPE_DEFAULT;
|
|
$strTarget = undef;
|
|
$bTargetExclusive = undef;
|
|
$bTargetResume = undef;
|
|
$strTargetTimeline = undef;
|
|
$oRecoveryHashRef = undef;
|
|
$strComment = undef;
|
|
$iExpectedExitStatus = undef;
|
|
|
|
&log(INFO, " testing recovery type = ${strType}");
|
|
|
|
# Expect failure because postmaster.pid exists
|
|
$strComment = 'postmaster running';
|
|
$iExpectedExitStatus = ERROR_POSTMASTER_RUNNING;
|
|
|
|
BackRestTestBackup_Restore($oFile, 'latest', $strStanza, $bRemote, undef, undef, $bDelta, $bForce,
|
|
$strType, $strTarget, $bTargetExclusive, $bTargetResume, $strTargetTimeline,
|
|
$oRecoveryHashRef, $strComment, $iExpectedExitStatus);
|
|
|
|
BackRestTestBackup_ClusterStop();
|
|
|
|
# Expect failure because db path is not empty
|
|
$strComment = 'path not empty';
|
|
$iExpectedExitStatus = ERROR_RESTORE_PATH_NOT_EMPTY;
|
|
|
|
BackRestTestBackup_Restore($oFile, 'latest', $strStanza, $bRemote, undef, undef, $bDelta, $bForce,
|
|
$strType, $strTarget, $bTargetExclusive, $bTargetResume, $strTargetTimeline,
|
|
$oRecoveryHashRef, $strComment, $iExpectedExitStatus);
|
|
|
|
# Drop and recreate db path
|
|
BackRestTestCommon_PathRemove(BackRestTestCommon_DbCommonPathGet());
|
|
BackRestTestCommon_PathCreate(BackRestTestCommon_DbCommonPathGet());
|
|
BackRestTestCommon_PathRemove(BackRestTestCommon_DbTablespacePathGet(1));
|
|
BackRestTestCommon_PathCreate(BackRestTestCommon_DbTablespacePathGet(1));
|
|
|
|
# Now the restore should work
|
|
$strComment = undef;
|
|
$iExpectedExitStatus = undef;
|
|
|
|
BackRestTestBackup_Restore($oFile, 'latest', $strStanza, $bRemote, undef, undef, $bDelta, $bForce,
|
|
$strType, $strTarget, $bTargetExclusive, $bTargetResume, $strTargetTimeline,
|
|
$oRecoveryHashRef, $strComment, $iExpectedExitStatus);
|
|
|
|
BackRestTestBackup_ClusterStart();
|
|
BackRestTestBackup_PgSelectOneTest('select message from test', $strNameMessage);
|
|
|
|
# Restore (restore type = xid, inclusive)
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
$bDelta = false;
|
|
$bForce = true;
|
|
$strType = RECOVERY_TYPE_XID;
|
|
$strTarget = $strXidTarget;
|
|
$bTargetExclusive = undef;
|
|
$bTargetResume = BackRestTestCommon_DbVersion() >= 9.1 && BackRestTestCommon_DbVersion() < 9.5 ? true : undef;
|
|
$strTargetTimeline = undef;
|
|
$oRecoveryHashRef = undef;
|
|
$strComment = undef;
|
|
$iExpectedExitStatus = undef;
|
|
|
|
&log(INFO, " testing recovery type = ${strType}");
|
|
|
|
BackRestTestBackup_ClusterStop();
|
|
|
|
BackRestTestBackup_Restore($oFile, $strIncrBackup, $strStanza, $bRemote, undef, undef, $bDelta, $bForce,
|
|
$strType, $strTarget, $bTargetExclusive, $bTargetResume, $strTargetTimeline,
|
|
$oRecoveryHashRef, $strComment, $iExpectedExitStatus, '--no-tablespace', false);
|
|
|
|
# Save recovery file to test so we can use it in the next test
|
|
$oFile->copy(PATH_ABSOLUTE, BackRestTestCommon_DbCommonPathGet() . '/recovery.conf',
|
|
PATH_ABSOLUTE, BackRestTestCommon_TestPathGet() . '/recovery.conf');
|
|
|
|
BackRestTestBackup_ClusterStart();
|
|
BackRestTestBackup_PgSelectOneTest('select message from test', $strXidMessage);
|
|
|
|
BackRestTestBackup_PgExecute("update test set message = '$strTimelineMessage'", false);
|
|
|
|
# Restore (restore type = preserve, inclusive)
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
$bDelta = true;
|
|
$bForce = false;
|
|
$strType = RECOVERY_TYPE_PRESERVE;
|
|
$strTarget = undef;
|
|
$bTargetExclusive = undef;
|
|
$bTargetResume = undef;
|
|
$strTargetTimeline = undef;
|
|
$oRecoveryHashRef = undef;
|
|
$strComment = undef;
|
|
$iExpectedExitStatus = undef;
|
|
|
|
&log(INFO, " testing recovery type = ${strType}");
|
|
|
|
BackRestTestBackup_ClusterStop();
|
|
|
|
# Restore recovery file that was saved in last test
|
|
$oFile->move(PATH_ABSOLUTE, BackRestTestCommon_TestPathGet() . '/recovery.conf',
|
|
PATH_ABSOLUTE, BackRestTestCommon_DbCommonPathGet() . '/recovery.conf');
|
|
|
|
BackRestTestBackup_Restore($oFile, 'latest', $strStanza, $bRemote, undef, undef, $bDelta, $bForce,
|
|
$strType, $strTarget, $bTargetExclusive, $bTargetResume, $strTargetTimeline,
|
|
$oRecoveryHashRef, $strComment, $iExpectedExitStatus);
|
|
|
|
BackRestTestBackup_ClusterStart();
|
|
BackRestTestBackup_PgSelectOneTest('select message from test', $strXidMessage);
|
|
|
|
BackRestTestBackup_PgExecute("update test set message = '$strTimelineMessage'", false);
|
|
|
|
# Restore (restore type = time, inclusive) - there is no exclusive time test because I can't find a way to find the
|
|
# exact commit time of a transaction.
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
$bDelta = true;
|
|
$bForce = false;
|
|
$strType = RECOVERY_TYPE_TIME;
|
|
$strTarget = $strTimeTarget;
|
|
$bTargetExclusive = undef;
|
|
$bTargetResume = undef;
|
|
$strTargetTimeline = undef;
|
|
$oRecoveryHashRef = undef;
|
|
$strComment = undef;
|
|
$iExpectedExitStatus = undef;
|
|
|
|
&log(INFO, " testing recovery type = ${strType}");
|
|
|
|
BackRestTestBackup_ClusterStop();
|
|
|
|
BackRestTestBackup_Restore($oFile, $strFullBackup, $strStanza, $bRemote, undef, undef, $bDelta, $bForce,
|
|
$strType, $strTarget, $bTargetExclusive, $bTargetResume, $strTargetTimeline,
|
|
$oRecoveryHashRef, $strComment, $iExpectedExitStatus);
|
|
|
|
BackRestTestBackup_ClusterStart();
|
|
BackRestTestBackup_PgSelectOneTest('select message from test', $strTimeMessage);
|
|
|
|
# Restore (restore type = xid, exclusive)
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
$bDelta = true;
|
|
$bForce = false;
|
|
$strType = RECOVERY_TYPE_XID;
|
|
$strTarget = $strXidTarget;
|
|
$bTargetExclusive = true;
|
|
$bTargetResume = undef;
|
|
$strTargetTimeline = undef;
|
|
$oRecoveryHashRef = undef;
|
|
$strComment = undef;
|
|
$iExpectedExitStatus = undef;
|
|
|
|
&log(INFO, " testing recovery type = ${strType}");
|
|
|
|
BackRestTestBackup_ClusterStop();
|
|
|
|
BackRestTestBackup_Restore($oFile, $strIncrBackup, $strStanza, $bRemote, undef, undef, $bDelta, $bForce,
|
|
$strType, $strTarget, $bTargetExclusive, $bTargetResume, $strTargetTimeline,
|
|
$oRecoveryHashRef, $strComment, $iExpectedExitStatus);
|
|
|
|
BackRestTestBackup_ClusterStart();
|
|
BackRestTestBackup_PgSelectOneTest('select message from test', $strIncrMessage);
|
|
|
|
# Restore (restore type = name)
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
if (BackRestTestCommon_DbVersion() >= 9.1)
|
|
{
|
|
$bDelta = true;
|
|
$bForce = true;
|
|
$strType = RECOVERY_TYPE_NAME;
|
|
$strTarget = $strNameTarget;
|
|
$bTargetExclusive = undef;
|
|
$bTargetResume = undef;
|
|
$strTargetTimeline = undef;
|
|
$oRecoveryHashRef = undef;
|
|
$strComment = undef;
|
|
$iExpectedExitStatus = undef;
|
|
|
|
&log(INFO, " testing recovery type = ${strType}");
|
|
|
|
BackRestTestBackup_ClusterStop();
|
|
|
|
BackRestTestBackup_Restore($oFile, 'latest', $strStanza, $bRemote, undef, undef, $bDelta, $bForce,
|
|
$strType, $strTarget, $bTargetExclusive, $bTargetResume, $strTargetTimeline,
|
|
$oRecoveryHashRef, $strComment, $iExpectedExitStatus);
|
|
|
|
BackRestTestBackup_ClusterStart();
|
|
BackRestTestBackup_PgSelectOneTest('select message from test', $strNameMessage);
|
|
}
|
|
|
|
# Restore (restore type = default, timeline = 3)
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
if (BackRestTestCommon_DbVersion() >= 8.4)
|
|
{
|
|
$bDelta = true;
|
|
$bForce = false;
|
|
$strType = RECOVERY_TYPE_DEFAULT;
|
|
$strTarget = undef;
|
|
$bTargetExclusive = undef;
|
|
$bTargetResume = undef;
|
|
$strTargetTimeline = 3;
|
|
$oRecoveryHashRef = BackRestTestCommon_DbVersion() >= 9.0 ? {'standby-mode' => 'on'} : undef;
|
|
$strComment = undef;
|
|
$iExpectedExitStatus = undef;
|
|
|
|
&log(INFO, " testing recovery type = ${strType}");
|
|
|
|
BackRestTestBackup_ClusterStop();
|
|
|
|
BackRestTestBackup_Restore($oFile, $strIncrBackup, $strStanza, $bRemote, undef, undef, $bDelta, $bForce,
|
|
$strType, $strTarget, $bTargetExclusive, $bTargetResume, $strTargetTimeline,
|
|
$oRecoveryHashRef, $strComment, $iExpectedExitStatus);
|
|
|
|
BackRestTestBackup_ClusterStart(undef, undef, true);
|
|
BackRestTestBackup_PgSelectOneTest('select message from test', $strTimelineMessage, 120);
|
|
}
|
|
|
|
# Incr backup - make sure a --no-stop-start backup fails
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
$strType = BACKUP_TYPE_INCR;
|
|
|
|
$strComment = 'fail on --' . OPTION_NO_START_STOP;
|
|
BackRestTestBackup_Backup($strType, $strStanza, $strComment,
|
|
{iExpectedExitStatus => ERROR_POSTMASTER_RUNNING,
|
|
strOptionalParam => '--' . OPTION_NO_START_STOP});
|
|
|
|
# Incr backup - allow --no-start-stop backup to succeed with --force
|
|
#-----------------------------------------------------------------------------------------------------------------------
|
|
$strType = BACKUP_TYPE_INCR;
|
|
|
|
$strComment = 'succeed on --' . OPTION_NO_START_STOP . ' with --' . OPTION_FORCE;
|
|
BackRestTestBackup_Backup($strType, $strStanza, $strComment,
|
|
{strOptionalParam => '--' . OPTION_NO_START_STOP . ' --' . OPTION_FORCE});
|
|
|
|
$bCreate = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (BackRestTestCommon_Cleanup())
|
|
{
|
|
&log(INFO, 'cleanup');
|
|
BackRestTestBackup_Drop(true);
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------------------------------------------------------------------------------
|
|
# Test collision
|
|
#
|
|
# See if it is possible for a table to be written to, have stop backup run, and be written to again all in the same second.
|
|
#-------------------------------------------------------------------------------------------------------------------------------
|
|
if ($strTest eq 'collision')
|
|
{
|
|
$iRun = 0;
|
|
my $iRunMax = 1000;
|
|
|
|
&log(INFO, "Test Backup Collision\n");
|
|
|
|
# Create the file object
|
|
my $oFile = (BackRest::File->new
|
|
(
|
|
$strStanza,
|
|
BackRestTestCommon_RepoPathGet(),
|
|
NONE,
|
|
$oLocal
|
|
))->clone();
|
|
|
|
# Create the test database
|
|
BackRestTestBackup_Create(false);
|
|
|
|
# Create the config file
|
|
BackRestTestCommon_ConfigCreate('db', # local
|
|
undef, # remote
|
|
false, # compress
|
|
false, # checksum
|
|
false, # hardlink
|
|
$iThreadMax, # thread-max
|
|
false, # archive-async
|
|
undef); # compress-async
|
|
|
|
# Create the test table
|
|
BackRestTestBackup_PgExecute("create table test_collision (id int)");
|
|
|
|
# Construct filename to test
|
|
my $strFile = BackRestTestCommon_DbCommonPathGet() . "/base";
|
|
|
|
# Get the oid of the postgres db
|
|
my $strSql = "select oid from pg_database where datname = 'postgres'";
|
|
my $hStatement = BackRestTestBackup_PgHandleGet()->prepare($strSql);
|
|
|
|
$hStatement->execute() or
|
|
confess &log(ERROR, "Unable to execute: ${strSql}");
|
|
|
|
my @oyRow = $hStatement->fetchrow_array();
|
|
$strFile .= '/' . $oyRow[0];
|
|
|
|
$hStatement->finish();
|
|
|
|
# Get the oid of the new table so we can check the file on disk
|
|
$strSql = "select oid from pg_class where relname = 'test_collision'";
|
|
$hStatement = BackRestTestBackup_PgHandleGet()->prepare($strSql);
|
|
|
|
$hStatement->execute() or
|
|
confess &log(ERROR, "Unable to execute: ${strSql}");
|
|
|
|
@oyRow = $hStatement->fetchrow_array();
|
|
$strFile .= '/' . $oyRow[0];
|
|
|
|
&log(INFO, 'table filename = ' . $strFile);
|
|
|
|
$hStatement->finish();
|
|
|
|
BackRestTestBackup_PgExecute("select pg_start_backup('test');");
|
|
|
|
# File modified in the same second after the manifest is taken and file is copied
|
|
while ($iRun < $iRunMax)
|
|
{
|
|
# Increment the run, log, and decide whether this unit test should be run
|
|
if (!BackRestTestCommon_Run(++$iRun,
|
|
"mod after manifest")) {next}
|
|
|
|
my $strTestChecksum = $oFile->hash(PATH_DB_ABSOLUTE, $strFile);
|
|
|
|
# Insert a row and do a checkpoint
|
|
BackRestTestBackup_PgExecute("insert into test_collision values (1)", true);
|
|
|
|
# Stat the file to get size/modtime after the backup has started
|
|
my $strBeginChecksum = $oFile->hash(PATH_DB_ABSOLUTE, $strFile);
|
|
my $oStat = lstat($strFile);
|
|
my $lBeginSize = $oStat->size;
|
|
my $lBeginTime = $oStat->mtime;
|
|
|
|
# Sleep .5 seconds to give a reasonable amount of time for the file to be copied after the manifest was generated
|
|
# Sleep for a while to show there is a large window where this can happen
|
|
&log(INFO, 'time ' . gettimeofday());
|
|
waitHiRes(.5);
|
|
&log(INFO, 'time ' . gettimeofday());
|
|
|
|
# Insert another row
|
|
BackRestTestBackup_PgExecute("insert into test_collision values (1)");
|
|
|
|
# Stop backup, start a new backup
|
|
BackRestTestBackup_PgExecute("select pg_stop_backup();");
|
|
BackRestTestBackup_PgExecute("select pg_start_backup('test');");
|
|
|
|
# Stat the file to get size/modtime after the backup has restarted
|
|
my $strEndChecksum = $oFile->hash(PATH_DB_ABSOLUTE, $strFile);
|
|
$oStat = lstat($strFile);
|
|
my $lEndSize = $oStat->size;
|
|
my $lEndTime = $oStat->mtime;
|
|
|
|
# Error if the size/modtime are the same between the backups
|
|
&log(INFO, " begin size = ${lBeginSize}, time = ${lBeginTime}, hash ${strBeginChecksum} - " .
|
|
"end size = ${lEndSize}, time = ${lEndTime}, hash ${strEndChecksum} - test hash ${strTestChecksum}");
|
|
|
|
if ($lBeginSize == $lEndSize && $lBeginTime == $lEndTime &&
|
|
$strTestChecksum ne $strBeginChecksum && $strBeginChecksum ne $strEndChecksum)
|
|
{
|
|
&log(ERROR, "size and mod time are the same between backups");
|
|
$iRun = $iRunMax;
|
|
next;
|
|
}
|
|
}
|
|
|
|
BackRestTestBackup_PgExecute("select pg_stop_backup();");
|
|
|
|
if (BackRestTestCommon_Cleanup())
|
|
{
|
|
&log(INFO, 'cleanup');
|
|
BackRestTestBackup_Drop(true);
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------------------------------------------------------------------------------
|
|
# rsync-collision
|
|
#
|
|
# See if it is possible for a table to be written to, have stop backup run, and be written to again all in the same second.
|
|
#-------------------------------------------------------------------------------------------------------------------------------
|
|
if ($strTest eq 'rsync-collision')
|
|
{
|
|
$iRun = 0;
|
|
my $iRunMax = 1000;
|
|
|
|
&log(INFO, "Test Rsync Collision\n");
|
|
|
|
# Create the file object
|
|
my $oFile = (BackRest::File->new
|
|
(
|
|
$strStanza,
|
|
BackRestTestCommon_RepoPathGet(),
|
|
NONE,
|
|
$oLocal
|
|
))->clone();
|
|
|
|
# Create the test database
|
|
BackRestTestBackup_Create(false, false);
|
|
|
|
# Create test paths
|
|
my $strPathRsync1 = BackRestTestCommon_TestPathGet() . "/rsync1";
|
|
my $strPathRsync2 = BackRestTestCommon_TestPathGet() . "/rsync2";
|
|
|
|
BackRestTestCommon_PathCreate($strPathRsync1);
|
|
BackRestTestCommon_PathCreate($strPathRsync2);
|
|
|
|
# Rsync command
|
|
my $strCommand = "rsync -vvrt ${strPathRsync1}/ ${strPathRsync2}";
|
|
|
|
# File modified in the same second after the manifest is taken and file is copied
|
|
while ($iRun < $iRunMax)
|
|
{
|
|
# Increment the run, log, and decide whether this unit test should be run
|
|
if (!BackRestTestCommon_Run(++$iRun,
|
|
"rsync test")) {next}
|
|
|
|
# Create test file
|
|
&log(INFO, "create test file");
|
|
BackRestTestCommon_FileCreate("${strPathRsync1}/test.txt", 'TEST1');
|
|
|
|
# Stat the file to get size/modtime after the backup has started
|
|
my $strBeginChecksum = $oFile->hash(PATH_DB_ABSOLUTE, "${strPathRsync1}/test.txt");
|
|
|
|
# Rsync
|
|
&log(INFO, "rsync 1st time");
|
|
executeTest($strCommand, {bShowOutput => true});
|
|
|
|
# Sleep for a while to show there is a large window where this can happen
|
|
&log(INFO, 'time ' . gettimeofday());
|
|
waitHiRes(.5);
|
|
&log(INFO, 'time ' . gettimeofday());
|
|
|
|
# Modify the test file within the same second
|
|
&log(INFO, "modify test file");
|
|
BackRestTestCommon_FileCreate("${strPathRsync1}/test.txt", 'TEST2');
|
|
|
|
my $strEndChecksum = $oFile->hash(PATH_DB_ABSOLUTE, "${strPathRsync1}/test.txt");
|
|
|
|
# Rsync again
|
|
&log(INFO, "rsync 2nd time");
|
|
excuteTest($strCommand, {bShowOutput => true});
|
|
|
|
my $strTestChecksum = $oFile->hash(PATH_DB_ABSOLUTE, "${strPathRsync2}/test.txt");
|
|
|
|
# Error if checksums are not the same after rsync
|
|
&log(INFO, " begin hash ${strBeginChecksum} - end hash ${strEndChecksum} - test hash ${strTestChecksum}");
|
|
|
|
if ($strTestChecksum ne $strEndChecksum)
|
|
{
|
|
&log(ERROR, "end and test checksums are not the same");
|
|
$iRun = $iRunMax;
|
|
next;
|
|
}
|
|
}
|
|
|
|
if (BackRestTestCommon_Cleanup())
|
|
{
|
|
&log(INFO, 'cleanup');
|
|
BackRestTestBackup_Drop(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
1;
|