You've already forked pgbackrest
mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2026-10-23 21:28:56 +03:00
At format 6 the repository can hold more than one encryption key. This is used to rotate the archive key, which is currently the only bulk data key that never changes.
Keys are stored by id in the new CipherSpecMap, with one key marked current for encrypting new files. At format 6 the cipher section of an info file stores every key by id along with the digest it derives with, e.g. cipher-pass={"1":{"digest":"sha256","key":"..."}}, and archive.info also records the current key id in cipher-pass-current. Format 5 files keep their single key, which is loaded as the key with no id.
archive-push writes the id of the archive key it used into the header of each WAL file. The readers use that id to select the key: archive-get, verify, the restore timeline read, the backup archive copy, and repo-get. A pusher that loaded archive.info before a rotation keeps working since each file records the key it was encrypted with. The filter that writes the header now also works through a repository host.
stanza-create for format 6 creates the first archive key with id 1. stanza-upgrade from format 5 keeps the existing archive key as id 0 so WAL already archived stays readable, and adds a key at id 1 for new WAL.
The new repo-cipher-rotate option sets how old the current archive key may be before expire replaces it, with a default of 90 days and a minimum of 15. Expire adds the new key, makes it current, and records the time of the rotation in archive.info as cipher-pass-rotate. At format 5 the option has no effect and warns only when set explicitly.
Expire now saves archive.info on every run. Backup did this only to refresh the timestamps for object store lifecycle rules and no longer writes archive.info, so with expire-auto disabled the timestamps are refreshed only when expire runs.
2634 lines
164 KiB
C
2634 lines
164 KiB
C
/***********************************************************************************************************************************
|
|
Test Verify Command
|
|
***********************************************************************************************************************************/
|
|
#include "command/backup/protocol.h"
|
|
#include "command/stanza/create.h"
|
|
#include "common/io/bufferRead.h"
|
|
#include "postgres/interface.h"
|
|
#include "postgres/version.h"
|
|
#include "storage/posix/storage.h"
|
|
|
|
#include "harness/backup.h"
|
|
#include "harness/config.h"
|
|
#include "harness/info.h"
|
|
#include "harness/postgres.h"
|
|
#include "harness/pq.h"
|
|
|
|
#include "harness/protocol.h"
|
|
|
|
/***********************************************************************************************************************************
|
|
Test Run
|
|
***********************************************************************************************************************************/
|
|
static void
|
|
testRun(void)
|
|
{
|
|
FUNCTION_HARNESS_VOID();
|
|
|
|
// Shim backupFileComparator to place pg_control at end of bundle
|
|
hrnBackupFileComparatorShim();
|
|
|
|
// Create storage
|
|
Storage *storageTest = storagePosixNewP(TEST_PATH_STR, .write = true);
|
|
|
|
// Install local command handler shim
|
|
static const ProtocolServerHandler testLocalHandlerList[] = {
|
|
PROTOCOL_SERVER_HANDLER_BACKUP_LIST
|
|
PROTOCOL_SERVER_HANDLER_VERIFY_LIST
|
|
};
|
|
hrnProtocolLocalShimInstall(LSTDEF(testLocalHandlerList));
|
|
|
|
StringList *argListBase = strLstNew();
|
|
hrnCfgArgRawZ(argListBase, cfgOptStanza, "db");
|
|
hrnCfgArgRawZ(argListBase, cfgOptRepoPath, TEST_PATH "/repo");
|
|
|
|
const char *fileContents = "acefile";
|
|
uint64_t fileSize = 7;
|
|
const Buffer *fileChecksum = bufNewDecode(encodingHex, STRDEF("d1cd8a7d11daa26814b93eb604e1d49ab4b43770"));
|
|
|
|
#define TEST_BACKUP_DB1_96 \
|
|
"db-catalog-version=201608131\n" \
|
|
"db-control-version=960\n" \
|
|
"db-id=1\n" \
|
|
"db-system-id=" HRN_PG_SYSTEMID_96_Z "\n" \
|
|
"db-version=\"9.6\"\n"
|
|
|
|
#define TEST_BACKUP_DB2_11 \
|
|
"db-catalog-version=201707211\n" \
|
|
"db-control-version=1100\n" \
|
|
"db-id=2\n" \
|
|
"db-system-id=" HRN_PG_SYSTEMID_11_Z "\n" \
|
|
"db-version=\"11\"\n"
|
|
|
|
#define TEST_BACKUP_DB1_CURRENT_FULL1 \
|
|
"20181119-152138F={" \
|
|
"\"backrest-format\":5,\"backrest-version\":\"2.28dev\"," \
|
|
"\"backup-archive-start\":\"000000010000000000000002\",\"backup-archive-stop\":\"000000010000000000000002\"," \
|
|
"\"backup-info-repo-size\":2369186,\"backup-info-repo-size-delta\":2369186," \
|
|
"\"backup-info-size\":20162900,\"backup-info-size-delta\":20162900," \
|
|
"\"backup-timestamp-start\":1482182846,\"backup-timestamp-stop\":1482182861,\"backup-type\":\"full\"," \
|
|
"\"db-id\":1,\"option-archive-check\":true,\"option-archive-copy\":false,\"option-backup-standby\":false," \
|
|
"\"option-checksum-page\":true,\"option-compress\":true,\"option-hardlink\":false,\"option-online\":true}\n"
|
|
|
|
#define TEST_BACKUP_DB1_CURRENT_FULL2 \
|
|
"20181119-152800F={" \
|
|
"\"backrest-format\":5,\"backrest-version\":\"2.08dev\"," \
|
|
"\"backup-info-repo-size\":2369186,\"backup-info-repo-size-delta\":2369186," \
|
|
"\"backup-info-size\":20162900,\"backup-info-size-delta\":20162900," \
|
|
"\"backup-timestamp-start\":1542640898,\"backup-timestamp-stop\":1542640911,\"backup-type\":\"full\"," \
|
|
"\"db-id\":1,\"option-archive-check\":true,\"option-archive-copy\":false,\"option-backup-standby\":false," \
|
|
"\"option-checksum-page\":true,\"option-compress\":true,\"option-hardlink\":false,\"option-online\":true}\n"
|
|
|
|
#define TEST_BACKUP_DB1_CURRENT_FULL3 \
|
|
"20181119-152900F={" \
|
|
"\"backrest-format\":5,\"backrest-version\":\"2.08dev\"," \
|
|
"\"backup-archive-start\":\"000000010000000000000004\",\"backup-archive-stop\":\"000000010000000000000004\"," \
|
|
"\"backup-info-repo-size\":2369186,\"backup-info-repo-size-delta\":2369186," \
|
|
"\"backup-info-size\":20162900,\"backup-info-size-delta\":20162900," \
|
|
"\"backup-timestamp-start\":1542640898,\"backup-timestamp-stop\":1542640911,\"backup-type\":\"full\"," \
|
|
"\"db-id\":1,\"option-archive-check\":true,\"option-archive-copy\":false,\"option-backup-standby\":false," \
|
|
"\"option-checksum-page\":true,\"option-compress\":true,\"option-hardlink\":false,\"option-online\":true}\n"
|
|
|
|
#define TEST_BACKUP_DB1_HISTORY \
|
|
"1={\"db-catalog-version\":201608131,\"db-control-version\":960,\"db-system-id\":" HRN_PG_SYSTEMID_96_Z "," \
|
|
"\"db-version\":\"9.6\"}"
|
|
|
|
#define TEST_BACKUP_DB2_HISTORY \
|
|
"2={\"db-catalog-version\":201707211,\"db-control-version\":1100,\"db-system-id\":" HRN_PG_SYSTEMID_11_Z "," \
|
|
"\"db-version\":\"11\"}"
|
|
|
|
#define TEST_BACKUP_INFO_MULTI_HISTORY_BASE \
|
|
"[backup:current]\n" \
|
|
TEST_BACKUP_DB1_CURRENT_FULL1 \
|
|
TEST_BACKUP_DB1_CURRENT_FULL2 \
|
|
TEST_BACKUP_DB1_CURRENT_FULL3 \
|
|
"\n" \
|
|
"[db]\n" \
|
|
TEST_BACKUP_DB2_11 \
|
|
"\n" \
|
|
"[db:history]\n" \
|
|
TEST_BACKUP_DB1_HISTORY \
|
|
"\n" \
|
|
TEST_BACKUP_DB2_HISTORY
|
|
|
|
#define TEST_NO_CURRENT_BACKUP \
|
|
"[db]\n" \
|
|
TEST_BACKUP_DB2_11 \
|
|
"\n" \
|
|
"[db:history]\n" \
|
|
TEST_BACKUP_DB1_HISTORY \
|
|
"\n" \
|
|
TEST_BACKUP_DB2_HISTORY
|
|
|
|
#define TEST_ARCHIVE_INFO_BASE \
|
|
"[db]\n" \
|
|
"db-id=1\n" \
|
|
"db-system-id=" HRN_PG_SYSTEMID_96_Z "\n" \
|
|
"db-version=\"9.6\"\n" \
|
|
"\n" \
|
|
"[db:history]\n" \
|
|
"1={\"db-id\":" HRN_PG_SYSTEMID_96_Z ",\"db-version\":\"9.6\"}"
|
|
|
|
#define TEST_ARCHIVE_INFO_MULTI_HISTORY_BASE \
|
|
"[db]\n" \
|
|
"db-id=2\n" \
|
|
"db-system-id=" HRN_PG_SYSTEMID_11_Z "\n" \
|
|
"db-version=\"11\"\n" \
|
|
"\n" \
|
|
"[db:history]\n" \
|
|
"1={\"db-id\":" HRN_PG_SYSTEMID_96_Z ",\"db-version\":\"9.6\"}\n" \
|
|
"2={\"db-id\":" HRN_PG_SYSTEMID_11_Z ",\"db-version\":\"11\"}"
|
|
|
|
#define TEST_MANIFEST_HEADER \
|
|
"[backup]\n" \
|
|
"backup-label=null\n" \
|
|
"backup-timestamp-copy-start=0\n" \
|
|
"backup-timestamp-start=1565282114\n" \
|
|
"backup-timestamp-stop=0\n" \
|
|
"backup-type=\"full\"\n"
|
|
|
|
#define TEST_MANIFEST_DB_94 \
|
|
"\n" \
|
|
"[backup:db]\n" \
|
|
"db-catalog-version=201608131\n" \
|
|
"db-control-version=960\n" \
|
|
"db-id=1\n" \
|
|
"db-system-id=" HRN_PG_SYSTEMID_96_Z "\n" \
|
|
"db-version=\"9.6\"\n"
|
|
|
|
#define TEST_MANIFEST_OPTION_ALL \
|
|
"\n" \
|
|
"[backup:option]\n" \
|
|
"option-archive-check=false\n" \
|
|
"option-archive-copy=false\n" \
|
|
"option-checksum-page=false\n" \
|
|
"option-compress=false\n" \
|
|
"option-compress-type=\"none\"\n" \
|
|
"option-hardlink=false\n" \
|
|
"option-online=false\n"
|
|
|
|
#define TEST_MANIFEST_OPTION_ARCHIVE_TRUE \
|
|
"\n" \
|
|
"[backup:option]\n" \
|
|
"option-archive-check=true\n" \
|
|
"option-archive-copy=true\n"
|
|
|
|
#define TEST_MANIFEST_TARGET \
|
|
"\n" \
|
|
"[backup:target]\n" \
|
|
"pg_data={\"path\":\"/pg/base\",\"type\":\"path\"}\n"
|
|
|
|
#define TEST_MANIFEST_DB \
|
|
"\n" \
|
|
"[db]\n" \
|
|
"postgres={\"db-id\":12173,\"db-last-system-id\":12168}\n"
|
|
|
|
#define TEST_MANIFEST_FILE \
|
|
"\n" \
|
|
"[target:file]\n" \
|
|
"pg_data/PG_VERSION={\"checksum\":\"184473f470864e067ee3a22e64b47b0a1c356f29\",\"size\":4,\"timestamp\":1565282114}\n"
|
|
|
|
#define TEST_MANIFEST_FILE_DEFAULT \
|
|
"\n" \
|
|
"[target:file:default]\n" \
|
|
"group=\"group1\"\n" \
|
|
"mode=\"0600\"\n" \
|
|
"user=\"user1\"\n"
|
|
|
|
#define TEST_MANIFEST_LINK \
|
|
"\n" \
|
|
"[target:link]\n" \
|
|
"pg_data/pg_stat={\"destination\":\"../pg_stat\"}\n"
|
|
|
|
#define TEST_MANIFEST_LINK_DEFAULT \
|
|
"\n" \
|
|
"[target:link:default]\n" \
|
|
"group=\"group1\"\n" \
|
|
"user=false\n"
|
|
|
|
#define TEST_MANIFEST_PATH \
|
|
"\n" \
|
|
"[target:path]\n" \
|
|
"pg_data={\"user\":\"user1\"}\n" \
|
|
|
|
#define TEST_MANIFEST_PATH_DEFAULT \
|
|
"\n" \
|
|
"[target:path:default]\n" \
|
|
"group=false\n" \
|
|
"mode=\"0700\"\n" \
|
|
"user=\"user1\"\n"
|
|
|
|
#define TEST_INVALID_BACKREST_INFO \
|
|
"[backrest]\n" \
|
|
"backrest-checksum=\"BOGUS\"\n" \
|
|
"backrest-format=5\n" \
|
|
"backrest-version=\"2.28\"\n"
|
|
|
|
// *****************************************************************************************************************************
|
|
if (testBegin("verifyManifestFile()"))
|
|
{
|
|
// Load Parameters
|
|
StringList *argList = strLstDup(argListBase);
|
|
HRN_CFG_LOAD(cfgCmdVerify, argList);
|
|
|
|
#define TEST_BACKUP_LABEL_FULL "20181119-152138F"
|
|
|
|
Manifest *manifest = NULL;
|
|
unsigned int jobErrorTotal = 0;
|
|
VerifyBackupResult backupResult = {.backupLabel = strNewZ(TEST_BACKUP_LABEL_FULL)};
|
|
|
|
InfoPg *infoPg = NULL;
|
|
TEST_ASSIGN(
|
|
infoPg,
|
|
infoArchivePg(infoArchiveNewLoad(ioBufferReadNew(harnessInfoChecksumZ(TEST_ARCHIVE_INFO_BASE)), cipherSpecNewNone())),
|
|
"infoPg from archive.info");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("manifest.copy exists, no manifest main, manifest db version not in history, not current db");
|
|
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), STORAGE_REPO_BACKUP "/" TEST_BACKUP_LABEL_FULL "/" BACKUP_MANIFEST_FILE,
|
|
TEST_MANIFEST_HEADER
|
|
"\n"
|
|
"[backup:db]\n"
|
|
"db-catalog-version=201608131\n"
|
|
"db-control-version=960\n"
|
|
"db-id=1\n"
|
|
"db-system-id=" HRN_PG_SYSTEMID_96_Z "\n"
|
|
"db-version=\"10\"\n"
|
|
TEST_MANIFEST_OPTION_ALL
|
|
TEST_MANIFEST_TARGET
|
|
TEST_MANIFEST_DB
|
|
TEST_MANIFEST_FILE
|
|
TEST_MANIFEST_FILE_DEFAULT
|
|
TEST_MANIFEST_LINK
|
|
TEST_MANIFEST_LINK_DEFAULT
|
|
TEST_MANIFEST_PATH
|
|
TEST_MANIFEST_PATH_DEFAULT,
|
|
.comment = "manifest db section mismatch");
|
|
|
|
harnessLogLevelSet(logLevelDetail);
|
|
|
|
backupResult.status = backupValid;
|
|
TEST_ASSIGN(
|
|
manifest, verifyManifestFile(&backupResult, cipherSpecNewNone(), false, infoPg, &jobErrorTotal), "verify manifest");
|
|
TEST_RESULT_PTR(manifest, NULL, "manifest not set - pg version mismatch");
|
|
TEST_RESULT_UINT(backupResult.status, backupInvalid, "manifest unusable - backup invalid");
|
|
TEST_RESULT_LOG(
|
|
"P00 DETAIL: unable to open missing file '" TEST_PATH "/repo/backup/db/20181119-152138F/backup.manifest.copy'"
|
|
" for read\n"
|
|
"P00 INFO: '20181119-152138F' may not be recoverable - PG data (id 1, version 10, system-id"
|
|
" " HRN_PG_SYSTEMID_96_Z ") is not in the backup.info history, skipping");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("rerun test with db-system-id invalid and no main");
|
|
|
|
HRN_STORAGE_REMOVE(storageRepoWrite(), STORAGE_REPO_BACKUP "/" TEST_BACKUP_LABEL_FULL "/" BACKUP_MANIFEST_FILE);
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), STORAGE_REPO_BACKUP "/" TEST_BACKUP_LABEL_FULL "/" BACKUP_MANIFEST_FILE INFO_COPY_EXT,
|
|
TEST_MANIFEST_HEADER
|
|
"\n"
|
|
"[backup:db]\n"
|
|
"db-catalog-version=201608131\n"
|
|
"db-control-version=960\n"
|
|
"db-id=1\n"
|
|
"db-system-id=0\n"
|
|
"db-version=\"9.6\"\n"
|
|
TEST_MANIFEST_OPTION_ALL
|
|
TEST_MANIFEST_TARGET
|
|
TEST_MANIFEST_DB
|
|
TEST_MANIFEST_FILE
|
|
TEST_MANIFEST_FILE_DEFAULT
|
|
TEST_MANIFEST_LINK
|
|
TEST_MANIFEST_LINK_DEFAULT
|
|
TEST_MANIFEST_PATH
|
|
TEST_MANIFEST_PATH_DEFAULT,
|
|
.comment = "manifest copy - invalid system-id");
|
|
|
|
backupResult.status = backupValid;
|
|
TEST_ASSIGN(
|
|
manifest, verifyManifestFile(&backupResult, cipherSpecNewNone(), false, infoPg, &jobErrorTotal), "verify manifest");
|
|
TEST_RESULT_PTR(manifest, NULL, "manifest not set - pg system-id mismatch");
|
|
TEST_RESULT_UINT(backupResult.status, backupInvalid, "manifest unusable - backup invalid");
|
|
TEST_RESULT_LOG(
|
|
"P00 DETAIL: unable to open missing file '" TEST_PATH "/repo/backup/db/20181119-152138F/backup.manifest' for read\n"
|
|
"P00 DETAIL: 20181119-152138F/backup.manifest is missing or unusable, using copy\n"
|
|
"P00 INFO: '20181119-152138F' may not be recoverable - PG data (id 1, version 9.6, system-id 0) is not in the"
|
|
" backup.info history, skipping");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("rerun copy test with db-id invalid");
|
|
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), STORAGE_REPO_BACKUP "/" TEST_BACKUP_LABEL_FULL "/" BACKUP_MANIFEST_FILE INFO_COPY_EXT,
|
|
TEST_MANIFEST_HEADER
|
|
"\n"
|
|
"[backup:db]\n"
|
|
"db-catalog-version=201608131\n"
|
|
"db-control-version=960\n"
|
|
"db-id=0\n"
|
|
"db-system-id=" HRN_PG_SYSTEMID_96_Z "\n"
|
|
"db-version=\"9.6\"\n"
|
|
TEST_MANIFEST_OPTION_ALL
|
|
TEST_MANIFEST_TARGET
|
|
TEST_MANIFEST_DB
|
|
TEST_MANIFEST_FILE
|
|
TEST_MANIFEST_FILE_DEFAULT
|
|
TEST_MANIFEST_LINK
|
|
TEST_MANIFEST_LINK_DEFAULT
|
|
TEST_MANIFEST_PATH
|
|
TEST_MANIFEST_PATH_DEFAULT,
|
|
.comment = "manifest copy - invalid db-id");
|
|
|
|
backupResult.status = backupValid;
|
|
TEST_ASSIGN(
|
|
manifest, verifyManifestFile(&backupResult, cipherSpecNewNone(), false, infoPg, &jobErrorTotal), "verify manifest");
|
|
TEST_RESULT_PTR(manifest, NULL, "manifest not set - pg db-id mismatch");
|
|
TEST_RESULT_UINT(backupResult.status, backupInvalid, "manifest unusable - backup invalid");
|
|
TEST_RESULT_LOG(
|
|
"P00 DETAIL: unable to open missing file '" TEST_PATH "/repo/backup/db/20181119-152138F/backup.manifest' for read\n"
|
|
"P00 DETAIL: 20181119-152138F/backup.manifest is missing or unusable, using copy\n"
|
|
"P00 INFO: '20181119-152138F' may not be recoverable - PG data (id 0, version 9.6, system-id"
|
|
" " HRN_PG_SYSTEMID_96_Z ") is not in the backup.info history, skipping");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("missing main manifest, errored copy");
|
|
|
|
backupResult.status = backupValid;
|
|
|
|
HRN_STORAGE_PUT_Z(
|
|
storageRepoWrite(), TEST_PATH "/repo/" STORAGE_PATH_BACKUP "/db/" TEST_BACKUP_LABEL_FULL "/" BACKUP_MANIFEST_FILE
|
|
INFO_COPY_EXT, TEST_INVALID_BACKREST_INFO, .comment = "invalid manifest copy");
|
|
|
|
TEST_ASSIGN(
|
|
manifest, verifyManifestFile(&backupResult, cipherSpecNewNone(), false, infoPg, &jobErrorTotal), "verify manifest");
|
|
TEST_RESULT_UINT(backupResult.status, backupInvalid, "manifest unusable - backup invalid");
|
|
TEST_RESULT_LOG(
|
|
"P00 DETAIL: unable to open missing file '" TEST_PATH "/repo/backup/db/20181119-152138F/backup.manifest' for read\n"
|
|
"P00 DETAIL: invalid checksum, actual 'e056f784a995841fd4e2802b809299b8db6803a2' but expected 'BOGUS'"
|
|
" <REPO:BACKUP>/20181119-152138F/backup.manifest.copy");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("current backup true");
|
|
|
|
HRN_STORAGE_PUT_Z(
|
|
storageRepoWrite(), TEST_PATH "/repo/" STORAGE_PATH_BACKUP "/db/" TEST_BACKUP_LABEL_FULL "/" BACKUP_MANIFEST_FILE,
|
|
TEST_INVALID_BACKREST_INFO, .comment = "invalid manifest");
|
|
|
|
TEST_ASSIGN(
|
|
manifest, verifyManifestFile(&backupResult, cipherSpecNewNone(), true, infoPg, &jobErrorTotal), "verify manifest");
|
|
TEST_RESULT_PTR(manifest, NULL, "manifest not set");
|
|
TEST_RESULT_UINT(backupResult.status, backupInvalid, "manifest unusable - backup invalid");
|
|
TEST_RESULT_LOG(
|
|
"P00 DETAIL: invalid checksum, actual 'e056f784a995841fd4e2802b809299b8db6803a2' but expected 'BOGUS'"
|
|
" <REPO:BACKUP>/20181119-152138F/backup.manifest\n"
|
|
"P00 DETAIL: invalid checksum, actual 'e056f784a995841fd4e2802b809299b8db6803a2' but expected 'BOGUS'"
|
|
" <REPO:BACKUP>/20181119-152138F/backup.manifest.copy");
|
|
|
|
// Write a valid manifest with a manifest copy that is invalid
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), STORAGE_REPO_BACKUP "/" TEST_BACKUP_LABEL_FULL "/" BACKUP_MANIFEST_FILE,
|
|
TEST_MANIFEST_HEADER
|
|
TEST_MANIFEST_DB_94
|
|
TEST_MANIFEST_OPTION_ALL
|
|
TEST_MANIFEST_TARGET
|
|
TEST_MANIFEST_DB
|
|
TEST_MANIFEST_FILE
|
|
TEST_MANIFEST_FILE_DEFAULT
|
|
TEST_MANIFEST_LINK
|
|
TEST_MANIFEST_LINK_DEFAULT
|
|
TEST_MANIFEST_PATH
|
|
TEST_MANIFEST_PATH_DEFAULT,
|
|
.comment = "valid manifest");
|
|
|
|
backupResult.status = backupValid;
|
|
TEST_ASSIGN(
|
|
manifest, verifyManifestFile(&backupResult, cipherSpecNewNone(), true, infoPg, &jobErrorTotal), "verify manifest");
|
|
TEST_RESULT_PTR_NE(manifest, NULL, "manifest set");
|
|
TEST_RESULT_UINT(backupResult.status, backupValid, "manifest usable");
|
|
TEST_RESULT_LOG("P00 DETAIL: backup '20181119-152138F' manifest.copy does not match manifest");
|
|
|
|
harnessLogLevelReset();
|
|
}
|
|
|
|
// *****************************************************************************************************************************
|
|
if (testBegin("verifyCreateArchiveIdRange()"))
|
|
{
|
|
VerifyWalRange *walRangeResult = NULL;
|
|
unsigned int errTotal = 0;
|
|
StringList *walFileList = strLstNew();
|
|
|
|
VerifyArchiveResult archiveResult =
|
|
{
|
|
.archiveId = strNewZ("9.6-1"),
|
|
.walRangeList = lstNewP(sizeof(VerifyWalRange), .comparator = lstComparatorStr),
|
|
};
|
|
List *archiveIdResultList = lstNewP(sizeof(VerifyArchiveResult), .comparator = archiveIdComparator);
|
|
lstAdd(archiveIdResultList, &archiveResult);
|
|
VerifyArchiveResult *archiveIdResult = lstGetLast(archiveIdResultList);
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("Single WAL");
|
|
|
|
archiveIdResult->pgWalInfo.size = HRN_PG_WAL_SEGMENT_SIZE_DEFAULT;
|
|
archiveIdResult->pgWalInfo.version = PG_VERSION_18;
|
|
|
|
strLstAddZ(walFileList, "000000020000000200000000-daa497dba64008db824607940609ba1cd7c6c501.gz");
|
|
|
|
TEST_RESULT_VOID(verifyCreateArchiveIdRange(archiveIdResult, walFileList, &errTotal), "create archiveId WAL range");
|
|
TEST_RESULT_UINT(errTotal, 0, "no errors");
|
|
TEST_RESULT_UINT(lstSize(((VerifyArchiveResult *)lstGet(archiveIdResultList, 0))->walRangeList), 1, "single range");
|
|
TEST_ASSIGN(
|
|
walRangeResult, (VerifyWalRange *)lstGet(((VerifyArchiveResult *)lstGet(archiveIdResultList, 0))->walRangeList, 0),
|
|
"get range");
|
|
TEST_RESULT_STR_Z(walRangeResult->start, "000000020000000200000000", "start range");
|
|
TEST_RESULT_STR_Z(walRangeResult->stop, "000000020000000200000000", "stop range");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("Duplicate WAL only - no range, all removed from list");
|
|
|
|
lstClear(archiveIdResult->walRangeList);
|
|
|
|
// Add a duplicate
|
|
strLstAddZ(walFileList, "000000020000000200000000");
|
|
|
|
TEST_RESULT_VOID(verifyCreateArchiveIdRange(archiveIdResult, walFileList, &errTotal), "create archiveId WAL range");
|
|
TEST_RESULT_UINT(errTotal, 1, "duplicate WAL error");
|
|
TEST_RESULT_UINT(strLstSize(walFileList), 0, "all WAL removed from WAL file list");
|
|
TEST_RESULT_UINT(lstSize(archiveIdResult->walRangeList), 0, "no range");
|
|
TEST_RESULT_LOG("P00 INFO: duplicate WAL '000000020000000200000000' for '9.6-1' exists, skipping");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("duplicates at beginning and end of list are removed");
|
|
|
|
errTotal = 0;
|
|
strLstAddZ(walFileList, "000000020000000100000000-daa497dba64008db824607940609ba1cd7c6c501.gz");
|
|
strLstAddZ(walFileList, "000000020000000100000000");
|
|
strLstAddZ(walFileList, "000000020000000100000000-aaaaaadba64008db824607940609ba1cd7c6c501");
|
|
strLstAddZ(walFileList, "0000000200000001000000FD-daa497dba64008db824607940609ba1cd7c6c501.gz");
|
|
strLstAddZ(walFileList, "0000000200000001000000FE-a6e1a64f0813352bc2e97f116a1800377e17d2e4.gz");
|
|
strLstAddZ(walFileList, "0000000200000001000000FF-daa497dba64008db824607940609ba1cd7c6c501");
|
|
strLstAddZ(walFileList, "000000020000000200000000");
|
|
strLstAddZ(walFileList, "000000020000000200000001");
|
|
strLstAddZ(walFileList, "000000020000000200000001");
|
|
|
|
TEST_RESULT_VOID(verifyCreateArchiveIdRange(archiveIdResult, walFileList, &errTotal), "create archiveId WAL range");
|
|
TEST_RESULT_UINT(errTotal, 2, "triplicate WAL error at beginning, duplicate WAL at end");
|
|
TEST_RESULT_UINT(strLstSize(walFileList), 4, "only duplicate WAL removed from WAL list");
|
|
TEST_RESULT_UINT(lstSize(archiveIdResultList), 1, "single archiveId result");
|
|
TEST_RESULT_UINT(lstSize(archiveIdResult->walRangeList), 1, "single range");
|
|
TEST_ASSIGN(
|
|
walRangeResult, (VerifyWalRange *)lstGet(((VerifyArchiveResult *)lstGet(archiveIdResultList, 0))->walRangeList, 0),
|
|
"get range");
|
|
TEST_RESULT_STR_Z(walRangeResult->start, "0000000200000001000000FD", "start range");
|
|
TEST_RESULT_STR_Z(walRangeResult->stop, "000000020000000200000000", "stop range");
|
|
TEST_RESULT_LOG(
|
|
"P00 INFO: duplicate WAL '000000020000000100000000' for '9.6-1' exists, skipping\n"
|
|
"P00 INFO: duplicate WAL '000000020000000200000001' for '9.6-1' exists, skipping");
|
|
}
|
|
|
|
// *****************************************************************************************************************************
|
|
if (testBegin("verifyPgHistory()"))
|
|
{
|
|
// Create backup.info
|
|
InfoBackup *backupInfo = NULL;
|
|
TEST_ASSIGN(
|
|
backupInfo,
|
|
infoBackupNewLoad(ioBufferReadNew(harnessInfoChecksumZ(TEST_BACKUP_INFO_MULTI_HISTORY_BASE)), cipherSpecNewNone()),
|
|
"backup.info multi-history");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("history mismatch - missing history");
|
|
|
|
// Create archive.info - history mismatch
|
|
InfoArchive *archiveInfo = NULL;
|
|
TEST_ASSIGN(
|
|
archiveInfo,
|
|
infoArchiveNewLoad(
|
|
ioBufferReadNew(
|
|
harnessInfoChecksumZ(
|
|
"[db]\n"
|
|
"db-id=2\n"
|
|
"db-system-id=" HRN_PG_SYSTEMID_11_Z "\n"
|
|
"db-version=\"11\"\n"
|
|
"\n"
|
|
"[db:history]\n"
|
|
"2={\"db-id\":" HRN_PG_SYSTEMID_11_Z ",\"db-version\":\"11\"}")), cipherSpecNewNone()),
|
|
"archive.info missing history");
|
|
|
|
TEST_ERROR(
|
|
verifyPgHistory(infoArchivePg(archiveInfo), infoBackupPg(backupInfo)), FormatError,
|
|
"archive and backup history lists do not match");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("history mismatch - system id");
|
|
|
|
TEST_ASSIGN(
|
|
archiveInfo,
|
|
infoArchiveNewLoad(
|
|
ioBufferReadNew(
|
|
harnessInfoChecksumZ(
|
|
"[db]\n"
|
|
"db-id=2\n"
|
|
"db-system-id=" HRN_PG_SYSTEMID_11_Z "\n"
|
|
"db-version=\"11\"\n"
|
|
"\n"
|
|
"[db:history]\n"
|
|
"1={\"db-id\":6625592122879095777,\"db-version\":\"9.6\"}\n"
|
|
"2={\"db-id\":" HRN_PG_SYSTEMID_11_Z ",\"db-version\":\"11\"}")), cipherSpecNewNone()),
|
|
"archive.info history system id mismatch");
|
|
|
|
TEST_ERROR(
|
|
verifyPgHistory(infoArchivePg(archiveInfo), infoBackupPg(backupInfo)), FormatError,
|
|
"archive and backup history lists do not match");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("history mismatch - version");
|
|
|
|
TEST_ASSIGN(
|
|
archiveInfo,
|
|
infoArchiveNewLoad(
|
|
ioBufferReadNew(
|
|
harnessInfoChecksumZ(
|
|
"[db]\n"
|
|
"db-id=2\n"
|
|
"db-system-id=" HRN_PG_SYSTEMID_11_Z "\n"
|
|
"db-version=\"11\"\n"
|
|
"\n"
|
|
"[db:history]\n"
|
|
"1={\"db-id\":" HRN_PG_SYSTEMID_96_Z ",\"db-version\":\"18\"}\n"
|
|
"2={\"db-id\":" HRN_PG_SYSTEMID_11_Z ",\"db-version\":\"11\"}")), cipherSpecNewNone()),
|
|
"archive.info history version mismatch");
|
|
|
|
TEST_ERROR(
|
|
verifyPgHistory(infoArchivePg(archiveInfo), infoBackupPg(backupInfo)), FormatError,
|
|
"archive and backup history lists do not match");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("history mismatch - id");
|
|
|
|
TEST_ASSIGN(
|
|
archiveInfo,
|
|
infoArchiveNewLoad(
|
|
ioBufferReadNew(
|
|
harnessInfoChecksumZ(
|
|
"[db]\n"
|
|
"db-id=2\n"
|
|
"db-system-id=" HRN_PG_SYSTEMID_11_Z "\n"
|
|
"db-version=\"11\"\n"
|
|
"\n"
|
|
"[db:history]\n"
|
|
"3={\"db-id\":" HRN_PG_SYSTEMID_96_Z ",\"db-version\":\"10\"}\n"
|
|
"2={\"db-id\":" HRN_PG_SYSTEMID_11_Z ",\"db-version\":\"11\"}")), cipherSpecNewNone()),
|
|
"archive.info history id mismatch");
|
|
|
|
TEST_ERROR(
|
|
verifyPgHistory(infoArchivePg(archiveInfo), infoBackupPg(backupInfo)), FormatError,
|
|
"archive and backup history lists do not match");
|
|
}
|
|
|
|
// *****************************************************************************************************************************
|
|
if (testBegin("verifySetBackupCheckArchive(), verifyLogInvalidResult(), verifyRender()"))
|
|
{
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("verifySetBackupCheckArchive()");
|
|
|
|
InfoBackup *backupInfo = NULL;
|
|
InfoArchive *archiveInfo = NULL;
|
|
TEST_ASSIGN(
|
|
backupInfo,
|
|
infoBackupNewLoad(ioBufferReadNew(harnessInfoChecksumZ(TEST_BACKUP_INFO_MULTI_HISTORY_BASE)), cipherSpecNewNone()),
|
|
"backup.info multi-history");
|
|
TEST_ASSIGN(
|
|
archiveInfo,
|
|
infoArchiveNewLoad(ioBufferReadNew(harnessInfoChecksumZ(TEST_ARCHIVE_INFO_MULTI_HISTORY_BASE)), cipherSpecNewNone()),
|
|
"archive.info multi-history");
|
|
InfoPg *pgHistory = infoArchivePg(archiveInfo);
|
|
|
|
StringList *backupList = strLstNew();
|
|
strLstAddZ(backupList, "20181119-152138F");
|
|
strLstAddZ(backupList, "20181119-152900F");
|
|
StringList *archiveIdList = strLstComparatorSet(strLstNew(), archiveIdComparator);
|
|
strLstAddZ(archiveIdList, "9.6-1");
|
|
strLstAddZ(archiveIdList, "11-2");
|
|
|
|
unsigned int errTotal = 0;
|
|
|
|
// Add backup to end of list
|
|
strLstAddZ(backupList, "20181119-153000F");
|
|
strLstAddZ(archiveIdList, "12-3");
|
|
|
|
TEST_RESULT_STR_Z(
|
|
verifySetBackupCheckArchive(backupList, backupInfo, archiveIdList, pgHistory, &errTotal),
|
|
"20181119-153000F", "current backup, missing archive");
|
|
TEST_RESULT_UINT(errTotal, 1, "error logged");
|
|
TEST_RESULT_LOG("P00 INFO: archiveIds '12-3' are not in the archive.info history list");
|
|
|
|
errTotal = 0;
|
|
strLstAddZ(archiveIdList, "13-4");
|
|
TEST_RESULT_STR_Z(
|
|
verifySetBackupCheckArchive(backupList, backupInfo, archiveIdList, pgHistory, &errTotal),
|
|
"20181119-153000F", "test multiple archiveIds on disk not in archive.info");
|
|
TEST_RESULT_UINT(errTotal, 1, "error logged");
|
|
TEST_RESULT_LOG("P00 INFO: archiveIds '12-3, 13-4' are not in the archive.info history list");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("verifyLogInvalidResult() - missing file");
|
|
|
|
TEST_RESULT_UINT(
|
|
verifyLogInvalidResult(STORAGE_REPO_ARCHIVE_STR, verifyFileMissing, 0, STRDEF("missingfilename")),
|
|
0, "file missing message");
|
|
TEST_RESULT_LOG("P00 WARN: file missing 'missingfilename'");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("verifyRender() - missing file, empty invalidList");
|
|
|
|
List *archiveIdResultList = lstNewP(sizeof(VerifyArchiveResult), .comparator = archiveIdComparator);
|
|
List *backupResultList = lstNewP(sizeof(VerifyBackupResult), .comparator = lstComparatorStr);
|
|
|
|
VerifyArchiveResult archiveIdResult =
|
|
{
|
|
.archiveId = strNewZ("9.6-1"),
|
|
.totalWalFile = 1,
|
|
.walRangeList = lstNewP(sizeof(VerifyWalRange), .comparator = lstComparatorStr),
|
|
};
|
|
VerifyWalRange walRange =
|
|
{
|
|
.start = strNewZ("0"),
|
|
.stop = strNewZ("2"),
|
|
.invalidFileList = lstNewP(sizeof(VerifyInvalidFile), .comparator = lstComparatorStr),
|
|
};
|
|
|
|
lstAdd(archiveIdResult.walRangeList, &walRange);
|
|
lstAdd(archiveIdResultList, &archiveIdResult);
|
|
|
|
TEST_RESULT_STR_Z(
|
|
verifyRender(archiveIdResultList, backupResultList, cfgOptionBool(cfgOptVerbose)),
|
|
"\n"
|
|
" archiveId: 9.6-1, total WAL checked: 1, total valid WAL: 0", "archive: no invalid file list");
|
|
|
|
VerifyInvalidFile invalidFile =
|
|
{
|
|
.fileName = strNewZ("file"),
|
|
.reason = verifyFileMissing,
|
|
};
|
|
lstAdd(walRange.invalidFileList, &invalidFile);
|
|
|
|
VerifyBackupResult backupResult =
|
|
{
|
|
.backupLabel = strNewZ("test-backup-label"),
|
|
.status = backupInvalid,
|
|
.totalFileVerify = 1,
|
|
.invalidFileList = lstNewP(sizeof(VerifyInvalidFile), .comparator = lstComparatorStr),
|
|
};
|
|
lstAdd(backupResult.invalidFileList, &invalidFile);
|
|
lstAdd(backupResultList, &backupResult);
|
|
|
|
TEST_RESULT_STR_Z(
|
|
verifyRender(archiveIdResultList, backupResultList, cfgOptionBool(cfgOptVerbose)),
|
|
"\n"
|
|
" archiveId: 9.6-1, total WAL checked: 1, total valid WAL: 0\n"
|
|
" missing: 1\n"
|
|
" backup: test-backup-label, status: invalid, total files checked: 1, total valid files: 0\n"
|
|
" missing: 1", "archive file missing, backup file missing, no text, no verbose");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("verifyAddInvalidWalFile() - file missing (coverage test)");
|
|
|
|
TEST_RESULT_VOID(
|
|
verifyAddInvalidWalFile(archiveIdResult.walRangeList, verifyFileMissing, STRDEF("test"), STRDEF("3")), "coverage test");
|
|
}
|
|
|
|
// *****************************************************************************************************************************
|
|
if (testBegin("cmdVerify() - info files"))
|
|
{
|
|
// Load Parameters
|
|
StringList *argList = strLstDup(argListBase);
|
|
HRN_CFG_LOAD(cfgCmdVerify, argList);
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("backup.info invalid checksum, neither backup copy nor archive infos exist");
|
|
|
|
HRN_STORAGE_PUT_Z(storageRepoWrite(), INFO_BACKUP_PATH_FILE, TEST_INVALID_BACKREST_INFO, .comment = "invalid backup.info");
|
|
|
|
harnessLogLevelSet(logLevelDetail);
|
|
|
|
// Redirect stdout to a file
|
|
int stdoutSave = dup(STDOUT_FILENO);
|
|
const String *stdoutFile = STRDEF(TEST_PATH "/stdout.info");
|
|
|
|
THROW_ON_SYS_ERROR(freopen(strZ(stdoutFile), "w", stdout) == NULL, FileWriteError, "unable to reopen stdout");
|
|
|
|
// Not in a test wrapper to avoid writing to stdout
|
|
cmdVerify();
|
|
|
|
// Restore normal stdout
|
|
dup2(stdoutSave, STDOUT_FILENO);
|
|
|
|
// Check output of verify command stored in file
|
|
TEST_STORAGE_GET(storageTest, strZ(stdoutFile), "", .remove = true);
|
|
TEST_RESULT_LOG(
|
|
"P00 DETAIL: invalid checksum, actual 'e056f784a995841fd4e2802b809299b8db6803a2' but expected 'BOGUS'"
|
|
" <REPO:BACKUP>/backup.info\n"
|
|
"P00 DETAIL: unable to open missing file '" TEST_PATH "/repo/backup/db/backup.info.copy' for read\n"
|
|
"P00 DETAIL: unable to open missing file '" TEST_PATH "/repo/archive/db/archive.info' for read\n"
|
|
"P00 DETAIL: unable to open missing file '" TEST_PATH "/repo/archive/db/archive.info.copy' for read\n"
|
|
"P00 INFO: stanza: db\n"
|
|
" status: error\n"
|
|
" No usable backup.info file\n"
|
|
" No usable archive.info file");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("backup.info invalid checksum, backup.info.copy valid, archive.info not exist, archive copy checksum invalid");
|
|
|
|
HRN_STORAGE_PUT_Z(
|
|
storageRepoWrite(), INFO_ARCHIVE_PATH_FILE INFO_COPY_EXT, TEST_INVALID_BACKREST_INFO,
|
|
.comment = "invalid archive.info.copy");
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), INFO_BACKUP_PATH_FILE INFO_COPY_EXT,
|
|
"[backup:current]\n"
|
|
TEST_BACKUP_DB1_CURRENT_FULL1
|
|
"\n"
|
|
"[db]\n"
|
|
TEST_BACKUP_DB1_96
|
|
"\n"
|
|
"[db:history]\n"
|
|
TEST_BACKUP_DB1_HISTORY,
|
|
.comment = "valid backup.info.copy");
|
|
|
|
// Redirect stdout to a file
|
|
stdoutSave = dup(STDOUT_FILENO);
|
|
stdoutFile = STRDEF(TEST_PATH "/stdout.info");
|
|
|
|
THROW_ON_SYS_ERROR(freopen(strZ(stdoutFile), "w", stdout) == NULL, FileWriteError, "unable to reopen stdout");
|
|
|
|
// Not in a test wrapper to avoid writing to stdout
|
|
cmdVerify();
|
|
|
|
// Restore normal stdout
|
|
dup2(stdoutSave, STDOUT_FILENO);
|
|
|
|
// Check output of verify command stored in file
|
|
TEST_STORAGE_GET(storageTest, strZ(stdoutFile), "", .remove = true);
|
|
TEST_RESULT_LOG(
|
|
"P00 DETAIL: invalid checksum, actual 'e056f784a995841fd4e2802b809299b8db6803a2' but expected 'BOGUS'"
|
|
" <REPO:BACKUP>/backup.info\n"
|
|
"P00 DETAIL: unable to open missing file '" TEST_PATH "/repo/archive/db/archive.info' for read\n"
|
|
"P00 DETAIL: invalid checksum, actual 'e056f784a995841fd4e2802b809299b8db6803a2' but expected 'BOGUS'"
|
|
" <REPO:ARCHIVE>/archive.info.copy\n"
|
|
"P00 INFO: stanza: db\n"
|
|
" status: error\n"
|
|
" No usable archive.info file");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("backup.info and copy valid but checksum mismatch, archive.info checksum invalid, archive.info copy valid");
|
|
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), INFO_BACKUP_PATH_FILE, TEST_NO_CURRENT_BACKUP, .comment = "valid backup.info");
|
|
HRN_STORAGE_PUT_Z(
|
|
storageRepoWrite(), INFO_ARCHIVE_PATH_FILE, TEST_INVALID_BACKREST_INFO, .comment = "invalid archive.info");
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), INFO_ARCHIVE_PATH_FILE INFO_COPY_EXT, TEST_ARCHIVE_INFO_BASE, .comment = "valid archive.info.copy");
|
|
|
|
// Redirect stdout to a file
|
|
stdoutSave = dup(STDOUT_FILENO);
|
|
stdoutFile = STRDEF(TEST_PATH "/stdout.info");
|
|
|
|
THROW_ON_SYS_ERROR(freopen(strZ(stdoutFile), "w", stdout) == NULL, FileWriteError, "unable to reopen stdout");
|
|
|
|
// Not in a test wrapper to avoid writing to stdout
|
|
cmdVerify();
|
|
|
|
// Restore normal stdout
|
|
dup2(stdoutSave, STDOUT_FILENO);
|
|
|
|
// Check output of verify command stored in file
|
|
TEST_STORAGE_GET(storageTest, strZ(stdoutFile), "", .remove = true);
|
|
TEST_RESULT_LOG(
|
|
"P00 DETAIL: backup.info.copy does not match backup.info\n"
|
|
"P00 DETAIL: invalid checksum, actual 'e056f784a995841fd4e2802b809299b8db6803a2' but expected 'BOGUS'"
|
|
" <REPO:ARCHIVE>/archive.info\n"
|
|
"P00 INFO: stanza: db\n"
|
|
" status: error\n"
|
|
" backup info file and archive info file do not match\n"
|
|
" archive: id = 1, version = 9.6, system-id = 10000000000000090600\n"
|
|
" backup : id = 2, version = 11, system-id = 10000000000000110000\n"
|
|
" HINT: this may be a symptom of repository corruption!");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("backup.info and copy valid and checksums match, archive.info and copy valid, but checksum mismatch");
|
|
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), INFO_BACKUP_PATH_FILE INFO_COPY_EXT, TEST_NO_CURRENT_BACKUP,
|
|
.comment = "valid backup.info.copy");
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), INFO_ARCHIVE_PATH_FILE, TEST_ARCHIVE_INFO_MULTI_HISTORY_BASE, .comment = "valid archive.info");
|
|
TEST_RESULT_VOID(cmdVerify(), "usable backup and archive info files");
|
|
TEST_RESULT_LOG(
|
|
"P00 DETAIL: archive.info.copy does not match archive.info");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("backup.info valid, copy invalid, archive.info valid, copy invalid");
|
|
|
|
HRN_STORAGE_REMOVE(storageRepoWrite(), INFO_BACKUP_PATH_FILE INFO_COPY_EXT, .comment = "remove backup.info.copy");
|
|
HRN_STORAGE_REMOVE(storageRepoWrite(), INFO_ARCHIVE_PATH_FILE INFO_COPY_EXT, .comment = "remove archive.info.copy");
|
|
TEST_RESULT_VOID(cmdVerify(), "usable backup and archive info files");
|
|
TEST_RESULT_LOG(
|
|
"P00 DETAIL: unable to open missing file '" TEST_PATH "/repo/backup/db/backup.info.copy' for read\n"
|
|
"P00 DETAIL: unable to open missing file '" TEST_PATH "/repo/archive/db/archive.info.copy' for read");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("backup.info and copy missing, archive.info and copy valid");
|
|
|
|
hrnCfgArgRawZ(argList, cfgOptVerbose, "y");
|
|
HRN_CFG_LOAD(cfgCmdVerify, argList);
|
|
|
|
HRN_STORAGE_REMOVE(storageRepoWrite(), INFO_BACKUP_PATH_FILE);
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), INFO_ARCHIVE_PATH_FILE INFO_COPY_EXT, TEST_ARCHIVE_INFO_MULTI_HISTORY_BASE,
|
|
.comment = "valid and matching archive.info.copy");
|
|
|
|
// Redirect stdout to a file
|
|
stdoutSave = dup(STDOUT_FILENO);
|
|
stdoutFile = STRDEF(TEST_PATH "/stdout.info");
|
|
|
|
THROW_ON_SYS_ERROR(freopen(strZ(stdoutFile), "w", stdout) == NULL, FileWriteError, "unable to reopen stdout");
|
|
|
|
// Not in a test wrapper to avoid writing to stdout
|
|
cmdVerify();
|
|
|
|
// Restore normal stdout
|
|
dup2(stdoutSave, STDOUT_FILENO);
|
|
|
|
// Check output of verify command stored in file
|
|
TEST_STORAGE_GET(storageTest, strZ(stdoutFile), "", .remove = true);
|
|
TEST_RESULT_LOG(
|
|
"P00 DETAIL: unable to open missing file '" TEST_PATH "/repo/backup/db/backup.info' for read\n"
|
|
"P00 DETAIL: unable to open missing file '" TEST_PATH "/repo/backup/db/backup.info.copy' for read\n"
|
|
"P00 INFO: stanza: db\n"
|
|
" status: error\n"
|
|
" No usable backup.info file");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("encrypted info files");
|
|
|
|
argList = strLstDup(argListBase);
|
|
hrnCfgArgRawStrId(argList, cfgOptRepoCipherType, cipherTypeAes256Cbc);
|
|
hrnCfgEnvKeyRawZ(cfgOptRepoCipherPass, 1, TEST_CIPHER_PASS);
|
|
HRN_CFG_LOAD(cfgCmdVerify, argList);
|
|
|
|
// The copy is a copy of the file rather than a second encryption of the same content, which is how the info files are
|
|
// saved, i.e. the content is built once in a buffer and that buffer is written twice
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), INFO_BACKUP_PATH_FILE, TEST_NO_CURRENT_BACKUP, .header = true,
|
|
.cipherSpec = TEST_CIPHER_SPEC, .comment = "encrypted backup.info");
|
|
HRN_STORAGE_COPY(
|
|
storageRepo(), INFO_BACKUP_PATH_FILE, storageRepoWrite(), INFO_BACKUP_PATH_FILE INFO_COPY_EXT,
|
|
.comment = "encrypted backup.info.copy");
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), INFO_ARCHIVE_PATH_FILE, TEST_ARCHIVE_INFO_MULTI_HISTORY_BASE, .header = true,
|
|
.cipherSpec = TEST_CIPHER_SPEC, .comment = "encrypted archive.info");
|
|
HRN_STORAGE_COPY(
|
|
storageRepo(), INFO_ARCHIVE_PATH_FILE, storageRepoWrite(), INFO_ARCHIVE_PATH_FILE INFO_COPY_EXT,
|
|
.comment = "encrypted archive.info.copy");
|
|
|
|
TEST_RESULT_VOID(cmdVerify(), "usable encrypted backup and archive info files");
|
|
TEST_RESULT_LOG("");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("encrypted format 6 info files");
|
|
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), INFO_BACKUP_PATH_FILE, TEST_NO_CURRENT_BACKUP, .format = REPOSITORY_FORMAT_6, .header = true,
|
|
.cipherSpec = TEST_CIPHER_SPEC, .comment = "encrypted backup.info at format 6");
|
|
HRN_STORAGE_COPY(
|
|
storageRepo(), INFO_BACKUP_PATH_FILE, storageRepoWrite(), INFO_BACKUP_PATH_FILE INFO_COPY_EXT,
|
|
.comment = "encrypted backup.info.copy at format 6");
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), INFO_ARCHIVE_PATH_FILE, TEST_ARCHIVE_INFO_MULTI_HISTORY_BASE, .format = REPOSITORY_FORMAT_6,
|
|
.header = true, .cipherSpec = TEST_CIPHER_SPEC, .comment = "encrypted archive.info at format 6");
|
|
HRN_STORAGE_COPY(
|
|
storageRepo(), INFO_ARCHIVE_PATH_FILE, storageRepoWrite(), INFO_ARCHIVE_PATH_FILE INFO_COPY_EXT,
|
|
.comment = "encrypted archive.info.copy at format 6");
|
|
|
|
TEST_RESULT_VOID(cmdVerify(), "usable encrypted backup and archive info files at format 6");
|
|
TEST_RESULT_LOG("");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("a copy that cannot be decrypted is reported as such rather than as a checksum that does not match");
|
|
|
|
// Damage the content of the copy, which nothing but the drain reads, so that it no longer decrypts
|
|
Buffer *const archiveInfoCopy = bufDup(
|
|
storageGetP(storageNewReadP(storageRepo(), STRDEF(INFO_ARCHIVE_PATH_FILE INFO_COPY_EXT))));
|
|
|
|
bufPtr(archiveInfoCopy)[bufUsed(archiveInfoCopy) - 1] ^= 0xff;
|
|
HRN_STORAGE_PUT(
|
|
storageRepoWrite(), INFO_ARCHIVE_PATH_FILE INFO_COPY_EXT, archiveInfoCopy, .comment = "damaged archive.info.copy");
|
|
|
|
TEST_RESULT_VOID(cmdVerify(), "encrypted archive.info.copy that cannot be decrypted");
|
|
TEST_RESULT_LOG("P00 DETAIL: unable to flush");
|
|
|
|
hrnCfgEnvKeyRemoveRaw(cfgOptRepoCipherPass, 1);
|
|
harnessLogLevelReset();
|
|
}
|
|
|
|
// *****************************************************************************************************************************
|
|
if (testBegin("verifyFile()"))
|
|
{
|
|
// Backup files contain no key id, so the key goes under the default id
|
|
CipherSpecMap *const cipherSpecMapNone = cipherSpecMapNew();
|
|
CipherSpecMap *const cipherSpecMapPass = cipherSpecMapNew();
|
|
cipherSpecMapAdd(
|
|
cipherSpecMapPass, CIPHER_SPEC_MAP_ID_DEFAULT_STR,
|
|
cipherSpecNewP(cipherTypeAes256Cbc, BUFSTRDEF("pass"), .digest = hashTypeSha1));
|
|
|
|
// Load Parameters
|
|
StringList *argList = strLstDup(argListBase);
|
|
HRN_CFG_LOAD(cfgCmdVerify, argList);
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("zero-sized file in archive");
|
|
|
|
String *filePathName = strNewZ(STORAGE_REPO_ARCHIVE "/testfile");
|
|
HRN_STORAGE_PUT_EMPTY(storageRepoWrite(), strZ(filePathName));
|
|
TEST_RESULT_UINT(
|
|
verifyFile(filePathName, 0, NULL, compressTypeNone, HASH_TYPE_SHA1_ZERO_BUF, 0, cipherSpecMapNone, false),
|
|
verifyOk, "file ok");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("file size invalid in archive");
|
|
|
|
HRN_STORAGE_PUT_Z(storageRepoWrite(), strZ(filePathName), fileContents);
|
|
TEST_RESULT_UINT(
|
|
verifyFile(filePathName, 0, NULL, compressTypeNone, fileChecksum, 0, cipherSpecMapNone, false),
|
|
verifySizeInvalid, "file size invalid");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("file missing in archive");
|
|
|
|
TEST_RESULT_UINT(
|
|
verifyFile(
|
|
strNewFmt(STORAGE_REPO_ARCHIVE "/missingFile"), 0, NULL, compressTypeNone, fileChecksum, 0, cipherSpecMapNone,
|
|
false),
|
|
verifyFileMissing, "file missing");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("encrypted/compressed file in backup");
|
|
|
|
// Create a compressed encrypted repo file in backup
|
|
filePathName = strCatZ(strNew(), STORAGE_REPO_BACKUP "/testfile");
|
|
HRN_STORAGE_PUT_Z(
|
|
storageRepoWrite(), strZ(filePathName), fileContents, .compressType = compressTypeGz,
|
|
.cipherSpec = TEST_CIPHER_SPEC_PASS("pass"));
|
|
|
|
strCatZ(filePathName, ".gz");
|
|
TEST_RESULT_UINT(
|
|
verifyFile(
|
|
filePathName, 0, NULL, compressTypeGz, fileChecksum, fileSize,
|
|
cipherSpecMapPass, false),
|
|
verifyOk, "file encrypted compressed ok");
|
|
TEST_RESULT_UINT(
|
|
verifyFile(
|
|
filePathName, 0, NULL, compressTypeGz, bufNewDecode(encodingHex, STRDEF("aa")), fileSize,
|
|
cipherSpecMapPass, false),
|
|
verifyChecksumMismatch, "file encrypted compressed checksum mismatch");
|
|
}
|
|
|
|
// *****************************************************************************************************************************
|
|
if (testBegin("cmdVerify(), verifyProcess() - errors"))
|
|
{
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
// Load Parameters with multi-repo
|
|
StringList *argList = strLstDup(argListBase);
|
|
hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 4, TEST_PATH "/repo4");
|
|
HRN_CFG_LOAD(cfgCmdVerify, argList);
|
|
|
|
// Store valid archive/backup info files
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), INFO_ARCHIVE_PATH_FILE, TEST_ARCHIVE_INFO_MULTI_HISTORY_BASE, .comment = "valid archive.info");
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), INFO_ARCHIVE_PATH_FILE INFO_COPY_EXT, TEST_ARCHIVE_INFO_MULTI_HISTORY_BASE,
|
|
.comment = "valid archive.info.copy");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("valid info files, WAL files present, no backups");
|
|
|
|
HRN_INFO_PUT(storageRepoWrite(), INFO_BACKUP_PATH_FILE, TEST_NO_CURRENT_BACKUP, .comment = "no current backups");
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), INFO_BACKUP_PATH_FILE INFO_COPY_EXT, TEST_NO_CURRENT_BACKUP, .comment = "no current backups copy");
|
|
|
|
// Create WAL file with just header info and small WAL size
|
|
Buffer *walBuffer = bufNew((size_t)(1024 * 1024));
|
|
bufUsedSet(walBuffer, bufSize(walBuffer));
|
|
memset(bufPtr(walBuffer), 0, bufSize(walBuffer));
|
|
HRN_PG_WAL_TO_BUFFER(walBuffer, PG_VERSION_11, .size = 1024 * 1024);
|
|
const char *walBufferSha1 = strZ(strNewEncode(encodingHex, cryptoHashOne(hashTypeSha1, walBuffer)));
|
|
|
|
HRN_STORAGE_PUT(
|
|
storageRepoIdxWrite(0),
|
|
zNewFmt(STORAGE_REPO_ARCHIVE "/11-2/0000000200000007/000000020000000700000FFE-%s", walBufferSha1), walBuffer,
|
|
.comment = "valid WAL");
|
|
HRN_STORAGE_PUT(
|
|
storageRepoIdxWrite(0),
|
|
STORAGE_REPO_ARCHIVE "/11-2/0000000200000007/000000020000000700000FFE-bad817043007aa2100c44c712bcb456db705dab9",
|
|
walBuffer, .comment = "duplicate WAL");
|
|
|
|
// Set log detail level to capture ranges (there should be none)
|
|
harnessLogLevelSet(logLevelDetail);
|
|
|
|
// Redirect stdout to a file
|
|
int stdoutSave = dup(STDOUT_FILENO);
|
|
const String *stdoutFile = STRDEF(TEST_PATH "/stdout.info");
|
|
|
|
THROW_ON_SYS_ERROR(freopen(strZ(stdoutFile), "w", stdout) == NULL, FileWriteError, "unable to reopen stdout");
|
|
|
|
// Not in a test wrapper to avoid writing to stdout
|
|
cmdVerify();
|
|
|
|
// Restore normal stdout
|
|
dup2(stdoutSave, STDOUT_FILENO);
|
|
|
|
// Check output of verify command stored in file
|
|
TEST_STORAGE_GET(storageTest, strZ(stdoutFile), "", .remove = true);
|
|
TEST_RESULT_LOG(
|
|
"P00 DETAIL: no backups exist in the repo\n"
|
|
"P00 INFO: duplicate WAL '000000020000000700000FFE' for '11-2' exists, skipping\n"
|
|
"P00 DETAIL: path '11-2/0000000200000007' does not contain any valid WAL to be processed\n"
|
|
"P00 INFO: stanza: db\n"
|
|
" status: error\n"
|
|
" archiveId: 11-2, total WAL checked: 2, total valid WAL: 0");
|
|
|
|
harnessLogLevelReset();
|
|
|
|
HRN_STORAGE_REMOVE(
|
|
storageRepoIdxWrite(0),
|
|
STORAGE_REPO_ARCHIVE "/11-2/0000000200000007/000000020000000700000FFE-bad817043007aa2100c44c712bcb456db705dab9",
|
|
.comment = "remove duplicate WAL");
|
|
|
|
HRN_STORAGE_PATH_CREATE(
|
|
storageRepoIdxWrite(0), STORAGE_REPO_ARCHIVE "/9.6-1", .comment = "empty path for old archiveId");
|
|
HRN_STORAGE_PATH_CREATE(
|
|
storageRepoIdxWrite(0), STORAGE_REPO_ARCHIVE "/11-2/0000000100000000", .comment = "empty timeline path");
|
|
|
|
HRN_STORAGE_PUT(
|
|
storageRepoIdxWrite(0),
|
|
STORAGE_REPO_ARCHIVE "/11-2/0000000200000007/000000020000000700000FFD-a6e1a64f0813352bc2e97f116a1800377e17d2e4",
|
|
walBuffer, .compressType = compressTypeGz, .comment = "first WAL compressed - but checksum failure");
|
|
HRN_STORAGE_PUT(
|
|
storageRepoIdxWrite(0),
|
|
zNewFmt(
|
|
STORAGE_REPO_ARCHIVE "/11-2/0000000200000007/000000020000000700000FFF-%s",
|
|
strZ(strNewEncode(encodingHex, cryptoHashOne(hashTypeSha1, BUFSTRDEF("invalidsize"))))),
|
|
BUFSTRDEF("invalidsize"), .comment = "WAL - invalid size");
|
|
HRN_STORAGE_PUT(
|
|
storageRepoIdxWrite(0),
|
|
zNewFmt(STORAGE_REPO_ARCHIVE "/11-2/0000000200000008/000000020000000800000000-%s", walBufferSha1), walBuffer,
|
|
.comment = "WAL - continue range");
|
|
|
|
// Set log detail level to capture ranges
|
|
harnessLogLevelSet(logLevelDetail);
|
|
|
|
// Test verifyProcess directly
|
|
TEST_RESULT_STR_Z(
|
|
verifyProcess(cfgOptionBool(cfgOptVerbose)),
|
|
"stanza: db\n"
|
|
"status: error\n"
|
|
" archiveId: 11-2, total WAL checked: 4, total valid WAL: 2\n"
|
|
" checksum invalid: 1, size invalid: 1", "verifyProcess() no text, no verbose");
|
|
TEST_RESULT_LOG(
|
|
"P00 DETAIL: no backups exist in the repo\n"
|
|
"P00 DETAIL: archive path '9.6-1' is empty\n"
|
|
"P00 DETAIL: path '11-2/0000000100000000' does not contain any valid WAL to be processed\n"
|
|
"P01 INFO: invalid checksum"
|
|
" '11-2/0000000200000007/000000020000000700000FFD-a6e1a64f0813352bc2e97f116a1800377e17d2e4.gz'\n"
|
|
"P01 INFO: invalid size"
|
|
" '11-2/0000000200000007/000000020000000700000FFF-ee161f898c9012dd0c28b3fd1e7140b9cf411306'\n"
|
|
"P00 DETAIL: archiveId: 11-2, wal start: 000000020000000700000FFD, wal stop: 000000020000000800000000");
|
|
|
|
harnessLogLevelReset();
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("no text output, verbose, with verify failures");
|
|
|
|
hrnCfgArgRawZ(argList, cfgOptVerbose, "y");
|
|
HRN_CFG_LOAD(cfgCmdVerify, argList);
|
|
|
|
// Verify text output, verbose, with verify failures
|
|
TEST_RESULT_STR_Z(
|
|
verifyProcess(cfgOptionBool(cfgOptVerbose)),
|
|
"stanza: db\n"
|
|
"status: error\n"
|
|
" archiveId: 9.6-1, total WAL checked: 0, total valid WAL: 0\n"
|
|
" archiveId: 11-2, total WAL checked: 4, total valid WAL: 2\n"
|
|
" missing: 0, checksum invalid: 1, size invalid: 1, other: 0\n"
|
|
" backup: none found",
|
|
"verbose, with failures");
|
|
TEST_RESULT_LOG(
|
|
"P01 INFO: invalid checksum"
|
|
" '11-2/0000000200000007/000000020000000700000FFD-a6e1a64f0813352bc2e97f116a1800377e17d2e4.gz'\n"
|
|
"P01 INFO: invalid size"
|
|
" '11-2/0000000200000007/000000020000000700000FFF-ee161f898c9012dd0c28b3fd1e7140b9cf411306'");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("text output, verbose, with verify failures");
|
|
|
|
hrnCfgArgRawZ(argList, cfgOptOutput, "text");
|
|
HRN_CFG_LOAD(cfgCmdVerify, argList);
|
|
|
|
// Verify text output, verbose, with verify failures
|
|
TEST_RESULT_STR_Z(
|
|
verifyProcess(cfgOptionBool(cfgOptVerbose)),
|
|
"stanza: db\n"
|
|
"status: error\n"
|
|
" archiveId: 9.6-1, total WAL checked: 0, total valid WAL: 0\n"
|
|
" archiveId: 11-2, total WAL checked: 4, total valid WAL: 2\n"
|
|
" missing: 0, checksum invalid: 1, size invalid: 1, other: 0\n"
|
|
" backup: none found", "verify text output, verbose, with verify failures");
|
|
TEST_RESULT_LOG(
|
|
"P01 INFO: invalid checksum"
|
|
" '11-2/0000000200000007/000000020000000700000FFD-a6e1a64f0813352bc2e97f116a1800377e17d2e4.gz'\n"
|
|
"P01 INFO: invalid size"
|
|
" '11-2/0000000200000007/000000020000000700000FFF-ee161f898c9012dd0c28b3fd1e7140b9cf411306'");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("valid info files, start next timeline");
|
|
|
|
// Load Parameters - single default repo
|
|
argList = strLstDup(argListBase);
|
|
HRN_CFG_LOAD(cfgCmdVerify, argList);
|
|
|
|
HRN_STORAGE_PUT(
|
|
storageRepoIdxWrite(0),
|
|
zNewFmt(STORAGE_REPO_ARCHIVE "/11-2/0000000200000008/000000020000000800000002-%s", walBufferSha1),
|
|
walBuffer, .comment = "WAL - starts next range");
|
|
HRN_STORAGE_PUT(
|
|
storageRepoIdxWrite(0),
|
|
zNewFmt(STORAGE_REPO_ARCHIVE "/11-2/0000000300000000/000000030000000000000000-%s", walBufferSha1),
|
|
walBuffer, .comment = "WAL - starts next timeline");
|
|
HRN_STORAGE_PUT(
|
|
storageRepoIdxWrite(0),
|
|
zNewFmt(STORAGE_REPO_ARCHIVE "/11-2/0000000300000000/000000030000000000000001-%s", walBufferSha1),
|
|
walBuffer, .comment = "WAL - end next timeline");
|
|
|
|
// Set log level to errors only
|
|
harnessLogLevelSet(logLevelError);
|
|
|
|
TEST_RESULT_BOOL(cfgOptionBool(cfgOptVerbose), false, "verbose is false");
|
|
TEST_RESULT_BOOL((cfgOptionStrId(cfgOptOutput) == CFGOPTVAL_VERIFY_OUTPUT_TEXT), false, "text is false");
|
|
|
|
// Redirect stdout to a file
|
|
stdoutSave = dup(STDOUT_FILENO);
|
|
stdoutFile = STRDEF(TEST_PATH "/stdout.info");
|
|
|
|
THROW_ON_SYS_ERROR(freopen(strZ(stdoutFile), "w", stdout) == NULL, FileWriteError, "unable to reopen stdout");
|
|
|
|
// Not in a test wrapper to avoid writing to stdout
|
|
cmdVerify();
|
|
|
|
// Restore normal stdout
|
|
dup2(stdoutSave, STDOUT_FILENO);
|
|
|
|
// Check output of verify command stored in file
|
|
TEST_STORAGE_GET(storageTest, strZ(stdoutFile), "", .remove = true);
|
|
TEST_RESULT_LOG("");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("none output, with verify failures");
|
|
|
|
hrnCfgArgRawZ(argList, cfgOptOutput, "none");
|
|
HRN_CFG_LOAD(cfgCmdVerify, argList);
|
|
|
|
// Verify none output
|
|
TEST_RESULT_STR_Z(
|
|
verifyProcess(cfgOptionBool(cfgOptVerbose)),
|
|
"stanza: db\n"
|
|
"status: error\n"
|
|
" archiveId: 11-2, total WAL checked: 7, total valid WAL: 5\n"
|
|
" checksum invalid: 1, size invalid: 1", "verify none output");
|
|
TEST_RESULT_LOG("");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("valid info files - various archive/backup errors");
|
|
|
|
// Load Parameters - single non-default repo
|
|
argList = strLstNew();
|
|
hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 2, TEST_PATH "/repo");
|
|
hrnCfgArgRawZ(argList, cfgOptStanza, "db");
|
|
hrnCfgArgRawZ(argList, cfgOptRepo, "2");
|
|
HRN_CFG_LOAD(cfgCmdVerify, argList, .jobRetry = 1);
|
|
|
|
HRN_STORAGE_PUT(
|
|
storageRepoIdxWrite(0),
|
|
STORAGE_REPO_ARCHIVE "/11-2/0000000200000008/000000020000000800000003-656817043007aa2100c44c712bcb456db705dab9",
|
|
walBuffer, .modeFile = 0200, .comment = "WAL - file not readable");
|
|
|
|
HRN_STORAGE_PATH_CREATE(
|
|
storageRepoIdxWrite(0), STORAGE_REPO_BACKUP "/20181119-152800F", .comment = "prior backup path missing manifests");
|
|
|
|
HRN_INFO_PUT(
|
|
storageRepoIdxWrite(0), STORAGE_REPO_BACKUP "/20181119-152810F/" BACKUP_MANIFEST_FILE,
|
|
TEST_MANIFEST_HEADER
|
|
TEST_MANIFEST_DB_94
|
|
TEST_MANIFEST_OPTION_ALL
|
|
TEST_MANIFEST_TARGET
|
|
TEST_MANIFEST_DB,
|
|
.comment = "manifest without target files");
|
|
|
|
// Create full backup with files
|
|
HRN_STORAGE_PUT_Z(
|
|
storageRepoIdxWrite(0), STORAGE_REPO_BACKUP "/20181119-152900F/pg_data/PG_VERSION", "BOGUS",
|
|
.comment = "put checksum-error backup file");
|
|
HRN_STORAGE_PUT_EMPTY(
|
|
storageRepoIdxWrite(0), STORAGE_REPO_BACKUP "/20181119-152900F/pg_data/testzero",
|
|
.comment = "put zero-size backup file");
|
|
HRN_STORAGE_PUT_Z(
|
|
storageRepoIdxWrite(0), STORAGE_REPO_BACKUP "/20181119-152900F/pg_data/testvalid", fileContents,
|
|
.comment = "put valid file");
|
|
|
|
// Write manifests for full backup
|
|
String *manifestContent = strNewFmt(
|
|
"[backup]\n"
|
|
"backup-label=\"20181119-152900F\"\n"
|
|
"backup-timestamp-copy-start=0\n"
|
|
"backup-timestamp-start=1565282114\n"
|
|
"backup-timestamp-stop=0\n"
|
|
"backup-type=\"full\"\n"
|
|
"\n"
|
|
"[backup:db]\n"
|
|
TEST_BACKUP_DB2_11
|
|
TEST_MANIFEST_OPTION_ALL
|
|
TEST_MANIFEST_TARGET
|
|
TEST_MANIFEST_DB
|
|
TEST_MANIFEST_FILE
|
|
"pg_data/testvalid={\"checksum\":\"%s\",\"size\":7,\"timestamp\":1565282114}\n"
|
|
"pg_data/testzero={\"repo-size\":20,\"size\":0,\"timestamp\":1601405663}\n"
|
|
TEST_MANIFEST_FILE_DEFAULT
|
|
TEST_MANIFEST_LINK
|
|
TEST_MANIFEST_LINK_DEFAULT
|
|
TEST_MANIFEST_PATH
|
|
TEST_MANIFEST_PATH_DEFAULT,
|
|
strZ(strNewEncode(encodingHex, fileChecksum)));
|
|
|
|
HRN_INFO_PUT(
|
|
storageRepoIdxWrite(0), STORAGE_REPO_BACKUP "/20181119-152900F/" BACKUP_MANIFEST_FILE, strZ(manifestContent),
|
|
.comment = "valid manifest");
|
|
HRN_INFO_PUT(
|
|
storageRepoIdxWrite(0), STORAGE_REPO_BACKUP "/20181119-152900F/" BACKUP_MANIFEST_FILE INFO_COPY_EXT,
|
|
strZ(manifestContent), .comment = "valid manifest copy");
|
|
|
|
// Create a manifest for the dependent that has references
|
|
manifestContent = strNewFmt(
|
|
"[backup]\n"
|
|
"backup-label=\"20181119-152900F_20181119-152909D\"\n"
|
|
"backup-timestamp-copy-start=0\n"
|
|
"backup-timestamp-start=1565282114\n"
|
|
"backup-timestamp-stop=0\n"
|
|
"backup-type=\"diff\"\n"
|
|
"\n"
|
|
"[backup:db]\n"
|
|
TEST_BACKUP_DB2_11
|
|
TEST_MANIFEST_OPTION_ALL
|
|
TEST_MANIFEST_TARGET
|
|
TEST_MANIFEST_DB
|
|
"\n"
|
|
"[target:file]\n"
|
|
"pg_data/PG_VERSION={\"checksum\":\"184473f470864e067ee3a22e64b47b0a1c356f29\""
|
|
",\"reference\":\"20181119-152900F\",\"size\":4,\"timestamp\":1565282114}\n"
|
|
"pg_data/testfile={\"checksum\":\"%s\",\"reference\":\"20181119-152900F\",\"size\":7,\"timestamp\":1565282114}\n"
|
|
"pg_data/testfile2={\"checksum\":\"%s\",\"size\":7,\"timestamp\":1565282114}\n"
|
|
"pg_data/repochk={\"checksum\":\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"rck\":\"%s\",\"repo-size\":7"
|
|
",\"size\":3,\"timestamp\":1565282114}\n"
|
|
"pg_data/testmissing={\"checksum\":\"123473f470864e067ee3a22e64b47b0a1c356abc\",\"size\":7,\"timestamp\":1565282114}\n"
|
|
"pg_data/testother={\"checksum\":\"%s\",\"reference\":\"UNPROCESSEDBACKUP\",\"size\":7,\"timestamp\":1565282114}\n"
|
|
TEST_MANIFEST_FILE_DEFAULT
|
|
TEST_MANIFEST_LINK
|
|
TEST_MANIFEST_LINK_DEFAULT
|
|
TEST_MANIFEST_PATH
|
|
TEST_MANIFEST_PATH_DEFAULT,
|
|
strZ(strNewEncode(encodingHex, fileChecksum)), strZ(strNewEncode(encodingHex, fileChecksum)),
|
|
strZ(strNewEncode(encodingHex, fileChecksum)), strZ(strNewEncode(encodingHex, fileChecksum)));
|
|
|
|
// Write manifests for dependent backup
|
|
HRN_INFO_PUT(
|
|
storageRepoIdxWrite(0), STORAGE_REPO_BACKUP "/20181119-152900F_20181119-152909D/" BACKUP_MANIFEST_FILE,
|
|
strZ(manifestContent), .comment = "manifest to dependent");
|
|
HRN_INFO_PUT(
|
|
storageRepoIdxWrite(0), STORAGE_REPO_BACKUP "/20181119-152900F_20181119-152909D/" BACKUP_MANIFEST_FILE INFO_COPY_EXT,
|
|
strZ(manifestContent), .comment = "manifest copy to dependent");
|
|
HRN_STORAGE_PUT_Z(
|
|
storageRepoIdxWrite(0),
|
|
STORAGE_REPO_BACKUP "/20181119-152900F_20181119-152909D/pg_data/testfile2", fileContents,
|
|
.comment = "put valid file to dependent");
|
|
HRN_STORAGE_PUT_Z(
|
|
storageRepoIdxWrite(0),
|
|
STORAGE_REPO_BACKUP "/20181119-152900F_20181119-152909D/pg_data/repochk", fileContents,
|
|
.comment = "put valid file to dependent");
|
|
|
|
// Create an unprocessed backup label with a file that will be referenced in the dependent manifest
|
|
HRN_STORAGE_PUT_Z(
|
|
storageRepoIdxWrite(0),
|
|
STORAGE_REPO_BACKUP "/UNPROCESSEDBACKUP/pg_data/testother", fileContents, .modeFile = 0200,
|
|
.comment = "put unreadable file to unprocessed backup");
|
|
|
|
// Create in-progress backup
|
|
HRN_STORAGE_PATH_CREATE(
|
|
storageRepoIdxWrite(0), STORAGE_REPO_BACKUP "/20181119-153000F",
|
|
.comment = "create empty backup path for newest backup so in-progress");
|
|
|
|
#define TEST_BACKUP_DB1_CURRENT_FULL3_DIFF1 \
|
|
"20181119-152900F_20181119-152909D={" \
|
|
"\"backrest-format\":5,\"backrest-version\":\"2.08dev\"," \
|
|
"\"backup-archive-start\":\"000000010000000000000006\",\"backup-archive-stop\":\"000000010000000000000007\"," \
|
|
"\"backup-info-repo-size\":2369186,\"backup-info-repo-size-delta\":2369186," \
|
|
"\"backup-info-size\":20162900,\"backup-info-size-delta\":20162900," \
|
|
"\"backup-timestamp-start\":1542640898,\"backup-timestamp-stop\":1542640911,\"backup-type\":\"full\"," \
|
|
"\"db-id\":1,\"option-archive-check\":true,\"option-archive-copy\":false,\"option-backup-standby\":false," \
|
|
"\"option-checksum-page\":true,\"option-compress\":true,\"option-hardlink\":false,\"option-online\":true}\n"
|
|
|
|
#define TEST_BACKUP_DB1_CURRENT_FULL4 \
|
|
"20181119-152810F={" \
|
|
"\"backrest-format\":5,\"backrest-version\":\"2.08dev\"," \
|
|
"\"backup-info-repo-size\":2369186,\"backup-info-repo-size-delta\":2369186," \
|
|
"\"backup-info-size\":20162900,\"backup-info-size-delta\":20162900," \
|
|
"\"backup-timestamp-start\":1542640898,\"backup-timestamp-stop\":1542640911,\"backup-type\":\"full\"," \
|
|
"\"db-id\":1,\"option-archive-check\":true,\"option-archive-copy\":false,\"option-backup-standby\":false," \
|
|
"\"option-checksum-page\":true,\"option-compress\":true,\"option-hardlink\":false,\"option-online\":true}\n"
|
|
|
|
#define TEST_BACKUP_INFO \
|
|
"[backup:current]\n" \
|
|
TEST_BACKUP_DB1_CURRENT_FULL1 \
|
|
TEST_BACKUP_DB1_CURRENT_FULL2 \
|
|
TEST_BACKUP_DB1_CURRENT_FULL3 \
|
|
TEST_BACKUP_DB1_CURRENT_FULL3_DIFF1 \
|
|
TEST_BACKUP_DB1_CURRENT_FULL4 \
|
|
"\n" \
|
|
"[db]\n" \
|
|
TEST_BACKUP_DB2_11 \
|
|
"\n" \
|
|
"[db:history]\n" \
|
|
TEST_BACKUP_DB1_HISTORY \
|
|
"\n" \
|
|
TEST_BACKUP_DB2_HISTORY
|
|
|
|
HRN_INFO_PUT(storageRepoWrite(), INFO_BACKUP_PATH_FILE, TEST_BACKUP_INFO);
|
|
HRN_INFO_PUT(storageRepoWrite(), INFO_BACKUP_PATH_FILE INFO_COPY_EXT, TEST_BACKUP_INFO);
|
|
|
|
// Set log level to capture ranges
|
|
harnessLogLevelSet(logLevelDetail);
|
|
|
|
TEST_RESULT_BOOL(cfgOptionBool(cfgOptVerbose), false, "verbose is false");
|
|
TEST_RESULT_BOOL((cfgOptionStrId(cfgOptOutput) == CFGOPTVAL_VERIFY_OUTPUT_TEXT), false, "text is false");
|
|
|
|
// Redirect stdout to a file
|
|
stdoutSave = dup(STDOUT_FILENO);
|
|
stdoutFile = STRDEF(TEST_PATH "/stdout.info");
|
|
|
|
THROW_ON_SYS_ERROR(freopen(strZ(stdoutFile), "w", stdout) == NULL, FileWriteError, "unable to reopen stdout");
|
|
|
|
// Not in a test wrapper to avoid writing to stdout
|
|
cmdVerify();
|
|
|
|
// Restore normal stdout
|
|
dup2(stdoutSave, STDOUT_FILENO);
|
|
|
|
// Check output of verify command stored in file
|
|
TEST_STORAGE_GET(storageTest, strZ(stdoutFile), "", .remove = true);
|
|
TEST_RESULT_LOG(
|
|
"P00 WARN: backup '20181119-153000F' found in the repository but not in backup.info\n"
|
|
"P00 WARN: backup '20181119-152138F' found in backup.info but not in the repository\n"
|
|
"P00 DETAIL: archive path '9.6-1' is empty\n"
|
|
"P00 DETAIL: path '11-2/0000000100000000' does not contain any valid WAL to be processed\n"
|
|
"P01 INFO: invalid checksum"
|
|
" '11-2/0000000200000007/000000020000000700000FFD-a6e1a64f0813352bc2e97f116a1800377e17d2e4.gz'\n"
|
|
"P01 INFO: invalid size"
|
|
" '11-2/0000000200000007/000000020000000700000FFF-ee161f898c9012dd0c28b3fd1e7140b9cf411306'\n"
|
|
"P01 INFO: invalid result"
|
|
" 11-2/0000000200000008/000000020000000800000003-656817043007aa2100c44c712bcb456db705dab9: [41] raised from "
|
|
"local-1 shim protocol: unable to open file '" TEST_PATH "/repo/archive/db"
|
|
"/11-2/0000000200000008/000000020000000800000003-656817043007aa2100c44c712bcb456db705dab9' for read:"
|
|
" [13] Permission denied\n"
|
|
" [RETRY DETAIL OMITTED]\n"
|
|
"P00 DETAIL: unable to open missing file '" TEST_PATH "/repo/backup/db/20181119-152138F/backup.manifest' for read\n"
|
|
"P00 DETAIL: unable to open missing file '" TEST_PATH "/repo/backup/db/20181119-152138F/backup.manifest.copy'"
|
|
" for read\n"
|
|
"P00 DETAIL: manifest missing for '20181119-152138F' - backup may have expired\n"
|
|
"P00 DETAIL: unable to open missing file '" TEST_PATH "/repo/backup/db/20181119-152800F/backup.manifest' for read\n"
|
|
"P00 DETAIL: unable to open missing file '" TEST_PATH "/repo/backup/db/20181119-152800F/backup.manifest.copy'"
|
|
" for read\n"
|
|
"P00 DETAIL: manifest missing for '20181119-152800F' - backup may have expired\n"
|
|
"P00 DETAIL: unable to open missing file '" TEST_PATH "/repo/backup/db/20181119-152810F/backup.manifest.copy'"
|
|
" for read\n"
|
|
"P00 INFO: backup '20181119-152810F' manifest does not contain any target files to verify\n"
|
|
"P01 INFO: invalid checksum '20181119-152900F/pg_data/PG_VERSION'\n"
|
|
"P01 INFO: file missing '20181119-152900F_20181119-152909D/pg_data/testmissing'\n"
|
|
"P00 DETAIL: unable to open missing file '" TEST_PATH "/repo/backup/db/20181119-153000F/backup.manifest' for read\n"
|
|
"P00 INFO: backup '20181119-153000F' appears to be in progress, skipping\n"
|
|
"P01 INFO: invalid result UNPROCESSEDBACKUP/pg_data/testother: [41] raised from local-1 shim protocol:"
|
|
" unable to open file '" TEST_PATH "/repo/backup/db/UNPROCESSEDBACKUP/pg_data/testother' for read: [13]"
|
|
" Permission denied\n"
|
|
" [RETRY DETAIL OMITTED]\n"
|
|
"P00 DETAIL: archiveId: 11-2, wal start: 000000020000000700000FFD, wal stop: 000000020000000800000000\n"
|
|
"P00 DETAIL: archiveId: 11-2, wal start: 000000020000000800000002, wal stop: 000000020000000800000003\n"
|
|
"P00 DETAIL: archiveId: 11-2, wal start: 000000030000000000000000, wal stop: 000000030000000000000001\n"
|
|
"P00 INFO: stanza: db\n"
|
|
" status: error\n"
|
|
" archiveId: 11-2, total WAL checked: 8, total valid WAL: 5\n"
|
|
" checksum invalid: 1, size invalid: 1, other: 1\n"
|
|
" backup: 20181119-152138F, status: manifest missing, total files checked: 0, total valid files: 0\n"
|
|
" backup: 20181119-152800F, status: manifest missing, total files checked: 0, total valid files: 0\n"
|
|
" backup: 20181119-152810F, status: invalid, total files checked: 0, total valid files: 0\n"
|
|
" backup: 20181119-152900F, status: invalid, total files checked: 3, total valid files: 2\n"
|
|
" checksum invalid: 1\n"
|
|
" backup: 20181119-152900F_20181119-152909D, status: invalid, total files checked: 6,"
|
|
" total valid files: 3\n"
|
|
" missing: 1, checksum invalid: 1, other: 1");
|
|
|
|
harnessLogLevelReset();
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("text output, not verbose, with verify failures");
|
|
|
|
hrnCfgArgRawZ(argList, cfgOptOutput, "text");
|
|
HRN_CFG_LOAD(cfgCmdVerify, argList);
|
|
|
|
// Verify text output, not verbose, with failures
|
|
TEST_RESULT_STR_Z(
|
|
verifyProcess(cfgOptionBool(cfgOptVerbose)),
|
|
"stanza: db\n"
|
|
"status: error\n"
|
|
" archiveId: 11-2, total WAL checked: 8, total valid WAL: 5\n"
|
|
" checksum invalid: 1, size invalid: 1, other: 1\n"
|
|
" backup: 20181119-152138F, status: manifest missing, total files checked: 0, total valid files: 0\n"
|
|
" backup: 20181119-152800F, status: manifest missing, total files checked: 0, total valid files: 0\n"
|
|
" backup: 20181119-152810F, status: invalid, total files checked: 0, total valid files: 0\n"
|
|
" backup: 20181119-152900F, status: invalid, total files checked: 3, total valid files: 2\n"
|
|
" checksum invalid: 1\n"
|
|
" backup: 20181119-152900F_20181119-152909D, status: invalid, total files checked: 6, total valid files: 3\n"
|
|
" missing: 1, checksum invalid: 1, other: 1", "verify text output, not verbose, with verify failures");
|
|
TEST_RESULT_LOG(
|
|
"P00 WARN: backup '20181119-153000F' found in the repository but not in backup.info\n"
|
|
"P00 WARN: backup '20181119-152138F' found in backup.info but not in the repository\n"
|
|
"P01 INFO: invalid checksum"
|
|
" '11-2/0000000200000007/000000020000000700000FFD-a6e1a64f0813352bc2e97f116a1800377e17d2e4.gz'\n"
|
|
"P01 INFO: invalid size"
|
|
" '11-2/0000000200000007/000000020000000700000FFF-ee161f898c9012dd0c28b3fd1e7140b9cf411306'\n"
|
|
"P01 INFO: invalid result"
|
|
" 11-2/0000000200000008/000000020000000800000003-656817043007aa2100c44c712bcb456db705dab9: [41] raised from "
|
|
"local-1 shim protocol: unable to open file '" TEST_PATH "/repo/archive/db"
|
|
"/11-2/0000000200000008/000000020000000800000003-656817043007aa2100c44c712bcb456db705dab9' for read:"
|
|
" [13] Permission denied\n"
|
|
"P00 INFO: backup '20181119-152810F' manifest does not contain any target files to verify\n"
|
|
"P01 INFO: invalid checksum '20181119-152900F/pg_data/PG_VERSION'\n"
|
|
"P01 INFO: file missing '20181119-152900F_20181119-152909D/pg_data/testmissing'\n"
|
|
"P00 INFO: backup '20181119-153000F' appears to be in progress, skipping\n"
|
|
"P01 INFO: invalid result UNPROCESSEDBACKUP/pg_data/testother: [41] raised from local-1 shim protocol:"
|
|
" unable to open file '" TEST_PATH "/repo/backup/db/UNPROCESSEDBACKUP/pg_data/testother' for read: [13]"
|
|
" Permission denied");
|
|
}
|
|
|
|
// *****************************************************************************************************************************
|
|
if (testBegin("cmdVerify()"))
|
|
{
|
|
// Load Parameters
|
|
StringList *argList = strLstDup(argListBase);
|
|
HRN_CFG_LOAD(cfgCmdVerify, argList);
|
|
|
|
#define TEST_BACKUP_DB2_CURRENT_FULL1 \
|
|
"20201119-163000F={" \
|
|
"\"backrest-format\":5,\"backrest-version\":\"2.08dev\"," \
|
|
"\"backup-archive-start\":\"000000020000000000000001\",\"backup-archive-stop\":\"000000020000000000000001\"," \
|
|
"\"backup-info-repo-size\":2369186,\"backup-info-repo-size-delta\":2369186," \
|
|
"\"backup-info-size\":20162900,\"backup-info-size-delta\":20162900," \
|
|
"\"backup-timestamp-start\":1542640898,\"backup-timestamp-stop\":1542640911,\"backup-type\":\"full\"," \
|
|
"\"db-id\":2,\"option-archive-check\":true,\"option-archive-copy\":false,\"option-backup-standby\":false," \
|
|
"\"option-checksum-page\":true,\"option-compress\":true,\"option-hardlink\":false,\"option-online\":true}\n"
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("prior backup verification incomplete - referenced file checked");
|
|
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), INFO_ARCHIVE_PATH_FILE, TEST_ARCHIVE_INFO_MULTI_HISTORY_BASE, .comment = "valid archive.info");
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), INFO_ARCHIVE_PATH_FILE INFO_COPY_EXT, TEST_ARCHIVE_INFO_MULTI_HISTORY_BASE,
|
|
.comment = "valid archive.info.copy");
|
|
|
|
#undef TEST_BACKUP_INFO
|
|
#define TEST_BACKUP_INFO \
|
|
"[backup:current]\n" \
|
|
TEST_BACKUP_DB1_CURRENT_FULL3 \
|
|
TEST_BACKUP_DB1_CURRENT_FULL3_DIFF1 \
|
|
"\n" \
|
|
"[db]\n" \
|
|
TEST_BACKUP_DB2_11 \
|
|
"\n" \
|
|
"[db:history]\n" \
|
|
TEST_BACKUP_DB1_HISTORY \
|
|
"\n" \
|
|
TEST_BACKUP_DB2_HISTORY
|
|
|
|
HRN_INFO_PUT(storageRepoWrite(), INFO_BACKUP_PATH_FILE, TEST_BACKUP_INFO);
|
|
HRN_INFO_PUT(storageRepoWrite(), INFO_BACKUP_PATH_FILE INFO_COPY_EXT, TEST_BACKUP_INFO);
|
|
|
|
// Create valid full backup for DB1
|
|
#define TEST_MANIFEST_FULL_DB1 \
|
|
TEST_MANIFEST_HEADER \
|
|
TEST_MANIFEST_DB_94 \
|
|
TEST_MANIFEST_OPTION_ALL \
|
|
TEST_MANIFEST_TARGET \
|
|
TEST_MANIFEST_DB \
|
|
TEST_MANIFEST_FILE \
|
|
TEST_MANIFEST_FILE_DEFAULT \
|
|
TEST_MANIFEST_LINK \
|
|
TEST_MANIFEST_LINK_DEFAULT \
|
|
TEST_MANIFEST_PATH \
|
|
TEST_MANIFEST_PATH_DEFAULT
|
|
|
|
// Write manifests for full backup
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), STORAGE_REPO_BACKUP "/20181119-152900F/" BACKUP_MANIFEST_FILE, TEST_MANIFEST_FULL_DB1,
|
|
.comment = "valid manifest - full");
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), STORAGE_REPO_BACKUP "/20181119-152900F/" BACKUP_MANIFEST_FILE INFO_COPY_EXT, TEST_MANIFEST_FULL_DB1,
|
|
.comment = "valid manifest copy - full");
|
|
|
|
// Create valid diff backup for DB1
|
|
#define TEST_MANIFEST_DIFF_DB1 \
|
|
TEST_MANIFEST_HEADER \
|
|
TEST_MANIFEST_DB_94 \
|
|
TEST_MANIFEST_OPTION_ALL \
|
|
TEST_MANIFEST_TARGET \
|
|
TEST_MANIFEST_DB \
|
|
"\n" \
|
|
"[target:file]\n" \
|
|
"pg_data/PG_VERSION={\"checksum\":\"184473f470864e067ee3a22e64b47b0a1c356f29\",\"reference\":\"20181119-152900F\"" \
|
|
",\"size\":4,\"timestamp\":1565282114}\n" \
|
|
TEST_MANIFEST_FILE_DEFAULT \
|
|
TEST_MANIFEST_LINK \
|
|
TEST_MANIFEST_LINK_DEFAULT \
|
|
TEST_MANIFEST_PATH \
|
|
TEST_MANIFEST_PATH_DEFAULT
|
|
|
|
// Write manifests for diff backup
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), STORAGE_REPO_BACKUP "/20181119-152900F_20181119-152909D/" BACKUP_MANIFEST_FILE,
|
|
TEST_MANIFEST_DIFF_DB1, .comment = "valid manifest - diff");
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), STORAGE_REPO_BACKUP "/20181119-152900F_20181119-152909D/" BACKUP_MANIFEST_FILE INFO_COPY_EXT,
|
|
TEST_MANIFEST_DIFF_DB1, .comment = "valid manifest copy - diff");
|
|
|
|
// Put the file referenced by both backups into the full backup
|
|
HRN_STORAGE_PUT_Z(storageRepoWrite(), STORAGE_REPO_BACKUP "/20181119-152900F/pg_data/PG_VERSION", fileContents);
|
|
|
|
// Redirect stdout to a file
|
|
int stdoutSave = dup(STDOUT_FILENO);
|
|
const String *stdoutFile = STRDEF(TEST_PATH "/stdout.info");
|
|
|
|
THROW_ON_SYS_ERROR(freopen(strZ(stdoutFile), "w", stdout) == NULL, FileWriteError, "unable to reopen stdout");
|
|
|
|
// Not in a test wrapper to avoid writing to stdout
|
|
cmdVerify();
|
|
|
|
// Restore normal stdout
|
|
dup2(stdoutSave, STDOUT_FILENO);
|
|
|
|
// Check output of verify command stored in file
|
|
TEST_STORAGE_GET(storageTest, strZ(stdoutFile), "", .remove = true);
|
|
// The error for the referenced file is logged twice because it is checked again by the second backup since the first backup
|
|
// verification had not yet completed before the second backup verification began
|
|
TEST_RESULT_LOG(
|
|
"P01 INFO: invalid checksum '20181119-152900F/pg_data/PG_VERSION'\n"
|
|
"P01 INFO: invalid checksum '20181119-152900F/pg_data/PG_VERSION'\n"
|
|
"P00 INFO: stanza: db\n"
|
|
" status: error\n"
|
|
" backup: 20181119-152900F, status: invalid, total files checked: 1, total valid files: 0\n"
|
|
" checksum invalid: 1\n"
|
|
" backup: 20181119-152900F_20181119-152909D, status: invalid, total files checked: 1,"
|
|
" total valid files: 0\n"
|
|
" checksum invalid: 1");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("valid backup, prior backup verification complete - referenced file not checked");
|
|
|
|
// Set process max to 1 and add more files to check so first backup completes before second is checked
|
|
hrnCfgArgRawZ(argList, cfgOptProcessMax, "1");
|
|
HRN_CFG_LOAD(cfgCmdVerify, argList);
|
|
|
|
#undef TEST_BACKUP_INFO
|
|
#define TEST_BACKUP_INFO \
|
|
"[backup:current]\n" \
|
|
TEST_BACKUP_DB1_CURRENT_FULL3 \
|
|
TEST_BACKUP_DB1_CURRENT_FULL3_DIFF1 \
|
|
TEST_BACKUP_DB2_CURRENT_FULL1 \
|
|
"\n" \
|
|
"[db]\n" \
|
|
TEST_BACKUP_DB2_11 \
|
|
"\n" \
|
|
"[db:history]\n" \
|
|
TEST_BACKUP_DB1_HISTORY \
|
|
"\n" \
|
|
TEST_BACKUP_DB2_HISTORY
|
|
|
|
HRN_INFO_PUT(storageRepoWrite(), INFO_BACKUP_PATH_FILE, TEST_BACKUP_INFO);
|
|
HRN_INFO_PUT(storageRepoWrite(), INFO_BACKUP_PATH_FILE INFO_COPY_EXT, TEST_BACKUP_INFO);
|
|
|
|
String *manifestContent = strNewFmt(
|
|
TEST_MANIFEST_HEADER
|
|
TEST_MANIFEST_DB_94
|
|
TEST_MANIFEST_OPTION_ALL
|
|
TEST_MANIFEST_TARGET
|
|
TEST_MANIFEST_DB
|
|
TEST_MANIFEST_FILE
|
|
"pg_data/base/1/555_init={\"checksum\":\"%s\",\"size\":1,\"timestamp\":1565282114}\n"
|
|
"pg_data/base/1/555_init.1={\"size\":0,\"timestamp\":1565282114}\n"
|
|
TEST_MANIFEST_FILE_DEFAULT
|
|
TEST_MANIFEST_LINK
|
|
TEST_MANIFEST_LINK_DEFAULT
|
|
TEST_MANIFEST_PATH
|
|
TEST_MANIFEST_PATH_DEFAULT,
|
|
strZ(strNewEncode(encodingHex, fileChecksum)));
|
|
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), STORAGE_REPO_BACKUP "/20181119-152900F/" BACKUP_MANIFEST_FILE, strZ(manifestContent),
|
|
.comment = "valid manifest - full");
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), STORAGE_REPO_BACKUP "/20181119-152900F/" BACKUP_MANIFEST_FILE INFO_COPY_EXT, strZ(manifestContent),
|
|
.comment = "valid manifest copy - full");
|
|
|
|
HRN_STORAGE_PUT_Z(
|
|
storageRepoWrite(),
|
|
STORAGE_REPO_BACKUP "/20181119-152900F/pg_data/base/1/555_init", fileContents, .comment = "invalid size");
|
|
|
|
// Diff manifest
|
|
manifestContent = strNewZ(
|
|
TEST_MANIFEST_HEADER
|
|
TEST_MANIFEST_DB_94
|
|
TEST_MANIFEST_OPTION_ALL
|
|
TEST_MANIFEST_TARGET
|
|
TEST_MANIFEST_DB
|
|
"\n"
|
|
"[target:file]\n"
|
|
"pg_data/PG_VERSION={\"checksum\":\"184473f470864e067ee3a22e64b47b0a1c356f29\",\"reference\":\"20181119-152900F\""
|
|
",\"size\":4,\"timestamp\":1565282114}\n"
|
|
TEST_MANIFEST_FILE_DEFAULT
|
|
TEST_MANIFEST_LINK
|
|
TEST_MANIFEST_LINK_DEFAULT
|
|
TEST_MANIFEST_PATH
|
|
TEST_MANIFEST_PATH_DEFAULT);
|
|
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), STORAGE_REPO_BACKUP "/20181119-152900F_20181119-152909D/" BACKUP_MANIFEST_FILE,
|
|
strZ(manifestContent), .comment = "valid manifest - diff");
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), STORAGE_REPO_BACKUP "/20181119-152900F_20181119-152909D/" BACKUP_MANIFEST_FILE INFO_COPY_EXT,
|
|
strZ(manifestContent), .comment = "valid manifest copy - diff");
|
|
|
|
// Create valid full backup and valid diff backup
|
|
manifestContent = strNewFmt(
|
|
TEST_MANIFEST_HEADER
|
|
"backup-bundle=true\n"
|
|
"\n"
|
|
"[backup:db]\n"
|
|
TEST_BACKUP_DB2_11
|
|
TEST_MANIFEST_OPTION_ALL
|
|
TEST_MANIFEST_TARGET
|
|
TEST_MANIFEST_DB
|
|
"\n"
|
|
"[target:file]\n"
|
|
"pg_data/validfile={\"bni\":1,\"bno\":3,\"checksum\":\"%s\",\"size\":%u,\"timestamp\":1565282114}\n"
|
|
"pg_data/zerofile={\"size\":0,\"timestamp\":1565282114}\n"
|
|
"pg_data/biind={\"bi\":1,\"bim\":3,\"checksum\":\"9865d483bc5a94f2e30056fc256ed3066af54d04\",\"size\":4"
|
|
",\"timestamp\":1565282114}\n"
|
|
TEST_MANIFEST_FILE_DEFAULT
|
|
TEST_MANIFEST_LINK
|
|
TEST_MANIFEST_LINK_DEFAULT
|
|
TEST_MANIFEST_PATH
|
|
TEST_MANIFEST_PATH_DEFAULT,
|
|
strZ(strNewEncode(encodingHex, fileChecksum)), (unsigned int)fileSize);
|
|
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), STORAGE_REPO_BACKUP "/20201119-163000F/" BACKUP_MANIFEST_FILE, strZ(manifestContent),
|
|
.comment = "valid manifest - full");
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), STORAGE_REPO_BACKUP "/20201119-163000F/" BACKUP_MANIFEST_FILE INFO_COPY_EXT, strZ(manifestContent),
|
|
.comment = "valid manifest copy - full");
|
|
|
|
HRN_STORAGE_PUT_Z(
|
|
storageRepoWrite(), STORAGE_REPO_BACKUP "/20201119-163000F/bundle/1", zNewFmt("XXX%s", fileContents),
|
|
.comment = "valid file");
|
|
HRN_STORAGE_PUT_Z(
|
|
storageRepoWrite(), STORAGE_REPO_BACKUP "/20201119-163000F/pg_data/biind.pgbi", "ZZZZ", .comment = "pgbi file");
|
|
|
|
// Create WAL file with just header info and small WAL size
|
|
Buffer *walBuffer = bufNew((size_t)(1024 * 1024));
|
|
bufUsedSet(walBuffer, bufSize(walBuffer));
|
|
memset(bufPtr(walBuffer), 0, bufSize(walBuffer));
|
|
HRN_PG_WAL_TO_BUFFER(walBuffer, PG_VERSION_11, .size = 1024 * 1024);
|
|
const char *walBufferSha1 = strZ(strNewEncode(encodingHex, cryptoHashOne(hashTypeSha1, walBuffer)));
|
|
|
|
HRN_STORAGE_PUT(
|
|
storageRepoWrite(),
|
|
zNewFmt(STORAGE_REPO_ARCHIVE "/11-2/0000000200000000/000000020000000000000001-%s", walBufferSha1), walBuffer,
|
|
.comment = "valid WAL");
|
|
|
|
// Redirect stdout to a file
|
|
stdoutSave = dup(STDOUT_FILENO);
|
|
stdoutFile = STRDEF(TEST_PATH "/stdout.info");
|
|
|
|
THROW_ON_SYS_ERROR(freopen(strZ(stdoutFile), "w", stdout) == NULL, FileWriteError, "unable to reopen stdout");
|
|
|
|
// Not in a test wrapper to avoid writing to stdout
|
|
cmdVerify();
|
|
|
|
// Restore normal stdout
|
|
dup2(stdoutSave, STDOUT_FILENO);
|
|
|
|
// Check output of verify command stored in file
|
|
TEST_STORAGE_GET(storageTest, strZ(stdoutFile), "", .remove = true);
|
|
TEST_RESULT_LOG(
|
|
"P01 INFO: invalid checksum '20181119-152900F/pg_data/PG_VERSION'\n"
|
|
"P01 INFO: invalid size '20181119-152900F/pg_data/base/1/555_init'\n"
|
|
"P01 INFO: file missing '20181119-152900F/pg_data/base/1/555_init.1'\n"
|
|
"P00 INFO: stanza: db\n"
|
|
" status: error\n"
|
|
" backup: 20181119-152900F, status: invalid, total files checked: 3, total valid files: 0\n"
|
|
" missing: 1, checksum invalid: 1, size invalid: 1\n"
|
|
" backup: 20181119-152900F_20181119-152909D, status: invalid, total files checked: 1,"
|
|
" total valid files: 0\n"
|
|
" checksum invalid: 1");
|
|
}
|
|
|
|
// *****************************************************************************************************************************
|
|
if (testBegin("cmdVerify() verbose text"))
|
|
{
|
|
// Load Parameters
|
|
StringList *argList = strLstDup(argListBase);
|
|
hrnCfgArgRawZ(argList, cfgOptOutput, "text");
|
|
hrnCfgArgRawZ(argList, cfgOptVerbose, "y");
|
|
HRN_CFG_LOAD(cfgCmdVerify, argList);
|
|
|
|
#define TEST_BACKUP_DB1_CURRENT_FULL3_DIFF1 \
|
|
"20181119-152900F_20181119-152909D={" \
|
|
"\"backrest-format\":5,\"backrest-version\":\"2.08dev\"," \
|
|
"\"backup-archive-start\":\"000000010000000000000006\",\"backup-archive-stop\":\"000000010000000000000007\"," \
|
|
"\"backup-info-repo-size\":2369186,\"backup-info-repo-size-delta\":2369186," \
|
|
"\"backup-info-size\":20162900,\"backup-info-size-delta\":20162900," \
|
|
"\"backup-timestamp-start\":1542640898,\"backup-timestamp-stop\":1542640911,\"backup-type\":\"full\"," \
|
|
"\"db-id\":1,\"option-archive-check\":true,\"option-archive-copy\":false,\"option-backup-standby\":false," \
|
|
"\"option-checksum-page\":true,\"option-compress\":true,\"option-hardlink\":false,\"option-online\":true}\n"
|
|
|
|
#define TEST_BACKUP_DB2_CURRENT_FULL1 \
|
|
"20201119-163000F={" \
|
|
"\"backrest-format\":5,\"backrest-version\":\"2.08dev\"," \
|
|
"\"backup-archive-start\":\"000000020000000000000001\",\"backup-archive-stop\":\"000000020000000000000001\"," \
|
|
"\"backup-info-repo-size\":2369186,\"backup-info-repo-size-delta\":2369186," \
|
|
"\"backup-info-size\":20162900,\"backup-info-size-delta\":20162900," \
|
|
"\"backup-timestamp-start\":1542640898,\"backup-timestamp-stop\":1542640911,\"backup-type\":\"full\"," \
|
|
"\"db-id\":2,\"option-archive-check\":true,\"option-archive-copy\":false,\"option-backup-standby\":false," \
|
|
"\"option-checksum-page\":true,\"option-compress\":true,\"option-hardlink\":false,\"option-online\":true}\n"
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("prior backup verification incomplete - referenced file checked verbose, text output");
|
|
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), INFO_ARCHIVE_PATH_FILE, TEST_ARCHIVE_INFO_MULTI_HISTORY_BASE, .comment = "valid archive.info");
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), INFO_ARCHIVE_PATH_FILE INFO_COPY_EXT, TEST_ARCHIVE_INFO_MULTI_HISTORY_BASE,
|
|
.comment = "valid archive.info.copy");
|
|
|
|
#undef TEST_BACKUP_INFO
|
|
#define TEST_BACKUP_INFO \
|
|
"[backup:current]\n" \
|
|
TEST_BACKUP_DB1_CURRENT_FULL3 \
|
|
TEST_BACKUP_DB1_CURRENT_FULL3_DIFF1 \
|
|
"\n" \
|
|
"[db]\n" \
|
|
TEST_BACKUP_DB2_11 \
|
|
"\n" \
|
|
"[db:history]\n" \
|
|
TEST_BACKUP_DB1_HISTORY \
|
|
"\n" \
|
|
TEST_BACKUP_DB2_HISTORY
|
|
|
|
HRN_INFO_PUT(storageRepoWrite(), INFO_BACKUP_PATH_FILE, TEST_BACKUP_INFO);
|
|
HRN_INFO_PUT(storageRepoWrite(), INFO_BACKUP_PATH_FILE INFO_COPY_EXT, TEST_BACKUP_INFO);
|
|
|
|
// Create valid full backup
|
|
#define TEST_MANIFEST_FULL_DB2 \
|
|
TEST_MANIFEST_HEADER \
|
|
TEST_MANIFEST_DB_94 \
|
|
TEST_MANIFEST_OPTION_ALL \
|
|
TEST_MANIFEST_TARGET \
|
|
TEST_MANIFEST_DB \
|
|
TEST_MANIFEST_FILE \
|
|
"pg_data/biind={\"bi\":1,\"bim\":3,\"checksum\":\"ffffffffffffffffffffffffffffffffffffffff\",\"size\":4" \
|
|
",\"timestamp\":1565282114}\n" \
|
|
TEST_MANIFEST_FILE_DEFAULT \
|
|
TEST_MANIFEST_LINK \
|
|
TEST_MANIFEST_LINK_DEFAULT \
|
|
TEST_MANIFEST_PATH \
|
|
TEST_MANIFEST_PATH_DEFAULT
|
|
|
|
// Write manifests for full backup
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), STORAGE_REPO_BACKUP "/20181119-152900F/" BACKUP_MANIFEST_FILE, TEST_MANIFEST_FULL_DB2,
|
|
.comment = "valid manifest - full");
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), STORAGE_REPO_BACKUP "/20181119-152900F/" BACKUP_MANIFEST_FILE INFO_COPY_EXT, TEST_MANIFEST_FULL_DB2,
|
|
.comment = "valid manifest copy - full");
|
|
HRN_STORAGE_PUT_Z(
|
|
storageRepoWrite(), STORAGE_REPO_BACKUP "/20181119-152900F/pg_data/biind.pgbi", "ZVZV", .comment = "pgbi file");
|
|
|
|
// Create valid diff backup
|
|
#define TEST_MANIFEST_DIFF_DB2 \
|
|
TEST_MANIFEST_HEADER \
|
|
TEST_MANIFEST_DB_94 \
|
|
TEST_MANIFEST_OPTION_ALL \
|
|
TEST_MANIFEST_TARGET \
|
|
TEST_MANIFEST_DB \
|
|
"\n" \
|
|
"[target:file]\n" \
|
|
"pg_data/PG_VERSION={\"checksum\":\"184473f470864e067ee3a22e64b47b0a1c356f29\",\"reference\":\"20181119-152900F\"" \
|
|
",\"size\":4,\"timestamp\":1565282114}\n" \
|
|
"pg_data/biind={\"bi\":1,\"bim\":3,\"checksum\":\"ffffffffffffffffffffffffffffffffffffffff\"," \
|
|
"\"reference\":\"20181119-152900F\",\"size\":4,\"timestamp\":1565282114}\n" \
|
|
TEST_MANIFEST_FILE_DEFAULT \
|
|
TEST_MANIFEST_LINK \
|
|
TEST_MANIFEST_LINK_DEFAULT \
|
|
TEST_MANIFEST_PATH \
|
|
TEST_MANIFEST_PATH_DEFAULT
|
|
|
|
// Write manifests for diff backup
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), STORAGE_REPO_BACKUP "/20181119-152900F_20181119-152909D/" BACKUP_MANIFEST_FILE,
|
|
TEST_MANIFEST_DIFF_DB2, .comment = "valid manifest - diff");
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), STORAGE_REPO_BACKUP "/20181119-152900F_20181119-152909D/" BACKUP_MANIFEST_FILE INFO_COPY_EXT,
|
|
TEST_MANIFEST_DIFF_DB2, .comment = "valid manifest copy - diff");
|
|
|
|
// Put the file referenced by both backups into the full backup
|
|
HRN_STORAGE_PUT_Z(storageRepoWrite(), STORAGE_REPO_BACKUP "/20181119-152900F/pg_data/PG_VERSION", fileContents);
|
|
|
|
// Redirect stdout to a file
|
|
int stdoutSave = dup(STDOUT_FILENO);
|
|
const String *stdoutFile = STRDEF(TEST_PATH "/stdout.info");
|
|
|
|
THROW_ON_SYS_ERROR(freopen(strZ(stdoutFile), "w", stdout) == NULL, FileWriteError, "unable to reopen stdout");
|
|
|
|
// Not in a test wrapper to avoid writing to stdout
|
|
cmdVerify();
|
|
|
|
// Restore normal stdout
|
|
dup2(stdoutSave, STDOUT_FILENO);
|
|
|
|
// Check output of verify command stored in file
|
|
TEST_STORAGE_GET(
|
|
storageTest, strZ(stdoutFile),
|
|
"stanza: db\n"
|
|
"status: error\n"
|
|
" archiveId: none found\n"
|
|
" backup: 20181119-152900F, status: invalid, total files checked: 2, total valid files: 0\n"
|
|
" missing: 0, checksum invalid: 2, size invalid: 0, other: 0\n"
|
|
" backup: 20181119-152900F_20181119-152909D, status: invalid, total files checked: 2, total valid files: 1\n"
|
|
" missing: 0, checksum invalid: 1, size invalid: 0, other: 0\n", .remove = true);
|
|
TEST_RESULT_LOG(
|
|
"P01 INFO: invalid checksum '20181119-152900F/pg_data/PG_VERSION'\n"
|
|
"P01 INFO: invalid checksum '20181119-152900F/pg_data/biind.pgbi'\n"
|
|
"P00 INFO: stanza: db\n"
|
|
" status: error\n"
|
|
" archiveId: none found\n"
|
|
" backup: 20181119-152900F, status: invalid, total files checked: 2, total valid files: 0\n"
|
|
" missing: 0, checksum invalid: 2, size invalid: 0, other: 0\n"
|
|
" backup: 20181119-152900F_20181119-152909D, status: invalid, total files checked: 2,"
|
|
" total valid files: 1\n"
|
|
" missing: 0, checksum invalid: 1, size invalid: 0, other: 0");
|
|
}
|
|
|
|
// *****************************************************************************************************************************
|
|
if (testBegin("verifyProcess(), none output, not verbose, no failures"))
|
|
{
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
// Load Parameters with multi-repo
|
|
StringList *argList = strLstDup(argListBase);
|
|
hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 4, TEST_PATH "/repo4");
|
|
hrnCfgArgRawZ(argList, cfgOptOutput, "none");
|
|
HRN_CFG_LOAD(cfgCmdVerify, argList);
|
|
|
|
// Store valid archive/backup info files
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), INFO_ARCHIVE_PATH_FILE, TEST_ARCHIVE_INFO_MULTI_HISTORY_BASE, .comment = "valid archive.info");
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), INFO_ARCHIVE_PATH_FILE INFO_COPY_EXT, TEST_ARCHIVE_INFO_MULTI_HISTORY_BASE,
|
|
.comment = "valid archive.info.copy");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("none output, not verbose, with no verify failures");
|
|
|
|
#define TEST_NO_CURRENT_BACKUP \
|
|
"[db]\n" \
|
|
TEST_BACKUP_DB2_11 \
|
|
"\n" \
|
|
"[db:history]\n" \
|
|
TEST_BACKUP_DB1_HISTORY \
|
|
"\n" \
|
|
TEST_BACKUP_DB2_HISTORY
|
|
|
|
HRN_INFO_PUT(storageRepoWrite(), INFO_BACKUP_PATH_FILE, TEST_NO_CURRENT_BACKUP, .comment = "no current backups");
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), INFO_BACKUP_PATH_FILE INFO_COPY_EXT, TEST_NO_CURRENT_BACKUP, .comment = "no current backups copy");
|
|
|
|
// Create WAL file with just header info and small WAL size
|
|
Buffer *walBuffer = bufNew((size_t)(1024 * 1024));
|
|
bufUsedSet(walBuffer, bufSize(walBuffer));
|
|
memset(bufPtr(walBuffer), 0, bufSize(walBuffer));
|
|
HRN_PG_WAL_TO_BUFFER(walBuffer, PG_VERSION_11, .size = 1024 * 1024);
|
|
const char *walBufferSha1 = strZ(strNewEncode(encodingHex, cryptoHashOne(hashTypeSha1, walBuffer)));
|
|
|
|
HRN_STORAGE_PUT(
|
|
storageRepoIdxWrite(0),
|
|
zNewFmt(STORAGE_REPO_ARCHIVE "/11-2/0000000200000007/000000020000000700000FFE-%s", walBufferSha1), walBuffer,
|
|
.comment = "valid WAL");
|
|
|
|
harnessLogLevelSet(logLevelDetail);
|
|
|
|
// Verify text output with no verify errors
|
|
TEST_RESULT_STR_Z(verifyProcess(cfgOptionBool(cfgOptVerbose)), "", "verify none output, not verbose, with no failures");
|
|
TEST_RESULT_LOG(
|
|
"P00 DETAIL: no backups exist in the repo\n"
|
|
"P00 DETAIL: archiveId: 11-2, wal start: 000000020000000700000FFE, wal stop: 000000020000000700000FFE");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("invalid WAL magic");
|
|
|
|
// Create WAL file with just header info and small WAL size
|
|
walBuffer = bufNew((size_t)(1024 * 1024));
|
|
bufUsedSet(walBuffer, bufSize(walBuffer));
|
|
memset(bufPtr(walBuffer), 0, bufSize(walBuffer));
|
|
HRN_PG_WAL_OVERRIDE_TO_BUFFER(walBuffer, PG_VERSION_11, 999, .size = 1024 * 1024);
|
|
const char *walBufferSha2 = strZ(strNewEncode(encodingHex, cryptoHashOne(hashTypeSha1, walBuffer)));
|
|
|
|
HRN_STORAGE_PUT(
|
|
storageRepoIdxWrite(0),
|
|
zNewFmt(STORAGE_REPO_ARCHIVE "/11-2/0000000200000007/000000020000000700000FFD-%s", walBufferSha2), walBuffer,
|
|
.comment = "invalid WAL magic");
|
|
|
|
HRN_CFG_LOAD(cfgCmdVerify, argList);
|
|
TEST_ERROR(
|
|
verifyProcess(cfgOptionBool(cfgOptVerbose)), VersionNotSupportedError,
|
|
"unexpected WAL magic 999\n"
|
|
"HINT: is this version of PostgreSQL supported?\n"
|
|
"HINT: is pgBackRest up to date on all hosts?");
|
|
TEST_RESULT_LOG(
|
|
"P00 DETAIL: no backups exist in the repo");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("none output, verbose, override WAL magic, with no verify failures");
|
|
|
|
hrnCfgArgRawZ(argList, cfgOptVerbose, "y");
|
|
hrnCfgArgRawZ(argList, cfgOptPgVersionForce, PG_VERSION_11_Z);
|
|
HRN_CFG_LOAD(cfgCmdVerify, argList);
|
|
TEST_RESULT_STR_Z(
|
|
verifyProcess(cfgOptionBool(cfgOptVerbose)),
|
|
"stanza: db\n"
|
|
"status: ok\n"
|
|
" archiveId: 11-2, total WAL checked: 2, total valid WAL: 2\n"
|
|
" missing: 0, checksum invalid: 0, size invalid: 0, other: 0\n"
|
|
" backup: none found",
|
|
"verify none output, verbose, with no failures");
|
|
TEST_RESULT_LOG(
|
|
"P00 DETAIL: no backups exist in the repo\n"
|
|
"P00 DETAIL: archiveId: 11-2, wal start: 000000020000000700000FFD, wal stop: 000000020000000700000FFE");
|
|
}
|
|
|
|
// *****************************************************************************************************************************
|
|
if (testBegin("verifyProcess(), text"))
|
|
{
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
// Load Parameters with multi-repo
|
|
StringList *argList = strLstDup(argListBase);
|
|
hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 4, TEST_PATH "/repo4");
|
|
hrnCfgArgRawZ(argList, cfgOptOutput, "text");
|
|
HRN_CFG_LOAD(cfgCmdVerify, argList);
|
|
|
|
// Store valid archive/backup info files
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), INFO_ARCHIVE_PATH_FILE, TEST_ARCHIVE_INFO_MULTI_HISTORY_BASE, .comment = "valid archive.info");
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), INFO_ARCHIVE_PATH_FILE INFO_COPY_EXT, TEST_ARCHIVE_INFO_MULTI_HISTORY_BASE,
|
|
.comment = "valid archive.info.copy");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("text output, not verbose, with no verify failures");
|
|
|
|
#define TEST_NO_CURRENT_BACKUP \
|
|
"[db]\n" \
|
|
TEST_BACKUP_DB2_11 \
|
|
"\n" \
|
|
"[db:history]\n" \
|
|
TEST_BACKUP_DB1_HISTORY \
|
|
"\n" \
|
|
TEST_BACKUP_DB2_HISTORY
|
|
|
|
HRN_INFO_PUT(storageRepoWrite(), INFO_BACKUP_PATH_FILE, TEST_NO_CURRENT_BACKUP, .comment = "no current backups");
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), INFO_BACKUP_PATH_FILE INFO_COPY_EXT, TEST_NO_CURRENT_BACKUP, .comment = "no current backups copy");
|
|
|
|
// Create WAL file with just header info and small WAL size
|
|
Buffer *walBuffer = bufNew((size_t)(1024 * 1024));
|
|
bufUsedSet(walBuffer, bufSize(walBuffer));
|
|
memset(bufPtr(walBuffer), 0, bufSize(walBuffer));
|
|
HRN_PG_WAL_TO_BUFFER(walBuffer, PG_VERSION_11, .size = 1024 * 1024);
|
|
const char *walBufferSha1 = strZ(strNewEncode(encodingHex, cryptoHashOne(hashTypeSha1, walBuffer)));
|
|
|
|
HRN_STORAGE_PUT(
|
|
storageRepoIdxWrite(0),
|
|
zNewFmt(STORAGE_REPO_ARCHIVE "/11-2/0000000200000007/000000020000000700000FFE-%s", walBufferSha1), walBuffer,
|
|
.comment = "valid WAL");
|
|
|
|
harnessLogLevelSet(logLevelDetail);
|
|
|
|
// Verify text output with no verify errors
|
|
TEST_RESULT_STR_Z(verifyProcess(cfgOptionBool(cfgOptVerbose)), "", "verify text output, not verbose, with no failures");
|
|
TEST_RESULT_LOG(
|
|
"P00 DETAIL: no backups exist in the repo\n"
|
|
"P00 DETAIL: archiveId: 11-2, wal start: 000000020000000700000FFE, wal stop: 000000020000000700000FFE");
|
|
}
|
|
|
|
// *****************************************************************************************************************************
|
|
if (testBegin("verifyProcess(), text, verbose"))
|
|
{
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
// Load Parameters with multi-repo
|
|
StringList *argList = strLstDup(argListBase);
|
|
hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 4, TEST_PATH "/repo4");
|
|
hrnCfgArgRawZ(argList, cfgOptOutput, "text");
|
|
hrnCfgArgRawZ(argList, cfgOptVerbose, "y");
|
|
HRN_CFG_LOAD(cfgCmdVerify, argList);
|
|
|
|
// Store valid archive/backup info files
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), INFO_ARCHIVE_PATH_FILE, TEST_ARCHIVE_INFO_MULTI_HISTORY_BASE, .comment = "valid archive.info");
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), INFO_ARCHIVE_PATH_FILE INFO_COPY_EXT, TEST_ARCHIVE_INFO_MULTI_HISTORY_BASE,
|
|
.comment = "valid archive.info.copy");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("text output, verbose with no verify failures");
|
|
|
|
#define TEST_NO_CURRENT_BACKUP \
|
|
"[db]\n" \
|
|
TEST_BACKUP_DB2_11 \
|
|
"\n" \
|
|
"[db:history]\n" \
|
|
TEST_BACKUP_DB1_HISTORY \
|
|
"\n" \
|
|
TEST_BACKUP_DB2_HISTORY
|
|
|
|
HRN_INFO_PUT(storageRepoWrite(), INFO_BACKUP_PATH_FILE, TEST_NO_CURRENT_BACKUP, .comment = "no current backups");
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), INFO_BACKUP_PATH_FILE INFO_COPY_EXT, TEST_NO_CURRENT_BACKUP, .comment = "no current backups copy");
|
|
|
|
// Create WAL file with just header info and small WAL size
|
|
Buffer *walBuffer = bufNew((size_t)(1024 * 1024));
|
|
bufUsedSet(walBuffer, bufSize(walBuffer));
|
|
memset(bufPtr(walBuffer), 0, bufSize(walBuffer));
|
|
HRN_PG_WAL_TO_BUFFER(walBuffer, PG_VERSION_11, .size = 1024 * 1024);
|
|
const char *walBufferSha1 = strZ(strNewEncode(encodingHex, cryptoHashOne(hashTypeSha1, walBuffer)));
|
|
|
|
HRN_STORAGE_PUT(
|
|
storageRepoIdxWrite(0),
|
|
zNewFmt(STORAGE_REPO_ARCHIVE "/11-2/0000000200000007/000000020000000700000FFE-%s", walBufferSha1), walBuffer,
|
|
.comment = "valid WAL");
|
|
|
|
harnessLogLevelSet(logLevelDetail);
|
|
|
|
// Verify text output with no verify errors
|
|
TEST_RESULT_STR_Z(
|
|
verifyProcess(cfgOptionBool(cfgOptVerbose)),
|
|
"stanza: db\n"
|
|
"status: ok\n"
|
|
" archiveId: 11-2, total WAL checked: 1, total valid WAL: 1\n"
|
|
" missing: 0, checksum invalid: 0, size invalid: 0, other: 0\n"
|
|
" backup: none found", "verify text output, verbose, with no failures");
|
|
TEST_RESULT_LOG(
|
|
"P00 DETAIL: no backups exist in the repo\n"
|
|
"P00 DETAIL: archiveId: 11-2, wal start: 000000020000000700000FFE, wal stop: 000000020000000700000FFE");
|
|
}
|
|
|
|
// *****************************************************************************************************************************
|
|
if (testBegin("verifyProcess(), text, verbose, --set"))
|
|
{
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
// Load Parameters
|
|
StringList *argList = strLstDup(argListBase);
|
|
hrnCfgArgRawZ(argList, cfgOptOutput, "text");
|
|
hrnCfgArgRawZ(argList, cfgOptVerbose, "y");
|
|
hrnCfgArgRawZ(argList, cfgOptSet, "20181119-152900F_20181119-152909D");
|
|
HRN_CFG_LOAD(cfgCmdVerify, argList);
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("--set with a valid backup label");
|
|
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), INFO_ARCHIVE_PATH_FILE, TEST_ARCHIVE_INFO_MULTI_HISTORY_BASE, .comment = "valid archive.info");
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), INFO_ARCHIVE_PATH_FILE INFO_COPY_EXT, TEST_ARCHIVE_INFO_MULTI_HISTORY_BASE,
|
|
.comment = "valid archive.info.copy");
|
|
|
|
#define TEST_BACKUP_DB1_CURRENT_FULL3_DIFF1 \
|
|
"20181119-152900F_20181119-152909D={" \
|
|
"\"backrest-format\":5,\"backrest-version\":\"2.08dev\"," \
|
|
"\"backup-archive-start\":\"000000010000000000000006\",\"backup-archive-stop\":\"000000010000000000000007\"," \
|
|
"\"backup-info-repo-size\":2369186,\"backup-info-repo-size-delta\":2369186," \
|
|
"\"backup-info-size\":20162900,\"backup-info-size-delta\":20162900," \
|
|
"\"backup-timestamp-start\":1542640898,\"backup-timestamp-stop\":1542640911,\"backup-type\":\"full\"," \
|
|
"\"db-id\":1,\"option-archive-check\":true,\"option-archive-copy\":false,\"option-backup-standby\":false," \
|
|
"\"option-checksum-page\":true,\"option-compress\":true,\"option-hardlink\":false,\"option-online\":true}\n"
|
|
|
|
#define TEST_BACKUP_DB2_CURRENT_FULL2 \
|
|
"20181119-153300F={" \
|
|
"\"backrest-format\":5,\"backrest-version\":\"2.08dev\"," \
|
|
"\"backup-archive-start\":\"000000050000000800000003\",\"backup-archive-stop\":\"000000050000000800000004\"," \
|
|
"\"backup-info-repo-size\":2369186,\"backup-info-repo-size-delta\":2369186," \
|
|
"\"backup-info-size\":20162900,\"backup-info-size-delta\":20162900," \
|
|
"\"backup-timestamp-start\":1542640898,\"backup-timestamp-stop\":1542640911,\"backup-type\":\"full\"," \
|
|
"\"db-id\":2,\"option-archive-check\":true,\"option-archive-copy\":false,\"option-backup-standby\":false," \
|
|
"\"option-checksum-page\":true,\"option-compress\":true,\"option-hardlink\":false,\"option-online\":true}\n"
|
|
|
|
#undef TEST_BACKUP_INFO
|
|
#define TEST_BACKUP_INFO \
|
|
"[backup:current]\n" \
|
|
TEST_BACKUP_DB1_CURRENT_FULL3 \
|
|
TEST_BACKUP_DB1_CURRENT_FULL3_DIFF1 \
|
|
TEST_BACKUP_DB2_CURRENT_FULL2 \
|
|
"\n" \
|
|
"[db]\n" \
|
|
TEST_BACKUP_DB2_11 \
|
|
"\n" \
|
|
"[db:history]\n" \
|
|
TEST_BACKUP_DB1_HISTORY \
|
|
"\n" \
|
|
TEST_BACKUP_DB2_HISTORY
|
|
|
|
HRN_INFO_PUT(storageRepoWrite(), INFO_BACKUP_PATH_FILE, TEST_BACKUP_INFO);
|
|
HRN_INFO_PUT(storageRepoWrite(), INFO_BACKUP_PATH_FILE INFO_COPY_EXT, TEST_BACKUP_INFO);
|
|
|
|
// Create valid full backup for DB1
|
|
#define TEST_MANIFEST_FULL_DB1 \
|
|
TEST_MANIFEST_HEADER \
|
|
TEST_MANIFEST_DB_94 \
|
|
TEST_MANIFEST_OPTION_ALL \
|
|
TEST_MANIFEST_TARGET \
|
|
TEST_MANIFEST_DB \
|
|
TEST_MANIFEST_FILE \
|
|
TEST_MANIFEST_FILE_DEFAULT \
|
|
TEST_MANIFEST_LINK \
|
|
TEST_MANIFEST_LINK_DEFAULT \
|
|
TEST_MANIFEST_PATH \
|
|
TEST_MANIFEST_PATH_DEFAULT
|
|
|
|
// Write manifests for full backup
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), STORAGE_REPO_BACKUP "/20181119-152900F/" BACKUP_MANIFEST_FILE, TEST_MANIFEST_FULL_DB1,
|
|
.comment = "valid manifest - full");
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), STORAGE_REPO_BACKUP "/20181119-152900F/" BACKUP_MANIFEST_FILE INFO_COPY_EXT, TEST_MANIFEST_FULL_DB1,
|
|
.comment = "valid manifest copy - full");
|
|
|
|
// Create valid diff backup for DB1
|
|
#define TEST_MANIFEST_DIFF_DB1 \
|
|
TEST_MANIFEST_HEADER \
|
|
TEST_MANIFEST_DB_94 \
|
|
TEST_MANIFEST_OPTION_ALL \
|
|
TEST_MANIFEST_TARGET \
|
|
TEST_MANIFEST_DB \
|
|
"\n" \
|
|
"[target:file]\n" \
|
|
"pg_data/PG_VERSION={\"checksum\":\"184473f470864e067ee3a22e64b47b0a1c356f29\",\"reference\":\"20181119-152900F\"" \
|
|
",\"size\":4,\"timestamp\":1565282114}\n" \
|
|
TEST_MANIFEST_FILE_DEFAULT \
|
|
TEST_MANIFEST_LINK \
|
|
TEST_MANIFEST_LINK_DEFAULT \
|
|
TEST_MANIFEST_PATH \
|
|
TEST_MANIFEST_PATH_DEFAULT
|
|
|
|
// Write manifests for diff backup
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), STORAGE_REPO_BACKUP "/20181119-152900F_20181119-152909D/" BACKUP_MANIFEST_FILE,
|
|
TEST_MANIFEST_DIFF_DB1, .comment = "valid manifest - diff");
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), STORAGE_REPO_BACKUP "/20181119-152900F_20181119-152909D/" BACKUP_MANIFEST_FILE INFO_COPY_EXT,
|
|
TEST_MANIFEST_DIFF_DB1, .comment = "valid manifest copy - diff");
|
|
|
|
// Put the file referenced by both backups into the full backup
|
|
HRN_STORAGE_PUT_Z(storageRepoWrite(), STORAGE_REPO_BACKUP "/20181119-152900F/pg_data/PG_VERSION", fileContents);
|
|
|
|
// Should only check the diff backup because of --set, no mention of full backup
|
|
TEST_RESULT_STR_Z(
|
|
verifyProcess(cfgOptionBool(cfgOptVerbose)),
|
|
"stanza: db\n"
|
|
"status: error\n"
|
|
" archiveId: none found\n"
|
|
" backup: 20181119-152900F_20181119-152909D, status: invalid, total files checked: 1, total valid files: 0\n"
|
|
" missing: 0, checksum invalid: 1, size invalid: 0, other: 0", "--set with a valid backup label\n");
|
|
TEST_RESULT_LOG(
|
|
"P00 DETAIL: no archives exist in the repo\n"
|
|
"P01 INFO: invalid checksum '20181119-152900F/pg_data/PG_VERSION'");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("--set with invalid backup label");
|
|
|
|
argList = strLstDup(argListBase);
|
|
hrnCfgArgRawZ(argList, cfgOptOutput, "text");
|
|
hrnCfgArgRawZ(argList, cfgOptVerbose, "y");
|
|
hrnCfgArgRawZ(argList, cfgOptSet, "20181119-152900F_20181119-152910D");
|
|
HRN_CFG_LOAD(cfgCmdVerify, argList);
|
|
|
|
TEST_RESULT_STR_Z(
|
|
verifyProcess(cfgOptionBool(cfgOptVerbose)),
|
|
"stanza: db\n"
|
|
"status: error\n"
|
|
" backup set 20181119-152900F_20181119-152910D is not valid",
|
|
"--set with invalid backup label, text");
|
|
TEST_RESULT_LOG("");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("--set with backup label of incorrect format");
|
|
|
|
argList = strLstDup(argListBase);
|
|
hrnCfgArgRawZ(argList, cfgOptOutput, "text");
|
|
hrnCfgArgRawZ(argList, cfgOptVerbose, "y");
|
|
hrnCfgArgRawZ(argList, cfgOptSet, "BOGUS");
|
|
HRN_CFG_LOAD(cfgCmdVerify, argList);
|
|
|
|
TEST_RESULT_STR_Z(
|
|
verifyProcess(cfgOptionBool(cfgOptVerbose)),
|
|
"stanza: db\n"
|
|
"status: error\n"
|
|
" 'BOGUS' is not a valid backup label format",
|
|
"--set with backup label of incorrect format, text");
|
|
TEST_RESULT_LOG("");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("--set with archive");
|
|
|
|
// Load Parameters - single default repo
|
|
argList = strLstDup(argListBase);
|
|
hrnCfgArgRawZ(argList, cfgOptOutput, "text");
|
|
hrnCfgArgRawZ(argList, cfgOptVerbose, "y");
|
|
hrnCfgArgRawZ(argList, cfgOptSet, "20181119-153300F");
|
|
HRN_CFG_LOAD(cfgCmdVerify, argList);
|
|
|
|
// Create WAL file with just header info and small WAL size
|
|
Buffer *walBuffer = bufNew((size_t)(1024 * 1024));
|
|
bufUsedSet(walBuffer, bufSize(walBuffer));
|
|
memset(bufPtr(walBuffer), 0, bufSize(walBuffer));
|
|
HRN_PG_WAL_TO_BUFFER(walBuffer, PG_VERSION_11, .size = 1024 * 1024);
|
|
const char *walBufferSha1 = strZ(strNewEncode(encodingHex, cryptoHashOne(hashTypeSha1, walBuffer)));
|
|
|
|
HRN_STORAGE_PUT(
|
|
storageRepoIdxWrite(0),
|
|
zNewFmt(STORAGE_REPO_ARCHIVE "/11-2/0000000500000007/000000050000000700000001-%s", walBufferSha1), walBuffer,
|
|
.comment = "valid WAL");
|
|
HRN_STORAGE_PUT(
|
|
storageRepoIdxWrite(0),
|
|
zNewFmt(STORAGE_REPO_ARCHIVE "/11-2/0000000500000008/000000050000000800000002-%s", walBufferSha1), walBuffer,
|
|
.comment = "valid WAL");
|
|
HRN_STORAGE_PUT(
|
|
storageRepoIdxWrite(0),
|
|
zNewFmt(STORAGE_REPO_ARCHIVE "/11-2/0000000500000008/000000050000000800000003-%s", walBufferSha1), walBuffer,
|
|
.comment = "valid WAL");
|
|
HRN_STORAGE_PUT(
|
|
storageRepoIdxWrite(0),
|
|
zNewFmt(STORAGE_REPO_ARCHIVE "/11-2/0000000500000008/000000050000000800000004-%s", walBufferSha1), walBuffer,
|
|
.comment = "valid WAL");
|
|
HRN_STORAGE_PUT(
|
|
storageRepoIdxWrite(0),
|
|
zNewFmt(STORAGE_REPO_ARCHIVE "/11-2/0000000500000008/000000050000000800000005-%s", walBufferSha1), walBuffer,
|
|
.comment = "valid WAL");
|
|
HRN_STORAGE_PUT(
|
|
storageRepoIdxWrite(0),
|
|
zNewFmt(STORAGE_REPO_ARCHIVE "/11-2/0000000500000009/000000050000000900000006-%s", walBufferSha1), walBuffer,
|
|
.comment = "valid WAL");
|
|
|
|
// Write manifest for full backup using 2 WAL files
|
|
String *manifestContent = strNewFmt(
|
|
"[backup]\n"
|
|
"backup-archive-start=\"000000050000000800000003\"\n"
|
|
"backup-archive-stop=\"000000050000000800000004\"\n"
|
|
"backup-label=\"20181119-153300F\"\n"
|
|
"backup-timestamp-copy-start=0\n"
|
|
"backup-timestamp-start=1565282114\n"
|
|
"backup-timestamp-stop=0\n"
|
|
"backup-type=\"full\"\n"
|
|
"\n"
|
|
"[backup:db]\n"
|
|
TEST_BACKUP_DB2_11
|
|
TEST_MANIFEST_OPTION_ALL
|
|
TEST_MANIFEST_TARGET
|
|
TEST_MANIFEST_DB
|
|
"\n"
|
|
"[target:file]\n"
|
|
"pg_data/testvalid={\"checksum\":\"%s\",\"size\":7,\"timestamp\":1565282114}\n"
|
|
TEST_MANIFEST_FILE_DEFAULT
|
|
TEST_MANIFEST_LINK
|
|
TEST_MANIFEST_LINK_DEFAULT
|
|
TEST_MANIFEST_PATH
|
|
TEST_MANIFEST_PATH_DEFAULT,
|
|
strZ(strNewEncode(encodingHex, fileChecksum)));
|
|
|
|
// Write manifests for backup
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), STORAGE_REPO_BACKUP "/20181119-153300F/" BACKUP_MANIFEST_FILE, strZ(manifestContent),
|
|
.comment = "valid manifest - full");
|
|
HRN_INFO_PUT(
|
|
storageRepoWrite(), STORAGE_REPO_BACKUP "/20181119-153300F/" BACKUP_MANIFEST_FILE INFO_COPY_EXT, strZ(manifestContent),
|
|
.comment = "valid manifest copy - full");
|
|
|
|
// Put the file into the backup
|
|
HRN_STORAGE_PUT_Z(storageRepoWrite(), STORAGE_REPO_BACKUP "/20181119-153300F/pg_data/testvalid", fileContents);
|
|
|
|
// Should only check 2 WAL files
|
|
TEST_RESULT_STR_Z(
|
|
verifyProcess(cfgOptionBool(cfgOptVerbose)),
|
|
"stanza: db\n"
|
|
"status: ok\n"
|
|
" archiveId: 11-2, total WAL checked: 2, total valid WAL: 2\n"
|
|
" missing: 0, checksum invalid: 0, size invalid: 0, other: 0\n"
|
|
" backup: 20181119-153300F, status: valid, total files checked: 1, total valid files: 1\n"
|
|
" missing: 0, checksum invalid: 0, size invalid: 0, other: 0", "--set with a valid backup label\n");
|
|
TEST_RESULT_LOG(
|
|
"P00 DETAIL: path '11-2/0000000500000007' does not contain any valid WAL to be processed\n"
|
|
"P00 DETAIL: path '11-2/0000000500000009' does not contain any valid WAL to be processed\n"
|
|
"P00 DETAIL: archiveId: 11-2, wal start: 000000050000000800000003, wal stop: 000000050000000800000004");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("--set with archive and broken backup");
|
|
|
|
argList = strLstDup(argListBase);
|
|
hrnCfgArgRawZ(argList, cfgOptOutput, "text");
|
|
hrnCfgArgRawZ(argList, cfgOptVerbose, "y");
|
|
hrnCfgArgRawZ(argList, cfgOptSet, "20181119-153400F");
|
|
HRN_CFG_LOAD(cfgCmdVerify, argList);
|
|
|
|
// Write invalid manifest for backup
|
|
manifestContent = strNewZ(
|
|
"[backrest]\n"
|
|
"backrest-format=1234\n");
|
|
|
|
// Deliberately using HRN_STORAGE_PUT_Z instead of HRN_INFO_PUT to write an invalid manifest
|
|
HRN_STORAGE_PUT_Z(storageRepoWrite(),
|
|
STORAGE_REPO_BACKUP "/20181119-153400F/" BACKUP_MANIFEST_FILE,
|
|
strZ(manifestContent),
|
|
.comment = "invalid manifest - full");
|
|
HRN_STORAGE_PUT_Z(storageRepoWrite(),
|
|
STORAGE_REPO_BACKUP "/20181119-153400F/" BACKUP_MANIFEST_FILE INFO_COPY_EXT,
|
|
strZ(manifestContent),
|
|
.comment = "invalid manifest copy - full");
|
|
|
|
#define TEST_BACKUP_DB2_CURRENT_FULL3 \
|
|
"20181119-153400F={" \
|
|
"\"backrest-format\":5,\"backrest-version\":\"2.08dev\"," \
|
|
"\"backup-archive-start\":\"000000050000000800000003\",\"backup-archive-stop\":\"000000050000000800000004\"," \
|
|
"\"backup-info-repo-size\":2369186,\"backup-info-repo-size-delta\":2369186," \
|
|
"\"backup-info-size\":20162900,\"backup-info-size-delta\":20162900," \
|
|
"\"backup-timestamp-start\":1542640898,\"backup-timestamp-stop\":1542640911,\"backup-type\":\"full\"," \
|
|
"\"db-id\":2,\"option-archive-check\":true,\"option-archive-copy\":false,\"option-backup-standby\":false," \
|
|
"\"option-checksum-page\":true,\"option-compress\":true,\"option-hardlink\":false,\"option-online\":true}\n"
|
|
|
|
#undef TEST_BACKUP_INFO
|
|
#define TEST_BACKUP_INFO \
|
|
"[backup:current]\n" \
|
|
TEST_BACKUP_DB1_CURRENT_FULL3 \
|
|
TEST_BACKUP_DB1_CURRENT_FULL3_DIFF1 \
|
|
TEST_BACKUP_DB2_CURRENT_FULL2 \
|
|
TEST_BACKUP_DB2_CURRENT_FULL3 \
|
|
"\n" \
|
|
"[db]\n" \
|
|
TEST_BACKUP_DB2_11 \
|
|
"\n" \
|
|
"[db:history]\n" \
|
|
TEST_BACKUP_DB1_HISTORY \
|
|
"\n" \
|
|
TEST_BACKUP_DB2_HISTORY
|
|
|
|
HRN_INFO_PUT(storageRepoWrite(), INFO_BACKUP_PATH_FILE, TEST_BACKUP_INFO);
|
|
HRN_INFO_PUT(storageRepoWrite(), INFO_BACKUP_PATH_FILE INFO_COPY_EXT, TEST_BACKUP_INFO);
|
|
|
|
TEST_RESULT_STR_Z(
|
|
verifyProcess(cfgOptionBool(cfgOptVerbose)),
|
|
"stanza: db\n"
|
|
"status: error\n"
|
|
" archiveId: 11-2, total WAL checked: 0, total valid WAL: 0\n"
|
|
" backup: 20181119-153400F, status: invalid, total files checked: 0, total valid files: 0",
|
|
"--set with broken backup\n");
|
|
TEST_RESULT_LOG(
|
|
"P00 DETAIL: path '11-2/0000000500000007' does not contain any valid WAL to be processed\n"
|
|
"P00 DETAIL: path '11-2/0000000500000008' does not contain any valid WAL to be processed\n"
|
|
"P00 DETAIL: path '11-2/0000000500000009' does not contain any valid WAL to be processed\n"
|
|
"P00 DETAIL: repository format 1234 requires a newer version of pgBackRest\n"
|
|
" HINT: pgBackRest " PROJECT_VERSION " supports repository format 5 to 6.\n"
|
|
"P00 DETAIL: repository format 1234 requires a newer version of pgBackRest\n"
|
|
" HINT: pgBackRest " PROJECT_VERSION " supports repository format 5 to 6.");
|
|
}
|
|
|
|
if (testBegin("cmdBackup() and verifyProcess()"))
|
|
{
|
|
// The test expects the timezone to be UTC
|
|
hrnTzSet("UTC");
|
|
// Replace checksums since they can differ between architectures (e.g. 32/64 bit)
|
|
hrnLogReplaceAdd("\\) checksum [a-f0-9]{40}", "[a-f0-9]{40}$", "SHA1", false);
|
|
|
|
// Add a second repository that is encrypted. Verify runs against one repository at a time, so this covers a repository
|
|
// written entirely by the commands both ways: repo1 in the clear and repo2 with the info files opened by the repo
|
|
// passphrase, the manifests by the passphrase stored in backup.info, and the backup files and WAL by the passphrase
|
|
// stored in the manifest.
|
|
StringList *const argListMulti = strLstDup(argListBase);
|
|
hrnCfgArgKeyRawZ(argListMulti, cfgOptRepoPath, 2, TEST_PATH "/repo2");
|
|
hrnCfgArgKeyRawStrId(argListMulti, cfgOptRepoCipherType, 2, cipherTypeAes256Cbc);
|
|
hrnCfgEnvKeyRawZ(cfgOptRepoCipherPass, 2, TEST_CIPHER_PASS);
|
|
|
|
// Options common to the backups. Retention is set for both repositories so that neither warns about the other.
|
|
StringList *const argListBackup = strLstDup(argListMulti);
|
|
hrnCfgArgKeyRawZ(argListBackup, cfgOptRepoRetentionFull, 1, "1");
|
|
hrnCfgArgKeyRawZ(argListBackup, cfgOptRepoRetentionFull, 2, "1");
|
|
hrnCfgArgRawZ(argListBackup, cfgOptPgPath, TEST_PATH "/pg1");
|
|
hrnCfgArgKeyRawBool(argListBackup, cfgOptRepoBundle, 1, true);
|
|
hrnCfgArgKeyRawBool(argListBackup, cfgOptRepoBundle, 2, true);
|
|
hrnCfgArgKeyRawBool(argListBackup, cfgOptRepoBlock, 1, true);
|
|
hrnCfgArgKeyRawBool(argListBackup, cfgOptRepoBlock, 2, true);
|
|
|
|
StringList *argList = strLstDup(argListMulti);
|
|
hrnCfgArgRawZ(argList, cfgOptPgPath, TEST_PATH "/pg1");
|
|
hrnCfgArgRawBool(argList, cfgOptOnline, false);
|
|
HRN_CFG_LOAD(cfgCmdStanzaCreate, argList);
|
|
|
|
// Created pg_control and PG_VERSION
|
|
HRN_PG_CONTROL_PUT(storagePgWrite(), PG_VERSION_11);
|
|
HRN_STORAGE_PUT_Z(storagePgWrite(), PG_FILE_PGVERSION, PG_VERSION_11_Z, .timeModified = BACKUP_EPOCH - 10);
|
|
|
|
TEST_RESULT_VOID(cmdStanzaCreate(), "stanza create");
|
|
TEST_RESULT_LOG(
|
|
"P00 INFO: stanza-create for stanza 'db' on repo1\n"
|
|
"P00 INFO: stanza-create for stanza 'db' on repo2");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("full backup with block incr");
|
|
|
|
// Zeroed file large enough to use block incr
|
|
time_t timeBase = BACKUP_EPOCH;
|
|
Buffer *relation = bufNew(256 * 1024);
|
|
memset(bufPtr(relation), 0, bufSize(relation));
|
|
bufUsedSet(relation, bufSize(relation));
|
|
|
|
HRN_STORAGE_PUT(storagePgWrite(), PG_PATH_BASE "/1/2", relation, .timeModified = timeBase - 2);
|
|
|
|
// Zeroed file large enough to use block incr (that will be truncated to zero before restore)
|
|
relation = bufNew(16 * 1024);
|
|
memset(bufPtr(relation), 0, bufSize(relation));
|
|
bufUsedSet(relation, bufSize(relation));
|
|
|
|
HRN_STORAGE_PUT(storagePgWrite(), PG_PATH_BASE "/1/44", relation, .timeModified = timeBase - 2);
|
|
|
|
// Add postgresql.auto.conf to contain recovery settings
|
|
HRN_STORAGE_PUT_EMPTY(storagePgWrite(), PG_FILE_POSTGRESQLAUTOCONF, .timeModified = timeBase - 1);
|
|
|
|
// Backup
|
|
argList = strLstDup(argListBackup);
|
|
hrnCfgArgRawZ(argList, cfgOptRepo, "1");
|
|
hrnCfgArgRawStrId(argList, cfgOptType, backupTypeFull);
|
|
HRN_CFG_LOAD(cfgCmdBackup, argList);
|
|
|
|
hrnBackupPqScriptP(PG_VERSION_11, BACKUP_EPOCH);
|
|
TEST_RESULT_VOID(hrnCmdBackup(), "backup");
|
|
TEST_RESULT_LOG(
|
|
"P00 INFO: execute backup start: backup begins after the next regular checkpoint completes\n"
|
|
"P00 INFO: backup start archive = 0000000105D944C000000000, lsn = 5d944c0/0\n"
|
|
"P00 INFO: check archive for prior segment 0000000105D944BF000000FF\n"
|
|
"P00 DETAIL: store zero-length file " TEST_PATH "/pg1/postgresql.auto.conf\n"
|
|
"P01 DETAIL: backup file " TEST_PATH "/pg1/base/1/44 (bundle 1/0, 16KB, 5.71%) checksum [SHA1]\n"
|
|
"P01 DETAIL: backup file " TEST_PATH "/pg1/base/1/2 (bundle 1/57, 256KB, 97.14%) checksum [SHA1]\n"
|
|
"P01 DETAIL: backup file " TEST_PATH "/pg1/PG_VERSION (bundle 1/533, 2B, 97.14%) checksum [SHA1]\n"
|
|
"P01 DETAIL: backup file " TEST_PATH "/pg1/global/pg_control (bundle 1/543, 8KB, 100.00%) checksum [SHA1]\n"
|
|
"P00 INFO: execute backup stop and wait for all WAL segments to archive\n"
|
|
"P00 INFO: backup stop archive = 0000000105D944C000000000, lsn = 5d944c0/800000\n"
|
|
"P00 DETAIL: wrote 'backup_label' file returned from backup stop function\n"
|
|
"P00 INFO: check archive for segment(s) 0000000105D944C000000000:0000000105D944C000000000\n"
|
|
"P00 INFO: new backup label = 20191002-070640F\n"
|
|
"P00 INFO: full backup size = 280KB, file total = 6");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("diff backup with block incr");
|
|
|
|
// Update file /1/2 to use block map without adding a reference in manifest
|
|
relation = bufNew(256 * 1024);
|
|
memset(bufPtr(relation), 0, bufSize(relation));
|
|
memset(bufPtr(relation), 1, 1024);
|
|
bufUsedSet(relation, bufSize(relation));
|
|
HRN_STORAGE_PUT(storagePgWrite(), PG_PATH_BASE "/1/2", relation, .timeModified = timeBase);
|
|
|
|
// Backup
|
|
argList = strLstDup(argListBackup);
|
|
hrnCfgArgRawZ(argList, cfgOptRepo, "1");
|
|
hrnCfgArgRawStrId(argList, cfgOptType, backupTypeDiff);
|
|
HRN_CFG_LOAD(cfgCmdBackup, argList);
|
|
|
|
hrnBackupPqScriptP(PG_VERSION_11, BACKUP_EPOCH + 100000);
|
|
TEST_RESULT_VOID(hrnCmdBackup(), "backup");
|
|
TEST_RESULT_LOG(
|
|
"P00 INFO: last backup label = 20191002-070640F, version = " PROJECT_VERSION "\n"
|
|
"P00 INFO: execute backup start: backup begins after the next regular checkpoint completes\n"
|
|
"P00 INFO: backup start archive = 0000000105D95D3000000000, lsn = 5d95d30/0\n"
|
|
"P00 INFO: check archive for prior segment 0000000105D95D2F000000FF\n"
|
|
"P00 DETAIL: store zero-length file " TEST_PATH "/pg1/postgresql.auto.conf\n"
|
|
"P01 DETAIL: backup file " TEST_PATH "/pg1/base/1/2 (bundle 1/0, 256KB, 96.97%) checksum [SHA1]\n"
|
|
"P01 DETAIL: backup file " TEST_PATH "/pg1/global/pg_control (bundle 1/237, 8KB, 100.00%) checksum [SHA1]\n"
|
|
"P00 DETAIL: reference pg_data/PG_VERSION to 20191002-070640F\n"
|
|
"P00 DETAIL: reference pg_data/base/1/44 to 20191002-070640F\n"
|
|
"P00 INFO: execute backup stop and wait for all WAL segments to archive\n"
|
|
"P00 INFO: backup stop archive = 0000000105D95D3000000000, lsn = 5d95d30/800000\n"
|
|
"P00 DETAIL: wrote 'backup_label' file returned from backup stop function\n"
|
|
"P00 INFO: check archive for segment(s) 0000000105D95D3000000000:0000000105D95D3000000000\n"
|
|
"P00 INFO: new backup label = 20191002-070640F_20191003-105320D\n"
|
|
"P00 INFO: diff backup size = 264KB, file total = 6");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("full backup with block incr on encrypted repo2");
|
|
|
|
argList = strLstDup(argListBackup);
|
|
hrnCfgArgRawZ(argList, cfgOptRepo, "2");
|
|
hrnCfgArgRawStrId(argList, cfgOptType, backupTypeFull);
|
|
HRN_CFG_LOAD(cfgCmdBackup, argList);
|
|
|
|
hrnBackupPqScriptP(
|
|
PG_VERSION_11, BACKUP_EPOCH + 200000, .cipherSpecMain = TEST_CIPHER_SPEC);
|
|
TEST_RESULT_VOID(hrnCmdBackup(), "backup");
|
|
TEST_RESULT_LOG(
|
|
"P00 INFO: execute backup start: backup begins after the next regular checkpoint completes\n"
|
|
"P00 INFO: backup start archive = 0000000105D9759000000000, lsn = 5d97590/0\n"
|
|
"P00 INFO: check archive for prior segment 0000000105D9758F000000FF\n"
|
|
"P00 DETAIL: store zero-length file " TEST_PATH "/pg1/postgresql.auto.conf\n"
|
|
"P01 DETAIL: backup file " TEST_PATH "/pg1/base/1/44 (bundle 1/0, 16KB, 5.71%) checksum [SHA1]\n"
|
|
"P01 DETAIL: backup file " TEST_PATH "/pg1/base/1/2 (bundle 1/96, 256KB, 97.14%) checksum [SHA1]\n"
|
|
"P01 DETAIL: backup file " TEST_PATH "/pg1/PG_VERSION (bundle 1/608, 2B, 97.14%) checksum [SHA1]\n"
|
|
"P01 DETAIL: backup file " TEST_PATH "/pg1/global/pg_control (bundle 1/632, 8KB, 100.00%) checksum [SHA1]\n"
|
|
"P00 INFO: execute backup stop and wait for all WAL segments to archive\n"
|
|
"P00 INFO: backup stop archive = 0000000105D9759000000000, lsn = 5d97590/800000\n"
|
|
"P00 DETAIL: wrote 'backup_label' file returned from backup stop function\n"
|
|
"P00 INFO: check archive for segment(s) 0000000105D9759000000000:0000000105D9759000000000\n"
|
|
"P00 INFO: new backup label = 20191004-144000F\n"
|
|
"P00 INFO: full backup size = 280KB, file total = 6");
|
|
|
|
hrnLogReplaceClear();
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("verify with block incr");
|
|
|
|
argList = strLstDup(argListMulti);
|
|
hrnCfgArgRawZ(argList, cfgOptRepo, "1");
|
|
hrnCfgArgRawZ(argList, cfgOptOutput, "text");
|
|
hrnCfgArgRawZ(argList, cfgOptVerbose, "y");
|
|
hrnCfgArgRawZ(argList, cfgOptSet, "20191002-070640F_20191003-105320D");
|
|
HRN_CFG_LOAD(cfgCmdVerify, argList);
|
|
|
|
// Should check both backups because of block increment
|
|
TEST_RESULT_STR_Z(
|
|
verifyProcess(cfgOptionBool(cfgOptVerbose)),
|
|
"stanza: db\n"
|
|
"status: ok\n"
|
|
" archiveId: 11-1, total WAL checked: 1, total valid WAL: 1\n"
|
|
" missing: 0, checksum invalid: 0, size invalid: 0, other: 0\n"
|
|
" backup: 20191002-070640F, status: valid, total files checked: 6, total valid files: 6\n"
|
|
" missing: 0, checksum invalid: 0, size invalid: 0, other: 0\n"
|
|
" backup: 20191002-070640F_20191003-105320D, status: valid, total files checked: 6, total valid files: 6\n"
|
|
" missing: 0, checksum invalid: 0, size invalid: 0, other: 0", "--set with block incremental backup\n");
|
|
TEST_RESULT_LOG(
|
|
"P00 DETAIL: path '11-1/0000000105D944BF' does not contain any valid WAL to be processed\n"
|
|
"P00 DETAIL: path '11-1/0000000105D944C0' does not contain any valid WAL to be processed\n"
|
|
"P00 DETAIL: path '11-1/0000000105D95D2F' does not contain any valid WAL to be processed\n"
|
|
"P00 DETAIL: archiveId: 11-1, wal start: 0000000105D95D3000000000, wal stop: 0000000105D95D3000000000");
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------
|
|
TEST_TITLE("verify encrypted repo2");
|
|
|
|
argList = strLstDup(argListMulti);
|
|
hrnCfgArgRawZ(argList, cfgOptRepo, "2");
|
|
hrnCfgArgRawZ(argList, cfgOptOutput, "text");
|
|
hrnCfgArgRawZ(argList, cfgOptVerbose, "y");
|
|
HRN_CFG_LOAD(cfgCmdVerify, argList);
|
|
|
|
TEST_RESULT_STR_Z(
|
|
verifyProcess(cfgOptionBool(cfgOptVerbose)),
|
|
"stanza: db\n"
|
|
"status: ok\n"
|
|
" archiveId: 11-1, total WAL checked: 2, total valid WAL: 2\n"
|
|
" missing: 0, checksum invalid: 0, size invalid: 0, other: 0\n"
|
|
" backup: 20191004-144000F, status: valid, total files checked: 6, total valid files: 6\n"
|
|
" missing: 0, checksum invalid: 0, size invalid: 0, other: 0", "encrypted repository");
|
|
TEST_RESULT_LOG(
|
|
"P00 DETAIL: archiveId: 11-1, wal start: 0000000105D9758F000000FF, wal stop: 0000000105D9759000000000");
|
|
|
|
hrnCfgEnvKeyRemoveRaw(cfgOptRepoCipherPass, 2);
|
|
}
|
|
|
|
FUNCTION_HARNESS_RETURN_VOID();
|
|
}
|