You've already forked pgbackrest
							
							
				mirror of
				https://github.com/pgbackrest/pgbackrest.git
				synced 2025-10-30 23:37:45 +02:00 
			
		
		
		
	Add restore progress to info command output.
The prior implementation of the restore command did not provide any progress information. Implement it similarly to the backup command and also update the info command to display restore progress alongside backup progress. Restore is a read operation and should not block other commands. The only exception is that multiple restores with the same lock and repository path are not allowed, as each restore must write progress to a separate file. Therefore, no lock is needed for restores with a remote role and the restore command should not be terminated when the stop command is executed.
This commit is contained in:
		| @@ -16,6 +16,19 @@ | ||||
|         </release-bug-list> | ||||
|  | ||||
|         <release-feature-list> | ||||
|             <release-item> | ||||
|                 <github-issue id="2546"/> | ||||
|                 <github-pull-request id="2652"/> | ||||
|  | ||||
|                 <release-item-contributor-list> | ||||
|                     <release-item-contributor id="denis.garsh"/> | ||||
|                     <release-item-contributor id="maxim.michkov"/> | ||||
|                     <release-item-reviewer id="david.steele"/> | ||||
|                 </release-item-contributor-list> | ||||
|  | ||||
|                 <p>Add restore progress to <cmd>info</cmd> command output.</p> | ||||
|             </release-item> | ||||
|  | ||||
|             <release-item> | ||||
|                 <github-pull-request id="2550"/> | ||||
|  | ||||
|   | ||||
| @@ -130,6 +130,8 @@ command: | ||||
|     command-role: | ||||
|       local: {} | ||||
|       remote: {} | ||||
|     lock-required: true | ||||
|     lock-type: restore | ||||
|  | ||||
|   server: {} | ||||
|  | ||||
|   | ||||
| @@ -2554,7 +2554,7 @@ | ||||
|  | ||||
|                     <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> | ||||
|                     <p>The '<id>backup/expire running</id>' and/or '<id>restore running</id>' messages will appear beside the '<id>status</id>' information if any of those commands are currently running on the host.</p> | ||||
|  | ||||
|                     <p>The backups are displayed oldest to newest. The oldest backup will <i>always</i> be a full backup (indicated by an <id>F</id> at the end of the label) but the newest backup can be full, differential (ends with <id>D</id>), or incremental (ends with <id>I</id>).</p> | ||||
|  | ||||
|   | ||||
| @@ -77,10 +77,11 @@ VARIANT_STRDEF_STATIC(STANZA_KEY_DB_VAR,                            "db"); | ||||
| VARIANT_STRDEF_STATIC(STATUS_KEY_CODE_VAR,                          "code"); | ||||
| VARIANT_STRDEF_STATIC(STATUS_KEY_LOCK_VAR,                          "lock"); | ||||
| VARIANT_STRDEF_STATIC(STATUS_KEY_LOCK_BACKUP_VAR,                   "backup"); | ||||
| VARIANT_STRDEF_STATIC(STATUS_KEY_LOCK_BACKUP_HELD_VAR,              "held"); | ||||
| VARIANT_STRDEF_STATIC(STATUS_KEY_LOCK_BACKUP_PERCENT_COMPLETE_VAR,  "pct-cplt"); | ||||
| VARIANT_STRDEF_STATIC(STATUS_KEY_LOCK_BACKUP_SIZE_COMPLETE_VAR,     "size-cplt"); | ||||
| VARIANT_STRDEF_STATIC(STATUS_KEY_LOCK_BACKUP_SIZE_VAR,              "size"); | ||||
| VARIANT_STRDEF_STATIC(STATUS_KEY_LOCK_HELD_VAR,                     "held"); | ||||
| VARIANT_STRDEF_STATIC(STATUS_KEY_LOCK_PERCENT_COMPLETE_VAR,         "pct-cplt"); | ||||
| VARIANT_STRDEF_STATIC(STATUS_KEY_LOCK_RESTORE_VAR,                  "restore"); | ||||
| VARIANT_STRDEF_STATIC(STATUS_KEY_LOCK_SIZE_COMPLETE_VAR,            "size-cplt"); | ||||
| VARIANT_STRDEF_STATIC(STATUS_KEY_LOCK_SIZE_VAR,                     "size"); | ||||
| VARIANT_STRDEF_STATIC(STATUS_KEY_MESSAGE_VAR,                       "message"); | ||||
|  | ||||
| #define INFO_STANZA_STATUS_OK                                       "ok" | ||||
| @@ -108,6 +109,7 @@ STRING_STATIC(INFO_STANZA_STATUS_MESSAGE_OTHER_STR,                 INFO_STANZA_ | ||||
| STRING_STATIC(INFO_STANZA_INVALID_STR,                              "[invalid]"); | ||||
|  | ||||
| #define INFO_STANZA_STATUS_MESSAGE_LOCK_BACKUP                      "backup/expire running" | ||||
| #define INFO_STANZA_STATUS_MESSAGE_LOCK_RESTORE                     "restore running" | ||||
|  | ||||
| /*********************************************************************************************************************************** | ||||
| Data types and structures | ||||
| @@ -131,15 +133,27 @@ typedef struct InfoRepoData | ||||
| #define FUNCTION_LOG_INFO_REPO_DATA_FORMAT(value, buffer, bufferSize)                                                              \ | ||||
|     objNameToLog(value, "InfoRepoData", buffer, bufferSize) | ||||
|  | ||||
| // Information for a lockfile of a stanza | ||||
| typedef struct InfoStanzaLock | ||||
| { | ||||
|     bool held;                                                      // Is lock held on the system where info command is run? | ||||
|     uint64_t sizeComplete;                                          // Completed size of the backup/restore in bytes | ||||
|     uint64_t size;                                                  // Total size of the backup/restore in bytes | ||||
| } InfoStanzaLock; | ||||
|  | ||||
| #define FUNCTION_LOG_INFO_STANZA_LOCK_TYPE                                                                                         \ | ||||
|     InfoStanzaLock * | ||||
| #define FUNCTION_LOG_INFO_STANZA_LOCK_FORMAT(value, buffer, bufferSize)                                                            \ | ||||
|     objNameToLog(value, "InfoStanzaLock", buffer, bufferSize) | ||||
|  | ||||
| // Stanza with repository list of information for each repository | ||||
| typedef struct InfoStanzaRepo | ||||
| { | ||||
|     const String *name;                                             // Name of the stanza | ||||
|     uint64_t currentPgSystemId;                                     // Current postgres system id for the stanza | ||||
|     unsigned int currentPgVersion;                                  // Current postgres version for the stanza | ||||
|     bool backupLockHeld;                                            // Is backup lock held on the system where info command is run? | ||||
|     uint64_t sizeComplete;                                          // Completed size of the backup in bytes | ||||
|     uint64_t size;                                                  // Total size of the backup in bytes | ||||
|     InfoStanzaLock backupLock;                                      // Info for backup lock | ||||
|     InfoStanzaLock restoreLock;                                     // Info for restore lock | ||||
|     InfoRepoData *repoList;                                         // List of configured repositories | ||||
| } InfoStanzaRepo; | ||||
|  | ||||
| @@ -182,6 +196,25 @@ infoStanzaErrorAdd(InfoRepoData *const repoList, const ErrorType *const type, co | ||||
|     repoList->manifest = NULL; | ||||
| } | ||||
|  | ||||
| /*********************************************************************************************************************************** | ||||
| Add lock information to the target key-value | ||||
| ***********************************************************************************************************************************/ | ||||
| static void | ||||
| stanzaStatusLockAdd(KeyValue *targetKv, const Variant *const lockKey, const InfoStanzaLock *lock) | ||||
| { | ||||
|     KeyValue *const lockKv = kvPutKv(targetKv, lockKey); | ||||
|     kvPut(lockKv, STATUS_KEY_LOCK_HELD_VAR, VARBOOL(lock->held)); | ||||
|  | ||||
|     if (lock->size != 0) | ||||
|     { | ||||
|         kvPut(lockKv, STATUS_KEY_LOCK_SIZE_COMPLETE_VAR, VARUINT64(lock->sizeComplete)); | ||||
|         kvPut(lockKv, STATUS_KEY_LOCK_SIZE_VAR, VARUINT64(lock->size)); | ||||
|  | ||||
|         if (cfgOptionStrId(cfgOptOutput) != CFGOPTVAL_OUTPUT_JSON) | ||||
|             kvPut(lockKv, STATUS_KEY_LOCK_PERCENT_COMPLETE_VAR, VARUINT(cvtPctToUInt(lock->sizeComplete, lock->size))); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /*********************************************************************************************************************************** | ||||
| Set the overall error status code and message for the stanza to the code and message passed | ||||
| ***********************************************************************************************************************************/ | ||||
| @@ -239,21 +272,9 @@ stanzaStatus(const int code, const InfoStanzaRepo *const stanzaData, const Varia | ||||
|  | ||||
|     // Construct a specific lock part | ||||
|     KeyValue *const lockKv = kvPutKv(statusKv, STATUS_KEY_LOCK_VAR); | ||||
|     KeyValue *const backupLockKv = kvPutKv(lockKv, STATUS_KEY_LOCK_BACKUP_VAR); | ||||
|     kvPut(backupLockKv, STATUS_KEY_LOCK_BACKUP_HELD_VAR, VARBOOL(stanzaData->backupLockHeld)); | ||||
|  | ||||
|     if (stanzaData->size != 0) | ||||
|     { | ||||
|         kvPut(backupLockKv, STATUS_KEY_LOCK_BACKUP_SIZE_COMPLETE_VAR, VARUINT64(stanzaData->sizeComplete)); | ||||
|         kvPut(backupLockKv, STATUS_KEY_LOCK_BACKUP_SIZE_VAR, VARUINT64(stanzaData->size)); | ||||
|  | ||||
|         if (cfgOptionStrId(cfgOptOutput) != CFGOPTVAL_OUTPUT_JSON) | ||||
|         { | ||||
|             kvPut( | ||||
|                 backupLockKv, STATUS_KEY_LOCK_BACKUP_PERCENT_COMPLETE_VAR, | ||||
|                 VARUINT(cvtPctToUInt(stanzaData->sizeComplete, stanzaData->size))); | ||||
|         } | ||||
|     } | ||||
|     stanzaStatusLockAdd(lockKv, STATUS_KEY_LOCK_BACKUP_VAR, &stanzaData->backupLock); | ||||
|     stanzaStatusLockAdd(lockKv, STATUS_KEY_LOCK_RESTORE_VAR, &stanzaData->restoreLock); | ||||
|  | ||||
|     FUNCTION_TEST_RETURN_VOID(); | ||||
| } | ||||
| @@ -1302,6 +1323,44 @@ formatTextDb( | ||||
|     FUNCTION_TEST_RETURN_VOID(); | ||||
| } | ||||
|  | ||||
| /*********************************************************************************************************************************** | ||||
| Get the lock info of the specified lock type for the stanza | ||||
| ***********************************************************************************************************************************/ | ||||
| static void | ||||
| infoUpdateStanzaLock( | ||||
|     InfoStanzaLock *const stanzaLock, const String *const stanzaName, const unsigned int repoIdx, const LockType lockType) | ||||
| { | ||||
|     FUNCTION_TEST_BEGIN(); | ||||
|         FUNCTION_TEST_PARAM(INFO_STANZA_LOCK, stanzaLock); | ||||
|         FUNCTION_TEST_PARAM(STRING, stanzaName); | ||||
|         FUNCTION_TEST_PARAM(UINT, repoIdx); | ||||
|         FUNCTION_TEST_PARAM(ENUM, lockType); | ||||
|     FUNCTION_TEST_END(); | ||||
|  | ||||
|     FUNCTION_AUDIT_HELPER(); | ||||
|  | ||||
|     ASSERT(stanzaLock != NULL); | ||||
|     ASSERT(stanzaName != NULL); | ||||
|  | ||||
|     // If there is a valid lock for this stanza then backup/expire/restore must be running | ||||
|     const LockReadResult lockResult = cmdLockRead(lockType, stanzaName, repoIdx); | ||||
|  | ||||
|     if (lockResult.status == lockReadStatusValid) | ||||
|     { | ||||
|         stanzaLock->held = true; | ||||
|  | ||||
|         if (lockResult.data.size != NULL) | ||||
|         { | ||||
|             ASSERT(lockResult.data.size != NULL); | ||||
|  | ||||
|             stanzaLock->sizeComplete += varUInt64(lockResult.data.sizeComplete); | ||||
|             stanzaLock->size += varUInt64(lockResult.data.size); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     FUNCTION_TEST_RETURN_VOID(); | ||||
| } | ||||
|  | ||||
| /*********************************************************************************************************************************** | ||||
| Get the backup and archive info files on the specified repo for the stanza | ||||
| ***********************************************************************************************************************************/ | ||||
| @@ -1378,25 +1437,12 @@ infoUpdateStanza( | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // 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. | ||||
|             // Read the lock files if backup.info is present. Exception: when only progress is requested, backup.info is skipped for | ||||
|             // performance, so the lock files are read unconditionally -- though they 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); | ||||
|  | ||||
|                 if (lockResult.status == lockReadStatusValid) | ||||
|                 { | ||||
|                     stanzaRepo->backupLockHeld = true; | ||||
|  | ||||
|                     if (lockResult.data.size != NULL) | ||||
|                     { | ||||
|                         ASSERT(lockResult.data.size != NULL); | ||||
|  | ||||
|                         stanzaRepo->sizeComplete += varUInt64(lockResult.data.sizeComplete); | ||||
|                         stanzaRepo->size += varUInt64(lockResult.data.size); | ||||
|                     } | ||||
|                 } | ||||
|                 infoUpdateStanzaLock(&stanzaRepo->backupLock, stanzaRepo->name, repoIdx, lockTypeBackup); | ||||
|                 infoUpdateStanzaLock(&stanzaRepo->restoreLock, stanzaRepo->name, repoIdx, lockTypeRestore); | ||||
|             } | ||||
|  | ||||
|             stanzaRepo->repoList[repoIdx].stanzaStatus = stanzaStatus; | ||||
| @@ -1655,91 +1701,109 @@ infoRender(void) | ||||
|                     const KeyValue *const stanzaStatus = varKv(kvGet(stanzaInfo, STANZA_KEY_STATUS_VAR)); | ||||
|                     const int statusCode = varInt(kvGet(stanzaStatus, STATUS_KEY_CODE_VAR)); | ||||
|  | ||||
|                     // Get the lock info | ||||
|                     // Get the backup lock info | ||||
|                     const KeyValue *const lockKv = varKv(kvGet(stanzaStatus, STATUS_KEY_LOCK_VAR)); | ||||
|                     const KeyValue *const backupLockKv = varKv(kvGet(lockKv, STATUS_KEY_LOCK_BACKUP_VAR)); | ||||
|                     const bool backupLockHeld = varBool(kvGet(backupLockKv, STATUS_KEY_LOCK_BACKUP_HELD_VAR)); | ||||
|                     const Variant *const percentComplete = kvGet(backupLockKv, STATUS_KEY_LOCK_BACKUP_PERCENT_COMPLETE_VAR); | ||||
|                     const String *const percentCompleteStr = | ||||
|                         percentComplete != NULL ? | ||||
|                             strNewFmt(" - %u.%02u%% complete", varUInt(percentComplete) / 100, varUInt(percentComplete) % 100) : | ||||
|                             EMPTY_STR; | ||||
|                     const bool backupLockHeld = varBool(kvGet(backupLockKv, STATUS_KEY_LOCK_HELD_VAR)); | ||||
|                     const Variant *const backupPercentComplete = kvGet(backupLockKv, STATUS_KEY_LOCK_PERCENT_COMPLETE_VAR); | ||||
|                     const String *const backupPercentCompleteStr = | ||||
|                         backupPercentComplete != NULL ? | ||||
|                             strNewFmt(" - %s complete", strZ(strNewPct(varUInt(backupPercentComplete), 10000))) : EMPTY_STR; | ||||
|  | ||||
|                     if (statusCode != INFO_STANZA_STATUS_CODE_OK) | ||||
|                     // Get the restore lock info | ||||
|                     const KeyValue *const restoreLockKv = varKv(kvGet(lockKv, STATUS_KEY_LOCK_RESTORE_VAR)); | ||||
|                     const bool restoreLockHeld = varBool(kvGet(restoreLockKv, STATUS_KEY_LOCK_HELD_VAR)); | ||||
|                     const Variant *const restorePercentComplete = kvGet(restoreLockKv, STATUS_KEY_LOCK_PERCENT_COMPLETE_VAR); | ||||
|                     const String *const restorePercentCompleteStr = | ||||
|                         restorePercentComplete != NULL ? | ||||
|                             strNewFmt(" - %s complete", strZ(strNewPct(varUInt(restorePercentComplete), 10000))) : EMPTY_STR; | ||||
|  | ||||
|                     // Build stanza status | ||||
|                     const bool errorStatus = | ||||
|                         statusCode != INFO_STANZA_STATUS_CODE_OK && | ||||
|                         (outputFull == false || statusCode != INFO_STANZA_STATUS_CODE_MIXED); | ||||
|                     const bool progressStatus = backupLockHeld == true || restoreLockHeld == true; | ||||
|                     const String *const statusLabelStr = | ||||
|                         statusCode == INFO_STANZA_STATUS_CODE_OK ? | ||||
|                             strNewZ(INFO_STANZA_STATUS_OK) : | ||||
|                             errorStatus == true ? strNewZ(INFO_STANZA_STATUS_ERROR) : strNewZ(INFO_STANZA_MIXED); | ||||
|                     const String *const statusErrorStr = | ||||
|                         errorStatus ? varStr(kvGet(stanzaStatus, STATUS_KEY_MESSAGE_VAR)) : EMPTY_STR; | ||||
|                     const String *const progressStr = | ||||
|                         backupLockHeld == true && restoreLockHeld == true ? | ||||
|                             strNewFmt( | ||||
|                                 INFO_STANZA_STATUS_MESSAGE_LOCK_BACKUP "%s, " INFO_STANZA_STATUS_MESSAGE_LOCK_RESTORE "%s", | ||||
|                                 strZ(backupPercentCompleteStr), | ||||
|                                 strZ(restorePercentCompleteStr)) : | ||||
|                             backupLockHeld == true ? | ||||
|                                 strNewFmt(INFO_STANZA_STATUS_MESSAGE_LOCK_BACKUP "%s", strZ(backupPercentCompleteStr)) : | ||||
|                                 restoreLockHeld == true ? | ||||
|                                     strNewFmt(INFO_STANZA_STATUS_MESSAGE_LOCK_RESTORE "%s", strZ(restorePercentCompleteStr)) : | ||||
|                                     EMPTY_STR; | ||||
|  | ||||
|                     if (progressStatus) | ||||
|                     { | ||||
|                         // Update the overall stanza status and change displayed status if backup lock is found | ||||
|                         if (outputFull && | ||||
|                             (statusCode == INFO_STANZA_STATUS_CODE_MIXED || statusCode == INFO_STANZA_STATUS_CODE_PG_MISMATCH || | ||||
|                              statusCode == INFO_STANZA_STATUS_CODE_OTHER)) | ||||
|                         // Status: error (message, progress) | ||||
|                         if (errorStatus) | ||||
|                         { | ||||
|                             // Stanza status | ||||
|                             strCatFmt( | ||||
|                                 resultStr, "%s%s\n", | ||||
|                                 statusCode == INFO_STANZA_STATUS_CODE_MIXED ? | ||||
|                                     INFO_STANZA_MIXED : | ||||
|                                     zNewFmt( | ||||
|                                         INFO_STANZA_STATUS_ERROR " (%s)", | ||||
|                                         strZ(varStr(kvGet(stanzaStatus, STATUS_KEY_MESSAGE_VAR)))), | ||||
|                                 backupLockHeld == true ? | ||||
|                                     zNewFmt(" (" INFO_STANZA_STATUS_MESSAGE_LOCK_BACKUP "%s)", strZ(percentCompleteStr)) : ""); | ||||
|  | ||||
|                             // Output the status per repo | ||||
|                             const VariantList *const repoSection = kvGetList(stanzaInfo, STANZA_KEY_REPO_VAR); | ||||
|                             const bool multiRepo = varLstSize(repoSection) > 1; | ||||
|                             const char *const formatSpacer = multiRepo ? "               " : "            "; | ||||
|  | ||||
|                             for (unsigned int repoIdx = 0; repoIdx < varLstSize(repoSection); repoIdx++) | ||||
|                             { | ||||
|                                 const KeyValue *const repoInfo = varKv(varLstGet(repoSection, repoIdx)); | ||||
|                                 const KeyValue *const repoStatus = varKv(kvGet(repoInfo, STANZA_KEY_STATUS_VAR)); | ||||
|  | ||||
|                                 // If more than one repo configured, then add the repo status per repo | ||||
|                                 if (multiRepo) | ||||
|                                     strCatFmt(resultStr, "        repo%u: ", varUInt(kvGet(repoInfo, REPO_KEY_KEY_VAR))); | ||||
|  | ||||
|                                 if (varInt(kvGet(repoStatus, STATUS_KEY_CODE_VAR)) == INFO_STANZA_STATUS_CODE_OK) | ||||
|                                     strCatZ(resultStr, INFO_STANZA_STATUS_OK "\n"); | ||||
|                                 else | ||||
|                                 { | ||||
|                                     if (varInt(kvGet(repoStatus, STATUS_KEY_CODE_VAR)) == INFO_STANZA_STATUS_CODE_OTHER) | ||||
|                                     { | ||||
|                                         const StringList *const repoError = strLstNewSplit( | ||||
|                                             varStr(kvGet(repoStatus, STATUS_KEY_MESSAGE_VAR)), STRDEF("\n")); | ||||
|  | ||||
|                                         strCatFmt( | ||||
|                                             resultStr, "%s%s%s\n", | ||||
|                                             multiRepo ? INFO_STANZA_STATUS_ERROR " (" INFO_STANZA_STATUS_MESSAGE_OTHER ")\n" : "", | ||||
|                                             formatSpacer, strZ(strLstJoin(repoError, zNewFmt("\n%s", formatSpacer)))); | ||||
|                                     } | ||||
|                                     else | ||||
|                                     { | ||||
|                                         strCatFmt( | ||||
|                                             resultStr, INFO_STANZA_STATUS_ERROR " (%s)\n", | ||||
|                                             strZ(varStr(kvGet(repoStatus, STATUS_KEY_MESSAGE_VAR)))); | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
|                             strCatFmt(resultStr, "%s (%s, %s)\n", strZ(statusLabelStr), strZ(statusErrorStr), strZ(progressStr)); | ||||
|                         } | ||||
|                         // Status: ok/mixed (progress) | ||||
|                         else | ||||
|                         { | ||||
|                             strCatFmt( | ||||
|                                 resultStr, "%s (%s%s\n", INFO_STANZA_STATUS_ERROR, | ||||
|                                 strZ(varStr(kvGet(stanzaStatus, STATUS_KEY_MESSAGE_VAR))), | ||||
|                                 backupLockHeld == true ? | ||||
|                                     zNewFmt(", " INFO_STANZA_STATUS_MESSAGE_LOCK_BACKUP "%s)", strZ(percentCompleteStr)) : ")"); | ||||
|                         } | ||||
|                             strCatFmt(resultStr, "%s (%s)\n", strZ(statusLabelStr), strZ(progressStr)); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         // Change displayed status if backup lock is found | ||||
|                         if (backupLockHeld) | ||||
|                         // Status: error (message) | ||||
|                         if (errorStatus) | ||||
|                         { | ||||
|                             strCatFmt( | ||||
|                                 resultStr, "%s (%s%s)\n", INFO_STANZA_STATUS_OK, INFO_STANZA_STATUS_MESSAGE_LOCK_BACKUP, | ||||
|                                 strZ(percentCompleteStr)); | ||||
|                             strCatFmt(resultStr, "%s (%s)\n", strZ(statusLabelStr), strZ(statusErrorStr)); | ||||
|                         } | ||||
|                         // Status: ok/mixed | ||||
|                         else | ||||
|                             strCatFmt(resultStr, "%s\n", INFO_STANZA_STATUS_OK); | ||||
|                             strCatFmt(resultStr, "%s\n", strZ(statusLabelStr)); | ||||
|                     } | ||||
|  | ||||
|                     // Output the status per repo | ||||
|                     if (outputFull && | ||||
|                         (statusCode == INFO_STANZA_STATUS_CODE_MIXED || statusCode == INFO_STANZA_STATUS_CODE_PG_MISMATCH || | ||||
|                          statusCode == INFO_STANZA_STATUS_CODE_OTHER)) | ||||
|                     { | ||||
|                         const VariantList *const repoSection = kvGetList(stanzaInfo, STANZA_KEY_REPO_VAR); | ||||
|                         const bool multiRepo = varLstSize(repoSection) > 1; | ||||
|                         const char *const formatSpacer = multiRepo ? "               " : "            "; | ||||
|  | ||||
|                         for (unsigned int repoIdx = 0; repoIdx < varLstSize(repoSection); repoIdx++) | ||||
|                         { | ||||
|                             const KeyValue *const repoInfo = varKv(varLstGet(repoSection, repoIdx)); | ||||
|                             const KeyValue *const repoStatus = varKv(kvGet(repoInfo, STANZA_KEY_STATUS_VAR)); | ||||
|  | ||||
|                             // If more than one repo configured, then add the repo status per repo | ||||
|                             if (multiRepo) | ||||
|                                 strCatFmt(resultStr, "        repo%u: ", varUInt(kvGet(repoInfo, REPO_KEY_KEY_VAR))); | ||||
|  | ||||
|                             if (varInt(kvGet(repoStatus, STATUS_KEY_CODE_VAR)) == INFO_STANZA_STATUS_CODE_OK) | ||||
|                                 strCatZ(resultStr, INFO_STANZA_STATUS_OK "\n"); | ||||
|                             else | ||||
|                             { | ||||
|                                 if (varInt(kvGet(repoStatus, STATUS_KEY_CODE_VAR)) == INFO_STANZA_STATUS_CODE_OTHER) | ||||
|                                 { | ||||
|                                     const StringList *const repoError = strLstNewSplit( | ||||
|                                         varStr(kvGet(repoStatus, STATUS_KEY_MESSAGE_VAR)), STRDEF("\n")); | ||||
|  | ||||
|                                     strCatFmt( | ||||
|                                         resultStr, "%s%s%s\n", | ||||
|                                         multiRepo ? INFO_STANZA_STATUS_ERROR " (" INFO_STANZA_STATUS_MESSAGE_OTHER ")\n" : "", | ||||
|                                         formatSpacer, strZ(strLstJoin(repoError, zNewFmt("\n%s", formatSpacer)))); | ||||
|                                 } | ||||
|                                 else | ||||
|                                 { | ||||
|                                     strCatFmt( | ||||
|                                         resultStr, INFO_STANZA_STATUS_ERROR " (%s)\n", | ||||
|                                         strZ(varStr(kvGet(repoStatus, STATUS_KEY_MESSAGE_VAR)))); | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     // Add cipher type if the stanza is found on at least one repo | ||||
|   | ||||
| @@ -23,6 +23,7 @@ static const char *const lockTypeName[] = | ||||
| { | ||||
|     "archive",                                                      // lockTypeArchive | ||||
|     "backup",                                                       // lockTypeBackup | ||||
|     "restore",                                                      // lockTypeRestore | ||||
| }; | ||||
|  | ||||
| /*********************************************************************************************************************************** | ||||
| @@ -140,7 +141,7 @@ cmdLockRead(const LockType lockType, const String *const stanza, const unsigned | ||||
|         FUNCTION_LOG_PARAM(UINT, repoIdx); | ||||
|     FUNCTION_LOG_END(); | ||||
|  | ||||
|     ASSERT(lockType == lockTypeBackup); | ||||
|     ASSERT(lockType == lockTypeBackup || lockType == lockTypeRestore); | ||||
|     ASSERT(stanza != NULL); | ||||
|  | ||||
|     FUNCTION_AUDIT_STRUCT(); | ||||
|   | ||||
| @@ -8,6 +8,7 @@ Restore Command | ||||
| #include <time.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| #include "command/lock.h" | ||||
| #include "command/restore/file.h" | ||||
| #include "command/restore/protocol.h" | ||||
| #include "command/restore/restore.h" | ||||
| @@ -2049,7 +2050,7 @@ restoreFilePgPath(const Manifest *const manifest, const String *const manifestNa | ||||
| static uint64_t | ||||
| restoreJobResult( | ||||
|     const Manifest *const manifest, ProtocolParallelJob *const job, RegExp *const zeroExp, const uint64_t sizeTotal, | ||||
|     uint64_t sizeRestored) | ||||
|     uint64_t sizeRestored, unsigned int *const currentPercentComplete) | ||||
| { | ||||
|     FUNCTION_LOG_BEGIN(logLevelDebug); | ||||
|         FUNCTION_LOG_PARAM(MANIFEST, manifest); | ||||
| @@ -2057,6 +2058,7 @@ restoreJobResult( | ||||
|         FUNCTION_LOG_PARAM(REGEXP, zeroExp); | ||||
|         FUNCTION_LOG_PARAM(UINT64, sizeTotal); | ||||
|         FUNCTION_LOG_PARAM(UINT64, sizeRestored); | ||||
|         FUNCTION_LOG_PARAM_P(UINT, currentPercentComplete); | ||||
|     FUNCTION_LOG_END(); | ||||
|  | ||||
|     ASSERT(manifest != NULL); | ||||
| @@ -2067,6 +2069,7 @@ restoreJobResult( | ||||
|         MEM_CONTEXT_TEMP_BEGIN() | ||||
|         { | ||||
|             PackRead *const jobResult = protocolParallelJobResult(job); | ||||
|             unsigned int percentComplete = 0; | ||||
|  | ||||
|             while (!pckReadNullP(jobResult)) | ||||
|             { | ||||
| @@ -2138,6 +2141,10 @@ restoreJobResult( | ||||
|  | ||||
|                 // Add size and percent complete | ||||
|                 sizeRestored += file.size; | ||||
|  | ||||
|                 // Store percentComplete as an integer (used to update progress in the lock file) | ||||
|                 percentComplete = cvtPctToUInt(sizeRestored, sizeTotal); | ||||
|  | ||||
|                 strCatFmt(log, "%s, %s)", strZ(strSizeFormat(file.size)), strZ(strNewPct(sizeRestored, sizeTotal))); | ||||
|  | ||||
|                 // If not zero-length add the checksum | ||||
| @@ -2146,6 +2153,15 @@ restoreJobResult( | ||||
|  | ||||
|                 LOG_DETAIL_PID(protocolParallelJobProcessId(job), strZ(log)); | ||||
|             } | ||||
|  | ||||
|             // Update currentPercentComplete and lock file when the change is significant enough | ||||
|             if (percentComplete - *currentPercentComplete > 10) | ||||
|             { | ||||
|                 *currentPercentComplete = percentComplete; | ||||
|                 cmdLockWriteP( | ||||
|                     .percentComplete = VARUINT(*currentPercentComplete), .sizeComplete = VARUINT64(sizeRestored), | ||||
|                     .size = VARUINT64(sizeTotal)); | ||||
|             } | ||||
|         } | ||||
|         MEM_CONTEXT_TEMP_END(); | ||||
|  | ||||
| @@ -2449,6 +2465,12 @@ cmdRestore(void) | ||||
|         // Process jobs | ||||
|         uint64_t sizeRestored = 0; | ||||
|  | ||||
|         // Initialize percent complete and bytes completed/total | ||||
|         unsigned int currentPercentComplete = 0; | ||||
|         cmdLockWriteP( | ||||
|             .percentComplete = VARUINT(currentPercentComplete), .sizeComplete = VARUINT64(sizeRestored), | ||||
|             .size = VARUINT64(sizeTotal)); | ||||
|  | ||||
|         MEM_CONTEXT_TEMP_RESET_BEGIN() | ||||
|         { | ||||
|             do | ||||
| @@ -2458,7 +2480,8 @@ cmdRestore(void) | ||||
|                 for (unsigned int jobIdx = 0; jobIdx < completed; jobIdx++) | ||||
|                 { | ||||
|                     sizeRestored = restoreJobResult( | ||||
|                         jobData.manifest, protocolParallelResult(parallelExec), jobData.zeroExp, sizeTotal, sizeRestored); | ||||
|                         jobData.manifest, protocolParallelResult(parallelExec), jobData.zeroExp, sizeTotal, sizeRestored, | ||||
|                         ¤tPercentComplete); | ||||
|                 } | ||||
|  | ||||
|                 // Reset the memory context occasionally so we don't use too much memory or slow down processing | ||||
|   | ||||
| @@ -48,6 +48,7 @@ typedef enum | ||||
| { | ||||
|     lockTypeArchive, | ||||
|     lockTypeBackup, | ||||
|     lockTypeRestore, | ||||
|     lockTypeAll, | ||||
|     lockTypeNone, | ||||
| } LockType; | ||||
|   | ||||
| @@ -895,7 +895,8 @@ static const ParseRuleCommand parseRuleCommand[CFG_COMMAND_TOTAL] = | ||||
|     PARSE_RULE_COMMAND                                                                                                // cmd/restore
 | ||||
|     (                                                                                                                 // cmd/restore
 | ||||
|         PARSE_RULE_COMMAND_NAME("restore"),                                                                           // cmd/restore
 | ||||
|         PARSE_RULE_COMMAND_LOCK_TYPE(None),                                                                           // cmd/restore
 | ||||
|         PARSE_RULE_COMMAND_LOCK_REQUIRED(true),                                                                       // cmd/restore
 | ||||
|         PARSE_RULE_COMMAND_LOCK_TYPE(Restore),                                                                        // cmd/restore
 | ||||
|         PARSE_RULE_COMMAND_LOG_FILE(true),                                                                            // cmd/restore
 | ||||
|         PARSE_RULE_COMMAND_LOG_LEVEL_DEFAULT(Info),                                                                   // cmd/restore
 | ||||
|                                                                                                                       // cmd/restore
 | ||||
|   | ||||
| @@ -68,7 +68,7 @@ typedef struct ParseRuleCommand | ||||
|     unsigned int commandRoleValid : CFG_COMMAND_ROLE_TOTAL;         // Valid for the command role? | ||||
|     bool lockRequired : 1;                                          // Is an immediate lock required? | ||||
|     bool lockRemoteRequired : 1;                                    // Is a lock required on the remote? | ||||
|     unsigned int lockType : 2;                                      // Lock type required | ||||
|     unsigned int lockType : 3;                                      // Lock type required | ||||
|     bool logFile : 1;                                               // Will the command log to a file? | ||||
|     unsigned int logLevelDefault : 4;                               // Default log level | ||||
|     bool parameterAllowed : 1;                                      // Command-line parameters are allowed | ||||
|   | ||||
| @@ -2823,6 +2823,14 @@ test/src/common/harnessProtocol.h: | ||||
|   class: test/harness | ||||
|   type: c/h | ||||
|  | ||||
| test/src/common/harnessRestore.c: | ||||
|   class: test/harness | ||||
|   type: c | ||||
|  | ||||
| test/src/common/harnessRestore.h: | ||||
|   class: test/harness | ||||
|   type: c/h | ||||
|  | ||||
| test/src/common/harnessServer.c: | ||||
|   class: test/harness | ||||
|   type: c | ||||
|   | ||||
| @@ -949,7 +949,9 @@ unit: | ||||
|  | ||||
|       # ---------------------------------------------------------------------------------------------------------------------------- | ||||
|       - name: restore | ||||
|         total: 15 | ||||
|         total: 16 | ||||
|  | ||||
|         harness: restore | ||||
|  | ||||
|         coverage: | ||||
|           - command/restore/blockChecksum | ||||
|   | ||||
							
								
								
									
										33
									
								
								test/src/common/harnessRestore.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								test/src/common/harnessRestore.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| /*********************************************************************************************************************************** | ||||
| Harness for Restore Testing | ||||
| ***********************************************************************************************************************************/ | ||||
| #include "build.auto.h" | ||||
|  | ||||
| #include "command/lock.h" | ||||
| #include "command/restore/restore.h" | ||||
| #include "common/harnessDebug.h" | ||||
| #include "common/harnessRestore.h" | ||||
| #include "common/harnessTest.intern.h" | ||||
| #include "config/config.h" | ||||
|  | ||||
| /**********************************************************************************************************************************/ | ||||
| void | ||||
| hrnCmdRestore(void) | ||||
| { | ||||
|     FUNCTION_HARNESS_VOID(); | ||||
|  | ||||
|     lockInit(STR(testPath()), cfgOptionStr(cfgOptExecId)); | ||||
|     cmdLockAcquireP(); | ||||
|  | ||||
|     TRY_BEGIN() | ||||
|     { | ||||
|         cmdRestore(); | ||||
|     } | ||||
|     FINALLY() | ||||
|     { | ||||
|         cmdLockReleaseP(); | ||||
|     } | ||||
|     TRY_END(); | ||||
|  | ||||
|     FUNCTION_HARNESS_RETURN_VOID(); | ||||
| } | ||||
							
								
								
									
										13
									
								
								test/src/common/harnessRestore.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								test/src/common/harnessRestore.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| /*********************************************************************************************************************************** | ||||
| Harness for Restore Testing | ||||
| ***********************************************************************************************************************************/ | ||||
| #ifndef TEST_COMMON_HARNESS_RESTORE_H | ||||
| #define TEST_COMMON_HARNESS_RESTORE_H | ||||
|  | ||||
| /*********************************************************************************************************************************** | ||||
| Functions | ||||
| ***********************************************************************************************************************************/ | ||||
| // Restore the database from the test backup and handle all the required locking. The restore configuration must already be loaded. | ||||
| void hrnCmdRestore(void); | ||||
|  | ||||
| #endif | ||||
| @@ -84,7 +84,10 @@ testRun(void) | ||||
|                     "]," | ||||
|                     "\"status\":{" | ||||
|                         "\"code\":1," | ||||
|                         "\"lock\":{\"backup\":{\"held\":false}}," | ||||
|                         "\"lock\":{" | ||||
|                             "\"backup\":{\"held\":false}," | ||||
|                             "\"restore\":{\"held\":false}" | ||||
|                         "}," | ||||
|                         "\"message\":\"missing stanza path\"" | ||||
|                     "}" | ||||
|                 "}" | ||||
| @@ -104,7 +107,8 @@ testRun(void) | ||||
|                     "\"status\":{" | ||||
|                         "\"code\":1," | ||||
|                         "\"lock\":{" | ||||
|                             "\"backup\":{\"held\":false}" | ||||
|                             "\"backup\":{\"held\":false}," | ||||
|                             "\"restore\":{\"held\":false}" | ||||
|                         "}," | ||||
|                         "\"message\":\"missing stanza path\"" | ||||
|                     "}" | ||||
| @@ -176,7 +180,10 @@ testRun(void) | ||||
|                     "]," | ||||
|                     "\"status\":{" | ||||
|                         "\"code\":3," | ||||
|                         "\"lock\":{\"backup\":{\"held\":false}}," | ||||
|                         "\"lock\":{" | ||||
|                             "\"backup\":{\"held\":false}," | ||||
|                             "\"restore\":{\"held\":false}" | ||||
|                         "}," | ||||
|                         "\"message\":\"missing stanza data\"" | ||||
|                     "}" | ||||
|                 "}" | ||||
| @@ -195,7 +202,8 @@ testRun(void) | ||||
|                     "\"status\":{" | ||||
|                         "\"code\":0," | ||||
|                         "\"lock\":{" | ||||
|                             "\"backup\":{\"held\":false}" | ||||
|                             "\"backup\":{\"held\":false}," | ||||
|                             "\"restore\":{\"held\":false}" | ||||
|                         "}," | ||||
|                         "\"message\":\"ok\"" | ||||
|                     "}" | ||||
| @@ -257,7 +265,10 @@ testRun(void) | ||||
|                     "]," | ||||
|                     "\"status\":{" | ||||
|                         "\"code\":99," | ||||
|                         "\"lock\":{\"backup\":{\"held\":false}}," | ||||
|                         "\"lock\":{" | ||||
|                             "\"backup\":{\"held\":false}," | ||||
|                             "\"restore\":{\"held\":false}" | ||||
|                         "}," | ||||
|                         "\"message\":\"other\"" | ||||
|                     "}" | ||||
|                 "}" | ||||
| @@ -314,6 +325,9 @@ testRun(void) | ||||
|                 TEST_RESULT_BOOL( | ||||
|                     lockAcquireP(cmdLockFileName(STRDEF("stanza1"), lockTypeBackup, 1)), true, "create backup/expire lock"); | ||||
|  | ||||
|                 TEST_RESULT_BOOL( | ||||
|                     lockAcquireP(cmdLockFileName(STRDEF("stanza1"), lockTypeRestore, 1)), true, "create restore lock"); | ||||
|  | ||||
|                 // Notify parent that lock has been acquired | ||||
|                 HRN_FORK_CHILD_NOTIFY_PUT(); | ||||
|  | ||||
| @@ -375,7 +389,10 @@ testRun(void) | ||||
|                             "]," | ||||
|                             "\"status\":{" | ||||
|                                 "\"code\":2," | ||||
|                                 "\"lock\":{\"backup\":{\"held\":true}}," | ||||
|                                 "\"lock\":{" | ||||
|                                     "\"backup\":{\"held\":true}," | ||||
|                                     "\"restore\":{\"held\":true}" | ||||
|                                 "}," | ||||
|                                 "\"message\":\"no valid backups\"" | ||||
|                             "}" | ||||
|                         "}" | ||||
| @@ -387,7 +404,7 @@ testRun(void) | ||||
|                 TEST_RESULT_STR_Z( | ||||
|                     infoRender(), | ||||
|                     "stanza: stanza1\n" | ||||
|                     "    status: error (no valid backups, backup/expire running)\n" | ||||
|                     "    status: error (no valid backups, backup/expire running, restore running)\n" | ||||
|                     "    cipher: none\n" | ||||
|                     "\n" | ||||
|                     "    db (current)\n" | ||||
| @@ -518,7 +535,7 @@ testRun(void) | ||||
|             "3={\"db-catalog-version\":201608131,\"db-control-version\":960,\"db-system-id\":6569239123849665679" | ||||
|             ",\"db-version\":\"9.6\"}\n"); | ||||
|  | ||||
|         // Execute while a backup lock is held | ||||
|         // Execute while backup and restore locks are held | ||||
|         HRN_FORK_BEGIN() | ||||
|         { | ||||
|             HRN_FORK_CHILD_BEGIN() | ||||
| @@ -528,6 +545,10 @@ testRun(void) | ||||
|                 TEST_RESULT_BOOL(lockAcquireP(lockFileName), true, "create backup/expire lock"); | ||||
|                 TEST_RESULT_VOID(lockWriteP(lockFileName), "write lock data"); | ||||
|  | ||||
|                 String *lockFileNameRestore = cmdLockFileName(STRDEF("stanza1"), lockTypeRestore, 1); | ||||
|                 TEST_RESULT_BOOL(lockAcquireP(lockFileNameRestore), true, "create restore lock"); | ||||
|                 TEST_RESULT_VOID(lockWriteP(lockFileNameRestore), "write lock data"); | ||||
|  | ||||
|                 // Notify parent that lock has been acquired | ||||
|                 HRN_FORK_CHILD_NOTIFY_PUT(); | ||||
|  | ||||
| @@ -674,7 +695,10 @@ testRun(void) | ||||
|                             "]," | ||||
|                             "\"status\":{" | ||||
|                                 "\"code\":0," | ||||
|                                 "\"lock\":{\"backup\":{\"held\":true}}," | ||||
|                                 "\"lock\":{" | ||||
|                                     "\"backup\":{\"held\":true}," | ||||
|                                     "\"restore\":{\"held\":true}" | ||||
|                                 "}," | ||||
|                                 "\"message\":\"ok\"" | ||||
|                             "}" | ||||
|                         "}" | ||||
| @@ -692,20 +716,22 @@ testRun(void) | ||||
|                             "\"status\":{" | ||||
|                                 "\"code\":0," | ||||
|                                 "\"lock\":{" | ||||
|                                     "\"backup\":{\"held\":true}" | ||||
|                                     "\"backup\":{\"held\":true}," | ||||
|                                     "\"restore\":{\"held\":true}" | ||||
|                                 "}," | ||||
|                                 "\"message\":\"ok\"" | ||||
|                             "}" | ||||
|                         "}" | ||||
|                     "]", | ||||
|                     // {uncrustify_on} | ||||
|                     "json (progress only) - single stanza, valid backup, no priors, no archives in latest DB, backup/expire lock detected"); | ||||
|                     "json (progress only) - single stanza, valid backup, no priors, no archives in latest DB, backup/expire and" | ||||
|                     " restore lock detected"); | ||||
|  | ||||
|                 HRN_CFG_LOAD(cfgCmdInfo, argListText); | ||||
|                 TEST_RESULT_STR_Z( | ||||
|                     infoRender(), | ||||
|                     "stanza: stanza1\n" | ||||
|                     "    status: ok (backup/expire running)\n" | ||||
|                     "    status: ok (backup/expire running, restore running)\n" | ||||
|                     "    cipher: none\n" | ||||
|                     "\n" | ||||
|                     "    db (prior)\n" | ||||
| @@ -731,8 +757,9 @@ testRun(void) | ||||
|                 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"); | ||||
|                     "    status: ok (backup/expire running, restore running)\n", | ||||
|                     "text (progress only) - single stanza, valid backup, no priors, no archives in latest DB, backup/expire and" | ||||
|                     " restore lock detected"); | ||||
|  | ||||
|                 // Notify child to release lock | ||||
|                 HRN_FORK_PARENT_NOTIFY_PUT(0); | ||||
| @@ -981,6 +1008,30 @@ testRun(void) | ||||
|             ",\"db-version\":\"9.4\"}\n", | ||||
|             .comment = "put backup info to file - stanza2, repo1"); | ||||
|  | ||||
|         HRN_INFO_PUT( | ||||
|             storageTest, TEST_PATH "/repo/" STORAGE_PATH_ARCHIVE "/stanza4/" INFO_ARCHIVE_FILE, | ||||
|             "[db]\n" | ||||
|             "db-id=1\n" | ||||
|             "db-system-id=6625633699176220261\n" | ||||
|             "db-version=\"9.4\"\n" | ||||
|             "\n" | ||||
|             "[db:history]\n" | ||||
|             "1={\"db-id\":6625633699176220261,\"db-version\":\"9.4\"}\n", | ||||
|             .comment = "put archive info to file - stanza4, repo1"); | ||||
|         HRN_INFO_PUT( | ||||
|             storageTest, TEST_PATH "/repo/" STORAGE_PATH_BACKUP "/stanza4/" INFO_BACKUP_FILE, | ||||
|             "[db]\n" | ||||
|             "db-catalog-version=201409291\n" | ||||
|             "db-control-version=942\n" | ||||
|             "db-id=1\n" | ||||
|             "db-system-id=6625633699176220261\n" | ||||
|             "db-version=\"9.4\"\n" | ||||
|             "\n" | ||||
|             "[db:history]\n" | ||||
|             "1={\"db-catalog-version\":201409291,\"db-control-version\":942,\"db-system-id\":6625633699176220261" | ||||
|             ",\"db-version\":\"9.4\"}\n", | ||||
|             .comment = "put backup info to file - stanza4, repo1"); | ||||
|  | ||||
|         // Write encrypted info files to encrypted repo2 | ||||
|         HRN_INFO_PUT( | ||||
|             storageTest, TEST_PATH "/repo2/" STORAGE_PATH_ARCHIVE "/stanza1/" INFO_ARCHIVE_FILE, | ||||
| @@ -1184,10 +1235,32 @@ testRun(void) | ||||
|             } | ||||
|             HRN_FORK_CHILD_END(); | ||||
|  | ||||
|             HRN_FORK_CHILD_BEGIN() | ||||
|             { | ||||
|                 String *lockFileName = cmdLockFileName(STRDEF("stanza4"), lockTypeRestore, 1); | ||||
|                 lockInit(cfgOptionStr(cfgOptLockPath), STRDEF("999-ffffffff")); | ||||
|                 TEST_RESULT_BOOL(lockAcquireP(lockFileName), true, "create restore lock"); | ||||
|                 TEST_RESULT_VOID( | ||||
|                     lockWriteP( | ||||
|                         lockFileName, .percentComplete = VARUINT(1234), .sizeComplete = VARUINT64(389820), | ||||
|                         .size = VARUINT64(3159000)), | ||||
|                     "write lock data"); | ||||
|  | ||||
|                 // Notify parent that lock has been acquired | ||||
|                 HRN_FORK_CHILD_NOTIFY_PUT(); | ||||
|  | ||||
|                 // Wait for parent to allow release lock | ||||
|                 HRN_FORK_CHILD_NOTIFY_GET(); | ||||
|  | ||||
|                 lockReleaseP(); | ||||
|             } | ||||
|             HRN_FORK_CHILD_END(); | ||||
|  | ||||
|             HRN_FORK_PARENT_BEGIN() | ||||
|             { | ||||
|                 // Wait for child to acquire lock | ||||
|                 HRN_FORK_PARENT_NOTIFY_GET(0); | ||||
|                 HRN_FORK_PARENT_NOTIFY_GET(1); | ||||
|  | ||||
|                 HRN_CFG_LOAD(cfgCmdInfo, argListMultiRepoJson); | ||||
|                 TEST_RESULT_STR_Z( | ||||
| @@ -1465,7 +1538,10 @@ testRun(void) | ||||
|                             "]," | ||||
|                             "\"status\":{" | ||||
|                                 "\"code\":0," | ||||
|                                 "\"lock\":{\"backup\":{\"held\":false}}," | ||||
|                                 "\"lock\":{" | ||||
|                                     "\"backup\":{\"held\":false}," | ||||
|                                     "\"restore\":{\"held\":false}" | ||||
|                                 "}," | ||||
|                                 "\"message\":\"ok\"" | ||||
|                             "}" | ||||
|                         "}," | ||||
| @@ -1512,7 +1588,10 @@ testRun(void) | ||||
|                             "]," | ||||
|                             "\"status\":{" | ||||
|                                 "\"code\":4," | ||||
|                                 "\"lock\":{\"backup\":{\"held\":true,\"size\":3159000,\"size-cplt\":1435765}}," | ||||
|                                 "\"lock\":{" | ||||
|                                     "\"backup\":{\"held\":true,\"size\":3159000,\"size-cplt\":1435765}," | ||||
|                                     "\"restore\":{\"held\":false}" | ||||
|                                 "}," | ||||
|                                 "\"message\":\"different across repos\"" | ||||
|                             "}" | ||||
|                         "}," | ||||
| @@ -1590,13 +1669,67 @@ testRun(void) | ||||
|                             "]," | ||||
|                             "\"status\":{" | ||||
|                                 "\"code\":4," | ||||
|                                 "\"lock\":{\"backup\":{\"held\":false}}," | ||||
|                                 "\"lock\":{" | ||||
|                                     "\"backup\":{\"held\":false}," | ||||
|                                     "\"restore\":{\"held\":false}" | ||||
|                                 "}," | ||||
|                                 "\"message\":\"different across repos\"" | ||||
|                             "}" | ||||
|                         "}," | ||||
|                         "{" | ||||
|                              "\"archive\":[" | ||||
|                                 "{" | ||||
|                                     "\"database\":{" | ||||
|                                         "\"id\":1," | ||||
|                                         "\"repo-key\":1" | ||||
|                                     "}," | ||||
|                                     "\"id\":\"9.4-1\"," | ||||
|                                     "\"max\":null," | ||||
|                                     "\"min\":null" | ||||
|                                 "}" | ||||
|                             "]," | ||||
|                              "\"backup\":[]," | ||||
|                              "\"cipher\":\"mixed\"," | ||||
|                              "\"db\":[" | ||||
|                                 "{" | ||||
|                                     "\"id\":1," | ||||
|                                     "\"repo-key\":1," | ||||
|                                     "\"system-id\":6625633699176220261," | ||||
|                                     "\"version\":\"9.4\"" | ||||
|                                 "}" | ||||
|                             "]," | ||||
|                             "\"name\":\"stanza4\"," | ||||
|                             "\"repo\":[" | ||||
|                                 "{" | ||||
|                                     "\"cipher\":\"none\"," | ||||
|                                     "\"key\":1," | ||||
|                                     "\"status\":{" | ||||
|                                         "\"code\":2," | ||||
|                                         "\"message\":\"no valid backups\"" | ||||
|                                     "}" | ||||
|                                 "}," | ||||
|                                 "{" | ||||
|                                     "\"cipher\":\"aes-256-cbc\"," | ||||
|                                     "\"key\":2," | ||||
|                                     "\"status\":{" | ||||
|                                         "\"code\":1," | ||||
|                                         "\"message\":\"missing stanza path\"" | ||||
|                                     "}" | ||||
|                                 "}" | ||||
|                             "]," | ||||
|                             "\"status\":{" | ||||
|                                 "\"code\":4," | ||||
|                                 "\"lock\":{" | ||||
|                                     "\"backup\":{\"held\":false}," | ||||
|                                     "\"restore\":{\"held\":true,\"size\":3159000,\"size-cplt\":389820}" | ||||
|                                 "}," | ||||
|                                 "\"message\":\"different across repos\"" | ||||
|                             "}" | ||||
|                         "}" | ||||
|                     "]", | ||||
|                     // {uncrustify_on} | ||||
|                     "json - multiple stanzas, some with valid backups, archives in latest DB, backup lock held on one stanza"); | ||||
|                     "json - multiple stanzas, some with valid backups, archives in latest DB, backup and restore lock held on one" | ||||
|                     " stanza"); | ||||
|  | ||||
|                 HRN_CFG_LOAD(cfgCmdInfo, argListMultiRepoJsonProgressOnly); | ||||
|                 TEST_RESULT_STR_Z( | ||||
| @@ -1608,7 +1741,8 @@ testRun(void) | ||||
|                             "\"status\":{" | ||||
|                                 "\"code\":0," | ||||
|                                 "\"lock\":{" | ||||
|                                     "\"backup\":{\"held\":false}" | ||||
|                                     "\"backup\":{\"held\":false}," | ||||
|                                     "\"restore\":{\"held\":false}" | ||||
|                                 "}," | ||||
|                                 "\"message\":\"ok\"" | ||||
|                             "}" | ||||
| @@ -1618,7 +1752,8 @@ testRun(void) | ||||
|                             "\"status\":{" | ||||
|                                 "\"code\":0," | ||||
|                                 "\"lock\":{" | ||||
|                                     "\"backup\":{\"held\":true,\"size\":3159000,\"size-cplt\":1435765}" | ||||
|                                     "\"backup\":{\"held\":true,\"size\":3159000,\"size-cplt\":1435765}," | ||||
|                                     "\"restore\":{\"held\":false}" | ||||
|                                 "}," | ||||
|                                 "\"message\":\"ok\"" | ||||
|                             "}" | ||||
| @@ -1628,17 +1763,31 @@ testRun(void) | ||||
|                             "\"status\":{" | ||||
|                                 "\"code\":0," | ||||
|                                 "\"lock\":{" | ||||
|                                     "\"backup\":{\"held\":false}" | ||||
|                                     "\"backup\":{\"held\":false}," | ||||
|                                     "\"restore\":{\"held\":false}" | ||||
|                                 "}," | ||||
|                                 "\"message\":\"ok\"" | ||||
|                             "}" | ||||
|                         "}," | ||||
|                         "{" | ||||
|                             "\"name\":\"stanza4\"," | ||||
|                             "\"status\":{" | ||||
|                                 "\"code\":0," | ||||
|                                 "\"lock\":{" | ||||
|                                     "\"backup\":{\"held\":false}," | ||||
|                                     "\"restore\":{\"held\":true,\"size\":3159000,\"size-cplt\":389820}" | ||||
|                                 "}," | ||||
|                                 "\"message\":\"ok\"" | ||||
|                             "}" | ||||
|                         "}" | ||||
|                     "]", | ||||
|                     // {uncrustify_on} | ||||
|                     "json (progress only) - multiple stanzas, some with valid backups, archives in latest DB, backup lock held on one stanza"); | ||||
|                     "json (progress only) - multiple stanzas, some with valid backups, archives in latest DB, backup and restore" | ||||
|                     " lock held on one stanza"); | ||||
|  | ||||
|                 // Notify child to release lock | ||||
|                 HRN_FORK_PARENT_NOTIFY_PUT(0); | ||||
|                 HRN_FORK_PARENT_NOTIFY_PUT(1); | ||||
|             } | ||||
|             HRN_FORK_PARENT_END(); | ||||
|         } | ||||
| @@ -1666,6 +1815,22 @@ testRun(void) | ||||
|                         .percentComplete = VARUINT(7500)), | ||||
|                     "write lock data"); | ||||
|  | ||||
|                 String *lockFileStanza1Repo1Restore = cmdLockFileName(STRDEF("stanza1"), lockTypeRestore, 1); | ||||
|                 TEST_RESULT_BOOL(lockAcquireP(lockFileStanza1Repo1Restore), true, "create restore lock"); | ||||
|                 TEST_RESULT_VOID( | ||||
|                     lockWriteP( | ||||
|                         lockFileStanza1Repo1Restore, .size = VARUINT64(3159000), .sizeComplete = VARUINT64(1754830), | ||||
|                         .percentComplete = VARUINT(5555)), | ||||
|                     "write lock data"); | ||||
|  | ||||
|                 String *lockFileStanza1Repo2Restore = cmdLockFileName(STRDEF("stanza1"), lockTypeRestore, 2); | ||||
|                 TEST_RESULT_BOOL(lockAcquireP(lockFileStanza1Repo2Restore), true, "create restore lock"); | ||||
|                 TEST_RESULT_VOID( | ||||
|                     lockWriteP( | ||||
|                         lockFileStanza1Repo2Restore, .size = VARUINT64(3159000), .sizeComplete = VARUINT64(2369250), | ||||
|                         .percentComplete = VARUINT(7500)), | ||||
|                     "write lock data"); | ||||
|  | ||||
|                 String *lockFileStanza2Repo1 = cmdLockFileName(STRDEF("stanza2"), lockTypeBackup, 1); | ||||
|                 TEST_RESULT_BOOL(lockAcquireP(lockFileStanza2Repo1), true, "create backup/expire lock"); | ||||
|                 TEST_RESULT_VOID( | ||||
| @@ -1684,16 +1849,39 @@ testRun(void) | ||||
|             } | ||||
|             HRN_FORK_CHILD_END(); | ||||
|  | ||||
|             HRN_FORK_CHILD_BEGIN() | ||||
|             { | ||||
|                 String *lockFileName = cmdLockFileName(STRDEF("stanza4"), lockTypeRestore, 1); | ||||
|  | ||||
|                 lockInit(cfgOptionStr(cfgOptLockPath), STRDEF("999-ffffffff")); | ||||
|                 TEST_RESULT_BOOL(lockAcquireP(lockFileName), true, "create restore lock"); | ||||
|                 TEST_RESULT_VOID( | ||||
|                     lockWriteP( | ||||
|                         lockFileName, .percentComplete = VARUINT(1234), .sizeComplete = VARUINT64(389820), | ||||
|                         .size = VARUINT64(3159000)), | ||||
|                     "write lock data"); | ||||
|  | ||||
|                 // Notify parent that lock has been acquired | ||||
|                 HRN_FORK_CHILD_NOTIFY_PUT(); | ||||
|  | ||||
|                 // Wait for parent to allow release lock | ||||
|                 HRN_FORK_CHILD_NOTIFY_GET(); | ||||
|  | ||||
|                 lockReleaseP(); | ||||
|             } | ||||
|             HRN_FORK_CHILD_END(); | ||||
|  | ||||
|             HRN_FORK_PARENT_BEGIN() | ||||
|             { | ||||
|                 // Wait for child to acquire lock | ||||
|                 HRN_FORK_PARENT_NOTIFY_GET(0); | ||||
|                 HRN_FORK_PARENT_NOTIFY_GET(1); | ||||
|  | ||||
|                 HRN_CFG_LOAD(cfgCmdInfo, argListMultiRepo); | ||||
|                 TEST_RESULT_STR_Z( | ||||
|                     infoRender(), | ||||
|                     "stanza: stanza1\n" | ||||
|                     "    status: ok (backup/expire running - 65.28% complete)\n" | ||||
|                     "    status: ok (backup/expire running - 65.28% complete, restore running - 65.28% complete)\n" | ||||
|                     "    cipher: mixed\n" | ||||
|                     "        repo1: none\n" | ||||
|                     "        repo2: aes-256-cbc\n" | ||||
| @@ -1770,29 +1958,48 @@ testRun(void) | ||||
|                     "            timestamp start/stop: 2020-11-10 10:00:00+00 / 2020-11-10 10:00:02+00\n" | ||||
|                     "            wal start/stop: 000000010000000000000001 / 000000010000000000000002\n" | ||||
|                     "            database size: 25.7MB, database backup size: 25.7MB\n" | ||||
|                     "            repo2: backup set size: 3MB, backup size: 3KB\n", | ||||
|                     "            repo2: backup set size: 3MB, backup size: 3KB\n" | ||||
|                     "\n" | ||||
|                     "stanza: stanza4\n" | ||||
|                     "    status: mixed (restore running - 12.34% complete)\n" | ||||
|                     "        repo1: error (no valid backups)\n" | ||||
|                     "        repo2: error (missing stanza path)\n" | ||||
|                     "    cipher: mixed\n" | ||||
|                     "        repo1: none\n" | ||||
|                     "        repo2: aes-256-cbc\n" | ||||
|                     "\n" | ||||
|                     "    db (current)\n" | ||||
|                     "        wal archive min/max (9.4): none present\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" | ||||
|                     "    status: ok (backup/expire running - 65.28% complete, restore 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", | ||||
|                     "    status: ok\n" | ||||
|                     "\n" | ||||
|                     "stanza: stanza4\n" | ||||
|                     "    status: ok (restore running - 12.34% complete)\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); | ||||
|                 HRN_FORK_PARENT_NOTIFY_PUT(1); | ||||
|             } | ||||
|             HRN_FORK_PARENT_END(); | ||||
|         } | ||||
|         HRN_FORK_END(); | ||||
|  | ||||
|         // Cleanup | ||||
|         HRN_STORAGE_PATH_REMOVE(storageTest, TEST_PATH "/repo/" STORAGE_PATH_ARCHIVE "/stanza4", .recurse = true); | ||||
|         HRN_STORAGE_PATH_REMOVE(storageTest, TEST_PATH "/repo/" STORAGE_PATH_BACKUP "/stanza4", .recurse = true); | ||||
|  | ||||
|         // ------------------------------------------------------------------------------------------------------------------------- | ||||
|         TEST_TITLE("multi-repo: stanza exists but requested backup does not"); | ||||
|  | ||||
| @@ -1843,7 +2050,10 @@ testRun(void) | ||||
|                     "]," | ||||
|                     "\"status\":{" | ||||
|                         "\"code\":6," | ||||
|                         "\"lock\":{\"backup\":{\"held\":false}}," | ||||
|                         "\"lock\":{" | ||||
|                             "\"backup\":{\"held\":false}," | ||||
|                             "\"restore\":{\"held\":false}" | ||||
|                         "}," | ||||
|                         "\"message\":\"requested backup not found\"" | ||||
|                     "}" | ||||
|                 "}" | ||||
| @@ -1995,7 +2205,10 @@ testRun(void) | ||||
|                     "]," | ||||
|                     "\"status\":{" | ||||
|                         "\"code\":0," | ||||
|                         "\"lock\":{\"backup\":{\"held\":false}}," | ||||
|                         "\"lock\":{" | ||||
|                             "\"backup\":{\"held\":false}," | ||||
|                             "\"restore\":{\"held\":false}" | ||||
|                         "}," | ||||
|                         "\"message\":\"ok\"" | ||||
|                     "}" | ||||
|                 "}" | ||||
| @@ -2210,7 +2423,10 @@ testRun(void) | ||||
|                     "]," | ||||
|                     "\"status\":{" | ||||
|                         "\"code\":0," | ||||
|                         "\"lock\":{\"backup\":{\"held\":false}}," | ||||
|                         "\"lock\":{" | ||||
|                             "\"backup\":{\"held\":false}," | ||||
|                             "\"restore\":{\"held\":false}" | ||||
|                         "}," | ||||
|                         "\"message\":\"ok\"" | ||||
|                     "}" | ||||
|                 "}" | ||||
| @@ -2389,7 +2605,10 @@ testRun(void) | ||||
|                     "]," | ||||
|                     "\"status\":{" | ||||
|                         "\"code\":0," | ||||
|                         "\"lock\":{\"backup\":{\"held\":false}}," | ||||
|                         "\"lock\":{" | ||||
|                             "\"backup\":{\"held\":false}," | ||||
|                             "\"restore\":{\"held\":false}" | ||||
|                         "}," | ||||
|                         "\"message\":\"ok\"" | ||||
|                     "}" | ||||
|                 "}" | ||||
| @@ -2562,7 +2781,10 @@ testRun(void) | ||||
|                     "]," | ||||
|                     "\"status\":{" | ||||
|                         "\"code\":0," | ||||
|                         "\"lock\":{\"backup\":{\"held\":false}}," | ||||
|                         "\"lock\":{" | ||||
|                             "\"backup\":{\"held\":false}," | ||||
|                             "\"restore\":{\"held\":false}" | ||||
|                         "}," | ||||
|                         "\"message\":\"ok\"" | ||||
|                     "}" | ||||
|                 "}" | ||||
| @@ -2668,7 +2890,10 @@ testRun(void) | ||||
|                     "]," | ||||
|                     "\"status\":{" | ||||
|                         "\"code\":4," | ||||
|                         "\"lock\":{\"backup\":{\"held\":false}}," | ||||
|                         "\"lock\":{" | ||||
|                             "\"backup\":{\"held\":false}," | ||||
|                             "\"restore\":{\"held\":false}" | ||||
|                         "}," | ||||
|                         "\"message\":\"different across repos\"" | ||||
|                     "}" | ||||
|                 "}" | ||||
| @@ -3202,7 +3427,10 @@ testRun(void) | ||||
|                     "]," | ||||
|                     "\"status\":{" | ||||
|                         "\"code\":5," | ||||
|                         "\"lock\":{\"backup\":{\"held\":false}}," | ||||
|                         "\"lock\":{" | ||||
|                             "\"backup\":{\"held\":false}," | ||||
|                             "\"restore\":{\"held\":false}" | ||||
|                         "}," | ||||
|                         "\"message\":\"database mismatch across repos\"" | ||||
|                     "}" | ||||
|                 "}" | ||||
| @@ -3632,7 +3860,10 @@ testRun(void) | ||||
|                     "]," | ||||
|                     "\"status\":{" | ||||
|                         "\"code\":99," | ||||
|                         "\"lock\":{\"backup\":{\"held\":false}}," | ||||
|                         "\"lock\":{" | ||||
|                             "\"backup\":{\"held\":false}," | ||||
|                             "\"restore\":{\"held\":false}" | ||||
|                         "}," | ||||
|                         "\"message\":\"other\"" | ||||
|                     "}" | ||||
|                 "}" | ||||
| @@ -3652,7 +3883,8 @@ testRun(void) | ||||
|                     "\"status\":{" | ||||
|                         "\"code\":99," | ||||
|                         "\"lock\":{" | ||||
|                             "\"backup\":{\"held\":false}" | ||||
|                             "\"backup\":{\"held\":false}," | ||||
|                             "\"restore\":{\"held\":false}" | ||||
|                         "}," | ||||
|                         "\"message\":\"other\"" | ||||
|                     "}" | ||||
|   | ||||
| @@ -76,7 +76,9 @@ testRun(void) | ||||
|  | ||||
|         TEST_RESULT_VOID(cmdLockAcquireP(), "acquire stanza-create lock"); | ||||
|         TEST_STORAGE_LIST( | ||||
|             storageTest, NULL, "test-archive-1.lock\ntest-archive-2.lock\ntest-backup-1.lock\ntest-backup-2.lock\n", | ||||
|             storageTest, NULL, | ||||
|             "test-archive-1.lock\ntest-archive-2.lock\ntest-backup-1.lock\ntest-backup-2.lock\ntest-restore-1.lock\n" | ||||
|             "test-restore-2.lock\n", | ||||
|             .comment = "check lock files"); | ||||
|  | ||||
|         // ------------------------------------------------------------------------------------------------------------------------- | ||||
|   | ||||
| @@ -19,6 +19,7 @@ Test Restore Command | ||||
| #include "common/harnessManifest.h" | ||||
| #include "common/harnessPostgres.h" | ||||
| #include "common/harnessProtocol.h" | ||||
| #include "common/harnessRestore.h" | ||||
| #include "common/harnessStorage.h" | ||||
| #include "common/harnessStorageHelper.h" | ||||
| #include "common/harnessTime.h" | ||||
| @@ -2152,6 +2153,51 @@ testRun(void) | ||||
|             "HINT: was the target timeline created by promoting from a timeline < latest?"); | ||||
|     } | ||||
|  | ||||
|     // ***************************************************************************************************************************** | ||||
|     if (testBegin("restoreJobResult()")) | ||||
|     { | ||||
|         // Set log level to detail | ||||
|         harnessLogLevelSet(logLevelDetail); | ||||
|  | ||||
|         // ------------------------------------------------------------------------------------------------------------------------- | ||||
|         TEST_TITLE("report host/100% progress on noop result"); | ||||
|  | ||||
|         // Create job that skips file | ||||
|         ProtocolParallelJob *job = protocolParallelJobNew(VARSTRDEF("pg_data/test"), strIdFromZ("x"), NULL); | ||||
|  | ||||
|         PackWrite *const resultPack = protocolPackNew(); | ||||
|         pckWriteStrP(resultPack, STRDEF("pg_data/test")); | ||||
|         pckWriteU32P(resultPack, restoreResultZero); | ||||
|         // No more fields need to be written since noop will ignore them anyway | ||||
|         pckWriteEndP(resultPack); | ||||
|  | ||||
|         protocolParallelJobResultSet(job, pckReadNew(pckWriteResult(resultPack))); | ||||
|  | ||||
|         // Create manifest with file | ||||
|         Manifest *manifest = NULL; | ||||
|  | ||||
|         OBJ_NEW_BASE_BEGIN(Manifest, .childQty = MEM_CONTEXT_QTY_MAX) | ||||
|         { | ||||
|             manifest = manifestNewInternal(); | ||||
|  | ||||
|             HRN_MANIFEST_TARGET_ADD(manifest, .name = MANIFEST_TARGET_PGDATA, .path = "pg_data"); | ||||
|             HRN_MANIFEST_FILE_ADD(manifest, .name = "pg_data/test"); | ||||
|         } | ||||
|         OBJ_NEW_END(); | ||||
|  | ||||
|         unsigned int currentPercentComplete = 4567; | ||||
|  | ||||
|         lockInit(TEST_PATH_STR, cfgOptionStr(cfgOptExecId)); | ||||
|         TEST_RESULT_VOID(cmdLockAcquireP(), "acquire restore lock"); | ||||
|  | ||||
|         TEST_RESULT_UINT( | ||||
|             restoreJobResult(manifest, job, NULL, 0, 0, | ||||
|                              ¤tPercentComplete), 0, "log noop result"); | ||||
|         TEST_RESULT_VOID(cmdLockReleaseP(), "release restore lock"); | ||||
|  | ||||
|         TEST_RESULT_LOG("P00 DETAIL: restore file pg_data/test (0B, 100.00%)"); | ||||
|     } | ||||
|  | ||||
|     // ***************************************************************************************************************************** | ||||
|     if (testBegin("cmdRestore()")) | ||||
|     { | ||||
| @@ -2182,7 +2228,7 @@ testRun(void) | ||||
|         hrnCfgArgRawZ(argList, cfgOptPgHost, "pg1"); | ||||
|         HRN_CFG_LOAD(cfgCmdRestore, argList); | ||||
|  | ||||
|         TEST_ERROR(cmdRestore(), HostInvalidError, "restore command must be run on the PostgreSQL host"); | ||||
|         TEST_ERROR(hrnCmdRestore(), HostInvalidError, "restore command must be run on the PostgreSQL host"); | ||||
|  | ||||
|         // ------------------------------------------------------------------------------------------------------------------------- | ||||
|         TEST_TITLE("full restore without delta, multi-repo"); | ||||
| @@ -2279,7 +2325,7 @@ testRun(void) | ||||
|         infoArchiveSaveFile( | ||||
|             infoArchive, storageRepoIdxWrite(1), INFO_ARCHIVE_PATH_FILE_STR, cipherTypeAes256Cbc, STRDEF(TEST_CIPHER_PASS)); | ||||
|  | ||||
|         TEST_RESULT_VOID(cmdRestore(), "successful restore"); | ||||
|         TEST_RESULT_VOID(hrnCmdRestore(), "successful restore"); | ||||
|  | ||||
|         TEST_RESULT_LOG( | ||||
|             zNewFmt( | ||||
| @@ -2437,7 +2483,7 @@ testRun(void) | ||||
|         #undef TEST_PGDATA | ||||
|         #undef TEST_REPO_PATH | ||||
|  | ||||
|         cmdRestore(); | ||||
|         hrnCmdRestore(); | ||||
|  | ||||
|         TEST_RESULT_LOG( | ||||
|             "P00   INFO: repo1: restore backup set 20161219-212741F, recovery will start at 2016-12-19 21:27:40\n" | ||||
| @@ -2514,7 +2560,7 @@ testRun(void) | ||||
|         hrnCfgArgRawBool(argList, cfgOptForce, true); | ||||
|         HRN_CFG_LOAD(cfgCmdRestore, argList); | ||||
|  | ||||
|         cmdRestore(); | ||||
|         hrnCmdRestore(); | ||||
|  | ||||
|         TEST_RESULT_LOG( | ||||
|             "P00   INFO: repo1: restore backup set 20161219-212741F, recovery will start at 2016-12-19 21:27:40\n" | ||||
| @@ -2908,7 +2954,7 @@ testRun(void) | ||||
|         // Add a few bogus links to be deleted | ||||
|         THROW_ON_SYS_ERROR(symlink("../wal", zNewFmt("%s/pg_wal2", strZ(pgPath))) == -1, FileOpenError, "unable to create symlink"); | ||||
|  | ||||
|         TEST_RESULT_VOID(cmdRestore(), "successful restore"); | ||||
|         TEST_RESULT_VOID(hrnCmdRestore(), "successful restore"); | ||||
|  | ||||
|         TEST_RESULT_LOG_FMT( | ||||
|             "P00   INFO: repo1: restore backup set 20161219-212741F_20161219-212918I\n" | ||||
| @@ -3102,7 +3148,7 @@ testRun(void) | ||||
|             storageWriteIo( | ||||
|                 storageNewWriteP(storageRepoWrite(), STRDEF(STORAGE_REPO_BACKUP "/" TEST_LABEL "/" BACKUP_MANIFEST_FILE)))); | ||||
|  | ||||
|         TEST_RESULT_VOID(cmdRestore(), "successful restore"); | ||||
|         TEST_RESULT_VOID(hrnCmdRestore(), "successful restore"); | ||||
|  | ||||
|         TEST_RESULT_LOG_FMT( | ||||
|             "P00   INFO: repo2: restore backup set 20161219-212741F_20161219-212918I, recovery will start at [TIME]\n" | ||||
| @@ -3199,7 +3245,7 @@ testRun(void) | ||||
|         harnessLogLevelSet(logLevelWarn); | ||||
|  | ||||
|         TEST_ERROR( | ||||
|             cmdRestore(), FileMissingError, | ||||
|             hrnCmdRestore(), FileMissingError, | ||||
|             "raised from local-1 shim protocol: unable to open missing file" | ||||
|             " '" TEST_PATH "/repo/backup/test1/20161219-212741F_20161219-212918I/pg_data/global/pg_control' for read\n" | ||||
|             "[RETRY DETAIL OMITTED]"); | ||||
| @@ -3314,7 +3360,7 @@ testRun(void) | ||||
|         hrnCfgEnvKeyRawZ(cfgOptRepoCipherPass, 2, TEST_CIPHER_PASS); | ||||
|         HRN_CFG_LOAD(cfgCmdRestore, argList); | ||||
|  | ||||
|         TEST_RESULT_VOID(cmdRestore(), "restore"); | ||||
|         TEST_RESULT_VOID(hrnCmdRestore(), "restore"); | ||||
|  | ||||
|         TEST_STORAGE_LIST( | ||||
|             storagePg(), NULL, | ||||
| @@ -3354,7 +3400,7 @@ testRun(void) | ||||
|         HRN_STORAGE_PUT(storageRepoWrite(), strZ(strNewFmt(STORAGE_REPO_BACKUP "/%s/bundle/1", strZ(backupFull))), NULL); | ||||
|  | ||||
|         // Make sure restore fails with the invalid file | ||||
|         TEST_ERROR(cmdRestore(), CryptoError, "raised from local-1 shim protocol: cipher header missing"); | ||||
|         TEST_ERROR(hrnCmdRestore(), CryptoError, "raised from local-1 shim protocol: cipher header missing"); | ||||
|  | ||||
|         // Use detail log level to catch block incremental restore message | ||||
|         harnessLogLevelSet(logLevelDetail); | ||||
| @@ -3377,7 +3423,7 @@ testRun(void) | ||||
|         hrnCfgArgRaw(argList, cfgOptRepoTargetTime, targetTime); | ||||
|         HRN_CFG_LOAD(cfgCmdRestore, argList); | ||||
|  | ||||
|         TEST_RESULT_VOID(cmdRestore(), "restore"); | ||||
|         TEST_RESULT_VOID(hrnCmdRestore(), "restore"); | ||||
|  | ||||
|         TEST_STORAGE_LIST( | ||||
|             storagePg(), NULL, | ||||
|   | ||||
| @@ -2135,7 +2135,8 @@ testRun(void) | ||||
|         TEST_RESULT_STR_Z(varStr(kvGet(recoveryKv, VARSTRDEF("a"))), "b", "check recovery option"); | ||||
|         TEST_ASSIGN(recoveryKv, varKv(cfgOptionIdxVar(cfgOptRecoveryOption, 0)), "get recovery options"); | ||||
|         TEST_RESULT_STR_Z(varStr(kvGet(recoveryKv, VARSTRDEF("c"))), "de=fg hi", "check recovery option"); | ||||
|         TEST_RESULT_BOOL(cfgLockRequired(), false, "restore command does not require lock"); | ||||
|         TEST_RESULT_BOOL(cfgLockRequired(), true, "restore command requires lock"); | ||||
|         TEST_RESULT_UINT(cfgLockType(), lockTypeRestore, "restore command requires restore lock type"); | ||||
|  | ||||
|         // ------------------------------------------------------------------------------------------------------------------------- | ||||
|         TEST_TITLE("recovery options, config file"); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user