1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-10-30 23:37:45 +02:00

Add progress-only detail level for info command output.

The info command fetches a lot of information from the repository about backups and archives, so this operation can be slow. Because progress data is stored in local lock files, accessing the repository is unnecessary when only progress information is required.

This patch introduces the `--detail-level=[progress|full]` option, with `full` as the default. The `progress` level limits the info command output to progress details without querying the repository. The only remaining operations are scanning the folder structure to list available stanzas and reading lock files.

Note: When `progress` is selected, the info command performs no checks beyond verifying stanza availability.
This commit is contained in:
Denis Garsh
2025-06-27 00:40:05 +03:00
committed by GitHub
parent 277b4db46f
commit 2706168f60
8 changed files with 420 additions and 52 deletions

View File

@@ -15,6 +15,20 @@
</release-item>
</release-bug-list>
<release-feature-list>
<release-item>
<github-pull-request id="2550"/>
<release-item-contributor-list>
<release-item-contributor id="denis.garsh"/>
<release-item-reviewer id="david.steele"/>
<release-item-reviewer id="stefan.fercot"/>
</release-item-contributor-list>
<p>Add progress-only detail level for <cmd>info</cmd> command output.</p>
</release-item>
</release-feature-list>
<release-improvement-list>
<release-item>
<github-pull-request id="2642"/>

View File

@@ -324,6 +324,11 @@
<contributor-id type="github">youattd</contributor-id>
</contributor>
<contributor id="denis.garsh">
<contributor-name-display>Denis Garsh</contributor-name-display>
<contributor-id type="github">hilltracer</contributor-id>
</contributor>
<contributor id="devrim.gunduz">
<contributor-name-display>Devrim G&amp;uuml;nd&amp;uuml;z</contributor-name-display>
<contributor-id type="github">devrimgunduz</contributor-id>

View File

@@ -284,6 +284,17 @@ option:
default-type: literal
default: CFGOPTDEF_CONFIG_PATH
detail-level:
type: string-id
default: full
command:
info: {}
allow-list:
- progress
- full
command-role:
main: {}
dry-run:
type: boolean
default: false

View File

@@ -2550,6 +2550,8 @@
<p>For machine-readable output use <br-option>--output=json</br-option>. The JSON output contains far more information than the text output and is kept stable unless a bug is found.</p>
<p>To speed up execution, limit the output to only progress information by specifying <br-option>--detail-level=progress</br-option>. Note that this skips all checks except for availability of the stanza.</p>
<p>Each stanza has a separate section and it is possible to limit output to a single stanza with the <br-option>--stanza</br-option> option. The stanza '<id>status</id>' gives a brief indication of the stanza's health. If this is '<id>ok</id>' then <backrest/> is functioning normally. If there are multiple repositories, then a status of '<id>mixed</id>' indicates that the stanza is not in a healthy state on one or more of the repositories; in this case the state of the stanza will be detailed per repository. For cases in which an error on a repository occurred that is not one of the known error codes, then an error code of '<id>other</id>' will be used and the full error details will be provided. The '<id>wal archive min/max</id>' shows the minimum and maximum WAL currently stored in the archive and, in the case of multiple repositories, will be reported across all repositories unless the <br-option>{[dash]}-repo</br-option> option is set. Note that there may be gaps due to archive retention policies or other reasons.</p>
<p>The '<id>backup/expire running</id>' message will appear beside the '<id>status</id>' information if one of those commands is currently running on the host.</p>
@@ -2570,6 +2572,21 @@
</text>
<option-list>
<option id="detail-level" name="Detail level">
<summary>Output detail level.</summary>
<text>
<p>The following levels are supported:</p>
<list>
<list-item><id>progress</id> - Output only the current backup/expire progress. This level cannot be used with the <br-option>--set</br-option> option.</list-item>
<list-item><id>full</id> - Output full info.</list-item>
</list>
</text>
<example>progress</example>
</option>
<option id="output" name="Output">
<summary>Output format.</summary>

View File

