apptask: asyncify
This commit is contained in:
+279
-425
File diff suppressed because it is too large
Load Diff
+2
-5
@@ -237,19 +237,16 @@ function getSnapshotInfo(id) {
|
|||||||
return info[id] || { };
|
return info[id] || { };
|
||||||
}
|
}
|
||||||
|
|
||||||
function setSnapshotInfo(id, info, callback) {
|
async function setSnapshotInfo(id, info) {
|
||||||
assert.strictEqual(typeof id, 'string');
|
assert.strictEqual(typeof id, 'string');
|
||||||
assert.strictEqual(typeof info, 'object');
|
assert.strictEqual(typeof info, 'object');
|
||||||
assert.strictEqual(typeof callback, 'function');
|
|
||||||
|
|
||||||
const contents = safe.fs.readFileSync(paths.SNAPSHOT_INFO_FILE, 'utf8');
|
const contents = safe.fs.readFileSync(paths.SNAPSHOT_INFO_FILE, 'utf8');
|
||||||
const data = safe.JSON.parse(contents) || { };
|
const data = safe.JSON.parse(contents) || { };
|
||||||
if (info) data[id] = info; else delete data[id];
|
if (info) data[id] = info; else delete data[id];
|
||||||
if (!safe.fs.writeFileSync(paths.SNAPSHOT_INFO_FILE, JSON.stringify(data, null, 4), 'utf8')) {
|
if (!safe.fs.writeFileSync(paths.SNAPSHOT_INFO_FILE, JSON.stringify(data, null, 4), 'utf8')) {
|
||||||
return callback(new BoxError(BoxError.FS_ERROR, safe.error.message));
|
throw new BoxError(BoxError.FS_ERROR, safe.error.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
callback();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function startCleanupTask(auditSource) {
|
async function startCleanupTask(auditSource) {
|
||||||
|
|||||||
+51
-115
@@ -645,54 +645,41 @@ function download(backupConfig, backupId, format, dataLayout, progressCallback,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function restore(backupConfig, backupId, progressCallback, callback) {
|
async function restore(backupConfig, backupId, progressCallback) {
|
||||||
assert.strictEqual(typeof backupConfig, 'object');
|
assert.strictEqual(typeof backupConfig, 'object');
|
||||||
assert.strictEqual(typeof backupId, 'string');
|
assert.strictEqual(typeof backupId, 'string');
|
||||||
assert.strictEqual(typeof progressCallback, 'function');
|
assert.strictEqual(typeof progressCallback, 'function');
|
||||||
assert.strictEqual(typeof callback, 'function');
|
|
||||||
|
|
||||||
const boxDataDir = safe.fs.realpathSync(paths.BOX_DATA_DIR);
|
const boxDataDir = safe.fs.realpathSync(paths.BOX_DATA_DIR);
|
||||||
if (!boxDataDir) return callback(new BoxError(BoxError.FS_ERROR, `Error resolving boxdata: ${safe.error.message}`));
|
if (!boxDataDir) throw new BoxError(BoxError.FS_ERROR, `Error resolving boxdata: ${safe.error.message}`);
|
||||||
const dataLayout = new DataLayout(boxDataDir, []);
|
const dataLayout = new DataLayout(boxDataDir, []);
|
||||||
|
|
||||||
download(backupConfig, backupId, backupConfig.format, dataLayout, progressCallback, function (error) {
|
await util.promisify(download)(backupConfig, backupId, backupConfig.format, dataLayout, progressCallback);
|
||||||
if (error) return callback(error);
|
|
||||||
|
|
||||||
debug('restore: download completed, importing database');
|
debug('restore: download completed, importing database');
|
||||||
|
|
||||||
database.importFromFile(`${dataLayout.localRoot()}/box.mysqldump`, async function (error) {
|
await database.importFromFile(`${dataLayout.localRoot()}/box.mysqldump`);
|
||||||
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
|
|
||||||
|
|
||||||
debug('restore: database imported');
|
debug('restore: database imported');
|
||||||
|
|
||||||
[error] = await safe(settings.initCache());
|
await settings.initCache();
|
||||||
callback(error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function downloadApp(app, restoreConfig, progressCallback, callback) {
|
async function downloadApp(app, restoreConfig, progressCallback) {
|
||||||
assert.strictEqual(typeof app, 'object');
|
assert.strictEqual(typeof app, 'object');
|
||||||
assert.strictEqual(typeof restoreConfig, 'object');
|
assert.strictEqual(typeof restoreConfig, 'object');
|
||||||
assert.strictEqual(typeof progressCallback, 'function');
|
assert.strictEqual(typeof progressCallback, 'function');
|
||||||
assert.strictEqual(typeof callback, 'function');
|
|
||||||
|
|
||||||
const appDataDir = safe.fs.realpathSync(path.join(paths.APPS_DATA_DIR, app.id));
|
const appDataDir = safe.fs.realpathSync(path.join(paths.APPS_DATA_DIR, app.id));
|
||||||
if (!appDataDir) return callback(new BoxError(BoxError.FS_ERROR, safe.error.message));
|
if (!appDataDir) throw new BoxError(BoxError.FS_ERROR, safe.error.message);
|
||||||
const dataLayout = new DataLayout(appDataDir, app.dataDir ? [{ localDir: app.dataDir, remoteDir: 'data' }] : []);
|
const dataLayout = new DataLayout(appDataDir, app.dataDir ? [{ localDir: app.dataDir, remoteDir: 'data' }] : []);
|
||||||
|
|
||||||
const startTime = new Date();
|
const startTime = new Date();
|
||||||
const getBackupConfigFunc = restoreConfig.backupConfig ? (next) => next(null, restoreConfig.backupConfig) : getBackupConfig;
|
const backupConfig = restoreConfig.backupConfig || await settings.getBackupConfig();
|
||||||
|
|
||||||
getBackupConfigFunc(function (error, backupConfig) {
|
const downloadAsync = util.promisify(download);
|
||||||
if (error) return callback(error);
|
await downloadAsync(backupConfig, restoreConfig.backupId, restoreConfig.backupFormat, dataLayout, progressCallback);
|
||||||
|
|
||||||
download(backupConfig, restoreConfig.backupId, restoreConfig.backupFormat, dataLayout, progressCallback, function (error) {
|
|
||||||
debug('downloadApp: time: %s', (new Date() - startTime)/1000);
|
debug('downloadApp: time: %s', (new Date() - startTime)/1000);
|
||||||
|
|
||||||
callback(error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function runBackupUpload(uploadConfig, progressCallback, callback) {
|
function runBackupUpload(uploadConfig, progressCallback, callback) {
|
||||||
@@ -731,33 +718,25 @@ function runBackupUpload(uploadConfig, progressCallback, callback) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function snapshotBox(progressCallback, callback) {
|
async function snapshotBox(progressCallback) {
|
||||||
assert.strictEqual(typeof progressCallback, 'function');
|
assert.strictEqual(typeof progressCallback, 'function');
|
||||||
assert.strictEqual(typeof callback, 'function');
|
|
||||||
|
|
||||||
progressCallback({ message: 'Snapshotting box' });
|
progressCallback({ message: 'Snapshotting box' });
|
||||||
|
|
||||||
const startTime = new Date();
|
const startTime = new Date();
|
||||||
|
|
||||||
database.exportToFile(`${paths.BOX_DATA_DIR}/box.mysqldump`, function (error) {
|
await database.exportToFile(`${paths.BOX_DATA_DIR}/box.mysqldump`);
|
||||||
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
|
|
||||||
|
|
||||||
debug(`snapshotBox: took ${(new Date() - startTime)/1000} seconds`);
|
debug(`snapshotBox: took ${(new Date() - startTime)/1000} seconds`);
|
||||||
|
|
||||||
return callback();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function uploadBoxSnapshot(backupConfig, progressCallback, callback) {
|
async function uploadBoxSnapshot(backupConfig, progressCallback) {
|
||||||
assert.strictEqual(typeof backupConfig, 'object');
|
assert.strictEqual(typeof backupConfig, 'object');
|
||||||
assert.strictEqual(typeof progressCallback, 'function');
|
assert.strictEqual(typeof progressCallback, 'function');
|
||||||
assert.strictEqual(typeof callback, 'function');
|
|
||||||
|
|
||||||
snapshotBox(progressCallback, function (error) {
|
await snapshotBox(progressCallback);
|
||||||
if (error) return callback(error);
|
|
||||||
|
|
||||||
const boxDataDir = safe.fs.realpathSync(paths.BOX_DATA_DIR);
|
const boxDataDir = safe.fs.realpathSync(paths.BOX_DATA_DIR);
|
||||||
if (!boxDataDir) return callback(new BoxError(BoxError.FS_ERROR, `Error resolving boxdata: ${safe.error.message}`));
|
if (!boxDataDir) throw new BoxError(BoxError.FS_ERROR, `Error resolving boxdata: ${safe.error.message}`);
|
||||||
|
|
||||||
const uploadConfig = {
|
const uploadConfig = {
|
||||||
backupId: 'snapshot/box',
|
backupId: 'snapshot/box',
|
||||||
@@ -770,14 +749,11 @@ function uploadBoxSnapshot(backupConfig, progressCallback, callback) {
|
|||||||
|
|
||||||
const startTime = new Date();
|
const startTime = new Date();
|
||||||
|
|
||||||
runBackupUpload(uploadConfig, progressCallback, function (error) {
|
await util.promisify(runBackupUpload)(uploadConfig, progressCallback);
|
||||||
if (error) return callback(error);
|
|
||||||
|
|
||||||
debug(`uploadBoxSnapshot: took ${(new Date() - startTime)/1000} seconds`);
|
debug(`uploadBoxSnapshot: took ${(new Date() - startTime)/1000} seconds`);
|
||||||
|
|
||||||
backups.setSnapshotInfo('box', { timestamp: new Date().toISOString(), format: backupConfig.format }, callback);
|
await backups.setSnapshotInfo('box', { timestamp: new Date().toISOString(), format: backupConfig.format });
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function rotateBoxBackup(backupConfig, tag, options, appBackupIds, progressCallback) {
|
async function rotateBoxBackup(backupConfig, tag, options, appBackupIds, progressCallback) {
|
||||||
@@ -822,23 +798,18 @@ async function rotateBoxBackup(backupConfig, tag, options, appBackupIds, progres
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function backupBoxWithAppBackupIds(appBackupIds, tag, options, progressCallback, callback) {
|
async function backupBoxWithAppBackupIds(appBackupIds, tag, options, progressCallback) {
|
||||||
assert(Array.isArray(appBackupIds));
|
assert(Array.isArray(appBackupIds));
|
||||||
assert.strictEqual(typeof tag, 'string');
|
assert.strictEqual(typeof tag, 'string');
|
||||||
assert.strictEqual(typeof options, 'object');
|
assert.strictEqual(typeof options, 'object');
|
||||||
assert.strictEqual(typeof progressCallback, 'function');
|
assert.strictEqual(typeof progressCallback, 'function');
|
||||||
assert.strictEqual(typeof callback, 'function');
|
|
||||||
|
|
||||||
getBackupConfig(function (error, backupConfig) {
|
const backupConfig = await settings.getBackupConfig();
|
||||||
if (error) return callback(error);
|
|
||||||
|
|
||||||
uploadBoxSnapshot(backupConfig, progressCallback, async function (error) {
|
await uploadBoxSnapshot(backupConfig, progressCallback);
|
||||||
if (error) return callback(error);
|
|
||||||
|
|
||||||
const [rotateError, backupId] = await safe(rotateBoxBackup(backupConfig, tag, options, appBackupIds, progressCallback));
|
const backupId = await rotateBoxBackup(backupConfig, tag, options, appBackupIds, progressCallback);
|
||||||
callback(rotateError, backupId);
|
return backupId;
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function rotateAppBackup(backupConfig, app, tag, options, progressCallback) {
|
async function rotateAppBackup(backupConfig, app, tag, options, progressCallback) {
|
||||||
@@ -888,56 +859,43 @@ async function rotateAppBackup(backupConfig, app, tag, options, progressCallback
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function backupApp(app, options, progressCallback, callback) {
|
async function backupApp(app, options, progressCallback) {
|
||||||
assert.strictEqual(typeof app, 'object');
|
assert.strictEqual(typeof app, 'object');
|
||||||
assert.strictEqual(typeof options, 'object');
|
assert.strictEqual(typeof options, 'object');
|
||||||
assert.strictEqual(typeof progressCallback, 'function');
|
assert.strictEqual(typeof progressCallback, 'function');
|
||||||
assert.strictEqual(typeof callback, 'function');
|
|
||||||
|
|
||||||
if (options.snapshotOnly) return snapshotApp(app, progressCallback, callback);
|
if (options.snapshotOnly) return await snapshotApp(app, progressCallback);
|
||||||
|
|
||||||
const tag = (new Date()).toISOString().replace(/[T.]/g, '-').replace(/[:Z]/g,'');
|
const tag = (new Date()).toISOString().replace(/[T.]/g, '-').replace(/[:Z]/g,'');
|
||||||
|
|
||||||
debug(`backupApp - Backing up ${app.fqdn} with tag ${tag}`);
|
debug(`backupApp - Backing up ${app.fqdn} with tag ${tag}`);
|
||||||
|
|
||||||
backupAppWithTag(app, tag, options, progressCallback, callback);
|
await backupAppWithTag(app, tag, options, progressCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function snapshotApp(app, progressCallback) {
|
||||||
function snapshotApp(app, progressCallback, callback) {
|
|
||||||
assert.strictEqual(typeof app, 'object');
|
assert.strictEqual(typeof app, 'object');
|
||||||
assert.strictEqual(typeof progressCallback, 'function');
|
assert.strictEqual(typeof progressCallback, 'function');
|
||||||
assert.strictEqual(typeof callback, 'function');
|
|
||||||
|
|
||||||
const startTime = new Date();
|
const startTime = new Date();
|
||||||
progressCallback({ message: `Snapshotting app ${app.fqdn}` });
|
progressCallback({ message: `Snapshotting app ${app.fqdn}` });
|
||||||
|
|
||||||
const appsBackupConfig = util.callbackify(apps.backupConfig);
|
await apps.backupConfig(app);
|
||||||
|
await services.backupAddons(app, app.manifest.addons);
|
||||||
appsBackupConfig(app, async function (error) {
|
|
||||||
if (error) return callback(error);
|
|
||||||
|
|
||||||
[error] = await safe(services.backupAddons(app, app.manifest.addons));
|
|
||||||
if (error) return callback(error);
|
|
||||||
|
|
||||||
debugApp(app, `snapshotApp: took ${(new Date() - startTime)/1000} seconds`);
|
debugApp(app, `snapshotApp: took ${(new Date() - startTime)/1000} seconds`);
|
||||||
|
|
||||||
return callback(null);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function uploadAppSnapshot(backupConfig, app, progressCallback, callback) {
|
async function uploadAppSnapshot(backupConfig, app, progressCallback) {
|
||||||
assert.strictEqual(typeof backupConfig, 'object');
|
assert.strictEqual(typeof backupConfig, 'object');
|
||||||
assert.strictEqual(typeof app, 'object');
|
assert.strictEqual(typeof app, 'object');
|
||||||
assert.strictEqual(typeof progressCallback, 'function');
|
assert.strictEqual(typeof progressCallback, 'function');
|
||||||
assert.strictEqual(typeof callback, 'function');
|
|
||||||
|
|
||||||
snapshotApp(app, progressCallback, function (error) {
|
await snapshotApp(app, progressCallback);
|
||||||
if (error) return callback(error);
|
|
||||||
|
|
||||||
const backupId = util.format('snapshot/app_%s', app.id);
|
const backupId = util.format('snapshot/app_%s', app.id);
|
||||||
const appDataDir = safe.fs.realpathSync(path.join(paths.APPS_DATA_DIR, app.id));
|
const appDataDir = safe.fs.realpathSync(path.join(paths.APPS_DATA_DIR, app.id));
|
||||||
if (!appDataDir) return callback(new BoxError(BoxError.FS_ERROR, `Error resolving appsdata: ${safe.error.message}`));
|
if (!appDataDir) throw new BoxError(BoxError.FS_ERROR, `Error resolving appsdata: ${safe.error.message}`);
|
||||||
|
|
||||||
const dataLayout = new DataLayout(appDataDir, app.dataDir ? [{ localDir: app.dataDir, remoteDir: 'data' }] : []);
|
const dataLayout = new DataLayout(appDataDir, app.dataDir ? [{ localDir: app.dataDir, remoteDir: 'data' }] : []);
|
||||||
|
|
||||||
@@ -952,86 +910,64 @@ function uploadAppSnapshot(backupConfig, app, progressCallback, callback) {
|
|||||||
|
|
||||||
const startTime = new Date();
|
const startTime = new Date();
|
||||||
|
|
||||||
runBackupUpload(uploadConfig, progressCallback, function (error) {
|
await util.promisify(runBackupUpload)(uploadConfig, progressCallback);
|
||||||
if (error) return callback(error);
|
|
||||||
|
|
||||||
debugApp(app, `uploadAppSnapshot: ${backupId} done. ${(new Date() - startTime)/1000} seconds`);
|
debugApp(app, `uploadAppSnapshot: ${backupId} done. ${(new Date() - startTime)/1000} seconds`);
|
||||||
|
|
||||||
backups.setSnapshotInfo(app.id, { timestamp: new Date().toISOString(), manifest: app.manifest, format: backupConfig.format }, callback);
|
await backups.setSnapshotInfo(app.id, { timestamp: new Date().toISOString(), manifest: app.manifest, format: backupConfig.format });
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function backupAppWithTag(app, tag, options, progressCallback, callback) {
|
async function backupAppWithTag(app, tag, options, progressCallback) {
|
||||||
assert.strictEqual(typeof app, 'object');
|
assert.strictEqual(typeof app, 'object');
|
||||||
assert.strictEqual(typeof tag, 'string');
|
assert.strictEqual(typeof tag, 'string');
|
||||||
assert.strictEqual(typeof options, 'object');
|
assert.strictEqual(typeof options, 'object');
|
||||||
assert.strictEqual(typeof progressCallback, 'function');
|
assert.strictEqual(typeof progressCallback, 'function');
|
||||||
assert.strictEqual(typeof callback, 'function');
|
|
||||||
|
|
||||||
if (!canBackupApp(app)) { // if we cannot backup, reuse it's most recent backup
|
if (!canBackupApp(app)) { // if we cannot backup, reuse it's most recent backup
|
||||||
const [error, results] = await safe(backups.getByIdentifierAndStatePaged(app.id, backups.BACKUP_STATE_NORMAL, 1, 1));
|
const results = await backups.getByIdentifierAndStatePaged(app.id, backups.BACKUP_STATE_NORMAL, 1, 1);
|
||||||
if (error) return callback(error);
|
if (results.length === 0) return null; // no backup to re-use
|
||||||
if (results.length === 0) return callback(null, null); // no backup to re-use
|
|
||||||
|
|
||||||
return callback(null, results[0].id);
|
return results[0].id;
|
||||||
}
|
}
|
||||||
|
|
||||||
getBackupConfig(function (error, backupConfig) {
|
const backupConfig = await settings.getBackupConfig();
|
||||||
if (error) return callback(error);
|
|
||||||
|
|
||||||
uploadAppSnapshot(backupConfig, app, progressCallback, async function (error) {
|
await uploadAppSnapshot(backupConfig, app, progressCallback);
|
||||||
if (error) return callback(error);
|
const backupId = await rotateAppBackup(backupConfig, app, tag, options, progressCallback);
|
||||||
|
return backupId;
|
||||||
const [rotateError, backupId] = await safe(rotateAppBackup(backupConfig, app, tag, options, progressCallback));
|
|
||||||
callback(rotateError, backupId);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// this function expects you to have a lock. Unlike other progressCallback this also has a progress field
|
// this function expects you to have a lock. Unlike other progressCallback this also has a progress field
|
||||||
function backupBoxAndApps(options, progressCallback, callback) {
|
async function backupBoxAndApps(options, progressCallback) {
|
||||||
assert.strictEqual(typeof options, 'object');
|
assert.strictEqual(typeof options, 'object');
|
||||||
assert.strictEqual(typeof progressCallback, 'function');
|
assert.strictEqual(typeof progressCallback, 'function');
|
||||||
assert.strictEqual(typeof callback, 'function');
|
|
||||||
|
|
||||||
const tag = (new Date()).toISOString().replace(/[T.]/g, '-').replace(/[:Z]/g,'');
|
const tag = (new Date()).toISOString().replace(/[T.]/g, '-').replace(/[:Z]/g,'');
|
||||||
|
|
||||||
util.callbackify(apps.list)(function (error, allApps) {
|
const allApps = await apps.list();
|
||||||
if (error) return callback(error);
|
|
||||||
|
|
||||||
let percent = 1;
|
let percent = 1;
|
||||||
let step = 100/(allApps.length+2);
|
let step = 100/(allApps.length+2);
|
||||||
|
|
||||||
async.mapSeries(allApps, function iterator(app, iteratorCallback) {
|
const appBackupIds = [];
|
||||||
|
for (const app of allApps) {
|
||||||
progressCallback({ percent: percent, message: `Backing up ${app.fqdn}` });
|
progressCallback({ percent: percent, message: `Backing up ${app.fqdn}` });
|
||||||
percent += step;
|
percent += step;
|
||||||
|
|
||||||
if (!app.enableBackup) {
|
if (!app.enableBackup) {
|
||||||
debug(`Skipped backup ${app.fqdn}`);
|
debug(`Skipped backup ${app.fqdn}`);
|
||||||
return iteratorCallback(null, null); // nothing to backup
|
return; // nothing to backup
|
||||||
}
|
}
|
||||||
|
|
||||||
const startTime = new Date();
|
const startTime = new Date();
|
||||||
backupAppWithTag(app, tag, options, (progress) => progressCallback({ percent: percent, message: progress.message }), function (error, backupId) {
|
const backupId = await backupAppWithTag(app, tag, options, (progress) => progressCallback({ percent: percent, message: progress.message }));
|
||||||
if (error) {
|
|
||||||
debugApp(app, 'Unable to backup', error);
|
|
||||||
return iteratorCallback(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
debugApp(app, `Backed up. Took ${(new Date() - startTime)/1000} seconds`);
|
debugApp(app, `Backed up. Took ${(new Date() - startTime)/1000} seconds`);
|
||||||
|
if (backupId) appBackupIds.push(backupId); // backupId can be null if in BAD_STATE and never backed up
|
||||||
iteratorCallback(null, backupId || null); // clear backupId if is in BAD_STATE and never backed up
|
}
|
||||||
});
|
|
||||||
}, function appsBackedUp(error, backupIds) {
|
|
||||||
if (error) return callback(error);
|
|
||||||
|
|
||||||
backupIds = backupIds.filter(function (id) { return id !== null; }); // remove apps in bad state that were never backed up
|
|
||||||
|
|
||||||
progressCallback({ percent: percent, message: 'Backing up system data' });
|
progressCallback({ percent: percent, message: 'Backing up system data' });
|
||||||
percent += step;
|
percent += step;
|
||||||
|
|
||||||
backupBoxWithAppBackupIds(backupIds, tag, options, (progress) => progressCallback({ percent: percent, message: progress.message }), callback);
|
const backupId = await backupBoxWithAppBackupIds(appBackupIds, tag, options, (progress) => progressCallback({ percent: percent, message: progress.message }));
|
||||||
});
|
return backupId;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-10
@@ -19,7 +19,7 @@ const assert = require('assert'),
|
|||||||
constants = require('./constants.js'),
|
constants = require('./constants.js'),
|
||||||
debug = require('debug')('box:database'),
|
debug = require('debug')('box:database'),
|
||||||
mysql = require('mysql'),
|
mysql = require('mysql'),
|
||||||
once = require('once'),
|
safe = require('safetydance'),
|
||||||
shell = require('./shell.js'),
|
shell = require('./shell.js'),
|
||||||
util = require('util');
|
util = require('util');
|
||||||
|
|
||||||
@@ -131,21 +131,18 @@ async function transaction(queries) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function importFromFile(file, callback) {
|
async function importFromFile(file) {
|
||||||
assert.strictEqual(typeof file, 'string');
|
assert.strictEqual(typeof file, 'string');
|
||||||
assert.strictEqual(typeof callback, 'function');
|
|
||||||
|
|
||||||
const cmd = `/usr/bin/mysql -h "${gDatabase.hostname}" -u ${gDatabase.username} -p${gDatabase.password} ${gDatabase.name} < ${file}`;
|
const cmd = `/usr/bin/mysql -h "${gDatabase.hostname}" -u ${gDatabase.username} -p${gDatabase.password} ${gDatabase.name} < ${file}`;
|
||||||
|
|
||||||
async.series([
|
await query('CREATE DATABASE IF NOT EXISTS box');
|
||||||
query.bind(null, 'CREATE DATABASE IF NOT EXISTS box'),
|
const [error] = await safe(shell.promises.exec('importFromFile', cmd));
|
||||||
child_process.exec.bind(null, cmd)
|
if (error) throw new BoxError(BoxError.DATABASE_ERROR, error);
|
||||||
], callback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function exportToFile(file, callback) {
|
async function exportToFile(file) {
|
||||||
assert.strictEqual(typeof file, 'string');
|
assert.strictEqual(typeof file, 'string');
|
||||||
assert.strictEqual(typeof callback, 'function');
|
|
||||||
|
|
||||||
// latest mysqldump enables column stats by default which is not present in MySQL 5.7 server
|
// latest mysqldump enables column stats by default which is not present in MySQL 5.7 server
|
||||||
// this option must not be set in production cloudrons which still use the old mysqldump
|
// this option must not be set in production cloudrons which still use the old mysqldump
|
||||||
@@ -153,5 +150,6 @@ function exportToFile(file, callback) {
|
|||||||
|
|
||||||
const cmd = `/usr/bin/mysqldump -h "${gDatabase.hostname}" -u root -p${gDatabase.password} ${disableColStats} --single-transaction --routines --triggers ${gDatabase.name} > "${file}"`;
|
const cmd = `/usr/bin/mysqldump -h "${gDatabase.hostname}" -u root -p${gDatabase.password} ${disableColStats} --single-transaction --routines --triggers ${gDatabase.name} > "${file}"`;
|
||||||
|
|
||||||
child_process.exec(cmd, callback);
|
const [error] = await safe(shell.promises.exec('exportToFile', cmd));
|
||||||
|
if (error) throw new BoxError(BoxError.DATABASE_ERROR, error);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -154,7 +154,7 @@ async function restoreTask(backupConfig, backupId, sysinfoConfig, options, audit
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
setProgress('restore', 'Downloading backup');
|
setProgress('restore', 'Downloading backup');
|
||||||
await util.promisify(backuptask.restore)(backupConfig, backupId, (progress) => setProgress('restore', progress.message));
|
await backuptask.restore(backupConfig, backupId, (progress) => setProgress('restore', progress.message));
|
||||||
await settings.setSysinfoConfig(sysinfoConfig);
|
await settings.setSysinfoConfig(sysinfoConfig);
|
||||||
await reverseProxy.restoreFallbackCertificates();
|
await reverseProxy.restoreFallbackCertificates();
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -92,7 +92,7 @@ async.series([
|
|||||||
if (updateError) return exitSync({ error: updateError, code: 50 });
|
if (updateError) return exitSync({ error: updateError, code: 50 });
|
||||||
|
|
||||||
const progressCallback = async function (progress, callback) {
|
const progressCallback = async function (progress, callback) {
|
||||||
await safe(tasks.update(taskId, progress));
|
await safe(tasks.update(taskId, progress), { debug });
|
||||||
if (callback) callback();
|
if (callback) callback();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+19
-36
@@ -10,6 +10,7 @@ const apptask = require('../apptask.js'),
|
|||||||
expect = require('expect.js'),
|
expect = require('expect.js'),
|
||||||
fs = require('fs'),
|
fs = require('fs'),
|
||||||
paths = require('../paths.js'),
|
paths = require('../paths.js'),
|
||||||
|
safe = require('safetydance'),
|
||||||
_ = require('underscore');
|
_ = require('underscore');
|
||||||
|
|
||||||
describe('apptask', function () {
|
describe('apptask', function () {
|
||||||
@@ -18,70 +19,52 @@ describe('apptask', function () {
|
|||||||
before(setup);
|
before(setup);
|
||||||
after(cleanup);
|
after(cleanup);
|
||||||
|
|
||||||
it('create volume', function (done) {
|
it('create volume', async function () {
|
||||||
apptask._createAppDir(app, function (error) {
|
await apptask._createAppDir(app);
|
||||||
expect(fs.existsSync(paths.APPS_DATA_DIR + '/' + app.id)).to.be(true);
|
expect(fs.existsSync(paths.APPS_DATA_DIR + '/' + app.id)).to.be(true);
|
||||||
expect(fs.existsSync(paths.APPS_DATA_DIR + '/' + app.id + '/data')).to.be(false);
|
expect(fs.existsSync(paths.APPS_DATA_DIR + '/' + app.id + '/data')).to.be(false);
|
||||||
expect(error).to.be(null);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('delete volume - removeDirectory (false) ', function (done) {
|
it('delete volume - removeDirectory (false) ', async function () {
|
||||||
apptask._deleteAppDir(app, { removeDirectory: false }, function (error) {
|
await apptask._deleteAppDir(app, { removeDirectory: false });
|
||||||
expect(fs.existsSync(paths.APPS_DATA_DIR + '/' + app.id)).to.be(true);
|
expect(fs.existsSync(paths.APPS_DATA_DIR + '/' + app.id)).to.be(true);
|
||||||
expect(fs.readdirSync(paths.APPS_DATA_DIR + '/' + app.id).length).to.be(0); // empty
|
expect(fs.readdirSync(paths.APPS_DATA_DIR + '/' + app.id).length).to.be(0); // empty
|
||||||
expect(error).to.be(null);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('delete volume - removeDirectory (true) ', function (done) {
|
it('delete volume - removeDirectory (true) ', async function () {
|
||||||
apptask._deleteAppDir(app, { removeDirectory: true }, function (error) {
|
await apptask._deleteAppDir(app, { removeDirectory: true });
|
||||||
expect(!fs.existsSync(paths.APPS_DATA_DIR + '/' + app.id)).to.be(true);
|
expect(!fs.existsSync(paths.APPS_DATA_DIR + '/' + app.id)).to.be(true);
|
||||||
expect(error).to.be(null);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('barfs on empty manifest', function (done) {
|
it('barfs on empty manifest', async function () {
|
||||||
var badApp = _.extend({ }, app);
|
const badApp = _.extend({ }, app);
|
||||||
badApp.manifest = { };
|
badApp.manifest = { };
|
||||||
|
|
||||||
apptask._verifyManifest(badApp.manifest, function (error) {
|
const [error] = await safe(apptask._verifyManifest(badApp.manifest));
|
||||||
expect(error).to.be.ok();
|
expect(error).to.be.ok();
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fails on bad manifest', function (done) {
|
it('fails on bad manifest', async function () {
|
||||||
var badApp = _.extend({ }, app);
|
const badApp = _.extend({ }, app);
|
||||||
badApp.manifest = _.extend({ }, app.manifest);
|
badApp.manifest = _.extend({ }, app.manifest);
|
||||||
delete badApp.manifest.httpPort;
|
delete badApp.manifest.httpPort;
|
||||||
|
|
||||||
apptask._verifyManifest(badApp.manifest, function (error) {
|
const [error] = await safe(apptask._verifyManifest(badApp.manifest));
|
||||||
expect(error).to.be.ok();
|
expect(error).to.be.ok();
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('barfs on incompatible manifest', function (done) {
|
it('barfs on incompatible manifest', async function () {
|
||||||
var badApp = _.extend({ }, app);
|
const badApp = _.extend({ }, app);
|
||||||
badApp.manifest = _.extend({ }, app.manifest);
|
badApp.manifest = _.extend({ }, app.manifest);
|
||||||
badApp.manifest.maxBoxVersion = '0.0.0'; // max box version is too small
|
badApp.manifest.maxBoxVersion = '0.0.0'; // max box version is too small
|
||||||
|
|
||||||
apptask._verifyManifest(badApp.manifest, function (error) {
|
const [error] = await safe(apptask._verifyManifest(badApp.manifest));
|
||||||
expect(error).to.be.ok();
|
expect(error).to.be.ok();
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('verifies manifest', function (done) {
|
it('verifies manifest', async function () {
|
||||||
var goodApp = _.extend({ }, app);
|
const goodApp = _.extend({ }, app);
|
||||||
|
|
||||||
apptask._verifyManifest(goodApp.manifest, function (error) {
|
await apptask._verifyManifest(goodApp.manifest);
|
||||||
expect(error).to.be(null);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+11
-18
@@ -5,7 +5,9 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const database = require('../database'),
|
const database = require('../database'),
|
||||||
expect = require('expect.js');
|
expect = require('expect.js'),
|
||||||
|
fs = require('fs'),
|
||||||
|
safe = require('safetydance');
|
||||||
|
|
||||||
describe('database', function () {
|
describe('database', function () {
|
||||||
describe('init', function () {
|
describe('init', function () {
|
||||||
@@ -28,32 +30,23 @@ describe('database', function () {
|
|||||||
await database._clear();
|
await database._clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('cannot import from non-existent file', function (done) {
|
it('cannot import from non-existent file', async function () {
|
||||||
database.importFromFile('/does/not/exist', function (error) {
|
const [error] = await safe(database.importFromFile('/does/not/exist'));
|
||||||
expect(error).to.be.ok();
|
expect(error).to.be.ok();
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can export to file', function (done) {
|
it('can export to file', async function () {
|
||||||
// arch only has maria db which lacks some mysqldump options we need, this is only here to allow running the tests :-/
|
// arch only has maria db which lacks some mysqldump options we need, this is only here to allow running the tests :-/
|
||||||
if (require('child_process').execSync('/usr/bin/mysqldump --version').toString().indexOf('MariaDB') !== -1) return done();
|
if (!fs.readFileSync('/etc/lsb-release', 'utf8').includes('Ubuntu')) return;
|
||||||
|
|
||||||
database.exportToFile('/tmp/box.mysqldump', function (error) {
|
await database.exportToFile('/tmp/box.mysqldump');
|
||||||
expect(error).to.be(null);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can import from file', function (done) {
|
it('can import from file', async function () {
|
||||||
// arch only has maria db which lacks some mysqldump options we need, this is only here to allow running the tests :-/
|
// arch only has maria db which lacks some mysqldump options we need, this is only here to allow running the tests :-/
|
||||||
if (require('child_process').execSync('/usr/bin/mysqldump --version').toString().indexOf('MariaDB') !== -1) return done();
|
if (!fs.readFileSync('/etc/lsb-release', 'utf8').includes('Ubuntu')) return;
|
||||||
|
|
||||||
database.importFromFile('/tmp/box.mysqldump', function (error) {
|
await database.importFromFile('/tmp/box.mysqldump');
|
||||||
expect(error).to.be(null);
|
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
+1
-1
@@ -148,7 +148,7 @@ async function update(boxUpdateInfo, options, progressCallback) {
|
|||||||
if (!options.skipBackup) {
|
if (!options.skipBackup) {
|
||||||
progressCallback({ percent: 10, message: 'Backing up' });
|
progressCallback({ percent: 10, message: 'Backing up' });
|
||||||
|
|
||||||
await util.promisify(backuptask.backupBoxAndApps)({ preserveSecs: 3*7*24*60*60 }, (progress) => progressCallback({ percent: 10+progress.percent*70/100, message: progress.message }));
|
await backuptask.backupBoxAndApps({ preserveSecs: 3*7*24*60*60 }, (progress) => progressCallback({ percent: 10+progress.percent*70/100, message: progress.message }));
|
||||||
}
|
}
|
||||||
|
|
||||||
debug('updating box %s', boxUpdateInfo.sourceTarballUrl);
|
debug('updating box %s', boxUpdateInfo.sourceTarballUrl);
|
||||||
|
|||||||
Reference in New Issue
Block a user