mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-04-15 11:36:40 +02:00
Multi-repository implementations for the archive-push, check, info, stanza-create, stanza-upgrade, and stanza-delete commands. Multi-repo configuration is disabled so there should be no behavioral changes between these commands and their current single-repo implementations. Multi-repo documentation and integration tests are still in the multi-repo development branch. All unit tests work as multi-repo since they are able to bypass the configuration restrictions.
161 lines
8.2 KiB
C
161 lines
8.2 KiB
C
/***********************************************************************************************************************************
|
|
Stanza Create Command
|
|
***********************************************************************************************************************************/
|
|
#include "build.auto.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <inttypes.h>
|
|
|
|
#include "command/check/common.h"
|
|
#include "command/control/common.h"
|
|
#include "command/stanza/common.h"
|
|
#include "command/stanza/create.h"
|
|
#include "common/debug.h"
|
|
#include "common/log.h"
|
|
#include "common/memContext.h"
|
|
#include "config/config.h"
|
|
#include "info/infoArchive.h"
|
|
#include "info/infoBackup.h"
|
|
#include "info/infoPg.h"
|
|
#include "postgres/interface.h"
|
|
#include "postgres/version.h"
|
|
#include "protocol/helper.h"
|
|
#include "storage/helper.h"
|
|
|
|
/**********************************************************************************************************************************/
|
|
void
|
|
cmdStanzaCreate(void)
|
|
{
|
|
FUNCTION_LOG_VOID(logLevelDebug);
|
|
|
|
// Verify that a stop was not issued before proceeding
|
|
lockStopTest();
|
|
|
|
MEM_CONTEXT_TEMP_BEGIN()
|
|
{
|
|
if (cfgOptionBool(cfgOptForce))
|
|
LOG_WARN("option --force is no longer supported");
|
|
|
|
// Verify all the repos are local (i.e. repo*-host is not set) - this is a simple way to confirm we are not executing
|
|
// stanza-create from a pg host as it will immediately error
|
|
for (unsigned int repoIdx = 0; repoIdx < cfgOptionGroupIdxTotal(cfgOptGrpRepo); repoIdx++)
|
|
repoIsLocalVerifyIdx(repoIdx);
|
|
|
|
// Get the version and system information - validating it if the database is online
|
|
PgControl pgControl = pgValidate();
|
|
|
|
// For each repository configured, create the stanza
|
|
for (unsigned int repoIdx = 0; repoIdx < cfgOptionGroupIdxTotal(cfgOptGrpRepo); repoIdx++)
|
|
{
|
|
LOG_INFO_FMT(
|
|
CFGCMD_STANZA_CREATE " for stanza '%s' on repo%u", strZ(cfgOptionStr(cfgOptStanza)),
|
|
cfgOptionGroupIdxToKey(cfgOptGrpRepo, repoIdx));
|
|
|
|
const Storage *storageRepoReadStanza = storageRepoIdx(repoIdx);
|
|
const Storage *storageRepoWriteStanza = storageRepoIdxWrite(repoIdx);
|
|
InfoArchive *infoArchive = NULL;
|
|
InfoBackup *infoBackup = NULL;
|
|
|
|
bool archiveInfoFileExists = storageExistsP(storageRepoReadStanza, INFO_ARCHIVE_PATH_FILE_STR);
|
|
bool archiveInfoFileCopyExists = storageExistsP(storageRepoReadStanza, INFO_ARCHIVE_PATH_FILE_COPY_STR);
|
|
bool backupInfoFileExists = storageExistsP(storageRepoReadStanza, INFO_BACKUP_PATH_FILE_STR);
|
|
bool backupInfoFileCopyExists = storageExistsP(storageRepoReadStanza, INFO_BACKUP_PATH_FILE_COPY_STR);
|
|
|
|
// If neither archive info nor backup info files exist and nothing else exists in the stanza directory
|
|
// then create the stanza
|
|
if (!archiveInfoFileExists && !archiveInfoFileCopyExists && !backupInfoFileExists && !backupInfoFileCopyExists)
|
|
{
|
|
bool archiveNotEmpty = strLstSize(storageListP(storageRepoReadStanza, STORAGE_REPO_ARCHIVE_STR)) > 0 ? true : false;
|
|
bool backupNotEmpty = strLstSize(storageListP(storageRepoReadStanza, STORAGE_REPO_BACKUP_STR)) > 0 ? true : false;
|
|
|
|
// If something else exists in the backup or archive directories for this stanza, then error
|
|
if (archiveNotEmpty || backupNotEmpty)
|
|
{
|
|
THROW_FMT(
|
|
PathNotEmptyError, "%s%s%snot empty", (backupNotEmpty ? "backup directory " : ""),
|
|
(backupNotEmpty && archiveNotEmpty ? "and/or " : ""), (archiveNotEmpty ? "archive directory " : ""));
|
|
}
|
|
|
|
// If the repo is encrypted, generate a cipher passphrase for encrypting subsequent archive files
|
|
String *cipherPassSub = cipherPassGen(cipherType(cfgOptionIdxStr(cfgOptRepoCipherType, repoIdx)));
|
|
|
|
// Create and save archive info
|
|
infoArchive = infoArchiveNew(pgControl.version, pgControl.systemId, cipherPassSub);
|
|
|
|
infoArchiveSaveFile(
|
|
infoArchive, storageRepoWriteStanza, INFO_ARCHIVE_PATH_FILE_STR,
|
|
cipherType(cfgOptionIdxStr(cfgOptRepoCipherType, repoIdx)), cfgOptionIdxStrNull(cfgOptRepoCipherPass, repoIdx));
|
|
|
|
// If the repo is encrypted, generate a cipher passphrase for encrypting subsequent backup files
|
|
cipherPassSub = cipherPassGen(cipherType(cfgOptionIdxStr(cfgOptRepoCipherType, repoIdx)));
|
|
|
|
// Create and save backup info
|
|
infoBackup = infoBackupNew(pgControl.version, pgControl.systemId, pgControl.catalogVersion, cipherPassSub);
|
|
|
|
infoBackupSaveFile(
|
|
infoBackup, storageRepoWriteStanza, INFO_BACKUP_PATH_FILE_STR,
|
|
cipherType(cfgOptionIdxStr(cfgOptRepoCipherType, repoIdx)), cfgOptionIdxStrNull(cfgOptRepoCipherPass, repoIdx));
|
|
}
|
|
// Else if at least one archive and one backup info file exists, then ensure both are valid
|
|
else if ((archiveInfoFileExists || archiveInfoFileCopyExists) && (backupInfoFileExists || backupInfoFileCopyExists))
|
|
{
|
|
// Error if there is a mismatch between the archive and backup info files or the database version/system Id matches
|
|
// current database
|
|
checkStanzaInfoPg(
|
|
storageRepoReadStanza, pgControl.version, pgControl.systemId,
|
|
cipherType(cfgOptionIdxStr(cfgOptRepoCipherType, repoIdx)), cfgOptionIdxStrNull(cfgOptRepoCipherPass, repoIdx));
|
|
|
|
// The files are valid - upgrade
|
|
const String *sourceFile = NULL;
|
|
const String *destinationFile = NULL;
|
|
|
|
// If the existing files are valid, then, if a file is missing, copy the existing one to the missing one to ensure
|
|
// there is both a .info and .info.copy
|
|
if (!archiveInfoFileExists || !archiveInfoFileCopyExists)
|
|
{
|
|
sourceFile = archiveInfoFileExists ? INFO_ARCHIVE_PATH_FILE_STR : INFO_ARCHIVE_PATH_FILE_COPY_STR;
|
|
destinationFile = !archiveInfoFileExists ? INFO_ARCHIVE_PATH_FILE_STR : INFO_ARCHIVE_PATH_FILE_COPY_STR;
|
|
|
|
storageCopyP(
|
|
storageNewReadP(storageRepoReadStanza, sourceFile),
|
|
storageNewWriteP(storageRepoWriteStanza, destinationFile));
|
|
}
|
|
|
|
if (!backupInfoFileExists || !backupInfoFileCopyExists)
|
|
{
|
|
sourceFile = backupInfoFileExists ? INFO_BACKUP_PATH_FILE_STR : INFO_BACKUP_PATH_FILE_COPY_STR;
|
|
destinationFile = !backupInfoFileExists ? INFO_BACKUP_PATH_FILE_STR : INFO_BACKUP_PATH_FILE_COPY_STR;
|
|
|
|
storageCopyP(
|
|
storageNewReadP(storageRepoReadStanza, sourceFile),
|
|
storageNewWriteP(storageRepoWriteStanza, destinationFile));
|
|
}
|
|
|
|
// If no files copied, then the stanza was already valid
|
|
if (sourceFile == NULL)
|
|
{
|
|
LOG_INFO_FMT(
|
|
"stanza '%s' already exists on repo%u and is valid", strZ(cfgOptionStr(cfgOptStanza)),
|
|
cfgOptionGroupIdxToKey(cfgOptGrpRepo, repoIdx));
|
|
}
|
|
}
|
|
// Else if both .info and corresponding .copy file are missing for one but not the other, then error - the user will
|
|
// have to make a conscious effort to determine if deleting the stanza on this repo is appropriate or other action is
|
|
else
|
|
{
|
|
THROW_FMT(
|
|
FileMissingError,
|
|
"%s on repo%u\n"
|
|
"HINT: this may be a symptom of repository corruption!",
|
|
((archiveInfoFileExists || archiveInfoFileCopyExists) ?
|
|
"archive.info exists but backup.info is missing" : "backup.info exists but archive.info is missing"),
|
|
cfgOptionGroupIdxToKey(cfgOptGrpRepo, repoIdx));
|
|
}
|
|
}
|
|
}
|
|
MEM_CONTEXT_TEMP_END();
|
|
|
|
FUNCTION_LOG_RETURN_VOID();
|
|
}
|