@@ -731,6 +731,9 @@ stanzaInfoList(
ASSERT(stanzaRepoList != NULL);
// Is full output requested?
const bool outputFull = cfgOptionStrId(cfgOptDetailLevel) == CFGOPTVAL_DETAIL_LEVEL_FULL;
VariantList *const result = varLstNew();
// Sort the list of stanzas
@@ -759,6 +762,20 @@ stanzaInfoList(
{
InfoRepoData *const repoData = &stanzaData->repoList[repoIdx];
// When full output is not requested (progress mode), skip collecting detailed information and only update status code
if (!outputFull)
{
if (repoIdx == repoIdxMin)
stanzaStatusCode = repoData->stanzaStatus;
else
{
stanzaStatusCode =
stanzaStatusCode != repoData->stanzaStatus ? INFO_STANZA_STATUS_CODE_MIXED : repoData->stanzaStatus;
}
continue;
}
Variant *const repoInfo = varNewKv(kvNew());
kvPut(varKv(repoInfo), REPO_KEY_KEY_VAR, VARUINT(repoData->key));
kvPut(varKv(repoInfo), KEY_CIPHER_VAR, VARSTR(strIdToStr(repoData->cipher)));
@@ -845,19 +862,23 @@ stanzaInfoList(
kvPut(varKv(stanzaInfo), STANZA_KEY_REPO_VAR, varNewVarLst(repoSection));
}
// Get a sorted list of the data for all existing backups for this stanza over all repos
backupList(backupSection, stanzaData, backupLabel, repoIdxMin, repoIdxMax);
kvPut(varKv(stanzaInfo), STANZA_KEY_BACKUP_VAR, varNewVarLst(backupSection));
// Collect backup and cipher data if full output is requested
if (outputFull)
{
// Get a sorted list of the data for all existing backups for this stanza over all repos
backupList(backupSection, stanzaData, backupLabel, repoIdxMin, repoIdxMax);
kvPut(varKv(stanzaInfo), STANZA_KEY_BACKUP_VAR, varNewVarLst(backupSection));
// Set the overall stanza status
// Set the overall cipher type
if (stanzaCipherType != INFO_STANZA_STATUS_CODE_MIXED)
kvPut(varKv(stanzaInfo), KEY_CIPHER_VAR, VARSTR(strIdToStr(stanzaCipherType)));
else
kvPut(varKv(stanzaInfo), KEY_CIPHER_VAR, VARSTRDEF(INFO_STANZA_MIXED));
}
// Set the overall stanza status and gather progress information
stanzaStatus(stanzaStatusCode, stanzaData, stanzaInfo);
// Set the overall cipher type
if (stanzaCipherType != INFO_STANZA_STATUS_CODE_MIXED)
kvPut(varKv(stanzaInfo), KEY_CIPHER_VAR, VARSTR(strIdToStr(stanzaCipherType)));
else
kvPut(varKv(stanzaInfo), KEY_CIPHER_VAR, VARSTRDEF(INFO_STANZA_MIXED));
varLstAdd(result, stanzaInfo);
}
@@ -1309,47 +1330,58 @@ infoUpdateStanza(
TRY_BEGIN()
{
// Catch certain errors
TRY_BEGIN()
{
// Attempt to load the backup info file
stanzaRepo->repoList[repoIdx].backupInfo = infoBackupLoadFile(
storage, strNewFmt(STORAGE_PATH_BACKUP "/%s/%s", strZ(stanzaRepo->name), INFO_BACKUP_FILE),
stanzaRepo->repoList[repoIdx].cipher, stanzaRepo->repoList[repoIdx].cipherPass);
}
CATCH(FileMissingError)
{
// If there is no backup.info then set the status to indicate missing
stanzaStatus = INFO_STANZA_STATUS_CODE_MISSING_STANZA_DATA;
}
CATCH(CryptoError)
{
// If a reason for the error is due to a an encryption error, add a hint
THROW_FMT(
CryptoError,
"%s\n"
"HINT: use option --stanza if encryption settings are different for the stanza than the global settings.",
errorMessage());
}
TRY_END();
// If full output is requested read info and manifest files
const bool outputFull = cfgOptionStrId(cfgOptDetailLevel) == CFGOPTVAL_DETAIL_LEVEL_FULL;
// If backup.info was found, then get the archive.info file, which must exist if the backup.info exists, else the failed
// load will throw an error which will be trapped and recorded
if (stanzaRepo->repoList[repoIdx].backupInfo != NULL)
if (outputFull)
{
stanzaRepo->repoList[repoIdx].archiveInfo = infoArchiveLoadFile(
storage, strNewFmt(STORAGE_PATH_ARCHIVE "/%s/%s", strZ(stanzaRepo->name), INFO_ARCHIVE_FILE),
stanzaRepo->repoList[repoIdx].cipher, stanzaRepo->repoList[repoIdx].cipherPass);
// If a specific backup exists on this repo then attempt to load the manifest
if (backupLabel != NULL)
// Catch certain errors
TRY_BEGIN()
{
stanzaRepo->repoList[repoIdx].manifest = manifestLoadFile(
storage, strNewFmt(STORAGE_REPO_BACKUP "/%s/" BACKUP_MANIFEST_FILE, strZ(backupLabel)),
stanzaRepo->repoList[repoIdx].cipher,
infoPgCipherPass(infoBackupPg(stanzaRepo->repoList[repoIdx].backupInfo)));
// Attempt to load the backup info file
stanzaRepo->repoList[repoIdx].backupInfo = infoBackupLoadFile(
storage, strNewFmt(STORAGE_PATH_BACKUP "/%s/%s", strZ(stanzaRepo->name), INFO_BACKUP_FILE),
stanzaRepo->repoList[repoIdx].cipher, stanzaRepo->repoList[repoIdx].cipherPass);
}
CATCH(FileMissingError)
{
// If there is no backup.info then set the status to indicate missing
stanzaStatus = INFO_STANZA_STATUS_CODE_MISSING_STANZA_DATA;
}
CATCH(CryptoError)
{
// If a reason for the error is due to a an encryption error, add a hint
THROW_FMT(
CryptoError,
"%s\n"
"HINT: use option --stanza if encryption settings are different for the stanza than the global settings.",
errorMessage());
}
TRY_END();
// If backup.info was found, then get the archive.info file, which must exist if backup.info exists, else the failed
// load will throw an error which will be trapped and recorded
if (stanzaRepo->repoList[repoIdx].backupInfo != NULL)
{
stanzaRepo->repoList[repoIdx].archiveInfo = infoArchiveLoadFile(
storage, strNewFmt(STORAGE_PATH_ARCHIVE "/%s/%s", strZ(stanzaRepo->name), INFO_ARCHIVE_FILE),
stanzaRepo->repoList[repoIdx].cipher, stanzaRepo->repoList[repoIdx].cipherPass);
// If a specific backup exists on this repo then attempt to load the manifest
if (backupLabel != NULL)
{
stanzaRepo->repoList[repoIdx].manifest = manifestLoadFile(
storage, strNewFmt(STORAGE_REPO_BACKUP "/%s/" BACKUP_MANIFEST_FILE, strZ(backupLabel)),
stanzaRepo->repoList[repoIdx].cipher,
infoPgCipherPass(infoBackupPg(stanzaRepo->repoList[repoIdx].backupInfo)));
}
}
}
// Read the lock file if backup.info is present. Exception: when only progress is requested, backup.info is skipped for
// performance, so the lock file is read unconditionally -- though it may be outdated in this case.
if (stanzaRepo->repoList[repoIdx].backupInfo != NULL || !outputFull)
{
// If there is a valid backup lock for this stanza then backup/expire must be running
const LockReadResult lockResult = cmdLockRead(lockTypeBackup, stanzaRepo->name, repoIdx);
@@ -1414,6 +1446,13 @@ infoRender(void)
const String *const backupLabel = cfgOptionStrNull(cfgOptSet);
bool backupFound = false;
// If only progress info is requested then details about a specific backup may not be requested
if (backupLabel != NULL && cfgOptionStrId(cfgOptDetailLevel) == CFGOPTVAL_DETAIL_LEVEL_PROGRESS)
{
THROW_FMT(OptionInvalidError, "option '%s' cannot be used with option '%s' = '%s'",
cfgOptionName(cfgOptSet), cfgOptionName(cfgOptDetailLevel), CFGOPTVAL_DETAIL_LEVEL_PROGRESS_Z);
}
// Initialize the repo index
unsigned int repoIdxMin = 0;
const unsigned int repoTotal = cfgOptionGroupIdxTotal(cfgOptGrpRepo);
@@ -1597,6 +1636,9 @@ infoRender(void)
// Process any stanza directories
if (!varLstEmpty(infoList))
{
// Is full output requested?
const bool outputFull = cfgOptionStrId(cfgOptDetailLevel) == CFGOPTVAL_DETAIL_LEVEL_FULL;
for (unsigned int stanzaIdx = 0; stanzaIdx < varLstSize(infoList); stanzaIdx++)
{
const KeyValue *const stanzaInfo = varKv(varLstGet(infoList, stanzaIdx));
@@ -1626,8 +1668,9 @@ infoRender(void)
if (statusCode != INFO_STANZA_STATUS_CODE_OK)
{
// Update the overall stanza status and change displayed status if backup lock is found
if (statusCode == INFO_STANZA_STATUS_CODE_MIXED || statusCode == INFO_STANZA_STATUS_CODE_PG_MISMATCH ||
statusCode == INFO_STANZA_STATUS_CODE_OTHER)
if (outputFull &&
(statusCode == INFO_STANZA_STATUS_CODE_MIXED || statusCode == INFO_STANZA_STATUS_CODE_PG_MISMATCH ||
statusCode == INFO_STANZA_STATUS_CODE_OTHER))
{
// Stanza status
strCatFmt(
@@ -1700,7 +1743,7 @@ infoRender(void)
}
// Add cipher type if the stanza is found on at least one repo
if (statusCode != INFO_STANZA_STATUS_CODE_MISSING_STANZA_PATH)
if (outputFull && statusCode != INFO_STANZA_STATUS_CODE_MISSING_STANZA_PATH)
{
strCatFmt(resultStr, " cipher: %s\n", strZ(varStr(kvGet(stanzaInfo, KEY_CIPHER_VAR))));
@@ -1721,7 +1764,7 @@ infoRender(void)
}
// Get the current database for this stanza
if (!varLstEmpty(kvGetList(stanzaInfo, STANZA_KEY_DB_VAR)))
if (outputFull && !varLstEmpty(kvGetList(stanzaInfo, STANZA_KEY_DB_VAR)))
{
const InfoStanzaRepo *const stanzaRepo = lstFind(stanzaRepoList, &stanzaName);

View File

@@ -72,6 +72,7 @@ Option constants
#define CFGOPT_DB_INCLUDE "db-include"
#define CFGOPT_DB_TIMEOUT "db-timeout"
#define CFGOPT_DELTA "delta"
#define CFGOPT_DETAIL_LEVEL "detail-level"
#define CFGOPT_DRY_RUN "dry-run"
#define CFGOPT_EXCLUDE "exclude"
#define CFGOPT_EXEC_ID "exec-id"
@@ -139,7 +140,7 @@ Option constants
#define CFGOPT_VERBOSE "verbose"
#define CFGOPT_VERSION "version"
#define CFG_OPTION_TOTAL 186
#define CFG_OPTION_TOTAL 187
/***********************************************************************************************************************************
Option value constants
@@ -167,6 +168,11 @@ Option value constants
#define CFGOPTVAL_COMPRESS_TYPE_ZST STRID5("zst", 0x527a0)
#define CFGOPTVAL_COMPRESS_TYPE_ZST_Z "zst"
#define CFGOPTVAL_DETAIL_LEVEL_FULL STRID5("full", 0x632a60)
#define CFGOPTVAL_DETAIL_LEVEL_FULL_Z "full"
#define CFGOPTVAL_DETAIL_LEVEL_PROGRESS STRID5("progress", 0x9ccb23be500)
#define CFGOPTVAL_DETAIL_LEVEL_PROGRESS_Z "progress"
#define CFGOPTVAL_LOG_LEVEL_CONSOLE_DEBUG STRID5("debug", 0x7a88a40)
#define CFGOPTVAL_LOG_LEVEL_CONSOLE_DEBUG_Z "debug"
#define CFGOPTVAL_LOG_LEVEL_CONSOLE_DETAIL STRID5("detail", 0x1890d0a40)
@@ -423,6 +429,7 @@ typedef enum
cfgOptDbInclude,
cfgOptDbTimeout,
cfgOptDelta,
cfgOptDetailLevel,
cfgOptDryRun,
cfgOptExclude,
cfgOptExecId,

View File

@@ -111,6 +111,7 @@ static const StringPubConst parseRuleValueStr[] =
PARSE_RULE_STRPUB("postgres"), // val/str
PARSE_RULE_STRPUB("prefer"), // val/str
PARSE_RULE_STRPUB("preserve"), // val/str
PARSE_RULE_STRPUB("progress"), // val/str
PARSE_RULE_STRPUB("promote"), // val/str
PARSE_RULE_STRPUB("repo"), // val/str
PARSE_RULE_STRPUB("s3"), // val/str
@@ -242,6 +243,7 @@ typedef enum
parseRuleValStrQT_postgres_QT, // val/str/enum
parseRuleValStrQT_prefer_QT, // val/str/enum
parseRuleValStrQT_preserve_QT, // val/str/enum
parseRuleValStrQT_progress_QT, // val/str/enum
parseRuleValStrQT_promote_QT, // val/str/enum
parseRuleValStrQT_repo_QT, // val/str/enum
parseRuleValStrQT_s3_QT, // val/str/enum
@@ -315,6 +317,7 @@ static const StringId parseRuleValueStrId[] =
STRID5("posix", 0x184cdf00), // val/strid
STRID5("prefer", 0x245316500), // val/strid
STRID5("preserve", 0x2da45996500), // val/strid
STRID5("progress", 0x9ccb23be500), // val/strid
STRID5("promote", 0x168f6be500), // val/strid
STRID5("repo", 0x7c0b20), // val/strid
STRID6("s3", 0x7d31), // val/strid
@@ -379,6 +382,7 @@ static const uint8_t parseRuleValueStrIdStrMap[] =
parseRuleValStrQT_posix_QT, // val/strid/strmap
parseRuleValStrQT_prefer_QT, // val/strid/strmap
parseRuleValStrQT_preserve_QT, // val/strid/strmap
parseRuleValStrQT_progress_QT, // val/strid/strmap
parseRuleValStrQT_promote_QT, // val/strid/strmap
parseRuleValStrQT_repo_QT, // val/strid/strmap
parseRuleValStrQT_s3_QT, // val/strid/strmap
@@ -443,6 +447,7 @@ typedef enum
parseRuleValStrIdPosix, // val/strid/enum
parseRuleValStrIdPrefer, // val/strid/enum
parseRuleValStrIdPreserve, // val/strid/enum
parseRuleValStrIdProgress, // val/strid/enum
parseRuleValStrIdPromote, // val/strid/enum
parseRuleValStrIdRepo, // val/strid/enum
parseRuleValStrIdS3, // val/strid/enum
@@ -2377,6 +2382,36 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] =
), // opt/delta
), // opt/delta
// -----------------------------------------------------------------------------------------------------------------------------
PARSE_RULE_OPTION // opt/detail-level
( // opt/detail-level
PARSE_RULE_OPTION_NAME("detail-level"), // opt/detail-level
PARSE_RULE_OPTION_TYPE(StringId), // opt/detail-level
PARSE_RULE_OPTION_REQUIRED(true), // opt/detail-level
PARSE_RULE_OPTION_SECTION(CommandLine), // opt/detail-level
// opt/detail-level
PARSE_RULE_OPTION_COMMAND_ROLE_MAIN_VALID_LIST // opt/detail-level
( // opt/detail-level
PARSE_RULE_OPTION_COMMAND(Info) // opt/detail-level
), // opt/detail-level
// opt/detail-level
PARSE_RULE_OPTIONAL // opt/detail-level
( // opt/detail-level
PARSE_RULE_OPTIONAL_GROUP // opt/detail-level
( // opt/detail-level
PARSE_RULE_OPTIONAL_ALLOW_LIST // opt/detail-level
( // opt/detail-level
PARSE_RULE_VAL_STRID(Progress), // opt/detail-level
PARSE_RULE_VAL_STRID(Full), // opt/detail-level
), // opt/detail-level
// opt/detail-level
PARSE_RULE_OPTIONAL_DEFAULT // opt/detail-level
( // opt/detail-level
PARSE_RULE_VAL_STRID(Full), // opt/detail-level
), // opt/detail-level
), // opt/detail-level
), // opt/detail-level
), // opt/detail-level
// -----------------------------------------------------------------------------------------------------------------------------
PARSE_RULE_OPTION // opt/dry-run
( // opt/dry-run
PARSE_RULE_OPTION_NAME("dry-run"), // opt/dry-run
@@ -11501,6 +11536,7 @@ static const uint8_t optionResolveOrder[] =
cfgOptDbInclude, // opt-resolve-order
cfgOptDbTimeout, // opt-resolve-order
cfgOptDelta, // opt-resolve-order
cfgOptDetailLevel, // opt-resolve-order
cfgOptDryRun, // opt-resolve-order
cfgOptExclude, // opt-resolve-order
cfgOptExecId, // opt-resolve-order

View File

@@ -36,14 +36,26 @@ testRun(void)
hrnCfgArgRawZ(argList, cfgOptOutput, "json");
HRN_CFG_LOAD(cfgCmdInfo, argList);
StringList *argListProgressOnly = strLstDup(argList);
hrnCfgArgRawZ(argListProgressOnly, cfgOptDetailLevel, "progress");
StringList *argListTextProgressOnly = strLstDup(argListText);
hrnCfgArgRawZ(argListTextProgressOnly, cfgOptDetailLevel, "progress");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("no stanzas have been created");
TEST_RESULT_STR_Z(infoRender(), "[]", "json - repo but no stanzas");
HRN_CFG_LOAD(cfgCmdInfo, argListProgressOnly);
TEST_RESULT_STR_Z(infoRender(), "[]", "json (progress only) - repo but no stanzas");
HRN_CFG_LOAD(cfgCmdInfo, argListText);
TEST_RESULT_STR_Z(infoRender(), "No stanzas exist in the repository.\n", "text - no stanzas");
HRN_CFG_LOAD(cfgCmdInfo, argListTextProgressOnly);
TEST_RESULT_STR_Z(infoRender(), "No stanzas exist in the repository.\n", "text (progress only) - no stanzas");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("repo is still empty but stanza option is specified");
@@ -80,6 +92,27 @@ testRun(void)
// {uncrustify_on}
"json - empty repo, stanza option specified");
StringList *argListProgressOnlyStanzaOpt = strLstDup(argListProgressOnly);
hrnCfgArgRawZ(argListProgressOnlyStanzaOpt, cfgOptStanza, "stanza1");
HRN_CFG_LOAD(cfgCmdInfo, argListProgressOnlyStanzaOpt);
TEST_RESULT_STR_Z(
infoRender(),
// {uncrustify_off - indentation}
"["
"{"
"\"name\":\"stanza1\","
"\"status\":{"
"\"code\":1,"
"\"lock\":{"
"\"backup\":{\"held\":false}"
"},"
"\"message\":\"missing stanza path\""
"}"
"}"
"]",
// {uncrustify_on}
"json (progress only) - empty repo, stanza option specified");
StringList *argListTextStanzaOpt = strLstDup(argListText);
hrnCfgArgRawZ(argListTextStanzaOpt, cfgOptStanza, "stanza1");
HRN_CFG_LOAD(cfgCmdInfo, argListTextStanzaOpt);
@@ -89,12 +122,22 @@ testRun(void)
" status: error (missing stanza path)\n",
"text - empty repo, stanza option specified");
StringList *argListTextProgressOnlyStanzaOpt = strLstDup(argListTextProgressOnly);
hrnCfgArgRawZ(argListTextProgressOnlyStanzaOpt, cfgOptStanza, "stanza1");
HRN_CFG_LOAD(cfgCmdInfo, argListTextProgressOnlyStanzaOpt);
TEST_RESULT_STR_Z(
infoRender(),
"stanza: stanza1\n"
" status: error (missing stanza path)\n",
"text (progress only) - empty repo, stanza option specified");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("stanza path exists but is empty");
HRN_STORAGE_PATH_CREATE(storageRepoWrite(), STORAGE_REPO_ARCHIVE, .comment = "create repo stanza archive path");
HRN_STORAGE_PATH_CREATE(storageRepoWrite(), STORAGE_REPO_BACKUP, .comment = "create repo stanza backup path");
HRN_CFG_LOAD(cfgCmdInfo, argListTextStanzaOpt);
TEST_RESULT_STR_Z(
infoRender(),
"stanza: stanza1\n"
@@ -102,7 +145,15 @@ testRun(void)
" cipher: none\n",
"text - missing stanza data");
HRN_CFG_LOAD(cfgCmdInfo, argList);
// If only progress is requested, the info command skips additional checks.
HRN_CFG_LOAD(cfgCmdInfo, argListTextProgressOnlyStanzaOpt);
TEST_RESULT_STR_Z(
infoRender(),
"stanza: stanza1\n"
" status: ok\n",
"text (progress only) - missing stanza data");
HRN_CFG_LOAD(cfgCmdInfo, argListStanzaOpt);
TEST_RESULT_STR_Z(
infoRender(),
// {uncrustify_off - indentation}
@@ -133,6 +184,26 @@ testRun(void)
// {uncrustify_on}
"json - missing stanza data");
// If only progress is requested, the info command skips additional checks.
HRN_CFG_LOAD(cfgCmdInfo, argListProgressOnlyStanzaOpt);
TEST_RESULT_STR_Z(
infoRender(),
// {uncrustify_off - indentation}
"["
"{"
"\"name\":\"stanza1\","
"\"status\":{"
"\"code\":0,"
"\"lock\":{"
"\"backup\":{\"held\":false}"
"},"
"\"message\":\"ok\""
"}"
"}"
"]",
// {uncrustify_on}
"json (progress only) - missing stanza data");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("backup.info file exists, but archive.info does not");
@@ -152,6 +223,7 @@ testRun(void)
"2={\"db-catalog-version\":201608131,\"db-control-version\":960,\"db-system-id\":6569239123849665679"
",\"db-version\":\"9.6\"}\n");
HRN_CFG_LOAD(cfgCmdInfo, argListStanzaOpt);
TEST_RESULT_STR_Z(
infoRender(),
// {uncrustify_off - indentation}
@@ -343,6 +415,16 @@ testRun(void)
" status: error (missing stanza path)\n",
"text - multi-repo, requested stanza missing on selected repo");
StringList *argList2ProgressOnly = strLstDup(argList2);
hrnCfgArgRawZ(argList2ProgressOnly, cfgOptDetailLevel, "progress");
HRN_CFG_LOAD(cfgCmdInfo, argList2ProgressOnly);
TEST_RESULT_STR_Z(
infoRender(),
"stanza: stanza1\n"
" status: error (missing stanza path)\n",
"text (progress only) - multi-repo, requested stanza missing on selected repo");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("multi-repo - WAL segment on repo1");
@@ -367,6 +449,16 @@ testRun(void)
" wal archive min/max (9.6): 000000030000000000000001/000000030000000000000001\n",
"text - multi-repo, single stanza, one wal segment");
argList2ProgressOnly = strLstDup(argList2);
hrnCfgArgRawZ(argList2ProgressOnly, cfgOptDetailLevel, "progress");
HRN_CFG_LOAD(cfgCmdInfo, argList2ProgressOnly);
// If only progress is requested, the info command skips additional checks.
TEST_RESULT_STR_Z(
infoRender(),
"stanza: stanza1\n"
" status: ok\n",
"text (progress only) - multi-repo, single stanza, one wal segment");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("coverage for stanzaStatus branches && percent complete null");
@@ -590,6 +682,25 @@ testRun(void)
// {uncrustify_on}
"json - single stanza, valid backup, no priors, no archives in latest DB, backup/expire lock detected");
HRN_CFG_LOAD(cfgCmdInfo, argListProgressOnly);
TEST_RESULT_STR_Z(
infoRender(),
// {uncrustify_off - indentation}
"["
"{"
"\"name\":\"stanza1\","
"\"status\":{"
"\"code\":0,"
"\"lock\":{"
"\"backup\":{\"held\":true}"
"},"
"\"message\":\"ok\""
"}"
"}"
"]",
// {uncrustify_on}
"json (progress only) - single stanza, valid backup, no priors, no archives in latest DB, backup/expire lock detected");
HRN_CFG_LOAD(cfgCmdInfo, argListText);
TEST_RESULT_STR_Z(
infoRender(),
@@ -616,6 +727,13 @@ testRun(void)
" repo1: backup set size: 3MB, backup size: 3.1KB\n",
"text - single stanza, valid backup, no priors, no archives in latest DB, backup/expire lock detected");
HRN_CFG_LOAD(cfgCmdInfo, argListTextProgressOnly);
TEST_RESULT_STR_Z(
infoRender(),
"stanza: stanza1\n"
" status: ok (backup/expire running)\n",
"text (progress only) - single stanza, valid backup, no priors, no archives in latest DB, backup/expire lock detected");
// Notify child to release lock
HRN_FORK_PARENT_NOTIFY_PUT(0);
}
@@ -1037,6 +1155,12 @@ testRun(void)
StringList *argListMultiRepoJson = strLstDup(argListMultiRepo);
hrnCfgArgRawZ(argListMultiRepoJson, cfgOptOutput, "json");
StringList *argListMultiRepoProgressOnly = strLstDup(argListMultiRepo);
hrnCfgArgRawZ(argListMultiRepoProgressOnly, cfgOptDetailLevel, "progress");
StringList *argListMultiRepoJsonProgressOnly = strLstDup(argListMultiRepoJson);
hrnCfgArgRawZ(argListMultiRepoJsonProgressOnly, cfgOptDetailLevel, "progress");
HRN_FORK_BEGIN()
{
HRN_FORK_CHILD_BEGIN()
@@ -1474,6 +1598,45 @@ testRun(void)
// {uncrustify_on}
"json - multiple stanzas, some with valid backups, archives in latest DB, backup lock held on one stanza");
HRN_CFG_LOAD(cfgCmdInfo, argListMultiRepoJsonProgressOnly);
TEST_RESULT_STR_Z(
infoRender(),
// {uncrustify_off - indentation}
"["
"{"
"\"name\":\"stanza1\","
"\"status\":{"
"\"code\":0,"
"\"lock\":{"
"\"backup\":{\"held\":false}"
"},"
"\"message\":\"ok\""
"}"
"},"
"{"
"\"name\":\"stanza2\","
"\"status\":{"
"\"code\":0,"
"\"lock\":{"
"\"backup\":{\"held\":true,\"size\":3159000,\"size-cplt\":1435765}"
"},"
"\"message\":\"ok\""
"}"
"},"
"{"
"\"name\":\"stanza3\","
"\"status\":{"
"\"code\":0,"
"\"lock\":{"
"\"backup\":{\"held\":false}"
"},"
"\"message\":\"ok\""
"}"
"}"
"]",
// {uncrustify_on}
"json (progress only) - multiple stanzas, some with valid backups, archives in latest DB, backup lock held on one stanza");
// Notify child to release lock
HRN_FORK_PARENT_NOTIFY_PUT(0);
}
@@ -1610,6 +1773,19 @@ testRun(void)
" repo2: backup set size: 3MB, backup size: 3KB\n",
"text - multiple stanzas, multi-repo with valid backups, backup lock held on one stanza");
HRN_CFG_LOAD(cfgCmdInfo, argListMultiRepoProgressOnly);
TEST_RESULT_STR_Z(
infoRender(),
"stanza: stanza1\n"
" status: ok (backup/expire running - 65.28% complete)\n"
"\n"
"stanza: stanza2\n"
" status: ok (backup/expire running - 55.55% complete)\n"
"\n"
"stanza: stanza3\n"
" status: ok\n",
"text (progress only) - multiple stanzas, multi-repo with valid backups, backup lock held on one stanza");
// Notify child to release lock
HRN_FORK_PARENT_NOTIFY_PUT(0);
}
@@ -3394,6 +3570,15 @@ testRun(void)
TEST_ERROR(hrnCfgLoadP(cfgCmdInfo, argList), OptionInvalidError, "option 'set' not valid without option 'stanza'");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("set option invalid if only progress info is requested");
hrnCfgArgRawZ(argList, cfgOptStanza, "bogus");
hrnCfgArgRawZ(argList, cfgOptDetailLevel, "progress");
HRN_CFG_LOAD(cfgCmdInfo, argList);
TEST_ERROR(infoRender(), OptionInvalidError, "option 'set' cannot be used with option 'detail-level' = 'progress'");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("repo-level error");
@@ -3411,6 +3596,16 @@ testRun(void)
" cipher: none\n",
"text - invalid stanza");
StringList *argListProgressOnly = strLstDup(argList);
hrnCfgArgRawZ(argListProgressOnly, cfgOptDetailLevel, "progress");
HRN_CFG_LOAD(cfgCmdInfo, argListProgressOnly);
TEST_RESULT_STR_Z(
infoRender(),
"stanza: [invalid]\n"
" status: error (other)\n",
"text (progress only) - invalid stanza");
hrnCfgArgRawZ(argList, cfgOptOutput, "json");
HRN_CFG_LOAD(cfgCmdInfo, argList);
@@ -3445,6 +3640,27 @@ testRun(void)
// {uncrustify_on}
"json - invalid stanza");
hrnCfgArgRawZ(argListProgressOnly, cfgOptOutput, "json");
HRN_CFG_LOAD(cfgCmdInfo, argListProgressOnly);
TEST_RESULT_STR_Z(
infoRender(),
// {uncrustify_off - indentation}
"["
"{"
"\"name\":\"[invalid]\","
"\"status\":{"
"\"code\":99,"
"\"lock\":{"
"\"backup\":{\"held\":false}"
"},"
"\"message\":\"other\""
"}"
"}"
"]",
// {uncrustify_on}
"json (progress only) - invalid stanza");
argList = strLstNew();
hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 1, TEST_PATH "/repo2");
hrnCfgArgRawZ(argList, cfgOptStanza, "stanza1");
@@ -3458,6 +3674,16 @@ testRun(void)
" cipher: none\n",
"text - stanza requested");
argListProgressOnly = strLstDup(argList);
hrnCfgArgRawZ(argListProgressOnly, cfgOptDetailLevel, "progress");
HRN_CFG_LOAD(cfgCmdInfo, argListProgressOnly);
TEST_RESULT_STR_Z(
infoRender(),
"stanza: stanza1\n"
" status: error (other)\n",
"text (progress only) - stanza requested");
hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 2, TEST_PATH "/repo");
HRN_CFG_LOAD(cfgCmdInfo, argList);
@@ -3471,6 +3697,15 @@ testRun(void)
" repo2: error (missing stanza path)\n"
" cipher: none\n",
"text - stanza repo structure exists");
hrnCfgArgKeyRawZ(argListProgressOnly, cfgOptRepoPath, 2, TEST_PATH "/repo");
HRN_CFG_LOAD(cfgCmdInfo, argListProgressOnly);
TEST_RESULT_STR_Z(
infoRender(),
"stanza: stanza1\n"
" status: error (different across repos)\n",
"text (progress only) - stanza repo structure exists");
}
FUNCTION_HARNESS_RETURN_VOID();