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:
parent
ac22c314a9
commit
f59aae101d
@ -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),
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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))
|
||||
{
|
||||
|
@ -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',
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user