#################################################################################################################################### # Unit tests for Stanza module #################################################################################################################################### package pgBackRestTest::Module::Stanza::StanzaAllPerlTest; use parent 'pgBackRestTest::Env::HostEnvTest'; #################################################################################################################################### # Perl includes #################################################################################################################################### use strict; use warnings FATAL => qw(all); use Carp qw(confess); use English '-no_match_vars'; use File::Basename qw(dirname); use Storable qw(dclone); use pgBackRest::Archive::Common; use pgBackRest::Archive::Info; use pgBackRest::Backup::Common; use pgBackRest::Backup::Info; use pgBackRest::Common::Cipher; use pgBackRest::Common::Exception; use pgBackRest::Common::Ini; use pgBackRest::Common::Lock; use pgBackRest::Common::Log; use pgBackRest::Common::String; use pgBackRest::Config::Config; use pgBackRest::DbVersion; use pgBackRest::InfoCommon; use pgBackRest::Manifest; use pgBackRest::Protocol::Helper; use pgBackRest::Stanza; use pgBackRest::Protocol::Storage::Helper; use pgBackRestTest::Env::HostEnvTest; use pgBackRestTest::Common::ExecuteTest; use pgBackRestTest::Common::FileTest; use pgBackRestTest::Env::Host::HostBackupTest; use pgBackRestTest::Common::RunTest; #################################################################################################################################### # initModule #################################################################################################################################### sub initModule { my $self = shift; $self->{strDbPath} = $self->testPath() . '/db'; $self->{strRepoPath} = $self->testPath() . '/repo'; $self->{strArchivePath} = "$self->{strRepoPath}/archive/" . $self->stanza(); $self->{strBackupPath} = "$self->{strRepoPath}/backup/" . $self->stanza(); $self->{strSpoolPath} = "$self->{strArchivePath}/out"; } #################################################################################################################################### # initTest #################################################################################################################################### sub initTest { my $self = shift; # Create archive info path storageTest()->pathCreate($self->{strArchivePath}, {bIgnoreExists => true, bCreateParent => true}); # Create backup info path storageTest()->pathCreate($self->{strBackupPath}, {bIgnoreExists => true, bCreateParent => true}); # Generate pg_control file storageTest()->pathCreate($self->{strDbPath} . '/' . DB_PATH_GLOBAL, {bCreateParent => true}); $self->controlGenerate($self->{strDbPath}, PG_VERSION_94); } #################################################################################################################################### # run #################################################################################################################################### sub run { my $self = shift; $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); $self->optionTestSet(CFGOPT_PG_PATH, $self->{strDbPath}); $self->optionTestSet(CFGOPT_REPO_PATH, $self->{strRepoPath}); $self->optionTestSet(CFGOPT_LOG_PATH, $self->testPath()); $self->optionTestSetBool(CFGOPT_ONLINE, false); $self->optionTestSet(CFGOPT_DB_TIMEOUT, 5); $self->optionTestSet(CFGOPT_PROTOCOL_TIMEOUT, 6); my $iDbControl = 942; my $iDbCatalog = 201409291; ################################################################################################################################ if ($self->begin("Stanza::new")) { #--------------------------------------------------------------------------------------------------------------------------- $self->optionTestSetBool(CFGOPT_ONLINE, true); $self->configTestLoad(CFGCMD_STANZA_CREATE); $self->testException(sub {(new pgBackRest::Stanza())}, ERROR_DB_CONNECT, "could not connect to server: No such file or directory\n"); $self->optionTestSetBool(CFGOPT_ONLINE, false); } ################################################################################################################################ if ($self->begin("Stanza::process()")) { #--------------------------------------------------------------------------------------------------------------------------- $self->configTestLoad(CFGCMD_CHECK); my $oStanza = new pgBackRest::Stanza(); $self->testException(sub {$oStanza->process()}, ERROR_ASSERT, "stanza->process() called with invalid command: " . cfgCommandName(CFGCMD_CHECK)); #--------------------------------------------------------------------------------------------------------------------------- $self->configTestLoad(CFGCMD_STANZA_CREATE); rmdir($self->{strArchivePath}); rmdir($self->{strBackupPath}); $self->testResult(sub {$oStanza->process()}, 0, 'parent paths recreated successfully'); } ################################################################################################################################ if ($self->begin("Stanza::stanzaCreate()")) { $self->configTestLoad(CFGCMD_STANZA_CREATE); my $oStanza = new pgBackRest::Stanza(); my $strBackupInfoFile = storageRepo()->pathGet(STORAGE_REPO_BACKUP . qw{/} . FILE_BACKUP_INFO); my $strBackupInfoFileCopy = storageRepo()->pathGet(STORAGE_REPO_BACKUP . qw{/} . FILE_BACKUP_INFO . INI_COPY_EXT); my $strArchiveInfoFile = storageRepo()->pathGet(STORAGE_REPO_ARCHIVE . qw{/} . ARCHIVE_INFO_FILE); # No force. Archive dir not empty. No archive.info file. Backup directory empty. #--------------------------------------------------------------------------------------------------------------------------- storageRepo()->pathCreate(STORAGE_REPO_ARCHIVE . "/9.4-1"); $self->testException(sub {$oStanza->stanzaCreate()}, ERROR_PATH_NOT_EMPTY, "archive directory not empty" . "\nHINT: use stanza-create --force to force the stanza data to be recreated."); # No force. Archive dir not empty. No archive.info file. Backup directory not empty. No backup.info file. #--------------------------------------------------------------------------------------------------------------------------- storageRepo()->pathCreate(STORAGE_REPO_BACKUP . "/12345"); $self->testException(sub {$oStanza->stanzaCreate()}, ERROR_PATH_NOT_EMPTY, "backup directory and/or archive directory not empty" . "\nHINT: use stanza-create --force to force the stanza data to be recreated."); # No force. Archive dir empty. No archive.info file. Backup directory not empty. No backup.info file. #--------------------------------------------------------------------------------------------------------------------------- forceStorageRemove(storageRepo(), STORAGE_REPO_ARCHIVE . "/9.4-1", {bRecurse => true}); $self->testException(sub {$oStanza->stanzaCreate()}, ERROR_PATH_NOT_EMPTY, "backup directory not empty" . "\nHINT: use stanza-create --force to force the stanza data to be recreated."); # No force. No archive.info file and no archive sub-directories or files. Backup.info exists and no backup sub-directories # or files #--------------------------------------------------------------------------------------------------------------------------- forceStorageRemove(storageRepo(), STORAGE_REPO_BACKUP . "/12345", {bRecurse => true}); (new pgBackRest::Backup::Info($self->{strBackupPath}, false, false, {bIgnoreMissing => true}))->create(PG_VERSION_94, $self->dbSysId(PG_VERSION_94), $iDbControl, $iDbCatalog, true); $self->testException(sub {$oStanza->stanzaCreate()}, ERROR_FILE_MISSING, "archive information missing" . "\nHINT: use stanza-create --force to force the stanza data to be recreated."); # No force. No backup.info file (backup.info.copy only) and no backup sub-directories or files. Archive.info exists and no # archive sub-directories or files #--------------------------------------------------------------------------------------------------------------------------- forceStorageRemove(storageRepo(), STORAGE_REPO_BACKUP . qw{/} . FILE_BACKUP_INFO); (new pgBackRest::Archive::Info($self->{strArchivePath}, false, {bIgnoreMissing => true}))->create(PG_VERSION_94, $self->dbSysId(PG_VERSION_94), true); $self->testResult(sub {$oStanza->stanzaCreate()}, 0, "no error on missing backup.info since backup.info.copy exists and DB section OK"); # No force. No backup.info file (backup.info.copy only) and no backup sub-directories or files. No archive.info file # (archive.info.copy only) and no archive sub-directories or files #--------------------------------------------------------------------------------------------------------------------------- forceStorageRemove(storageRepo(), STORAGE_REPO_ARCHIVE . qw{/} . ARCHIVE_INFO_FILE); (new pgBackRest::Archive::Info($self->{strArchivePath}, false, {bIgnoreMissing => true}))->create(PG_VERSION_94, $self->dbSysId(PG_VERSION_94), true); $self->testResult(sub {$oStanza->stanzaCreate()}, 0, "no error on missing archive.info since archive.info.copy exists and DB section OK"); # No force. No backup.info files and no backup sub-directories or files. Archive.info exists and no # archive sub-directories or files #--------------------------------------------------------------------------------------------------------------------------- forceStorageRemove(storageRepo(), STORAGE_REPO_BACKUP . qw{/} . FILE_BACKUP_INFO . INI_COPY_EXT); (new pgBackRest::Archive::Info($self->{strArchivePath}, false, {bIgnoreMissing => true}))->create(PG_VERSION_94, $self->dbSysId(PG_VERSION_94), true); $self->testException(sub {$oStanza->stanzaCreate()}, ERROR_FILE_MISSING, "backup information missing" . "\nHINT: use stanza-create --force to force the stanza data to be recreated."); # No force. archive.info DB mismatch. backup.info correct DB. #--------------------------------------------------------------------------------------------------------------------------- forceStorageRemove(storageRepo(), $strArchiveInfoFile . "*"); forceStorageRemove(storageRepo(), $strBackupInfoFile . "*"); (new pgBackRest::Archive::Info($self->{strArchivePath}, false, {bIgnoreMissing => true}))->create(PG_VERSION_93, $self->dbSysId(PG_VERSION_93), true); (new pgBackRest::Backup::Info($self->{strBackupPath}, false, false, {bIgnoreMissing => true}))->create(PG_VERSION_94, $self->dbSysId(PG_VERSION_94), $iDbControl, $iDbCatalog, true); $self->testException(sub {$oStanza->stanzaCreate()}, ERROR_FILE_INVALID, "backup info file or archive info file invalid\n" . "HINT: use stanza-upgrade if the database has been upgraded or use --force"); # No force. archive.info DB mismatch. backup.info DB mismatch. #--------------------------------------------------------------------------------------------------------------------------- forceStorageRemove(storageRepo(), $strBackupInfoFile . "*"); (new pgBackRest::Backup::Info($self->{strBackupPath}, false, false, {bIgnoreMissing => true}))->create(PG_VERSION_93, $self->dbSysId(PG_VERSION_93), $iDbControl, $iDbCatalog, true); $self->testException(sub {$oStanza->stanzaCreate()}, ERROR_FILE_INVALID, "backup info file or archive info file invalid\n" . "HINT: use stanza-upgrade if the database has been upgraded or use --force"); # No force. Create stanza. #--------------------------------------------------------------------------------------------------------------------------- forceStorageRemove(storageRepo(), $strBackupInfoFile . "*"); forceStorageRemove(storageRepo(), $strArchiveInfoFile . "*"); $self->testResult(sub {$oStanza->stanzaCreate()}, 0, 'successfully created stanza without force'); # No force with .info and .info.copy files already existing #-------------------------------------------------------------------------------------------------------------------------- $self->testResult(sub {$oStanza->stanzaCreate()}, 0, "info files exist and check out ok - stanza create not needed"); # No force. Remove only backup.info.copy file - confirm stanza create does not throw an error since copy is still valid #--------------------------------------------------------------------------------------------------------------------------- forceStorageRemove(storageRepo(), $strBackupInfoFileCopy); $self->testResult(sub {$oStanza->stanzaCreate()}, 0, "info.copy file exists and check out ok - stanza create not needed"); # Force on. Valid archive.info exists. Invalid backup.info exists. #--------------------------------------------------------------------------------------------------------------------------- $self->optionTestSetBool(CFGOPT_FORCE, true); $self->configTestLoad(CFGCMD_STANZA_CREATE); forceStorageRemove(storageRepo(), $strBackupInfoFile . "*"); (new pgBackRest::Backup::Info($self->{strBackupPath}, false, false, {bIgnoreMissing => true}))->create(PG_VERSION_94, $self->dbSysId(PG_VERSION_93), $iDbControl, $iDbCatalog, true); $self->testResult(sub {$oStanza->stanzaCreate()}, 0, 'successfully created stanza with force and existing info files'); $self->testResult(sub {(new pgBackRest::Backup::Info($self->{strBackupPath}))->check(PG_VERSION_94, $iDbControl, $iDbCatalog, $self->dbSysId(PG_VERSION_94))}, 2, ' backup.info reconstructed'); # Force on, Repo-Sync off. Archive dir empty. No archive.info file. Backup directory not empty. No backup.info file. #--------------------------------------------------------------------------------------------------------------------------- forceStorageRemove(storageRepo(), $strBackupInfoFile . "*"); forceStorageRemove(storageRepo(), $strArchiveInfoFile . "*"); storageRepo()->pathCreate(STORAGE_REPO_BACKUP . "/12345"); $oStanza = new pgBackRest::Stanza(); $self->testResult(sub {$oStanza->stanzaCreate()}, 0, 'successfully created stanza with force'); $self->testResult( sub {(new pgBackRest::Archive::Info($self->{strArchivePath}))->check( PG_VERSION_94, $self->dbSysId(PG_VERSION_94)) && (new pgBackRest::Backup::Info($self->{strBackupPath}))->check(PG_VERSION_94, $iDbControl, $iDbCatalog, $self->dbSysId(PG_VERSION_94))}, 1, ' new info files correct'); forceStorageRemove(storageRepo(), storageRepo()->pathGet(STORAGE_REPO_BACKUP . "/12345"), {bRecurse => true}); # Force on. Attempt to change encryption on the repo #--------------------------------------------------------------------------------------------------------------------------- # Create unencrypted archive file storageRepo()->pathCreate(STORAGE_REPO_ARCHIVE . "/" . PG_VERSION_94 . "-1"); storageRepo()->pathCreate(STORAGE_REPO_ARCHIVE . "/" . PG_VERSION_94 . "-1/0000000100000001"); my $strArchiveIdPath = storageRepo()->pathGet(STORAGE_REPO_ARCHIVE . "/" . PG_VERSION_94 . "-1"); my $strArchivedFile = storageRepo()->pathGet($strArchiveIdPath . "/0000000100000001/000000010000000100000001-1e34fa1c833090d94b9bb14f2a8d3153dca6ea27"); executeTest('cp ' . $self->dataPath() . "/filecopy.archive2.bin ${strArchivedFile}"); # Create unencrypted backup manifest file my $strBackupLabel = timestampFileFormat(undef, 1482000000) . 'F'; my $strBackupPath = storageRepo->pathGet(STORAGE_REPO_BACKUP . "/${strBackupLabel}"); my $strBackupManifestFile = "$strBackupPath/" . FILE_MANIFEST; my $oBackupManifest = new pgBackRest::Manifest($strBackupManifestFile, {bLoad => false, strDbVersion => PG_VERSION_94}); storageRepo()->pathCreate($strBackupPath); $oBackupManifest->save(); # Get the unencrypted content for later encryption my $tUnencryptedArchiveContent = ${storageRepo()->get($strArchivedFile)}; my $tUnencryptedBackupContent = ${storageRepo()->get($strBackupManifestFile)}; # Change the permissions on the archived file so reconstruction fails executeTest('sudo chmod 220 ' . $strArchivedFile); $self->testException(sub {(new pgBackRest::Stanza())->stanzaCreate()}, ERROR_FILE_OPEN, "unable to open '" . $strArchivedFile . "': Permission denied"); executeTest('sudo chmod 644 ' . $strArchivedFile); # Clear the cached repo settings and change repo settings to encrypted storageRepoCacheClear($self->stanza()); $self->optionTestSet(CFGOPT_REPO_CIPHER_TYPE, CFGOPTVAL_REPO_CIPHER_TYPE_AES_256_CBC); $self->optionTestSet(CFGOPT_REPO_CIPHER_PASS, 'x'); $self->configTestLoad(CFGCMD_STANZA_CREATE); $self->testException(sub {(new pgBackRest::Stanza())->stanzaCreate()}, ERROR_PATH_NOT_EMPTY, "files exist - the encryption type or passphrase cannot be changed"); # Remove the backup sub directories and files so that the archive file is attempted to be read forceStorageRemove(storageRepo(), $strBackupPath, {bRecurse => true}); $self->testException(sub {(new pgBackRest::Stanza())->stanzaCreate()}, ERROR_PATH_NOT_EMPTY, "files exist - the encryption type or passphrase cannot be changed"); # Remove the archive sub directories and files so that only the info files exist - stanza create is allowed with force #--------------------------------------------------------------------------------------------------------------------------- forceStorageRemove(storageRepo(), $strArchiveIdPath, {bRecurse => true}); $self->testResult(sub {$oStanza->stanzaCreate()}, 0, 'successfully created stanza with force and new encrypted settings'); # Confirm encrypted $self->testResult(sub {storageRepo()->encrypted(storageRepo()->pathGet(STORAGE_REPO_ARCHIVE) . '/' . ARCHIVE_INFO_FILE)}, true, ' new archive info encrypted'); $self->testResult(sub {storageRepo()->encrypted(storageRepo()->pathGet(STORAGE_REPO_BACKUP) . '/' . FILE_BACKUP_INFO)}, true, ' new backup info encrypted'); # Store the unencrypted archived file as encrypted and check stanza-create #--------------------------------------------------------------------------------------------------------------------------- storageRepo()->pathCreate(STORAGE_REPO_ARCHIVE . "/" . PG_VERSION_94 . "-1"); storageRepo()->pathCreate(STORAGE_REPO_ARCHIVE . "/" . PG_VERSION_94 . "-1/0000000100000001"); storageRepo()->put($strArchivedFile, $tUnencryptedArchiveContent); storageRepo()->pathCreate($strBackupPath); # Empty backup path - no backup in progress # Confirm encrypted and create the stanza with force $self->testResult(sub {storageRepo()->encrypted($strArchivedFile)}, true, 'new archive WAL encrypted'); $self->testResult(sub {$oStanza->stanzaCreate()}, 0, ' successfully recreate stanza with force from encrypted WAL'); # Confirm the backup and archive info are encrypted and check the contents $self->testResult(sub {storageRepo()->encrypted(storageRepo()->pathGet(STORAGE_REPO_ARCHIVE) . '/' . ARCHIVE_INFO_FILE)}, true, ' new archive info encrypted'); $self->testResult(sub {storageRepo()->encrypted(storageRepo()->pathGet(STORAGE_REPO_BACKUP) . '/' . FILE_BACKUP_INFO)}, true, ' new backup info encrypted'); $self->testResult( sub {(new pgBackRest::Archive::Info($self->{strArchivePath}))->check( PG_VERSION_94, $self->dbSysId(PG_VERSION_94)) && (new pgBackRest::Backup::Info($self->{strBackupPath}))->check(PG_VERSION_94, $iDbControl, $iDbCatalog, $self->dbSysId(PG_VERSION_94))}, 1, ' new archive.info and backup.info files correct'); # Store the unencrypted backup.manifest file as encrypted and check stanza-create #--------------------------------------------------------------------------------------------------------------------------- # Try to create a manifest without a passphrase in an encrypted storage $self->testException(sub {new pgBackRest::Manifest($strBackupManifestFile, {bLoad => false, strDbVersion => PG_VERSION_94})}, ERROR_CIPHER, 'passphrase is required when storage is encrypted'); # Get the encryption passphrase and create the new manifest my $oBackupInfo = new pgBackRest::Backup::Info($self->{strBackupPath}); $oBackupManifest = new pgBackRest::Manifest($strBackupManifestFile, {bLoad => false, strDbVersion => PG_VERSION_94, strCipherPass => $oBackupInfo->cipherPassSub(), strCipherPassSub => cipherPassGen()}); $oBackupManifest->set(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_LABEL, undef, $strBackupLabel); $oBackupManifest->boolSet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_ARCHIVE_CHECK, undef, true); $oBackupManifest->boolSet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_ARCHIVE_COPY, undef, false); $oBackupManifest->boolSet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_BACKUP_STANDBY, undef, false); $oBackupManifest->set(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_ARCHIVE_START, undef, 1); $oBackupManifest->set(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_ARCHIVE_STOP, undef, 1); $oBackupManifest->boolSet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_CHECKSUM_PAGE, undef, true); $oBackupManifest->boolSet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_COMPRESS, undef, true); $oBackupManifest->boolSet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_HARDLINK, undef, false); $oBackupManifest->boolSet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_ONLINE, undef, true); $oBackupManifest->numericSet(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_TIMESTAMP_START, undef, time()); $oBackupManifest->numericSet(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_TIMESTAMP_STOP, undef, time()); $oBackupManifest->set(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_TYPE, undef, CFGOPTVAL_BACKUP_TYPE_FULL); $oBackupManifest->set(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_DB_VERSION, undef, PG_VERSION_94); $oBackupManifest->numericSet(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_CONTROL, undef, $iDbControl); $oBackupManifest->numericSet(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_CATALOG, undef, $iDbCatalog); $oBackupManifest->numericSet(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_SYSTEM_ID, undef, $self->dbSysId(PG_VERSION_94)); $oBackupManifest->numericSet(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_DB_ID, undef, 1); $oBackupManifest->save(); # Confirm encrypted and create the stanza with force $self->testResult(sub {storageRepo()->encrypted($strBackupManifestFile)}, true, 'new backup manifest encrypted'); $self->testResult(sub {$oStanza->stanzaCreate()}, 0, ' successfully recreate stanza with force from encrypted manifest and WAL'); # Confirm the backup and archive info are encrypted and check the contents $self->testResult(sub {storageRepo()->encrypted(storageRepo()->pathGet(STORAGE_REPO_ARCHIVE) . '/' . ARCHIVE_INFO_FILE)}, true, ' recreated archive info encrypted'); $self->testResult(sub {storageRepo()->encrypted(storageRepo()->pathGet(STORAGE_REPO_BACKUP) . '/' . FILE_BACKUP_INFO)}, true, ' recreated backup info encrypted'); $self->testResult( sub {(new pgBackRest::Archive::Info($self->{strArchivePath}))->check( PG_VERSION_94, $self->dbSysId(PG_VERSION_94)) && (new pgBackRest::Backup::Info($self->{strBackupPath}))->check(PG_VERSION_94, $iDbControl, $iDbCatalog, $self->dbSysId(PG_VERSION_94))}, 1, ' recreated archive.info and backup.info files correct'); # Move the encrypted info files out of the repo so they are missing but backup exists. --force cannot be used #--------------------------------------------------------------------------------------------------------------------------- executeTest('sudo mv ' . storageRepo()->pathGet(STORAGE_REPO_ARCHIVE) . '/' . ARCHIVE_INFO_FILE . '* ' . $self->testPath() . '/'); executeTest('sudo mv ' . storageRepo()->pathGet(STORAGE_REPO_BACKUP) . '/' . FILE_BACKUP_INFO . '* ' . $self->testPath() . '/'); $self->testException(sub {$oStanza->stanzaCreate()}, ERROR_PATH_NOT_EMPTY, "backup directory and/or archive directory not empty and repo is encrypted and info file(s) are missing," . " --force cannot be used"); # Move the files back for the next test executeTest('sudo mv ' . $self->testPath() . '/' . ARCHIVE_INFO_FILE . '* ' . storageRepo()->pathGet(STORAGE_REPO_ARCHIVE) . '/'); executeTest('sudo mv ' . $self->testPath() . '/' . FILE_BACKUP_INFO . '* ' . storageRepo()->pathGet(STORAGE_REPO_BACKUP) . '/'); # Change repo encryption settings to unencrypted - stanza create is not allowed even with force #--------------------------------------------------------------------------------------------------------------------------- # Clear the cached repo settings and change repo settings to unencrypted storageRepoCacheClear($self->stanza()); $self->optionTestClear(CFGOPT_REPO_CIPHER_TYPE); $self->optionTestClear(CFGOPT_REPO_CIPHER_PASS); $self->configTestLoad(CFGCMD_STANZA_CREATE); $self->testException(sub {$oStanza->stanzaCreate()}, ERROR_PATH_NOT_EMPTY, "files exist - the encryption type or passphrase cannot be changed"); # With only info files - stanza create is allowed with force #--------------------------------------------------------------------------------------------------------------------------- forceStorageRemove(storageRepo(), $strArchiveIdPath, {bRecurse => true}); forceStorageRemove(storageRepo(), $strBackupPath, {bRecurse => true}); $self->testResult(sub {$oStanza->stanzaCreate()}, 0, 'successfully created stanza with force and new unencrypted settings'); # Confirm unencrypted $self->testResult(sub {storageRepo()->encrypted(storageRepo()->pathGet(STORAGE_REPO_ARCHIVE) . '/' . ARCHIVE_INFO_FILE)}, false, ' new archive info unencrypted'); $self->testResult(sub {storageRepo()->encrypted(storageRepo()->pathGet(STORAGE_REPO_BACKUP) . '/' . FILE_BACKUP_INFO)}, false, ' new backup info unencrypted'); # Clear --force $self->optionTestClear(CFGOPT_FORCE); } ################################################################################################################################ if ($self->begin("Stanza::infoFileCreate")) { $self->configTestLoad(CFGCMD_STANZA_CREATE); my $oStanza = new pgBackRest::Stanza(); my $oArchiveInfo = new pgBackRest::Archive::Info($self->{strArchivePath}, false, {bIgnoreMissing => true}); # Archive dir not empty. Warning returned. #--------------------------------------------------------------------------------------------------------------------------- storageTest()->pathCreate($self->{strArchivePath} . "/9.3-0", {bIgnoreExists => true, bCreateParent => true}); $self->testResult(sub {$oStanza->infoFileCreate($oArchiveInfo)}, "(0, [undef])", 'successful with archive.info file warning', {strLogExpect => "WARN: found empty directory " . $self->{strArchivePath} . "/9.3-0"}); } ################################################################################################################################ if ($self->begin("Stanza::infoObject()")) { $self->configTestLoad(CFGCMD_STANZA_UPGRADE); my $oStanza = new pgBackRest::Stanza(); $self->testException(sub {$oStanza->infoObject(STORAGE_REPO_BACKUP, $self->{strBackupPath})}, ERROR_FILE_MISSING, storageRepo()->pathGet(STORAGE_REPO_BACKUP . qw{/} . FILE_BACKUP_INFO) . " does not exist and is required to perform a backup." . "\nHINT: has a stanza-create been performed?"); # Force valid but not set. #--------------------------------------------------------------------------------------------------------------------------- $self->configTestLoad(CFGCMD_STANZA_CREATE); $oStanza = new pgBackRest::Stanza(); $self->testException(sub {$oStanza->infoObject(STORAGE_REPO_BACKUP, $self->{strBackupPath})}, ERROR_FILE_MISSING, storageRepo()->pathGet(STORAGE_REPO_BACKUP . qw{/} . FILE_BACKUP_INFO) . " does not exist and is required to perform a backup." . "\nHINT: has a stanza-create been performed?" . "\nHINT: use stanza-create --force to force the stanza data to be recreated."); # Force. #--------------------------------------------------------------------------------------------------------------------------- $self->optionTestSetBool(CFGOPT_FORCE, true); $self->configTestLoad(CFGCMD_STANZA_CREATE); $self->testResult(sub {$oStanza->infoObject(STORAGE_REPO_ARCHIVE, $self->{strArchivePath})}, "[object]", 'archive force successful'); $self->testResult(sub {$oStanza->infoObject(STORAGE_REPO_BACKUP, $self->{strBackupPath})}, "[object]", 'backup force successful'); # Cause an error to be thrown by changing the permissions of the archive directory so it cannot be read #--------------------------------------------------------------------------------------------------------------------------- executeTest('sudo chmod 220 ' . $self->{strArchivePath}); $self->testException(sub {$oStanza->infoObject(STORAGE_REPO_ARCHIVE, $self->{strArchivePath})}, ERROR_FILE_OPEN, "unable to open '" . $self->{strArchivePath} . "/archive.info': Permission denied"); executeTest('sudo chmod 640 ' . $self->{strArchivePath}); # Reset force option -------- $self->optionTestClear(CFGOPT_FORCE); # Cause an error to be thrown by changing the permissions of the backup file so it cannot be read #--------------------------------------------------------------------------------------------------------------------------- $self->configTestLoad(CFGCMD_STANZA_CREATE); (new pgBackRest::Backup::Info($self->{strBackupPath}, false, false, {bIgnoreMissing => true}))->create(PG_VERSION_94, $self->dbSysId(PG_VERSION_94), $iDbControl, $iDbCatalog, true); forceStorageRemove(storageRepo(), storageRepo()->pathGet(STORAGE_REPO_BACKUP . qw{/} . FILE_BACKUP_INFO . INI_COPY_EXT)); executeTest('sudo chmod 220 ' . storageRepo()->pathGet(STORAGE_REPO_BACKUP . qw{/} . FILE_BACKUP_INFO)); $self->testException(sub {$oStanza->infoObject(STORAGE_REPO_BACKUP, $self->{strBackupPath})}, ERROR_FILE_OPEN, "unable to open '" . storageRepo()->pathGet(STORAGE_REPO_BACKUP . qw{/} . FILE_BACKUP_INFO) . "': Permission denied"); executeTest('sudo chmod 640 ' . storageRepo()->pathGet(STORAGE_REPO_BACKUP . qw{/} . FILE_BACKUP_INFO)); } ################################################################################################################################ if ($self->begin("Stanza::stanzaUpgrade()")) { $self->configTestLoad(CFGCMD_STANZA_UPGRADE); my $oArchiveInfo = new pgBackRest::Archive::Info($self->{strArchivePath}, false, {bIgnoreMissing => true}); $oArchiveInfo->create('9.3', '6999999999999999999', true); my $oBackupInfo = new pgBackRest::Backup::Info($self->{strBackupPath}, false, false, {bIgnoreMissing => true}); $oBackupInfo->create('9.3', '6999999999999999999', '937', '201306121', true); $self->configTestLoad(CFGCMD_STANZA_UPGRADE); my $oStanza = new pgBackRest::Stanza(); #--------------------------------------------------------------------------------------------------------------------------- $self->testResult(sub {$oStanza->stanzaUpgrade()}, undef, 'successfully upgraded'); #--------------------------------------------------------------------------------------------------------------------------- $self->testResult(sub {$oStanza->stanzaUpgrade()}, undef, 'upgrade not required'); # Attempt to change the encryption settings #--------------------------------------------------------------------------------------------------------------------------- # Clear the cached repo settings and change repo settings to encrypted storageRepoCacheClear($self->stanza()); $self->optionTestSet(CFGOPT_REPO_CIPHER_TYPE, CFGOPTVAL_REPO_CIPHER_TYPE_AES_256_CBC); $self->optionTestSet(CFGOPT_REPO_CIPHER_PASS, 'x'); $self->configTestLoad(CFGCMD_STANZA_UPGRADE); $self->testException(sub {$oStanza->stanzaUpgrade()}, ERROR_CIPHER, "unable to parse '" . $self->{strArchivePath} . "/archive.info'" . "\nHINT: Is or was the repo encrypted?"); forceStorageRemove(storageRepo(), storageRepo()->pathGet(STORAGE_REPO_BACKUP . qw{/} . FILE_BACKUP_INFO) . "*"); forceStorageRemove(storageRepo(), storageRepo()->pathGet(STORAGE_REPO_ARCHIVE . qw{/} . ARCHIVE_INFO_FILE) . "*"); # Create encrypted info files with prior passphrase then attempt to change #--------------------------------------------------------------------------------------------------------------------------- $oArchiveInfo = new pgBackRest::Archive::Info($self->{strArchivePath}, false, {bIgnoreMissing => true, strCipherPassSub => cipherPassGen()}); $oArchiveInfo->create(PG_VERSION_93, $self->dbSysId(PG_VERSION_93), true); $oBackupInfo = new pgBackRest::Backup::Info($self->{strBackupPath}, false, false, {bIgnoreMissing => true, strCipherPassSub => cipherPassGen()}); $oBackupInfo->create(PG_VERSION_93, $self->dbSysId(PG_VERSION_93), '937', '201306121', true); # Attempt to upgrade with a different passphrase storageRepoCacheClear($self->stanza()); $self->optionTestSet(CFGOPT_REPO_CIPHER_TYPE, CFGOPTVAL_REPO_CIPHER_TYPE_AES_256_CBC); $self->optionTestSet(CFGOPT_REPO_CIPHER_PASS, 'y'); $self->configTestLoad(CFGCMD_STANZA_UPGRADE); $self->testException(sub {$oStanza->stanzaUpgrade()}, ERROR_CIPHER, "unable to parse '" . $self->{strArchivePath} . "/archive.info'" . "\nHINT: Is or was the repo encrypted?"); storageRepoCacheClear($self->stanza()); $self->optionTestSet(CFGOPT_REPO_CIPHER_TYPE, CFGOPTVAL_REPO_CIPHER_TYPE_AES_256_CBC); $self->optionTestSet(CFGOPT_REPO_CIPHER_PASS, 'x'); $self->configTestLoad(CFGCMD_STANZA_UPGRADE); # Create encrypted archived file storageRepo()->pathCreate(STORAGE_REPO_ARCHIVE . "/" . PG_VERSION_93 . "-1"); storageRepo()->pathCreate(STORAGE_REPO_ARCHIVE . "/" . PG_VERSION_93 . "-1/0000000100000001"); my $strArchiveIdPath = storageRepo()->pathGet(STORAGE_REPO_ARCHIVE . "/" . PG_VERSION_93 . "-1"); my $strArchivedFile = storageRepo()->pathGet($strArchiveIdPath . "/0000000100000001/000000010000000100000001-" . $self->walGenerateContentChecksum(PG_VERSION_93)); storageRepo()->put( $strArchivedFile, $self->walGenerateContent(PG_VERSION_93), {strCipherPass => $oArchiveInfo->cipherPassSub()}); $self->testResult(sub {storageRepo()->encrypted($strArchivedFile)}, true, 'created encrypted archive WAL'); # Upgrade $self->testResult(sub {$oStanza->stanzaUpgrade()}, undef, ' successfully upgraded'); $self->testResult(sub {storageRepo()->encrypted(storageRepo()->pathGet(STORAGE_REPO_ARCHIVE) . '/' . ARCHIVE_INFO_FILE)}, true, ' upgraded archive info encrypted'); $self->testResult(sub {storageRepo()->encrypted(storageRepo()->pathGet(STORAGE_REPO_BACKUP) . '/' . FILE_BACKUP_INFO)}, true, ' upgraded backup info encrypted'); $oArchiveInfo = new pgBackRest::Archive::Info($self->{strArchivePath}); $oBackupInfo = new pgBackRest::Backup::Info($self->{strBackupPath}); my $hHistoryArchive = $oArchiveInfo->dbHistoryList(); my $hHistoryBackup = $oBackupInfo->dbHistoryList(); $self->testResult(sub {($hHistoryArchive->{1}{&INFO_DB_VERSION} eq PG_VERSION_93) && ($hHistoryArchive->{1}{&INFO_SYSTEM_ID} eq $self->dbSysId(PG_VERSION_93)) && ($hHistoryArchive->{2}{&INFO_DB_VERSION} eq PG_VERSION_94) && ($hHistoryArchive->{2}{&INFO_SYSTEM_ID} eq $self->dbSysId(PG_VERSION_94)) && ($hHistoryBackup->{1}{&INFO_DB_VERSION} eq PG_VERSION_93) && ($hHistoryBackup->{1}{&INFO_SYSTEM_ID} eq $self->dbSysId(PG_VERSION_93)) && ($hHistoryBackup->{2}{&INFO_DB_VERSION} eq PG_VERSION_94) && ($hHistoryBackup->{2}{&INFO_SYSTEM_ID} eq $self->dbSysId(PG_VERSION_94)) && ($oArchiveInfo->check(PG_VERSION_94, $self->dbSysId(PG_VERSION_94)) eq PG_VERSION_94 . "-2") && ($oBackupInfo->check(PG_VERSION_94, $iDbControl, $iDbCatalog, $self->dbSysId(PG_VERSION_94)) == 2) }, true, ' encrypted archive and backup info files upgraded'); # Clear configuration storageRepoCacheClear($self->stanza()); $self->optionTestClear(CFGOPT_REPO_CIPHER_TYPE); $self->optionTestClear(CFGOPT_REPO_CIPHER_PASS); } ################################################################################################################################ if ($self->begin("Stanza::upgradeCheck()")) { $self->configTestLoad(CFGCMD_STANZA_UPGRADE); my $oStanza = new pgBackRest::Stanza(); # Create the archive file with current data my $oArchiveInfo = new pgBackRest::Archive::Info($self->{strArchivePath}, false, {bIgnoreMissing => true}); $oArchiveInfo->create(PG_VERSION_94, $self->dbSysId(PG_VERSION_94), true); # Create the backup file with outdated data my $oBackupInfo = new pgBackRest::Backup::Info($self->{strBackupPath}, false, false, {bIgnoreMissing => true}); $oBackupInfo->create(PG_VERSION_93, 6999999999999999999, '937', '201306121', true); # Confirm upgrade is needed for backup $self->testResult(sub {$oStanza->upgradeCheck($oBackupInfo, STORAGE_REPO_BACKUP, ERROR_BACKUP_MISMATCH)}, true, 'backup upgrade needed'); $self->testResult(sub {$oStanza->upgradeCheck($oArchiveInfo, STORAGE_REPO_ARCHIVE, ERROR_ARCHIVE_MISMATCH)}, false, 'archive upgrade not needed'); # Change archive file to contain outdated data $oArchiveInfo->create(PG_VERSION_93, 6999999999999999999, true); # Confirm upgrade is needed for both $self->testResult(sub {$oStanza->upgradeCheck($oArchiveInfo, STORAGE_REPO_ARCHIVE, ERROR_ARCHIVE_MISMATCH)}, true, 'archive upgrade needed'); $self->testResult(sub {$oStanza->upgradeCheck($oBackupInfo, STORAGE_REPO_BACKUP, ERROR_BACKUP_MISMATCH)}, true, 'backup upgrade needed'); # Change the backup file to contain current data $oBackupInfo->create(PG_VERSION_94, $self->dbSysId(PG_VERSION_94), $iDbControl, $iDbCatalog, true); # Confirm upgrade is needed for archive $self->testResult(sub {$oStanza->upgradeCheck($oBackupInfo, STORAGE_REPO_BACKUP, ERROR_BACKUP_MISMATCH)}, false, 'backup upgrade not needed'); $self->testResult(sub {$oStanza->upgradeCheck($oArchiveInfo, STORAGE_REPO_ARCHIVE, ERROR_ARCHIVE_MISMATCH)}, true, 'archive upgrade needed'); #--------------------------------------------------------------------------------------------------------------------------- # Perform an upgrade and then confirm upgrade is not necessary $oStanza->process(); $oArchiveInfo = new pgBackRest::Archive::Info($self->{strArchivePath}); $oBackupInfo = new pgBackRest::Backup::Info($self->{strBackupPath}); $self->testResult(sub {$oStanza->upgradeCheck($oArchiveInfo, STORAGE_REPO_ARCHIVE, ERROR_ARCHIVE_MISMATCH)}, false, 'archive upgrade not necessary'); $self->testResult(sub {$oStanza->upgradeCheck($oBackupInfo, STORAGE_REPO_BACKUP, ERROR_BACKUP_MISMATCH)}, false, 'backup upgrade not necessary'); #--------------------------------------------------------------------------------------------------------------------------- # Change the DB data $oStanza->{oDb}{strDbVersion} = '9.3'; $oStanza->{oDb}{ullDbSysId} = 6999999999999999999; # Pass an expected error that is different than the actual error and confirm an error is thrown $self->testException(sub {$oStanza->upgradeCheck($oArchiveInfo, STORAGE_REPO_ARCHIVE, ERROR_ASSERT)}, ERROR_ARCHIVE_MISMATCH, "WAL segment version 9.3 does not match archive version 9.4\n" . 'WAL segment system-id 6999999999999999999 does not match archive system-id ' . $self->dbSysId(PG_VERSION_94) . "\n" . "HINT: are you archiving to the correct stanza?"); $self->testException(sub {$oStanza->upgradeCheck($oBackupInfo, STORAGE_REPO_BACKUP, ERROR_ASSERT)}, ERROR_BACKUP_MISMATCH, "database version = 9.3, system-id 6999999999999999999 does not match backup version = 9.4, " . 'system-id = ' . $self->dbSysId(PG_VERSION_94) . "\nHINT: is this the correct stanza?"); } ################################################################################################################################ if ($self->begin("Stanza::errorForce()")) { $self->configTestLoad(CFGCMD_STANZA_CREATE); my $oStanza = new pgBackRest::Stanza(); my $strMessage = "archive information missing" . "\nHINT: use stanza-create --force to force the stanza data to be recreated."; $self->testException(sub {$oStanza->errorForce($strMessage, ERROR_FILE_MISSING, undef, true, $self->{strArchivePath}, $self->{strBackupPath})}, ERROR_FILE_MISSING, $strMessage); my $strFile = $self->{strArchivePath} . qw{/} . 'file.txt'; my $strFileContent = 'TESTDATA'; executeTest("echo -n '${strFileContent}' | tee ${strFile}"); $self->testException(sub {$oStanza->errorForce($strMessage, ERROR_FILE_MISSING, $strFile, true, $self->{strArchivePath}, $self->{strBackupPath})}, ERROR_FILE_MISSING, $strMessage); } ################################################################################################################################ if ($self->begin("Stanza::stanzaDelete()")) { # Create the stanza $self->configTestLoad(CFGCMD_STANZA_CREATE); my $oStanza = new pgBackRest::Stanza(); $oStanza->stanzaCreate(); # Attempt to delete without running stop #--------------------------------------------------------------------------------------------------------------------------- $self->optionTestClear(CFGOPT_ONLINE); $self->configTestLoad(CFGCMD_STANZA_DELETE); $self->testException(sub {$oStanza->stanzaDelete()}, ERROR_FILE_MISSING, "stop file does not exist for stanza '" . $self->stanza() . "'" . "\nHINT: has the pgbackrest stop command been run on this server?"); # Create a stop file and attempt to delete with postgres running #--------------------------------------------------------------------------------------------------------------------------- lockStop(); # Simulate postgres still running executeTest('touch ' . $self->{strDbPath} . qw(/) . DB_FILE_POSTMASTERPID); $self->testException(sub {$oStanza->stanzaDelete()}, ERROR_POSTMASTER_RUNNING, DB_FILE_POSTMASTERPID . " exists - looks like the postmaster is running. " . "To delete stanza '" . $self->stanza() . "', shutdown the postmaster for stanza '" . $self->stanza() . "' and try again, or use --force."); # Force deletion #--------------------------------------------------------------------------------------------------------------------------- $self->optionTestSetBool(CFGOPT_FORCE, true); $self->configTestLoad(CFGCMD_STANZA_DELETE); $self->testResult(sub {$oStanza->stanzaDelete()}, undef, 'successfully delete stanza with force'); $self->testResult(sub {storageRepo()->pathExists($self->{strArchivePath}) || storageRepo()->pathExists($self->{strBackupPath})}, false, ' neither archive nor backup repo paths for the stanza exist'); # Remove postmaster.pid and clear force storageTest()->remove($self->{strDbPath} . qw(/) . DB_FILE_POSTMASTERPID); $self->optionTestClear(CFGOPT_FORCE); # Rerun stanza-delete without force and with missing stanza directories #--------------------------------------------------------------------------------------------------------------------------- $self->testResult(sub {$oStanza->stanzaDelete()}, undef, 'successful - stanza already deleted'); # Recursive dir delete with archive directory and stanza directory but missing info files #--------------------------------------------------------------------------------------------------------------------------- storageTest()->pathCreate($self->{strArchivePath}, {bIgnoreExists => true, bCreateParent => true}); storageRepo()->pathCreate(STORAGE_REPO_ARCHIVE . "/" . PG_VERSION_94 . "-1", {bCreateParent => true}); storageRepo()->pathCreate(STORAGE_REPO_ARCHIVE . "/" . PG_VERSION_94 . "-1/0000000100000001"); executeTest('touch ' . $self->{strArchivePath} . "/" . PG_VERSION_94 . "-1/0000000100000001/" . BOGUS); storageTest()->pathCreate($self->{strBackupPath}, {bIgnoreExists => true, bCreateParent => true}); my $strFullLabel = backupLabelFormat(CFGOPTVAL_BACKUP_TYPE_FULL, undef, 1482000000); storageRepo()->pathCreate(STORAGE_REPO_BACKUP . "/${strFullLabel}", {bCreateParent => true}); executeTest('touch ' . $self->{strBackupPath} . "/${strFullLabel}/" . BOGUS); # Create an inaccessible file executeTest("sudo chgrp 777 " . $self->{strBackupPath} . "/${strFullLabel}/" . BOGUS); executeTest("sudo chown 777 " . $self->{strBackupPath} . "/${strFullLabel}/" . BOGUS); lockStop(); $self->testResult(sub {$oStanza->stanzaDelete()}, undef, 'successful - recursive delete with missing info files and inaccessible file'); $self->testResult(sub {storageRepo()->pathExists($self->{strArchivePath}) || storageRepo()->pathExists($self->{strBackupPath})}, false, ' neither archive nor backup repo paths for the stanza exist'); # Make the archive directory inaccessible #--------------------------------------------------------------------------------------------------------------------------- storageTest()->pathCreate($self->{strArchivePath}, {bIgnoreExists => true, bCreateParent => true}); executeTest("sudo chgrp 7777 " . $self->{strArchivePath}); executeTest("sudo chown 7777 " . $self->{strArchivePath}); lockStop(); $self->testException(sub {$oStanza->stanzaDelete()}, ERROR_FILE_OPEN, "unable to remove file '" . $self->{strArchivePath} . "/" . ARCHIVE_INFO_FILE . "': Permission denied"); # Remove the repo executeTest("sudo rm -rf " . $self->{strArchivePath}); # Clear the cached repo settings and change repo settings to encrypted #--------------------------------------------------------------------------------------------------------------------------- storageRepoCacheClear($self->stanza()); $self->optionTestSet(CFGOPT_REPO_CIPHER_TYPE, CFGOPTVAL_REPO_CIPHER_TYPE_AES_256_CBC); $self->optionTestSet(CFGOPT_REPO_CIPHER_PASS, 'x'); $self->configTestLoad(CFGCMD_STANZA_CREATE); $self->testResult(sub {$oStanza->stanzaCreate()}, 0, 'successfully created encrypted stanza'); # Create encrypted archived file storageRepo()->pathCreate(STORAGE_REPO_ARCHIVE . "/" . PG_VERSION_93 . "-1"); storageRepo()->pathCreate(STORAGE_REPO_ARCHIVE . "/" . PG_VERSION_93 . "-1/0000000100000001"); my $strArchiveIdPath = storageRepo()->pathGet(STORAGE_REPO_ARCHIVE . "/" . PG_VERSION_93 . "-1"); my $strArchivedFile = storageRepo()->pathGet($strArchiveIdPath . "/0000000100000001/000000010000000100000001-" . $self->walGenerateContentChecksum(PG_VERSION_93)); my $oArchiveInfo = new pgBackRest::Archive::Info($self->{strArchivePath}); storageRepo()->put( $strArchivedFile, $self->walGenerateContent(PG_VERSION_93), {strCipherPass => $oArchiveInfo->cipherPassSub()}); $self->testResult(sub {storageRepo()->encrypted($strArchivedFile)}, true, ' created encrypted archive WAL'); my $strBackupPath = storageRepo->pathGet(STORAGE_REPO_BACKUP . "/${strFullLabel}"); my $strBackupManifestFile = "$strBackupPath/" . FILE_MANIFEST; my $iDbCatalogVersion = 201409291; storageRepo()->pathCreate(STORAGE_REPO_BACKUP . "/${strFullLabel}", {bCreateParent => true}); my $oBackupInfo = new pgBackRest::Backup::Info($self->{strBackupPath}); $self->testResult(sub {(new pgBackRest::Manifest($strBackupManifestFile, {bLoad => false, strDbVersion => PG_VERSION_94, strCipherPass => $oBackupInfo->cipherPassSub(), strCipherPassSub => 'x'}))->save()}, "[undef]", ' manifest saved'); lockStop(); $self->testResult(sub {$oStanza->stanzaDelete()}, undef, ' successful - recursive delete on encrypted repo'); $self->testResult(sub {storageRepo()->pathExists($self->{strArchivePath}) || storageRepo()->pathExists($self->{strBackupPath})}, false, ' neither archive nor backup repo paths for the stanza exist'); # For test coverage: create new stanza with delete command, call process and remove only backup path #--------------------------------------------------------------------------------------------------------------------------- lockStop(); $self->configTestLoad(CFGCMD_STANZA_DELETE); $oStanza = new pgBackRest::Stanza(); storageTest()->pathCreate($self->{strBackupPath}, {bIgnoreExists => true, bCreateParent => true}); $self->testResult(sub {$oStanza->process()}, 0, 'successfully remove backup path'); $self->testResult(sub {storageRepo()->pathExists($self->{strArchivePath}) || storageRepo()->pathExists($self->{strBackupPath})}, false, ' neither archive nor backup repo paths for the stanza exist'); } } 1;