diff --git a/doc/xml/user-guide.xml b/doc/xml/user-guide.xml
index 8de696abc..59437035f 100644
--- a/doc/xml/user-guide.xml
+++ b/doc/xml/user-guide.xml
@@ -1539,7 +1539,7 @@
Although automatically removes archived WAL segments when expiring backups (the default expires WAL for full backups based on the repo1-retention-full option), it may be useful to expire archive more aggressively to save disk space. Note that full backups are treated as differential backups for the purpose of differential archive retention.
- Expiring archive will never remove WAL segments that are required to make a backup consistent. However, since Point-in-Time-Recovery (PITR) only works on a continuous WAL stream, care should be taken when aggressively expiring archive outside of the normal backup expiration process.
+ Expiring archive will never remove WAL segments that are required to make a backup consistent. However, since Point-in-Time-Recovery (PITR) only works on a continuous WAL stream, care should be taken when aggressively expiring archive outside of the normal backup expiration process. To determine what will be expired without actually expiring anything, the dry-run option can be provided on the command line with the expire command.
Configure repo1-retention-diff
diff --git a/src/command/expire/expire.c b/src/command/expire/expire.c
index bfd44ba72..aa13dc298 100644
--- a/src/command/expire/expire.c
+++ b/src/command/expire/expire.c
@@ -59,9 +59,14 @@ expireBackup(InfoBackup *infoBackup, String *removeBackupLabel, String *backupEx
ASSERT(removeBackupLabel != NULL);
ASSERT(backupExpired != NULL);
- storageRemoveP(storageRepoWrite(), strNewFmt(STORAGE_REPO_BACKUP "/%s/" BACKUP_MANIFEST_FILE, strPtr(removeBackupLabel)));
- storageRemoveP(
- storageRepoWrite(), strNewFmt(STORAGE_REPO_BACKUP "/%s/" BACKUP_MANIFEST_FILE INFO_COPY_EXT, strPtr(removeBackupLabel)));
+ // Execute the real expiration and deletion only if the dry-run option is disabled
+ if (!cfgOptionValid(cfgOptDryRun) || !cfgOptionBool(cfgOptDryRun))
+ {
+ storageRemoveP(storageRepoWrite(), strNewFmt(STORAGE_REPO_BACKUP "/%s/" BACKUP_MANIFEST_FILE, strPtr(removeBackupLabel)));
+ storageRemoveP(
+ storageRepoWrite(),
+ strNewFmt(STORAGE_REPO_BACKUP "/%s/" BACKUP_MANIFEST_FILE INFO_COPY_EXT, strPtr(removeBackupLabel)));
+ }
// Remove the backup from the info object
infoBackupDataDelete(infoBackup, removeBackupLabel);
@@ -359,8 +364,12 @@ removeExpiredArchive(InfoBackup *infoBackup)
{
String *fullPath = storagePathP(
storageRepo(), strNewFmt(STORAGE_REPO_ARCHIVE "/%s", strPtr(archiveId)));
- storagePathRemoveP(storageRepoWrite(), fullPath, .recurse = true);
+
LOG_INFO_FMT("remove archive path: %s", strPtr(fullPath));
+
+ // Execute the real expiration and deletion only if the dry-run option is disabled
+ if (!cfgOptionValid(cfgOptDryRun) || !cfgOptionBool(cfgOptDryRun))
+ storagePathRemoveP(storageRepoWrite(), fullPath, .recurse = true);
}
// Continue to next directory
@@ -503,9 +512,14 @@ removeExpiredArchive(InfoBackup *infoBackup)
// Remove the entire directory if all archive is expired
if (removeArchive)
{
- storagePathRemoveP(
- storageRepoWrite(), strNewFmt(STORAGE_REPO_ARCHIVE "/%s/%s", strPtr(archiveId),
- strPtr(walPath)), .recurse = true);
+ // Execute the real expiration and deletion only if the dry-run mode is disabled
+ if (!cfgOptionValid(cfgOptDryRun) || !cfgOptionBool(cfgOptDryRun))
+ {
+ storagePathRemoveP(
+ storageRepoWrite(),
+ strNewFmt(STORAGE_REPO_ARCHIVE "/%s/%s", strPtr(archiveId), strPtr(walPath)),
+ .recurse = true);
+ }
archiveExpire.total++;
archiveExpire.start = strDup(walPath);
@@ -547,10 +561,15 @@ removeExpiredArchive(InfoBackup *infoBackup)
// Remove archive log if it is not used in a backup
if (removeArchive)
{
- storageRemoveP(
- storageRepoWrite(),
- strNewFmt(STORAGE_REPO_ARCHIVE "/%s/%s/%s",
- strPtr(archiveId), strPtr(walPath), strPtr(walSubPath)));
+ // Execute the real expiration and deletion only if the dry-run mode is disabled
+ if (!cfgOptionValid(cfgOptDryRun) || !cfgOptionBool(cfgOptDryRun))
+ {
+ storageRemoveP(
+ storageRepoWrite(),
+ strNewFmt(
+ STORAGE_REPO_ARCHIVE "/%s/%s/%s", strPtr(archiveId), strPtr(walPath),
+ strPtr(walSubPath)));
+ }
// Track that this archive was removed
archiveExpire.total++;
@@ -610,9 +629,13 @@ removeExpiredBackup(InfoBackup *infoBackup)
{
LOG_INFO_FMT("remove expired backup %s", strPtr(strLstGet(backupList, backupIdx)));
- storagePathRemoveP(
- storageRepoWrite(), strNewFmt(STORAGE_REPO_BACKUP "/%s", strPtr(strLstGet(backupList, backupIdx))),
- .recurse = true);
+ // Execute the real expiration and deletion only if the dry-run mode is disabled
+ if (!cfgOptionValid(cfgOptDryRun) || !cfgOptionBool(cfgOptDryRun))
+ {
+ storagePathRemoveP(
+ storageRepoWrite(), strNewFmt(STORAGE_REPO_BACKUP "/%s", strPtr(strLstGet(backupList, backupIdx))),
+ .recurse = true);
+ }
}
}
@@ -640,9 +663,13 @@ cmdExpire(void)
expireFullBackup(infoBackup);
expireDiffBackup(infoBackup);
- infoBackupSaveFile(
- infoBackup, storageRepoWrite(), INFO_BACKUP_PATH_FILE_STR, cipherType(cfgOptionStr(cfgOptRepoCipherType)),
- cfgOptionStr(cfgOptRepoCipherPass));
+ // Store the new backup info only if the dry-run mode is disabled
+ if (!cfgOptionValid(cfgOptDryRun) || !cfgOptionBool(cfgOptDryRun))
+ {
+ infoBackupSaveFile(
+ infoBackup, storageRepoWrite(), INFO_BACKUP_PATH_FILE_STR, cipherType(cfgOptionStr(cfgOptRepoCipherType)),
+ cfgOptionStr(cfgOptRepoCipherPass));
+ }
removeExpiredBackup(infoBackup);
removeExpiredArchive(infoBackup);
diff --git a/src/config/config.auto.c b/src/config/config.auto.c
index 84ea40710..842789f54 100644
--- a/src/config/config.auto.c
+++ b/src/config/config.auto.c
@@ -304,6 +304,7 @@ STRING_EXTERN(CFGOPT_CONFIG_PATH_STR, CFGOPT_CONFI
STRING_EXTERN(CFGOPT_DB_INCLUDE_STR, CFGOPT_DB_INCLUDE);
STRING_EXTERN(CFGOPT_DB_TIMEOUT_STR, CFGOPT_DB_TIMEOUT);
STRING_EXTERN(CFGOPT_DELTA_STR, CFGOPT_DELTA);
+STRING_EXTERN(CFGOPT_DRY_RUN_STR, CFGOPT_DRY_RUN);
STRING_EXTERN(CFGOPT_EXCLUDE_STR, CFGOPT_EXCLUDE);
STRING_EXTERN(CFGOPT_FILTER_STR, CFGOPT_FILTER);
STRING_EXTERN(CFGOPT_FORCE_STR, CFGOPT_FORCE);
@@ -633,6 +634,14 @@ static ConfigOptionData configOptionData[CFG_OPTION_TOTAL] = CONFIG_OPTION_LIST
CONFIG_OPTION_DEFINE_ID(cfgDefOptDelta)
)
+ //------------------------------------------------------------------------------------------------------------------------------
+ CONFIG_OPTION
+ (
+ CONFIG_OPTION_NAME(CFGOPT_DRY_RUN)
+ CONFIG_OPTION_INDEX(0)
+ CONFIG_OPTION_DEFINE_ID(cfgDefOptDryRun)
+ )
+
//------------------------------------------------------------------------------------------------------------------------------
CONFIG_OPTION
(
diff --git a/src/config/config.auto.h b/src/config/config.auto.h
index 768e4648d..52e2d0c28 100644
--- a/src/config/config.auto.h
+++ b/src/config/config.auto.h
@@ -95,6 +95,8 @@ Option constants
STRING_DECLARE(CFGOPT_DB_TIMEOUT_STR);
#define CFGOPT_DELTA "delta"
STRING_DECLARE(CFGOPT_DELTA_STR);
+#define CFGOPT_DRY_RUN "dry-run"
+ STRING_DECLARE(CFGOPT_DRY_RUN_STR);
#define CFGOPT_EXCLUDE "exclude"
STRING_DECLARE(CFGOPT_EXCLUDE_STR);
#define CFGOPT_FILTER "filter"
@@ -406,7 +408,7 @@ Option constants
#define CFGOPT_TYPE "type"
STRING_DECLARE(CFGOPT_TYPE_STR);
-#define CFG_OPTION_TOTAL 176
+#define CFG_OPTION_TOTAL 177
/***********************************************************************************************************************************
Command enum
@@ -461,6 +463,7 @@ typedef enum
cfgOptDbInclude,
cfgOptDbTimeout,
cfgOptDelta,
+ cfgOptDryRun,
cfgOptExclude,
cfgOptFilter,
cfgOptForce,
diff --git a/src/config/define.auto.c b/src/config/define.auto.c
index 28aa03ae6..f31bfb700 100644
--- a/src/config/define.auto.c
+++ b/src/config/define.auto.c
@@ -1108,6 +1108,37 @@ static ConfigDefineOptionData configDefineOptionData[] = CFGDEFDATA_OPTION_LIST
)
)
+ // -----------------------------------------------------------------------------------------------------------------------------
+ CFGDEFDATA_OPTION
+ (
+ CFGDEFDATA_OPTION_NAME("dry-run")
+ CFGDEFDATA_OPTION_REQUIRED(true)
+ CFGDEFDATA_OPTION_SECTION(cfgDefSectionCommandLine)
+ CFGDEFDATA_OPTION_TYPE(cfgDefOptTypeBoolean)
+ CFGDEFDATA_OPTION_INTERNAL(false)
+
+ CFGDEFDATA_OPTION_INDEX_TOTAL(1)
+ CFGDEFDATA_OPTION_SECURE(false)
+
+ CFGDEFDATA_OPTION_HELP_SECTION("general")
+ CFGDEFDATA_OPTION_HELP_SUMMARY("Execute a dry-run for the command.")
+ CFGDEFDATA_OPTION_HELP_DESCRIPTION
+ (
+ "The --dry-run option is a command-line only option and can be passed when it is desireable to determine what "
+ "modifications will be made by the command without the command actually making any modifications."
+ )
+
+ CFGDEFDATA_OPTION_COMMAND_LIST
+ (
+ CFGDEFDATA_OPTION_COMMAND(cfgDefCmdExpire)
+ )
+
+ CFGDEFDATA_OPTION_OPTIONAL_LIST
+ (
+ CFGDEFDATA_OPTION_OPTIONAL_DEFAULT("0")
+ )
+ )
+
// -----------------------------------------------------------------------------------------------------------------------------
CFGDEFDATA_OPTION
(
diff --git a/src/config/define.auto.h b/src/config/define.auto.h
index e0f83f400..46fc9411f 100644
--- a/src/config/define.auto.h
+++ b/src/config/define.auto.h
@@ -73,6 +73,7 @@ typedef enum
cfgDefOptDbInclude,
cfgDefOptDbTimeout,
cfgDefOptDelta,
+ cfgDefOptDryRun,
cfgDefOptExclude,
cfgDefOptFilter,
cfgDefOptForce,
diff --git a/src/config/parse.auto.c b/src/config/parse.auto.c
index 4d8bcb7fb..7855aca91 100644
--- a/src/config/parse.auto.c
+++ b/src/config/parse.auto.c
@@ -279,6 +279,13 @@ static const struct option optionList[] =
.val = PARSE_OPTION_FLAG | PARSE_RESET_FLAG | cfgOptDelta,
},
+ // dry-run option
+ // -----------------------------------------------------------------------------------------------------------------------------
+ {
+ .name = CFGOPT_DRY_RUN,
+ .val = PARSE_OPTION_FLAG | cfgOptDryRun,
+ },
+
// exclude option
// -----------------------------------------------------------------------------------------------------------------------------
{
@@ -2380,6 +2387,7 @@ static const ConfigOption optionResolveOrder[] =
cfgOptDbInclude,
cfgOptDbTimeout,
cfgOptDelta,
+ cfgOptDryRun,
cfgOptExclude,
cfgOptFilter,
cfgOptHostId,
diff --git a/test/lib/pgBackRestTest/LibCAuto.pm b/test/lib/pgBackRestTest/LibCAuto.pm
new file mode 100644
index 000000000..e79e54bd2
--- /dev/null
+++ b/test/lib/pgBackRestTest/LibCAuto.pm
@@ -0,0 +1,354 @@
+####################################################################################################################################
+# Automatically generated by Build.pm -- do not modify directly.
+####################################################################################################################################
+package pgBackRest::LibCAuto;
+
+use strict;
+use warnings;
+
+# Configuration option value constants
+sub libcAutoConstant
+{
+ return
+ {
+ CFGOPTVAL_INFO_OUTPUT_TEXT => 'text',
+ CFGOPTVAL_INFO_OUTPUT_JSON => 'json',
+
+ CFGOPTVAL_LS_OUTPUT_TEXT => 'text',
+ CFGOPTVAL_LS_OUTPUT_JSON => 'json',
+
+ CFGOPTVAL_REMOTE_TYPE_PG => 'pg',
+ CFGOPTVAL_REMOTE_TYPE_REPO => 'repo',
+
+ CFGOPTVAL_REPO_CIPHER_TYPE_NONE => 'none',
+ CFGOPTVAL_REPO_CIPHER_TYPE_AES_256_CBC => 'aes-256-cbc',
+
+ CFGOPTVAL_REPO_RETENTION_ARCHIVE_TYPE_FULL => 'full',
+ CFGOPTVAL_REPO_RETENTION_ARCHIVE_TYPE_DIFF => 'diff',
+ CFGOPTVAL_REPO_RETENTION_ARCHIVE_TYPE_INCR => 'incr',
+
+ CFGOPTVAL_REPO_S3_URI_STYLE_HOST => 'host',
+ CFGOPTVAL_REPO_S3_URI_STYLE_PATH => 'path',
+
+ CFGOPTVAL_REPO_TYPE_CIFS => 'cifs',
+ CFGOPTVAL_REPO_TYPE_POSIX => 'posix',
+ CFGOPTVAL_REPO_TYPE_S3 => 's3',
+
+ CFGOPTVAL_SORT_NONE => 'none',
+ CFGOPTVAL_SORT_ASC => 'asc',
+ CFGOPTVAL_SORT_DESC => 'desc',
+
+ CFGOPTVAL_RESTORE_TARGET_ACTION_PAUSE => 'pause',
+ CFGOPTVAL_RESTORE_TARGET_ACTION_PROMOTE => 'promote',
+ CFGOPTVAL_RESTORE_TARGET_ACTION_SHUTDOWN => 'shutdown',
+
+ CFGOPTVAL_BACKUP_TYPE_FULL => 'full',
+ CFGOPTVAL_BACKUP_TYPE_DIFF => 'diff',
+ CFGOPTVAL_BACKUP_TYPE_INCR => 'incr',
+
+ CFGOPTVAL_RESTORE_TYPE_NAME => 'name',
+ CFGOPTVAL_RESTORE_TYPE_TIME => 'time',
+ CFGOPTVAL_RESTORE_TYPE_XID => 'xid',
+ CFGOPTVAL_RESTORE_TYPE_PRESERVE => 'preserve',
+ CFGOPTVAL_RESTORE_TYPE_NONE => 'none',
+ CFGOPTVAL_RESTORE_TYPE_IMMEDIATE => 'immediate',
+ CFGOPTVAL_RESTORE_TYPE_DEFAULT => 'default',
+ CFGOPTVAL_RESTORE_TYPE_STANDBY => 'standby',
+
+ CFGDEF_TYPE_BOOLEAN => 0,
+ CFGDEF_TYPE_FLOAT => 1,
+ CFGDEF_TYPE_HASH => 2,
+ CFGDEF_TYPE_INTEGER => 3,
+ CFGDEF_TYPE_LIST => 4,
+ CFGDEF_TYPE_PATH => 5,
+ CFGDEF_TYPE_SIZE => 6,
+ CFGDEF_TYPE_STRING => 7,
+
+ ENCODE_TYPE_BASE64 => 0,
+
+ CIPHER_MODE_ENCRYPT => 0,
+ CIPHER_MODE_DECRYPT => 1,
+ }
+}
+
+# Export function and constants
+sub libcAutoExportTag
+{
+ return
+ {
+ checksum =>
+ [
+ 'pageChecksum',
+ ],
+
+ config =>
+ [
+ 'CFGOPTVAL_INFO_OUTPUT_TEXT',
+ 'CFGOPTVAL_INFO_OUTPUT_JSON',
+ 'CFGOPTVAL_LS_OUTPUT_TEXT',
+ 'CFGOPTVAL_LS_OUTPUT_JSON',
+ 'CFGOPTVAL_REMOTE_TYPE_PG',
+ 'CFGOPTVAL_REMOTE_TYPE_REPO',
+ 'CFGOPTVAL_REPO_CIPHER_TYPE_NONE',
+ 'CFGOPTVAL_REPO_CIPHER_TYPE_AES_256_CBC',
+ 'CFGOPTVAL_REPO_RETENTION_ARCHIVE_TYPE_FULL',
+ 'CFGOPTVAL_REPO_RETENTION_ARCHIVE_TYPE_DIFF',
+ 'CFGOPTVAL_REPO_RETENTION_ARCHIVE_TYPE_INCR',
+ 'CFGOPTVAL_REPO_S3_URI_STYLE_HOST',
+ 'CFGOPTVAL_REPO_S3_URI_STYLE_PATH',
+ 'CFGOPTVAL_REPO_TYPE_CIFS',
+ 'CFGOPTVAL_REPO_TYPE_POSIX',
+ 'CFGOPTVAL_REPO_TYPE_S3',
+ 'CFGOPTVAL_SORT_NONE',
+ 'CFGOPTVAL_SORT_ASC',
+ 'CFGOPTVAL_SORT_DESC',
+ 'CFGOPTVAL_RESTORE_TARGET_ACTION_PAUSE',
+ 'CFGOPTVAL_RESTORE_TARGET_ACTION_PROMOTE',
+ 'CFGOPTVAL_RESTORE_TARGET_ACTION_SHUTDOWN',
+ 'CFGOPTVAL_BACKUP_TYPE_FULL',
+ 'CFGOPTVAL_BACKUP_TYPE_DIFF',
+ 'CFGOPTVAL_BACKUP_TYPE_INCR',
+ 'CFGOPTVAL_RESTORE_TYPE_NAME',
+ 'CFGOPTVAL_RESTORE_TYPE_TIME',
+ 'CFGOPTVAL_RESTORE_TYPE_XID',
+ 'CFGOPTVAL_RESTORE_TYPE_PRESERVE',
+ 'CFGOPTVAL_RESTORE_TYPE_NONE',
+ 'CFGOPTVAL_RESTORE_TYPE_IMMEDIATE',
+ 'CFGOPTVAL_RESTORE_TYPE_DEFAULT',
+ 'CFGOPTVAL_RESTORE_TYPE_STANDBY',
+ 'CFGCMD_ARCHIVE_GET',
+ 'CFGCMD_ARCHIVE_PUSH',
+ 'CFGCMD_BACKUP',
+ 'CFGCMD_CHECK',
+ 'CFGCMD_EXPIRE',
+ 'CFGCMD_HELP',
+ 'CFGCMD_INFO',
+ 'CFGCMD_LS',
+ 'CFGCMD_RESTORE',
+ 'CFGCMD_STANZA_CREATE',
+ 'CFGCMD_STANZA_DELETE',
+ 'CFGCMD_STANZA_UPGRADE',
+ 'CFGCMD_START',
+ 'CFGCMD_STOP',
+ 'CFGCMD_VERSION',
+ 'CFGOPT_ARCHIVE_ASYNC',
+ 'CFGOPT_ARCHIVE_CHECK',
+ 'CFGOPT_ARCHIVE_COPY',
+ 'CFGOPT_ARCHIVE_GET_QUEUE_MAX',
+ 'CFGOPT_ARCHIVE_PUSH_QUEUE_MAX',
+ 'CFGOPT_ARCHIVE_TIMEOUT',
+ 'CFGOPT_BACKUP_STANDBY',
+ 'CFGOPT_BUFFER_SIZE',
+ 'CFGOPT_CHECKSUM_PAGE',
+ 'CFGOPT_CMD_SSH',
+ 'CFGOPT_COMPRESS',
+ 'CFGOPT_COMPRESS_LEVEL',
+ 'CFGOPT_COMPRESS_LEVEL_NETWORK',
+ 'CFGOPT_CONFIG',
+ 'CFGOPT_CONFIG_INCLUDE_PATH',
+ 'CFGOPT_CONFIG_PATH',
+ 'CFGOPT_DB_INCLUDE',
+ 'CFGOPT_DB_TIMEOUT',
+ 'CFGOPT_DELTA',
+ 'CFGOPT_DRY_RUN',
+ 'CFGOPT_EXCLUDE',
+ 'CFGOPT_FILTER',
+ 'CFGOPT_FORCE',
+ 'CFGOPT_HOST_ID',
+ 'CFGOPT_LINK_ALL',
+ 'CFGOPT_LINK_MAP',
+ 'CFGOPT_LOCK_PATH',
+ 'CFGOPT_LOG_LEVEL_CONSOLE',
+ 'CFGOPT_LOG_LEVEL_FILE',
+ 'CFGOPT_LOG_LEVEL_STDERR',
+ 'CFGOPT_LOG_PATH',
+ 'CFGOPT_LOG_SUBPROCESS',
+ 'CFGOPT_LOG_TIMESTAMP',
+ 'CFGOPT_MANIFEST_SAVE_THRESHOLD',
+ 'CFGOPT_NEUTRAL_UMASK',
+ 'CFGOPT_ONLINE',
+ 'CFGOPT_OUTPUT',
+ 'CFGOPT_PG_HOST',
+ 'CFGOPT_PG_HOST2',
+ 'CFGOPT_PG_HOST3',
+ 'CFGOPT_PG_HOST4',
+ 'CFGOPT_PG_HOST5',
+ 'CFGOPT_PG_HOST6',
+ 'CFGOPT_PG_HOST7',
+ 'CFGOPT_PG_HOST8',
+ 'CFGOPT_PG_HOST_CMD',
+ 'CFGOPT_PG_HOST_CMD2',
+ 'CFGOPT_PG_HOST_CMD3',
+ 'CFGOPT_PG_HOST_CMD4',
+ 'CFGOPT_PG_HOST_CMD5',
+ 'CFGOPT_PG_HOST_CMD6',
+ 'CFGOPT_PG_HOST_CMD7',
+ 'CFGOPT_PG_HOST_CMD8',
+ 'CFGOPT_PG_HOST_CONFIG',
+ 'CFGOPT_PG_HOST_CONFIG2',
+ 'CFGOPT_PG_HOST_CONFIG3',
+ 'CFGOPT_PG_HOST_CONFIG4',
+ 'CFGOPT_PG_HOST_CONFIG5',
+ 'CFGOPT_PG_HOST_CONFIG6',
+ 'CFGOPT_PG_HOST_CONFIG7',
+ 'CFGOPT_PG_HOST_CONFIG8',
+ 'CFGOPT_PG_HOST_CONFIG_INCLUDE_PATH',
+ 'CFGOPT_PG_HOST_CONFIG_INCLUDE_PATH2',
+ 'CFGOPT_PG_HOST_CONFIG_INCLUDE_PATH3',
+ 'CFGOPT_PG_HOST_CONFIG_INCLUDE_PATH4',
+ 'CFGOPT_PG_HOST_CONFIG_INCLUDE_PATH5',
+ 'CFGOPT_PG_HOST_CONFIG_INCLUDE_PATH6',
+ 'CFGOPT_PG_HOST_CONFIG_INCLUDE_PATH7',
+ 'CFGOPT_PG_HOST_CONFIG_INCLUDE_PATH8',
+ 'CFGOPT_PG_HOST_CONFIG_PATH',
+ 'CFGOPT_PG_HOST_CONFIG_PATH2',
+ 'CFGOPT_PG_HOST_CONFIG_PATH3',
+ 'CFGOPT_PG_HOST_CONFIG_PATH4',
+ 'CFGOPT_PG_HOST_CONFIG_PATH5',
+ 'CFGOPT_PG_HOST_CONFIG_PATH6',
+ 'CFGOPT_PG_HOST_CONFIG_PATH7',
+ 'CFGOPT_PG_HOST_CONFIG_PATH8',
+ 'CFGOPT_PG_HOST_PORT',
+ 'CFGOPT_PG_HOST_PORT2',
+ 'CFGOPT_PG_HOST_PORT3',
+ 'CFGOPT_PG_HOST_PORT4',
+ 'CFGOPT_PG_HOST_PORT5',
+ 'CFGOPT_PG_HOST_PORT6',
+ 'CFGOPT_PG_HOST_PORT7',
+ 'CFGOPT_PG_HOST_PORT8',
+ 'CFGOPT_PG_HOST_USER',
+ 'CFGOPT_PG_HOST_USER2',
+ 'CFGOPT_PG_HOST_USER3',
+ 'CFGOPT_PG_HOST_USER4',
+ 'CFGOPT_PG_HOST_USER5',
+ 'CFGOPT_PG_HOST_USER6',
+ 'CFGOPT_PG_HOST_USER7',
+ 'CFGOPT_PG_HOST_USER8',
+ 'CFGOPT_PG_PATH',
+ 'CFGOPT_PG_PATH2',
+ 'CFGOPT_PG_PATH3',
+ 'CFGOPT_PG_PATH4',
+ 'CFGOPT_PG_PATH5',
+ 'CFGOPT_PG_PATH6',
+ 'CFGOPT_PG_PATH7',
+ 'CFGOPT_PG_PATH8',
+ 'CFGOPT_PG_PORT',
+ 'CFGOPT_PG_PORT2',
+ 'CFGOPT_PG_PORT3',
+ 'CFGOPT_PG_PORT4',
+ 'CFGOPT_PG_PORT5',
+ 'CFGOPT_PG_PORT6',
+ 'CFGOPT_PG_PORT7',
+ 'CFGOPT_PG_PORT8',
+ 'CFGOPT_PG_SOCKET_PATH',
+ 'CFGOPT_PG_SOCKET_PATH2',
+ 'CFGOPT_PG_SOCKET_PATH3',
+ 'CFGOPT_PG_SOCKET_PATH4',
+ 'CFGOPT_PG_SOCKET_PATH5',
+ 'CFGOPT_PG_SOCKET_PATH6',
+ 'CFGOPT_PG_SOCKET_PATH7',
+ 'CFGOPT_PG_SOCKET_PATH8',
+ 'CFGOPT_PG_USER',
+ 'CFGOPT_PG_USER2',
+ 'CFGOPT_PG_USER3',
+ 'CFGOPT_PG_USER4',
+ 'CFGOPT_PG_USER5',
+ 'CFGOPT_PG_USER6',
+ 'CFGOPT_PG_USER7',
+ 'CFGOPT_PG_USER8',
+ 'CFGOPT_PROCESS',
+ 'CFGOPT_PROCESS_MAX',
+ 'CFGOPT_PROTOCOL_TIMEOUT',
+ 'CFGOPT_RECOVERY_OPTION',
+ 'CFGOPT_RECURSE',
+ 'CFGOPT_REMOTE_TYPE',
+ 'CFGOPT_REPO_CIPHER_PASS',
+ 'CFGOPT_REPO_CIPHER_TYPE',
+ 'CFGOPT_REPO_HARDLINK',
+ 'CFGOPT_REPO_HOST',
+ 'CFGOPT_REPO_HOST_CMD',
+ 'CFGOPT_REPO_HOST_CONFIG',
+ 'CFGOPT_REPO_HOST_CONFIG_INCLUDE_PATH',
+ 'CFGOPT_REPO_HOST_CONFIG_PATH',
+ 'CFGOPT_REPO_HOST_PORT',
+ 'CFGOPT_REPO_HOST_USER',
+ 'CFGOPT_REPO_PATH',
+ 'CFGOPT_REPO_RETENTION_ARCHIVE',
+ 'CFGOPT_REPO_RETENTION_ARCHIVE_TYPE',
+ 'CFGOPT_REPO_RETENTION_DIFF',
+ 'CFGOPT_REPO_RETENTION_FULL',
+ 'CFGOPT_REPO_S3_BUCKET',
+ 'CFGOPT_REPO_S3_CA_FILE',
+ 'CFGOPT_REPO_S3_CA_PATH',
+ 'CFGOPT_REPO_S3_ENDPOINT',
+ 'CFGOPT_REPO_S3_HOST',
+ 'CFGOPT_REPO_S3_KEY',
+ 'CFGOPT_REPO_S3_KEY_SECRET',
+ 'CFGOPT_REPO_S3_PORT',
+ 'CFGOPT_REPO_S3_REGION',
+ 'CFGOPT_REPO_S3_TOKEN',
+ 'CFGOPT_REPO_S3_URI_STYLE',
+ 'CFGOPT_REPO_S3_VERIFY_TLS',
+ 'CFGOPT_REPO_TYPE',
+ 'CFGOPT_RESUME',
+ 'CFGOPT_SET',
+ 'CFGOPT_SORT',
+ 'CFGOPT_SPOOL_PATH',
+ 'CFGOPT_STANZA',
+ 'CFGOPT_START_FAST',
+ 'CFGOPT_STOP_AUTO',
+ 'CFGOPT_TABLESPACE_MAP',
+ 'CFGOPT_TABLESPACE_MAP_ALL',
+ 'CFGOPT_TARGET',
+ 'CFGOPT_TARGET_ACTION',
+ 'CFGOPT_TARGET_EXCLUSIVE',
+ 'CFGOPT_TARGET_TIMELINE',
+ 'CFGOPT_TYPE',
+ 'cfgCommandName',
+ 'cfgOptionIndexTotal',
+ 'cfgOptionName',
+ ],
+
+ configDefine =>
+ [
+ 'CFGDEF_TYPE_BOOLEAN',
+ 'CFGDEF_TYPE_FLOAT',
+ 'CFGDEF_TYPE_HASH',
+ 'CFGDEF_TYPE_INTEGER',
+ 'CFGDEF_TYPE_LIST',
+ 'CFGDEF_TYPE_PATH',
+ 'CFGDEF_TYPE_SIZE',
+ 'CFGDEF_TYPE_STRING',
+ 'cfgCommandId',
+ 'cfgDefOptionDefault',
+ 'cfgDefOptionPrefix',
+ 'cfgDefOptionSecure',
+ 'cfgDefOptionType',
+ 'cfgDefOptionValid',
+ 'cfgOptionId',
+ 'cfgOptionTotal',
+ ],
+
+ crypto =>
+ [
+ 'cryptoHashOne',
+ ],
+
+ debug =>
+ [
+ 'libcUvSize',
+ ],
+
+ storage =>
+ [
+ 'storageRepoFree',
+ ],
+
+ test =>
+ [
+ 'cfgParseTest',
+ ],
+ }
+}
+
+1;
diff --git a/test/src/module/command/expireTest.c b/test/src/module/command/expireTest.c
index a8b1ad946..7b35a1b51 100644
--- a/test/src/module/command/expireTest.c
+++ b/test/src/module/command/expireTest.c
@@ -716,6 +716,7 @@ testRun(void)
strLstAddZ(argList, "--repo1-retention-diff=3");
strLstAddZ(argList, "--repo1-retention-archive=2");
strLstAddZ(argList, "--repo1-retention-archive-type=diff");
+ strLstAddZ(argList, "--dry-run");
harnessCfgLoad(cfgCmdExpire, argList);
// Write backup.manifest so infoBackup reconstruct produces same results as backup.info on disk
@@ -741,6 +742,64 @@ testRun(void)
storageNewWriteP(storageTest, strNewFmt("%s/20181119-152900F_20181119-152500I/" BACKUP_MANIFEST_FILE,
strPtr(backupStanzaPath))), BUFSTRDEF("tmp"));
+ TEST_RESULT_VOID(cmdExpire(), "expire (dry-run) do not remove last backup in archive sub path or sub path");
+ TEST_RESULT_BOOL(
+ storagePathExistsP(storageTest, strNewFmt("%s/%s", strPtr(archiveStanzaPath), "9.4-1/0000000100000000")),
+ true, " archive sub path not removed");
+ TEST_RESULT_BOOL(
+ storageExistsP(storageTest, strNewFmt("%s/20181119-152138F/" BACKUP_MANIFEST_FILE, strPtr(backupStanzaPath))),
+ true, " backup not removed");
+ harnessLogResult(
+ "P00 INFO: expire full backup 20181119-152138F\n"
+ "P00 INFO: remove expired backup 20181119-152138F");
+
+ // Save a copy of the info files for a later test
+ storageCopy(
+ storageNewReadP(storageTest, backupInfoFileName),
+ storageNewWriteP(storageTest, strNewFmt("%s%s", strPtr(backupInfoFileName), ".save")));
+ storageCopy(
+ storageNewReadP(storageTest, archiveInfoFileName),
+ storageNewWriteP(storageTest, strNewFmt("%s%s", strPtr(archiveInfoFileName), ".save")));
+
+ //--------------------------------------------------------------------------------------------------------------------------
+ argList = strLstDup(argListBase);
+ strLstAddZ(argList, "--repo1-retention-full=2");
+ strLstAddZ(argList, "--repo1-retention-diff=3");
+ strLstAddZ(argList, "--repo1-retention-archive=2");
+ strLstAddZ(argList, "--repo1-retention-archive-type=diff");
+ strLstAdd(argList, strNewFmt("--pg1-path=%s/pg", testPath()));
+ harnessCfgLoad(cfgCmdBackup, argList);
+
+ TEST_RESULT_VOID(cmdExpire(), "via backup command: expire last backup in archive sub path and remove sub path");
+ TEST_RESULT_BOOL(
+ storagePathExistsP(storageTest, strNewFmt("%s/%s", strPtr(archiveStanzaPath), "9.4-1/0000000100000000")),
+ false, " archive sub path removed");
+ harnessLogResult(
+ "P00 INFO: expire full backup 20181119-152138F\n"
+ "P00 INFO: remove expired backup 20181119-152138F");
+
+ //--------------------------------------------------------------------------------------------------------------------------
+ argList = strLstDup(argListBase);
+ strLstAddZ(argList, "--repo1-retention-full=2");
+ strLstAddZ(argList, "--repo1-retention-diff=3");
+ strLstAddZ(argList, "--repo1-retention-archive=2");
+ strLstAddZ(argList, "--repo1-retention-archive-type=diff");
+ harnessCfgLoad(cfgCmdExpire, argList);
+
+ // Restore info files from a previous test
+ storageCopy(
+ storageNewReadP(storageTest, strNewFmt("%s%s", strPtr(backupInfoFileName), ".save")),
+ storageNewWriteP(storageTest, backupInfoFileName));
+ storageCopy(
+ storageNewReadP(storageTest, strNewFmt("%s%s", strPtr(archiveInfoFileName), ".save")),
+ storageNewWriteP(storageTest, archiveInfoFileName));
+
+ // Write out manifest and archive that will be removed
+ storagePutP(
+ storageNewWriteP(storageTest, strNewFmt("%s/20181119-152138F/" BACKUP_MANIFEST_FILE, strPtr(backupStanzaPath))),
+ BUFSTRDEF("tmp"));
+ archiveGenerate(storageTest, archiveStanzaPath, 2, 2, "9.4-1", "0000000100000000");
+
TEST_RESULT_VOID(cmdExpire(), "expire last backup in archive sub path and remove sub path");
TEST_RESULT_BOOL(
storagePathExistsP(storageTest, strNewFmt("%s/%s", strPtr(archiveStanzaPath), "9.4-1/0000000100000000")),
@@ -752,9 +811,37 @@ testRun(void)
//--------------------------------------------------------------------------------------------------------------------------
argList = strLstDup(argListAvoidWarn);
strLstAddZ(argList, "--repo1-retention-archive=1");
+ strLstAddZ(argList, "--dry-run");
harnessCfgLoad(cfgCmdExpire, argList);
- TEST_RESULT_VOID(cmdExpire(), "expire last backup in archive path and remove path");
+ TEST_RESULT_VOID(cmdExpire(), "expire (dry-run) - log expired backups and archive path to remove");
+ TEST_RESULT_BOOL(
+ storagePathExistsP(storageTest, strNewFmt("%s/%s", strPtr(archiveStanzaPath), "9.4-1")),
+ true, " archive path not removed");
+ TEST_RESULT_BOOL(
+ (storageExistsP(storageTest, strNewFmt("%s/20181119-152800F/" BACKUP_MANIFEST_FILE, strPtr(backupStanzaPath))) &&
+ storageExistsP(
+ storageTest, strNewFmt("%s/20181119-152800F_20181119-152152D/" BACKUP_MANIFEST_FILE, strPtr(backupStanzaPath))) &&
+ storageExistsP(
+ storageTest, strNewFmt("%s/20181119-152800F_20181119-152155I/" BACKUP_MANIFEST_FILE, strPtr(backupStanzaPath))) &&
+ storageExistsP(
+ storageTest, strNewFmt("%s/20181119-152800F_20181119-152252D/" BACKUP_MANIFEST_FILE, strPtr(backupStanzaPath)))),
+ true, " backup not removed");
+ harnessLogResult(strPtr(strNewFmt(
+ "P00 INFO: expire full backup set: 20181119-152800F, 20181119-152800F_20181119-152152D, "
+ "20181119-152800F_20181119-152155I, 20181119-152800F_20181119-152252D\n"
+ "P00 INFO: remove expired backup 20181119-152800F_20181119-152252D\n"
+ "P00 INFO: remove expired backup 20181119-152800F_20181119-152155I\n"
+ "P00 INFO: remove expired backup 20181119-152800F_20181119-152152D\n"
+ "P00 INFO: remove expired backup 20181119-152800F\n"
+ "P00 INFO: remove archive path: %s/%s/9.4-1", testPath(), strPtr(archiveStanzaPath))));
+
+ argList = strLstDup(argListAvoidWarn);
+ strLstAddZ(argList, "--repo1-retention-archive=1");
+ strLstAdd(argList, strNewFmt("--pg1-path=%s/pg", testPath()));
+ harnessCfgLoad(cfgCmdBackup, argList);
+
+ TEST_RESULT_VOID(cmdExpire(), "via backup command: expire backups and remove archive path");
TEST_RESULT_BOOL(
storagePathExistsP(storageTest, strNewFmt("%s/%s", strPtr(archiveStanzaPath), "9.4-1")),
false, " archive path removed");
@@ -774,6 +861,15 @@ testRun(void)
strLstJoin(strLstSort(infoBackupDataLabelList(infoBackup, NULL), sortOrderAsc), ", "),
"20181119-152900F, 20181119-152900F_20181119-152500I", " remaining current backups correct");
+ archiveGenerate(storageTest, archiveStanzaPath, 1, 1, "9.4-1", "0000000100000000");
+ argList = strLstDup(argListAvoidWarn);
+ strLstAddZ(argList, "--repo1-retention-archive=1");
+ harnessCfgLoad(cfgCmdExpire, argList);
+
+ TEST_RESULT_VOID(cmdExpire(), "expire remove archive path");
+ harnessLogResult(
+ strPtr(strNewFmt("P00 INFO: remove archive path: %s/%s/9.4-1", testPath(), strPtr(archiveStanzaPath))));
+
//--------------------------------------------------------------------------------------------------------------------------
storagePutP(storageNewWriteP(storageTest, backupInfoFileName),
harnessInfoChecksumZ(