rework backup root
notes: * backup root cannot come from backend. for dynamic mounts backend cannot know where it is mounted * backupConfig is 3 parts - format / mount / password . there is also this rootPath (which should not be in db) * password should be stored separately in settings at some point * format has to be passed along everywhere because we allow restore from same backupConfig but different format. we do this by saving the format in the backups table fixes #819
This commit is contained in:
+63
-43
@@ -15,7 +15,6 @@ exports = module.exports = {
|
||||
startCleanupTask,
|
||||
cleanupCacheFilesSync,
|
||||
|
||||
injectPrivateFields,
|
||||
removePrivateFields,
|
||||
|
||||
generateEncryptionKeysSync,
|
||||
@@ -26,7 +25,7 @@ exports = module.exports = {
|
||||
validatePolicy,
|
||||
validateEncryptionPassword,
|
||||
testStorage,
|
||||
validateBackupFormat,
|
||||
validateFormat,
|
||||
|
||||
getPolicy,
|
||||
setPolicy,
|
||||
@@ -36,6 +35,8 @@ exports = module.exports = {
|
||||
setStorage,
|
||||
setLimits,
|
||||
|
||||
setupStorage,
|
||||
|
||||
remount,
|
||||
getMountStatus,
|
||||
|
||||
@@ -85,24 +86,13 @@ function postProcess(result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
function injectPrivateFields(newConfig, currentConfig) {
|
||||
if ('password' in newConfig) {
|
||||
if (newConfig.password === constants.SECRET_PLACEHOLDER) {
|
||||
delete newConfig.password;
|
||||
}
|
||||
newConfig.encryption = currentConfig.encryption || null;
|
||||
} else {
|
||||
newConfig.encryption = null;
|
||||
}
|
||||
if (newConfig.provider === currentConfig.provider) storage.api(newConfig.provider).injectPrivateFields(newConfig, currentConfig);
|
||||
}
|
||||
|
||||
function removePrivateFields(backupConfig) {
|
||||
assert.strictEqual(typeof backupConfig, 'object');
|
||||
if (backupConfig.encryption) {
|
||||
delete backupConfig.encryption;
|
||||
backupConfig.password = constants.SECRET_PLACEHOLDER;
|
||||
}
|
||||
delete backupConfig.rootPath;
|
||||
return storage.api(backupConfig.provider).removePrivateFields(backupConfig);
|
||||
}
|
||||
|
||||
@@ -414,10 +404,26 @@ async function setPolicy(policy) {
|
||||
await cron.handleBackupPolicyChanged(policy);
|
||||
}
|
||||
|
||||
function getRootPath(storageConfig, mountPath) {
|
||||
assert.strictEqual(typeof storageConfig, 'object');
|
||||
assert.strictEqual(typeof mountPath, 'string');
|
||||
|
||||
if (mounts.isManagedProvider(storageConfig.provider)) {
|
||||
return path.join(mountPath, storageConfig.prefix);
|
||||
} else if (storageConfig.provider === 'mountpoint') {
|
||||
return path.join(storageConfig.mountPoint, storageConfig.prefix);
|
||||
} else if (storageConfig.provider === 'filesystem') {
|
||||
return storageConfig.backupFolder;
|
||||
} else {
|
||||
return storageConfig.prefix;
|
||||
}
|
||||
}
|
||||
|
||||
async function getConfig() {
|
||||
const result = await settings.getJson(settings.BACKUP_STORAGE_KEY) || { provider: 'filesystem', backupFolder: paths.DEFAULT_BACKUP_DIR, format: 'tgz', encryption: null };
|
||||
const limits = await settings.getJson(settings.BACKUP_LIMITS_KEY);
|
||||
if (limits) result.limits = limits;
|
||||
result.rootPath = getRootPath(result, paths.MANAGED_BACKUP_MOUNT_DIR); // note: rootPath will be dynamic for managed mount providers during app import
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -434,7 +440,7 @@ async function setLimits(limits) {
|
||||
await settings.setJson(settings.BACKUP_LIMITS_KEY, limits);
|
||||
}
|
||||
|
||||
function validateBackupFormat(format) {
|
||||
function validateFormat(format) {
|
||||
assert.strictEqual(typeof format, 'string');
|
||||
|
||||
if (format === 'tgz' || format == 'rsync') return null;
|
||||
@@ -447,36 +453,34 @@ async function setStorage(storageConfig) {
|
||||
|
||||
const oldConfig = await getConfig();
|
||||
|
||||
injectPrivateFields(storageConfig, oldConfig);
|
||||
if (storageConfig.provider === oldConfig.provider) storage.api(storageConfig.provider).injectPrivateFields(storageConfig, oldConfig);
|
||||
|
||||
if (mounts.isManagedProvider(storageConfig.provider)) {
|
||||
let error = mounts.validateMountOptions(storageConfig.provider, storageConfig.mountOptions);
|
||||
if (error) throw error;
|
||||
|
||||
[error] = await safe(mounts.tryAddMount(mountObjectFromBackupConfig(storageConfig), { timeout: 10 })); // 10 seconds
|
||||
|
||||
if (error) {
|
||||
if (mounts.isManagedProvider(oldConfig.provider)) { // put back the old mount configuration
|
||||
debug('setBackupConfig: rolling back to previous mount configuration');
|
||||
|
||||
await safe(mounts.tryAddMount(mountObjectFromBackupConfig(oldConfig), { timeout: 10 }));
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
let error = validateBackupFormat(storageConfig.format);
|
||||
let error = validateFormat(storageConfig.format);
|
||||
if (error) throw error;
|
||||
|
||||
debug('setStorage: validating new storage configuration');
|
||||
await setupStorage(storageConfig, '/mnt/backup-storage-validation');
|
||||
storageConfig.rootPath = getRootPath(storageConfig, '/mnt/backup-storage-validation');
|
||||
error = await testStorage(storageConfig);
|
||||
delete storageConfig.rootPath;
|
||||
if (error) throw error;
|
||||
|
||||
if ('password' in storageConfig) { // user set password
|
||||
const error = await validateEncryptionPassword(storageConfig.password);
|
||||
if (error) throw error;
|
||||
debug('setStorage: removing old storage configuration');
|
||||
if (mounts.isManagedProvider(oldConfig.provider)) await safe(mounts.removeMount(mountObjectFromBackupConfig(oldConfig)));
|
||||
|
||||
storageConfig.encryption = generateEncryptionKeysSync(storageConfig.password);
|
||||
debug('setStorage: setting up new storage configuration');
|
||||
await setupStorage(storageConfig, paths.MANAGED_BACKUP_MOUNT_DIR);
|
||||
|
||||
storageConfig.encryption = null;
|
||||
if ('password' in storageConfig) { // user set password
|
||||
if (storageConfig.password === constants.SECRET_PLACEHOLDER) {
|
||||
storageConfig.encryption = oldConfig.encryption || null;
|
||||
} else {
|
||||
const error = await validateEncryptionPassword(storageConfig.password);
|
||||
if (error) throw error;
|
||||
|
||||
storageConfig.encryption = generateEncryptionKeysSync(storageConfig.password);
|
||||
}
|
||||
delete storageConfig.password;
|
||||
}
|
||||
|
||||
@@ -484,9 +488,25 @@ async function setStorage(storageConfig) {
|
||||
cleanupCacheFilesSync();
|
||||
|
||||
await settings.setJson(settings.BACKUP_STORAGE_KEY, storageConfig);
|
||||
|
||||
if (mounts.isManagedProvider(oldConfig.provider) && !mounts.isManagedProvider(storageConfig.provider)) {
|
||||
debug('setBackupConfig: removing old backup mount point');
|
||||
await safe(mounts.removeMount(mountObjectFromBackupConfig(oldConfig)));
|
||||
}
|
||||
}
|
||||
|
||||
async function setupStorage(storageConfig, hostPath) {
|
||||
assert.strictEqual(typeof storageConfig, 'object');
|
||||
assert.strictEqual(typeof hostPath, 'string');
|
||||
|
||||
if (!mounts.isManagedProvider(storageConfig.provider)) return null;
|
||||
|
||||
const error = mounts.validateMountOptions(storageConfig.provider, storageConfig.mountOptions);
|
||||
if (error) throw error;
|
||||
|
||||
const newMount = {
|
||||
name: path.basename(hostPath),
|
||||
hostPath: hostPath,
|
||||
mountType: storageConfig.provider,
|
||||
mountOptions: storageConfig.mountOptions
|
||||
};
|
||||
|
||||
await mounts.tryAddMount(newMount, { timeout: 10 }); // 10 seconds
|
||||
|
||||
return newMount;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user