1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-01-18 04:58:51 +02:00

Ready to start writing recovery unit tests.

This commit is contained in:
David Steele 2015-01-26 14:59:58 -05:00
parent ac22c314a9
commit f59aae101d
6 changed files with 156 additions and 32 deletions

View File

@ -454,6 +454,7 @@ if (operation_get() eq OP_RESTORE)
undef, #param_get(PARAM_THREAD),
param_get(PARAM_DELTA),
param_get(PARAM_FORCE),
param_get(PARAM_TYPE),
param_get(PARAM_TARGET),
param_get(PARAM_TARGET_EXCLUSIVE),
param_get(PARAM_TARGET_RESUME),

View File

@ -843,8 +843,9 @@ sub backup_manifest_build
foreach my $strName (sort(keys $oManifestHash{name}))
{
# Skip certain files during backup
if (($strName =~ /^pg\_xlog\/.*/ && !$bNoStartStop) || # pg_xlog/ - this will be reconstructed
$strName =~ /^postmaster\.pid$/) # postmaster.pid - to avoid confusing postgres when restoring
if (($strName =~ /^pg\_xlog\/.*/ && !$bNoStartStop) || # pg_xlog/ - this will be reconstructed
$strName =~ /^postmaster\.pid$/ || # postmaster.pid - to avoid confusing postgres when restoring
$strName =~ /^recovery\.conf$/) # recovery.conf - doesn't make sense to backup this file
{
next;
}

View File

@ -20,13 +20,16 @@ use Exporter qw(import);
our @EXPORT = qw(config_load config_key_load config_section_load operation_get operation_set param_get
FILE_MANIFEST FILE_VERSION FILE_POSTMASTER_PID
FILE_MANIFEST FILE_VERSION FILE_POSTMASTER_PID FILE_RECOVERY_CONF
PATH_LATEST
OP_ARCHIVE_GET OP_ARCHIVE_PUSH OP_BACKUP OP_RESTORE OP_EXPIRE
BACKUP_TYPE_FULL BACKUP_TYPE_DIFF BACKUP_TYPE_INCR
RECOVERY_TYPE_NAME RECOVERY_TYPE_TIME RECOVERY_TYPE_XID RECOVERY_TYPE_PRESERVE RECOVERY_TYPE_NONE
RECOVERY_TYPE_DEFAULT
PARAM_CONFIG PARAM_STANZA PARAM_TYPE PARAM_DELTA PARAM_SET PARAM_NO_START_STOP PARAM_FORCE PARAM_TARGET
PARAM_TARGET_EXCLUSIVE PARAM_TARGET_RESUME PARAM_TARGET_TIMELINE CONFIG_SECTION_RECOVERY
@ -56,6 +59,7 @@ use constant
FILE_MANIFEST => 'backup.manifest',
FILE_VERSION => 'version',
FILE_POSTMASTER_PID => 'postmaster.pid',
FILE_RECOVERY_CONF => 'recovery.conf',
PATH_LATEST => 'latest'
};
@ -83,16 +87,16 @@ use constant
};
####################################################################################################################################
# RESTORE Type Constants
# RECOVERY Type Constants
####################################################################################################################################
use constant
{
RESTORE_TYPE_NAME => 'name',
RESTORE_TYPE_TIME => 'time',
RESTORE_TYPE_XID => 'xid',
RESTORE_TYPE_PRESERVE => 'preserve',
RESTORE_TYPE_NONE => 'none',
RESTORE_TYPE_DEFAULT => 'default'
RECOVERY_TYPE_NAME => 'name',
RECOVERY_TYPE_TIME => 'time',
RECOVERY_TYPE_XID => 'xid',
RECOVERY_TYPE_PRESERVE => 'preserve',
RECOVERY_TYPE_NONE => 'none',
RECOVERY_TYPE_DEFAULT => 'default'
};
####################################################################################################################################
@ -393,19 +397,19 @@ sub param_valid
# Check types for restore
elsif (operation_test(OP_RESTORE))
{
# If type is not defined set to RESTORE_TYPE_DEFAULT
# If type is not defined set to RECOVERY_TYPE_DEFAULT
if (!defined($strType))
{
$strType = RESTORE_TYPE_DEFAULT;
$strType = RECOVERY_TYPE_DEFAULT;
param_set($strParam, $strType);
}
if (!($strType eq RESTORE_TYPE_NAME || $strType eq RESTORE_TYPE_TIME || $strType eq RESTORE_TYPE_XID ||
$strType eq RESTORE_TYPE_PRESERVE || $strType eq RESTORE_TYPE_NONE || $strType eq RESTORE_TYPE_DEFAULT))
if (!($strType eq RECOVERY_TYPE_NAME || $strType eq RECOVERY_TYPE_TIME || $strType eq RECOVERY_TYPE_XID ||
$strType eq RECOVERY_TYPE_PRESERVE || $strType eq RECOVERY_TYPE_NONE || $strType eq RECOVERY_TYPE_DEFAULT))
{
confess &log(ERROR, "invalid type '${strType}' for ${strOperation}, must be: '" . RESTORE_TYPE_NAME .
"', '" . RESTORE_TYPE_TIME . "', '" . RESTORE_TYPE_XID . "', '" . RESTORE_TYPE_PRESERVE .
"', '" . RESTORE_TYPE_NONE . "', '" . RESTORE_TYPE_DEFAULT . "'", ERROR_PARAM);
confess &log(ERROR, "invalid type '${strType}' for ${strOperation}, must be: '" . RECOVERY_TYPE_NAME .
"', '" . RECOVERY_TYPE_TIME . "', '" . RECOVERY_TYPE_XID . "', '" . RECOVERY_TYPE_PRESERVE .
"', '" . RECOVERY_TYPE_NONE . "', '" . RECOVERY_TYPE_DEFAULT . "'", ERROR_PARAM);
}
}
}
@ -420,11 +424,11 @@ sub param_valid
# Check target param
$strParam = PARAM_TARGET;
my $strTarget = param_get($strParam);
my $strTargetMessage = 'for ' . OP_RESTORE . " operations where type is '" . RESTORE_TYPE_NAME .
"', '" . RESTORE_TYPE_TIME . "', or '" . RESTORE_TYPE_XID . "'";
my $strTargetMessage = 'for ' . OP_RESTORE . " operations where type is '" . RECOVERY_TYPE_NAME .
"', '" . RECOVERY_TYPE_TIME . "', or '" . RECOVERY_TYPE_XID . "'";
if (operation_test(OP_RESTORE) &&
($strType eq RESTORE_TYPE_NAME || $strType eq RESTORE_TYPE_TIME || $strType eq RESTORE_TYPE_XID))
($strType eq RECOVERY_TYPE_NAME || $strType eq RECOVERY_TYPE_TIME || $strType eq RECOVERY_TYPE_XID))
{
if (!defined($strTarget))
{

View File

@ -17,7 +17,9 @@ use BackRest::Utility;
# Exports
use Exporter qw(import);
our @EXPORT = qw(MANIFEST_SECTION_BACKUP MANIFEST_SECTION_BACKUP_OPTION MANIFEST_SECTION_BACKUP_PATH
our @EXPORT = qw(MANIFEST_PATH MANIFEST_FILE MANIFEST_LINK
MANIFEST_SECTION_BACKUP MANIFEST_SECTION_BACKUP_OPTION MANIFEST_SECTION_BACKUP_PATH
MANIFEST_SECTION_BACKUP_TABLESPACE
MANIFEST_KEY_ARCHIVE_START MANIFEST_KEY_ARCHIVE_STOP MANIFEST_KEY_BASE MANIFEST_KEY_CHECKSUM MANIFEST_KEY_COMPRESS
@ -34,6 +36,10 @@ our @EXPORT = qw(MANIFEST_SECTION_BACKUP MANIFEST_SECTION_BACKUP_OPTION MANIFEST
####################################################################################################################################
use constant
{
MANIFEST_PATH => 'path',
MANIFEST_FILE => 'file',
MANIFEST_LINK => 'link',
MANIFEST_SECTION_BACKUP => 'backup',
MANIFEST_SECTION_BACKUP_OPTION => 'backup:option',
MANIFEST_SECTION_BACKUP_PATH => 'backup:path',

View File

@ -90,7 +90,7 @@ sub manifest_ownership_check
my $strDefaultUser = getpwuid($<);
my $strDefaultGroup = getgrgid($();
my %oFileTypeHash = ('path' => true, 'link' => true, 'file' => true);
my %oFileTypeHash = (&MANIFEST_PATH => true, &MANIFEST_LINK => true, &MANIFEST_FILE => true);
my %oOwnerTypeHash = (&MANIFEST_SUBKEY_USER => $strDefaultUser, &MANIFEST_SUBKEY_GROUP => $strDefaultGroup);
# Loop through owner types (user, group)
@ -250,7 +250,7 @@ sub clean
my $oManifest = shift; # Backup manifest
# Track if files/links/paths where removed
my %oRemoveHash = ('file' => 0, 'path' => 0, 'link' => 0);
my %oRemoveHash = (&MANIFEST_FILE => 0, &MANIFEST_PATH => 0, &MANIFEST_LINK => 0);
# Check each restore directory in the manifest and make sure that it exists and is empty.
# The --force option can be used to override the empty requirement.
@ -287,15 +287,15 @@ sub clean
my $strFile = "${strPath}/${strName}";
# Determine the file/path/link type
my $strType = 'file';
my $strType = MANIFEST_FILE;
if ($oPathManifest{name}{$strName}{type} eq 'd')
{
$strType = 'path';
$strType = MANIFEST_PATH;
}
elsif ($oPathManifest{name}{$strName}{type} eq 'l')
{
$strType = 'link';
$strType = MANIFEST_LINK;
}
# Build the section name
@ -317,9 +317,9 @@ sub clean
}
# If a link does not have the same destination, then delete it (it will be recreated later)
if ($strType eq 'link')
if ($strType eq MANIFEST_LINK)
{
if ($strType eq 'link' && $oManifest->get($strSection, $strName, MANIFEST_SUBKEY_DESTINATION) ne
if ($strType eq MANIFEST_LINK && $oManifest->get($strSection, $strName, MANIFEST_SUBKEY_DESTINATION) ne
$oPathManifest{name}{$strName}{link_destination})
{
&log(DEBUG, "removing link ${strFile} - destination changed");
@ -331,7 +331,7 @@ sub clean
{
my $strMode = $oManifest->get($strSection, $strName, MANIFEST_SUBKEY_MODE);
if ($strType ne 'link' && $strMode ne $oPathManifest{name}{$strName}{permission})
if ($strType ne MANIFEST_LINK && $strMode ne $oPathManifest{name}{$strName}{permission})
{
&log(DEBUG, "setting ${strFile} mode to ${strMode}");
@ -344,7 +344,7 @@ sub clean
else
{
# If a path then remove it, all the files should have already been deleted since we are going in reverse order
if ($strType eq 'path')
if ($strType eq MANIFEST_PATH)
{
&log(DEBUG, "removing path ${strFile}");
rmdir($strFile) or confess &log(ERROR, "unable to delete path ${strFile}, is it empty?");
@ -352,8 +352,13 @@ sub clean
# Else delete a file/link
else
{
&log(DEBUG, "removing file/link ${strFile}");
unlink($strFile) or confess &log(ERROR, "unable to delete file/link ${strFile}");
# Delete only if this is not the recovery.conf file. This is in case the use wants the recovery.conf file
# preserved. It will be written/deleted/preserved as needed in recovery().
if (!($strName eq FILE_RECOVERY_CONF && $strType eq MANIFEST_FILE))
{
&log(DEBUG, "removing file/link ${strFile}");
unlink($strFile) or confess &log(ERROR, "unable to delete file/link ${strFile}");
}
}
$oRemoveHash{$strType} += 1;
@ -427,6 +432,101 @@ sub build
}
}
####################################################################################################################################
# RECOVERY
#
# Creates the recovery.conf file.
####################################################################################################################################
sub recovery
{
my $self = shift; # Class hash
# Create recovery.conf path/file
my $strRecoveryConf = $self->{strDbClusterPath} . '/' . FILE_RECOVERY_CONF;
# See if recovery.conf already exists
my $bRecoveryConfExists = $self->{oFile}->exists(PATH_DB_ABSOLUTE, $strRecoveryConf);
# If RECOVERY_TYPE_PRESERVE then make sure recovery.conf exists and return
if ($self->{strType} eq RECOVERY_TYPE_PRESERVE)
{
if (!$bRecoveryConfExists)
{
confess &log(ERROR, "recovery type is $self->{strType} but recovery file does not exist at ${strRecoveryConf}");
}
return;
}
# In all other cases the old recovery.conf should be removed if it exists
if ($bRecoveryConfExists)
{
$self->{oFile}->remove(PATH_DB_ABSOLUTE, $strRecoveryConf);
}
# If RECOVERY_TYPE_NONE then return
if ($self->{strType} eq RECOVERY_TYPE_NONE)
{
return;
}
# Write the recovery options from pg_backrest.conf
my $strRecovery = '';
if (defined($self->{strRecoveryRef}))
{
foreach my $strKey (sort(keys $self->{strRecoveryRef}))
{
$strRecovery .= ${$self->{strRecoveryRef}}{$strKey} . "\n";
}
}
# If RECOVERY_TYPE_DEFAULT then return
if ($self->{strType} eq RECOVERY_TYPE_DEFAULT)
{
return;
}
# Write the recovery target
$strRecovery .= "recovery_target_$self->{strType} = $self->{strTarget}\n";
# Write recovery_target_inclusive
if ($self->{bTargetExclusive})
{
$strRecovery .= "recovery_target_inclusive = false\n";
}
# Write recovery_target_inclusive
if ($self->{bTargetExclusive})
{
$strRecovery .= "recovery_target_inclusive = false\n";
}
# Write pause_at_recovery_target
if ($self->{bTargetResult})
{
$strRecovery .= "pause_at_recovery_target = false\n";
}
# Write recovery_target_timeline
if (defined($self->{strTargetTimeline}))
{
$strRecovery .= "recovery_target_timeline = $self->{strTargetTimeline}\n";
}
# Write recovery.conf
my $hFile;
open($hFile, '>', $strRecoveryConf)
or confess &log(ERROR, "unable to open ${strRecoveryConf}: $!");
syswrite($hFile, $strRecovery)
or confess "unable to write section ${strRecoveryConf}: $!";
close($hFile)
or confess "unable to close ${strRecoveryConf}: $!";
}
####################################################################################################################################
# RESTORE
#
@ -483,6 +583,9 @@ sub restore
}
$oThreadGroup->complete();
# Create recovery.conf file
$self->recovery();
}
####################################################################################################################################

View File

@ -830,6 +830,12 @@ sub BackRestTestBackup_Restore
my $oRemapHashRef = shift;
my $bDelta = shift;
my $bForce = shift;
my $strType = shift;
my $strTarget = shift;
my $bTargetExclusive = shift;
my $bTargetResume = shift;
my $strTargetTimeline = shift;
my $oRecoveryHashRef = shift;
my $strComment = shift;
my $iExpectedExitStatus = shift;
@ -1288,6 +1294,7 @@ sub BackRestTestBackup_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');
# Incr backup - add a tablespace
@ -1340,6 +1347,7 @@ sub BackRestTestBackup_Test
$bDelta = false;
BackRestTestBackup_Restore($oFile, $strFullBackup, $strStanza, $bRemote, \%oManifest, undef, $bDelta, $bForce,
undef, undef, undef, undef, undef, undef,
'fail on used path', ERROR_RESTORE_PATH_NOT_EMPTY);
# Remap the base path
@ -1349,6 +1357,7 @@ sub BackRestTestBackup_Test
$oRemapHash{2} = BackRestTestCommon_DbTablespacePathGet(2, 2);
BackRestTestBackup_Restore($oFile, $strFullBackup, $strStanza, $bRemote, \%oManifest, \%oRemapHash, $bDelta, $bForce,
undef, undef, undef, undef, undef, undef,
'remap all paths');
# Incr Backup