backups: fix app restore with tgz
This commit is contained in:
43
src/apps.js
43
src/apps.js
@@ -1260,12 +1260,12 @@ async function scheduleTask(appId, installationState, taskId, auditSource) {
|
||||
assert.strictEqual(typeof taskId, 'string');
|
||||
assert.strictEqual(typeof auditSource, 'object');
|
||||
|
||||
const backupConfig = await backupTargets.getConfig();
|
||||
const backupTarget = await backupTargets.getPrimary();
|
||||
|
||||
let memoryLimit = 400;
|
||||
if (installationState === exports.ISTATE_PENDING_CLONE || installationState === exports.ISTATE_PENDING_RESTORE
|
||||
|| installationState === exports.ISTATE_PENDING_IMPORT || installationState === exports.ISTATE_PENDING_UPDATE) {
|
||||
memoryLimit = backupConfig.limits?.memoryLimit ? Math.max(backupConfig.limits.memoryLimit/1024/1024, 400) : 400;
|
||||
memoryLimit = backupTarget.limits?.memoryLimit ? Math.max(backupTarget.limits.memoryLimit/1024/1024, 400) : 400;
|
||||
} else if (installationState === exports.ISTATE_PENDING_DATA_DIR_MIGRATION) {
|
||||
memoryLimit = 1024; // cp takes more memory than we think
|
||||
}
|
||||
@@ -2302,12 +2302,12 @@ async function restore(app, backupId, auditSource) {
|
||||
if (error) throw error;
|
||||
|
||||
// for empty or null backupId, use existing manifest to mimic a reinstall
|
||||
const backupInfo = backupId ? await backups.get(backupId) : { manifest: app.manifest };
|
||||
if (!backupInfo) throw new BoxError(BoxError.BAD_FIELD, 'No such backup');
|
||||
const manifest = backupInfo.manifest;
|
||||
const backup = backupId ? await backups.get(backupId) : { manifest: app.manifest };
|
||||
if (!backup) throw new BoxError(BoxError.BAD_FIELD, 'No such backup');
|
||||
const manifest = backup.manifest;
|
||||
|
||||
if (!manifest) throw new BoxError(BoxError.EXTERNAL_ERROR, 'Could not get restore manifest');
|
||||
if (backupInfo.encryptionVersion === 1) throw new BoxError(BoxError.BAD_FIELD, 'This encrypted backup was created with an older Cloudron version and has to be restored using the CLI tool');
|
||||
if (backup.encryptionVersion === 1) throw new BoxError(BoxError.BAD_FIELD, 'This encrypted backup was created with an older Cloudron version and has to be restored using the CLI tool');
|
||||
|
||||
// re-validate because this new box version may not accept old configs
|
||||
error = await checkManifest(manifest);
|
||||
@@ -2326,7 +2326,7 @@ async function restore(app, backupId, auditSource) {
|
||||
values.inboxName = values.inboxDomain = null;
|
||||
}
|
||||
|
||||
const restoreConfig = { remotePath: backupInfo.remotePath, backupFormat: backupInfo.format };
|
||||
const restoreConfig = { backupId: backup.id };
|
||||
|
||||
const task = {
|
||||
args: {
|
||||
@@ -2340,7 +2340,7 @@ async function restore(app, backupId, auditSource) {
|
||||
|
||||
const taskId = await addTask(appId, exports.ISTATE_PENDING_RESTORE, task, auditSource);
|
||||
|
||||
await eventlog.add(eventlog.ACTION_APP_RESTORE, auditSource, { app, backupId: backupInfo.id, remotePath: backupInfo.remotePath, fromManifest: app.manifest, toManifest: manifest, taskId });
|
||||
await eventlog.add(eventlog.ACTION_APP_RESTORE, auditSource, { app, backupId: backup.id, remotePath: backup.remotePath, fromManifest: app.manifest, toManifest: manifest, taskId });
|
||||
|
||||
return { taskId };
|
||||
}
|
||||
@@ -2405,7 +2405,7 @@ async function exportApp(app, data, auditSource) {
|
||||
|
||||
if (!canBackupApp(app)) throw new BoxError(BoxError.BAD_STATE, 'App cannot be backed up in this state');
|
||||
|
||||
const backupTarget = await backupTargets._getDefault();
|
||||
const backupTarget = await backupTargets.getPrimary();
|
||||
const taskId = await tasks.add(`${tasks.TASK_APP_BACKUP_PREFIX}${app.id}`, [ appId, backupTarget.id, { snapshotOnly: true } ]);
|
||||
safe(tasks.startTask(taskId, {}), { debug }); // background
|
||||
return { taskId };
|
||||
@@ -2427,13 +2427,13 @@ async function clone(app, data, user, auditSource) {
|
||||
assert.strictEqual(typeof subdomain, 'string');
|
||||
assert.strictEqual(typeof domain, 'string');
|
||||
|
||||
const backupInfo = await backups.get(backupId);
|
||||
const backup = await backups.get(backupId);
|
||||
|
||||
if (!backupInfo) throw new BoxError(BoxError.NOT_FOUND, 'Backup not found');
|
||||
if (!backupInfo.manifest) throw new BoxError(BoxError.EXTERNAL_ERROR, 'Could not detect restore manifest');
|
||||
if (backupInfo.encryptionVersion === 1) throw new BoxError(BoxError.BAD_FIELD, 'This encrypted backup was created with an older Cloudron version and cannot be cloned');
|
||||
if (!backup) throw new BoxError(BoxError.NOT_FOUND, 'Backup not found');
|
||||
if (!backup.manifest) throw new BoxError(BoxError.EXTERNAL_ERROR, 'Could not detect restore manifest');
|
||||
if (backup.encryptionVersion === 1) throw new BoxError(BoxError.BAD_FIELD, 'This encrypted backup was created with an older Cloudron version and cannot be cloned');
|
||||
|
||||
const manifest = backupInfo.manifest, appStoreId = app.appStoreId;
|
||||
const manifest = backup.manifest, appStoreId = app.appStoreId;
|
||||
|
||||
let error = validateSecondaryDomains(data.secondaryDomains || {}, manifest);
|
||||
if (error) throw error;
|
||||
@@ -2460,7 +2460,7 @@ async function clone(app, data, user, auditSource) {
|
||||
const newAppId = uuid.v4();
|
||||
|
||||
// label, checklist intentionally omitted . icon is loaded in apptask from the backup
|
||||
const dolly = _.pick(backupInfo.appConfig || app, ['memoryLimit', 'cpuQuota', 'crontab', 'reverseProxyConfig', 'env', 'servicesConfig', 'tags', 'devices',
|
||||
const dolly = _.pick(backup.appConfig || app, ['memoryLimit', 'cpuQuota', 'crontab', 'reverseProxyConfig', 'env', 'servicesConfig', 'tags', 'devices',
|
||||
'enableMailbox', 'mailboxDisplayName', 'mailboxName', 'mailboxDomain', 'enableInbox', 'inboxName', 'inboxDomain', 'debugMode',
|
||||
'enableTurn', 'enableRedis', 'mounts', 'enableBackup', 'enableAutomaticUpdate', 'accessRestriction', 'operators', 'sso',
|
||||
'notes', 'checklist']);
|
||||
@@ -2482,7 +2482,7 @@ async function clone(app, data, user, auditSource) {
|
||||
if (addError && addError.reason === BoxError.ALREADY_EXISTS) throw getDuplicateErrorDetails(addError.message, locations, portBindings);
|
||||
if (addError) throw addError;
|
||||
|
||||
const restoreConfig = { remotePath: backupInfo.remotePath, backupFormat: backupInfo.format };
|
||||
const restoreConfig = { backupId: backup.id };
|
||||
const task = {
|
||||
args: { restoreConfig, overwriteDns, skipDnsSetup, oldManifest: null },
|
||||
values: {},
|
||||
@@ -2496,7 +2496,7 @@ async function clone(app, data, user, auditSource) {
|
||||
newApp.redirectDomains.forEach(function (ad) { ad.fqdn = dns.fqdn(ad.subdomain, ad.domain); });
|
||||
newApp.aliasDomains.forEach(function (ad) { ad.fqdn = dns.fqdn(ad.subdomain, ad.domain); });
|
||||
|
||||
await eventlog.add(eventlog.ACTION_APP_CLONE, auditSource, { appId: newAppId, oldAppId: app.id, backupId, remotePath: backupInfo.remotePath, oldApp: app, newApp, taskId });
|
||||
await eventlog.add(eventlog.ACTION_APP_CLONE, auditSource, { appId: newAppId, oldAppId: app.id, backupId, remotePath: backup.remotePath, oldApp: app, newApp, taskId });
|
||||
|
||||
return { id: newAppId, taskId };
|
||||
}
|
||||
@@ -2507,7 +2507,7 @@ async function unarchive(archive, data, auditSource) {
|
||||
assert(auditSource && typeof auditSource === 'object');
|
||||
|
||||
const backup = await backups.get(archive.backupId);
|
||||
const restoreConfig = { remotePath: backup.remotePath, backupFormat: backup.format };
|
||||
const restoreConfig = { backupId: backup.id };
|
||||
|
||||
const subdomain = data.subdomain.toLowerCase(),
|
||||
domain = data.domain.toLowerCase(),
|
||||
@@ -2780,7 +2780,7 @@ async function backup(app, auditSource) {
|
||||
|
||||
if (!canBackupApp(app)) throw new BoxError(BoxError.BAD_STATE, 'App cannot be backed up in this state');
|
||||
|
||||
const backupTarget = await backupTargets._getDefault();
|
||||
const backupTarget = await backupTargets.getPrimary();
|
||||
|
||||
const taskId = await tasks.add(`${tasks.TASK_APP_BACKUP_PREFIX}${app.id}`, [ app.id, backupTarget.id, { snapshotOnly: false } ]);
|
||||
|
||||
@@ -2833,11 +2833,12 @@ async function getBackupDownloadStream(app, backupId) {
|
||||
if (backup.identifier !== app.id) throw new BoxError(BoxError.NOT_FOUND, 'Backup not found'); // some other app's backup
|
||||
if (backup.format !== 'tgz') throw new BoxError(BoxError.BAD_STATE, 'only tgz backups can be downloaded');
|
||||
|
||||
const backupConfig = await backupTargets.getConfig();
|
||||
const backupTarget = await backupTargets.get(backup.targetId);
|
||||
if (!backupTarget) throw new BoxError(BoxError.NOT_FOUND, 'Backup target not found'); // not possible
|
||||
|
||||
const ps = new PassThrough();
|
||||
|
||||
const stream = await storage.api(backupConfig.provider).download(backupConfig, tgz.getBackupFilePath(backupConfig, backup.remotePath));
|
||||
const stream = await storage.api(backupTarget.provider).download(backupTarget.config, tgz.getBackupFilePath(backupTarget, backup.remotePath));
|
||||
stream.on('error', function(error) {
|
||||
debug(`getBackupDownloadStream: read stream error: ${error.message}`);
|
||||
ps.emit('error', new BoxError(BoxError.EXTERNAL_ERROR, error));
|
||||
|
||||
Reference in New Issue
Block a user