diff --git a/src/apps.js b/src/apps.js index 5bcea4f8b..77ae74f88 100644 --- a/src/apps.js +++ b/src/apps.js @@ -108,7 +108,6 @@ var appdb = require('./appdb.js'), assert = require('assert'), async = require('async'), backups = require('./backups.js'), - BackupsError = backups.BackupsError, BoxError = require('./boxerror.js'), constants = require('./constants.js'), DatabaseError = require('./databaseerror.js'), @@ -1453,8 +1452,8 @@ function restore(appId, data, auditSource, callback) { var func = data.backupId ? backups.get.bind(null, data.backupId) : function (next) { return next(null, { manifest: app.manifest }); }; func(function (error, backupInfo) { - if (error && error.reason === BackupsError.NOT_FOUND) return callback(new AppsError(AppsError.EXTERNAL_ERROR, error.message)); - if (error && error.reason === BackupsError.EXTERNAL_ERROR) return callback(new AppsError(AppsError.EXTERNAL_ERROR, error.message)); + if (error && error.reason === BoxError.NOT_FOUND) return callback(new AppsError(AppsError.EXTERNAL_ERROR, error.message)); + if (error && error.reason === BoxError.EXTERNAL_ERROR) return callback(new AppsError(AppsError.EXTERNAL_ERROR, error.message)); if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error)); if (!backupInfo.manifest) callback(new AppsError(AppsError.EXTERNAL_ERROR, 'Could not get restore manifest')); @@ -1570,8 +1569,8 @@ function clone(appId, data, user, auditSource, callback) { if (error) return callback(error); backups.get(backupId, function (error, backupInfo) { - if (error && error.reason === BackupsError.EXTERNAL_ERROR) return callback(new AppsError(AppsError.EXTERNAL_ERROR, error.message)); - if (error && error.reason === BackupsError.NOT_FOUND) return callback(new AppsError(AppsError.EXTERNAL_ERROR, 'Backup not found')); + if (error && error.reason === BoxError.EXTERNAL_ERROR) return callback(new AppsError(AppsError.EXTERNAL_ERROR, error.message)); + if (error && error.reason === BoxError.NOT_FOUND) return callback(new AppsError(AppsError.EXTERNAL_ERROR, 'Backup not found')); if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error)); if (!backupInfo.manifest) callback(new AppsError(AppsError.EXTERNAL_ERROR, 'Could not get restore config')); diff --git a/src/backups.js b/src/backups.js index 942868b8b..d425cd577 100644 --- a/src/backups.js +++ b/src/backups.js @@ -1,8 +1,6 @@ 'use strict'; exports = module.exports = { - BackupsError: BackupsError, - testConfig: testConfig, getByStatePaged: getByStatePaged, @@ -45,6 +43,7 @@ var addons = require('./addons.js'), async = require('async'), assert = require('assert'), backupdb = require('./backupdb.js'), + BoxError = require('./boxerror.js'), constants = require('./constants.js'), crypto = require('crypto'), database = require('./database.js'), @@ -78,31 +77,6 @@ function debugApp(app) { debug(app.fqdn + ' ' + util.format.apply(util, Array.prototype.slice.call(arguments, 1))); } -function BackupsError(reason, errorOrMessage) { - assert.strictEqual(typeof reason, 'string'); - assert(errorOrMessage instanceof Error || typeof errorOrMessage === 'string' || typeof errorOrMessage === 'undefined'); - - Error.call(this); - Error.captureStackTrace(this, this.constructor); - - this.name = this.constructor.name; - this.reason = reason; - if (typeof errorOrMessage === 'undefined') { - this.message = reason; - } else if (typeof errorOrMessage === 'string') { - this.message = errorOrMessage; - } else { - this.message = 'Internal error'; - this.nestedError = errorOrMessage; - } -} -util.inherits(BackupsError, Error); -BackupsError.EXTERNAL_ERROR = 'external error'; -BackupsError.INTERNAL_ERROR = 'internal error'; -BackupsError.BAD_STATE = 'bad state'; -BackupsError.BAD_FIELD = 'bad field'; -BackupsError.NOT_FOUND = 'not found'; - // choose which storage backend we use for test purpose we use s3 function api(provider) { switch (provider) { @@ -136,12 +110,12 @@ function testConfig(backupConfig, callback) { assert.strictEqual(typeof callback, 'function'); var func = api(backupConfig.provider); - if (!func) return callback(new BackupsError(BackupsError.BAD_FIELD, 'unknown storage provider')); + if (!func) return callback(new BoxError(BoxError.BAD_FIELD, 'unknown storage provider', { field: 'provider' })); - if (backupConfig.format !== 'tgz' && backupConfig.format !== 'rsync') return callback(new BackupsError(BackupsError.BAD_FIELD, 'unknown format')); + if (backupConfig.format !== 'tgz' && backupConfig.format !== 'rsync') return callback(new BoxError(BoxError.BAD_FIELD, 'unknown format', { field: 'format' })); // remember to adjust the cron ensureBackup task interval accordingly - if (backupConfig.intervalSecs < 6 * 60 * 60) return callback(new BackupsError(BackupsError.BAD_FIELD, 'Interval must be atleast 6 hours')); + if (backupConfig.intervalSecs < 6 * 60 * 60) return callback(new BoxError(BoxError.BAD_FIELD, 'Interval must be atleast 6 hours', { field: 'interval' })); api(backupConfig.provider).testConfig(backupConfig, callback); } @@ -153,7 +127,7 @@ function getByStatePaged(state, page, perPage, callback) { assert.strictEqual(typeof callback, 'function'); backupdb.getByTypeAndStatePaged(backupdb.BACKUP_TYPE_BOX, state, page, perPage, function (error, results) { - if (error) return callback(new BackupsError(BackupsError.INTERNAL_ERROR, error)); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); callback(null, results); }); @@ -166,7 +140,7 @@ function getByAppIdPaged(page, perPage, appId, callback) { assert.strictEqual(typeof callback, 'function'); backupdb.getByAppIdPaged(page, perPage, appId, function (error, results) { - if (error) return callback(new BackupsError(BackupsError.INTERNAL_ERROR, error)); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); callback(null, results); }); @@ -177,8 +151,8 @@ function get(backupId, callback) { assert.strictEqual(typeof callback, 'function'); backupdb.get(backupId, function (error, result) { - if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new BackupsError(BackupsError.NOT_FOUND)); - if (error) return callback(new BackupsError(BackupsError.INTERNAL_ERROR, error)); + if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new BoxError(BoxError.NOT_FOUND)); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); callback(null, result); }); @@ -246,14 +220,14 @@ function createReadStream(sourceFile, key) { stream.on('error', function (error) { debug('createReadStream: read stream error.', error); - ps.emit('error', new BackupsError(BackupsError.EXTERNAL_ERROR, error.message)); + ps.emit('error', new BoxError(BoxError.EXTERNAL_ERROR, error.message)); }); if (key !== null) { var encrypt = crypto.createCipher('aes-256-cbc', key); encrypt.on('error', function (error) { debug('createReadStream: encrypt stream error.', error); - ps.emit('error', new BackupsError(BackupsError.EXTERNAL_ERROR, error.message)); + ps.emit('error', new BoxError(BoxError.EXTERNAL_ERROR, error.message)); }); return stream.pipe(encrypt).pipe(ps); } else { @@ -306,19 +280,19 @@ function tarPack(dataLayout, key, callback) { pack.on('error', function (error) { debug('tarPack: tar stream error.', error); - ps.emit('error', new BackupsError(BackupsError.EXTERNAL_ERROR, error.message)); + ps.emit('error', new BoxError(BoxError.EXTERNAL_ERROR, error.message)); }); gzip.on('error', function (error) { debug('tarPack: gzip stream error.', error); - ps.emit('error', new BackupsError(BackupsError.EXTERNAL_ERROR, error.message)); + ps.emit('error', new BoxError(BoxError.EXTERNAL_ERROR, error.message)); }); if (key !== null) { var encrypt = crypto.createCipher('aes-256-cbc', key); encrypt.on('error', function (error) { debug('tarPack: encrypt stream error.', error); - ps.emit('error', new BackupsError(BackupsError.EXTERNAL_ERROR, error.message)); + ps.emit('error', new BoxError(BoxError.EXTERNAL_ERROR, error.message)); }); pack.pipe(gzip).pipe(encrypt).pipe(ps); } else { @@ -379,7 +353,7 @@ function sync(backupConfig, backupId, dataLayout, progressCallback, callback) { } }, iteratorCallback); }, concurrency, function (error) { - if (error) return callback(new BackupsError(BackupsError.EXTERNAL_ERROR, error.message)); + if (error) return callback(new BoxError(BoxError.EXTERNAL_ERROR, error.message)); callback(); }); @@ -425,7 +399,7 @@ function checkFreeDiskSpace(backupConfig, dataLayout, callback) { for (let localPath of dataLayout.localPaths()) { debug(`checkFreeDiskSpace: getting disk usage of ${localPath}`); let result = safe.child_process.execSync(`du -Dsb ${localPath}`, { encoding: 'utf8' }); - if (!result) return callback(new BackupsError(BackupsError.INTERNAL_ERROR, safe.error)); + if (!result) return callback(new BoxError(BoxError.FS_ERROR, safe.error)); used += parseInt(result, 10); } @@ -433,11 +407,11 @@ function checkFreeDiskSpace(backupConfig, dataLayout, callback) { df.file(backupConfig.backupFolder).then(function (diskUsage) { const needed = used + (1024 * 1024 * 1024); // check if there is atleast 1GB left afterwards - if (diskUsage.available <= needed) return callback(new BackupsError(BackupsError.EXTERNAL_ERROR, `Not enough disk space for backup. Needed: ${prettyBytes(needed)} Available: ${prettyBytes(diskUsage.available)}`)); + if (diskUsage.available <= needed) return callback(new BoxError(BoxError.FS_ERROR, `Not enough disk space for backup. Needed: ${prettyBytes(needed)} Available: ${prettyBytes(diskUsage.available)}`)); callback(null); }).catch(function (error) { - callback(new BackupsError(BackupsError.INTERNAL_ERROR, error)); + callback(new BoxError(BoxError.FS_ERROR, error)); }); } @@ -454,7 +428,7 @@ function upload(backupId, format, dataLayoutString, progressCallback, callback) const dataLayout = DataLayout.fromString(dataLayoutString); settings.getBackupConfig(function (error, backupConfig) { - if (error) return callback(new BackupsError(BackupsError.INTERNAL_ERROR, error)); + if (error) return callback(error); checkFreeDiskSpace(backupConfig, dataLayout, function (error) { if (error) return callback(error); @@ -471,7 +445,7 @@ function upload(backupId, format, dataLayoutString, progressCallback, callback) if (!transferred && !speed) return progressCallback({ message: 'Uploading backup' }); // 0M@0Mbps looks wrong progressCallback({ message: `Uploading backup ${transferred}M@${speed}Mbps` }); }); - tarStream.on('error', retryCallback); // already returns BackupsError + tarStream.on('error', retryCallback); // already returns BoxError api(backupConfig.provider).upload(backupConfig, getBackupFilePath(backupConfig, backupId, format), tarStream, retryCallback); }); @@ -505,17 +479,17 @@ function tarExtract(inStream, dataLayout, key, callback) { inStream.on('error', function (error) { debug('tarExtract: input stream error.', error); - emitError(new BackupsError(BackupsError.EXTERNAL_ERROR, error.message)); + emitError(new BoxError(BoxError.EXTERNAL_ERROR, error.message)); }); gunzip.on('error', function (error) { debug('tarExtract: gunzip stream error.', error); - emitError(new BackupsError(BackupsError.EXTERNAL_ERROR, error.message)); + emitError(new BoxError(BoxError.EXTERNAL_ERROR, error.message)); }); extract.on('error', function (error) { debug('tarExtract: extract stream error.', error); - emitError(new BackupsError(BackupsError.EXTERNAL_ERROR, error.message)); + emitError(new BoxError(BoxError.EXTERNAL_ERROR, error.message)); }); extract.on('finish', function () { @@ -528,7 +502,7 @@ function tarExtract(inStream, dataLayout, key, callback) { var decrypt = crypto.createDecipher('aes-256-cbc', key); decrypt.on('error', function (error) { debug('tarExtract: decrypt stream error.', error); - emitError(new BackupsError(BackupsError.EXTERNAL_ERROR, `Failed to decrypt: ${error.message}`)); + emitError(new BoxError(BoxError.EXTERNAL_ERROR, `Failed to decrypt: ${error.message}`)); }); inStream.pipe(ps).pipe(decrypt).pipe(gunzip).pipe(extract); } else { @@ -546,19 +520,19 @@ function restoreFsMetadata(dataLayout, metadataFile, callback) { debug(`Recreating empty directories in ${dataLayout.toString()}`); var metadataJson = safe.fs.readFileSync(metadataFile, 'utf8'); - if (metadataJson === null) return callback(new BackupsError(BackupsError.EXTERNAL_ERROR, 'Error loading fsmetadata.json:' + safe.error.message)); + if (metadataJson === null) return callback(new BoxError(BoxError.EXTERNAL_ERROR, 'Error loading fsmetadata.json:' + safe.error.message)); var metadata = safe.JSON.parse(metadataJson); - if (metadata === null) return callback(new BackupsError(BackupsError.EXTERNAL_ERROR, 'Error parsing fsmetadata.json:' + safe.error.message)); + if (metadata === null) return callback(new BoxError(BoxError.EXTERNAL_ERROR, 'Error parsing fsmetadata.json:' + safe.error.message)); async.eachSeries(metadata.emptyDirs, function createPath(emptyDir, iteratorDone) { mkdirp(dataLayout.toLocalPath(emptyDir), iteratorDone); }, function (error) { - if (error) return callback(new BackupsError(BackupsError.EXTERNAL_ERROR, `unable to create path: ${error.message}`)); + if (error) return callback(new BoxError(BoxError.EXTERNAL_ERROR, `unable to create path: ${error.message}`)); async.eachSeries(metadata.execFiles, function createPath(execFile, iteratorDone) { fs.chmod(dataLayout.toLocalPath(execFile), parseInt('0755', 8), iteratorDone); }, function (error) { - if (error) return callback(new BackupsError(BackupsError.EXTERNAL_ERROR, `unable to chmod: ${error.message}`)); + if (error) return callback(new BoxError(BoxError.EXTERNAL_ERROR, `unable to chmod: ${error.message}`)); callback(); }); @@ -578,12 +552,12 @@ function downloadDir(backupConfig, backupFilePath, dataLayout, progressCallback, let relativePath = path.relative(backupFilePath, entry.fullPath); if (backupConfig.key) { relativePath = decryptFilePath(relativePath, backupConfig.key); - if (!relativePath) return callback(new BackupsError(BackupsError.BAD_STATE, 'Unable to decrypt file')); + if (!relativePath) return callback(new BoxError(BoxError.BAD_STATE, 'Unable to decrypt file')); } const destFilePath = dataLayout.toLocalPath('./' + relativePath); mkdirp(path.dirname(destFilePath), function (error) { - if (error) return callback(new BackupsError(BackupsError.EXTERNAL_ERROR, error.message)); + if (error) return callback(new BoxError(BoxError.FS_ERROR, error.message)); async.retry({ times: 5, interval: 20000 }, function (retryCallback) { let destStream = createWriteStream(destFilePath, backupConfig.key || null); @@ -671,7 +645,7 @@ function restore(backupConfig, backupId, progressCallback, callback) { debug('restore: download completed, importing database'); database.importFromFile(`${dataLayout.localRoot()}/box.mysqldump`, function (error) { - if (error) return callback(new BackupsError(BackupsError.INTERNAL_ERROR, error)); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); debug('restore: database imported'); @@ -694,7 +668,7 @@ function restoreApp(app, addonsToRestore, restoreConfig, progressCallback, callb var startTime = new Date(); settings.getBackupConfig(function (error, backupConfig) { - if (error) return callback(new BackupsError(BackupsError.INTERNAL_ERROR, error)); + if (error) return callback(error); async.series([ download.bind(null, backupConfig, restoreConfig.backupId, restoreConfig.backupFormat, dataLayout, progressCallback), @@ -718,9 +692,9 @@ function runBackupUpload(backupId, format, dataLayout, progressCallback, callbac shell.sudo(`backup-${backupId}`, [ BACKUP_UPLOAD_CMD, backupId, format, dataLayout.toString() ], { preserveEnv: true, ipc: true }, function (error) { if (error && (error.code === null /* signal */ || (error.code !== 0 && error.code !== 50))) { // backuptask crashed - return callback(new BackupsError(BackupsError.INTERNAL_ERROR, 'Backuptask crashed')); + return callback(new BoxError(BoxError.INTERNAL_ERROR, 'Backuptask crashed')); } else if (error && error.code === 50) { // exited with error - return callback(new BackupsError(BackupsError.EXTERNAL_ERROR, result)); + return callback(new BoxError(BoxError.EXTERNAL_ERROR, result)); } callback(); @@ -748,7 +722,9 @@ function setSnapshotInfo(id, info, callback) { var contents = safe.fs.readFileSync(paths.SNAPSHOT_INFO_FILE, 'utf8'); var data = safe.JSON.parse(contents) || { }; if (info) data[id] = info; else delete data[id]; - if (!safe.fs.writeFileSync(paths.SNAPSHOT_INFO_FILE, JSON.stringify(data, null, 4), 'utf8')) return callback(new BackupsError(BackupsError.EXTERNAL_ERROR, safe.error.message)); + if (!safe.fs.writeFileSync(paths.SNAPSHOT_INFO_FILE, JSON.stringify(data, null, 4), 'utf8')) { + return callback(new BoxError(BoxError.FS_ERROR, safe.error.message)); + } callback(); } @@ -760,7 +736,7 @@ function snapshotBox(progressCallback, callback) { progressCallback({ message: 'Snapshotting box' }); database.exportToFile(`${paths.BOX_DATA_DIR}/box.mysqldump`, function (error) { - if (error) return callback(new BackupsError(BackupsError.INTERNAL_ERROR, error)); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); return callback(); }); @@ -798,7 +774,7 @@ function rotateBoxBackup(backupConfig, tag, appBackupIds, progressCallback, call assert.strictEqual(typeof callback, 'function'); var snapshotInfo = getSnapshotInfo('box'); - if (!snapshotInfo) return callback(new BackupsError(BackupsError.INTERNAL_ERROR, 'Snapshot info missing or corrupt')); + if (!snapshotInfo) return callback(new BoxError(BoxError.INTERNAL_ERROR, 'Snapshot info missing or corrupt')); const snapshotTime = snapshotInfo.timestamp.replace(/[T.]/g, '-').replace(/[:Z]/g,''); // add this to filename to make it unique, so it's easy to download them const backupId = util.format('%s/box_%s_v%s', tag, snapshotTime, constants.VERSION); @@ -807,7 +783,7 @@ function rotateBoxBackup(backupConfig, tag, appBackupIds, progressCallback, call debug(`Rotating box backup to id ${backupId}`); backupdb.add(backupId, { version: constants.VERSION, type: backupdb.BACKUP_TYPE_BOX, dependsOn: appBackupIds, manifest: null, format: format }, function (error) { - if (error) return callback(new BackupsError(BackupsError.INTERNAL_ERROR, error)); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); var copy = api(backupConfig.provider).copy(backupConfig, getBackupFilePath(backupConfig, 'snapshot/box', format), getBackupFilePath(backupConfig, backupId, format)); copy.on('progress', (message) => progressCallback({ message })); @@ -816,7 +792,7 @@ function rotateBoxBackup(backupConfig, tag, appBackupIds, progressCallback, call backupdb.update(backupId, { state: state }, function (error) { if (copyBackupError) return callback(copyBackupError); - if (error) return callback(new BackupsError(BackupsError.INTERNAL_ERROR, error)); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); debug(`Rotated box backup successfully as id ${backupId}`); @@ -833,7 +809,7 @@ function backupBoxWithAppBackupIds(appBackupIds, tag, progressCallback, callback assert.strictEqual(typeof callback, 'function'); settings.getBackupConfig(function (error, backupConfig) { - if (error) return callback(new BackupsError(BackupsError.INTERNAL_ERROR, error)); + if (error) return callback(error); uploadBoxSnapshot(backupConfig, progressCallback, function (error) { if (error) return callback(error); @@ -860,11 +836,11 @@ function snapshotApp(app, progressCallback, callback) { progressCallback({ message: `Snapshotting app ${app.fqdn}` }); if (!safe.fs.writeFileSync(path.join(paths.APPS_DATA_DIR, app.id + '/config.json'), JSON.stringify(app))) { - return callback(new BackupsError(BackupsError.EXTERNAL_ERROR, 'Error creating config.json: ' + safe.error.message)); + return callback(new BoxError(BoxError.FS_ERROR, 'Error creating config.json: ' + safe.error.message)); } addons.backupAddons(app, app.manifest.addons, function (error) { - if (error) return callback(new BackupsError(BackupsError.EXTERNAL_ERROR, error.message)); + if (error) return callback(new BoxError(BoxError.EXTERNAL_ERROR, error.message)); return callback(null); }); @@ -879,7 +855,7 @@ function rotateAppBackup(backupConfig, app, tag, options, progressCallback, call assert.strictEqual(typeof callback, 'function'); var snapshotInfo = getSnapshotInfo(app.id); - if (!snapshotInfo) return callback(new BackupsError(BackupsError.INTERNAL_ERROR, 'Snapshot info missing or corrupt')); + if (!snapshotInfo) return callback(new BoxError(BoxError.INTERNAL_ERROR, 'Snapshot info missing or corrupt')); var manifest = snapshotInfo.restoreConfig ? snapshotInfo.restoreConfig.manifest : snapshotInfo.manifest; // compat const snapshotTime = snapshotInfo.timestamp.replace(/[T.]/g, '-').replace(/[:Z]/g,''); // add this for unique filename which helps when downloading them @@ -889,7 +865,7 @@ function rotateAppBackup(backupConfig, app, tag, options, progressCallback, call debug(`Rotating app backup of ${app.id} to id ${backupId}`); backupdb.add(backupId, { version: manifest.version, type: backupdb.BACKUP_TYPE_APP, dependsOn: [ ], manifest: manifest, format: format }, function (error) { - if (error) return callback(new BackupsError(BackupsError.INTERNAL_ERROR, error)); + if (error) return callback(error); var copy = api(backupConfig.provider).copy(backupConfig, getBackupFilePath(backupConfig, `snapshot/app_${app.id}`, format), getBackupFilePath(backupConfig, backupId, format)); copy.on('progress', (message) => progressCallback({ message })); @@ -898,7 +874,7 @@ function rotateAppBackup(backupConfig, app, tag, options, progressCallback, call backupdb.update(backupId, { preserveSecs: options.preserveSecs || 0, state: state }, function (error) { if (copyBackupError) return callback(copyBackupError); - if (error) return callback(new BackupsError(BackupsError.INTERNAL_ERROR, error)); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); debug(`Rotated app backup of ${app.id} successfully to id ${backupId}`); @@ -947,7 +923,7 @@ function backupAppWithTag(app, tag, options, progressCallback, callback) { if (!canBackupApp(app)) return callback(); // nothing to do settings.getBackupConfig(function (error, backupConfig) { - if (error) return callback(new BackupsError(BackupsError.INTERNAL_ERROR, error)); + if (error) return callback(error); uploadAppSnapshot(backupConfig, app, progressCallback, function (error) { if (error) return callback(error); @@ -978,7 +954,7 @@ function backupBoxAndApps(progressCallback, callback) { const tag = (new Date()).toISOString().replace(/[T.]/g, '-').replace(/[:Z]/g,''); apps.getAll(function (error, allApps) { - if (error) return callback(new BackupsError(BackupsError.INTERNAL_ERROR, error)); + if (error) return callback(new BoxError(BoxError.INTERNAL_ERROR, error)); let percent = 1; let step = 100/(allApps.length+2); @@ -993,7 +969,7 @@ function backupBoxAndApps(progressCallback, callback) { } backupAppWithTag(app, tag, { /* options */ }, (progress) => progressCallback({ percent: percent, message: progress.message }), function (error, backupId) { - if (error && error.reason !== BackupsError.BAD_STATE) { + if (error && error.reason !== BoxError.BAD_STATE) { debugApp(app, 'Unable to backup', error); return iteratorCallback(error); } @@ -1017,10 +993,10 @@ function backupBoxAndApps(progressCallback, callback) { function startBackupTask(auditSource, callback) { let error = locker.lock(locker.OP_FULL_BACKUP); - if (error) return callback(new BackupsError(BackupsError.BAD_STATE, `Cannot backup now: ${error.message}`)); + if (error) return callback(new BoxError(BoxError.BAD_STATE, `Cannot backup now: ${error.message}`)); tasks.add(tasks.TASK_BACKUP, [ ], function (error, taskId) { - if (error) return callback(new BackupsError(BackupsError.INTERNAL_ERROR, error)); + if (error) return callback(new BoxError(BoxError.INTERNAL_ERROR, error)); eventlog.add(eventlog.ACTION_BACKUP_START, auditSource, { taskId }); @@ -1106,7 +1082,7 @@ function cleanupAppBackups(backupConfig, referencedAppBackups, callback) { // we clean app backups of any state because the ones to keep are determined by the box cleanup code backupdb.getByTypePaged(backupdb.BACKUP_TYPE_APP, 1, 1000, function (error, appBackups) { - if (error) return callback(new BackupsError(BackupsError.INTERNAL_ERROR, error)); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); async.eachSeries(appBackups, function iterator(appBackup, iteratorDone) { if (referencedAppBackups.indexOf(appBackup.id) !== -1) return iteratorDone(); @@ -1260,7 +1236,7 @@ function cleanup(auditSource, progressCallback, callback) { function startCleanupTask(auditSource, callback) { tasks.add(tasks.TASK_CLEAN_BACKUPS, [ auditSource ], function (error, taskId) { - if (error) return callback(new BackupsError(BackupsError.INTERNAL_ERROR, error)); + if (error) return callback(error); tasks.startTask(taskId, {}, (error, result) => { // result is { removedBoxBackups, removedAppBackups } eventlog.add(eventlog.ACTION_BACKUP_CLEANUP_FINISH, auditSource, { diff --git a/src/provision.js b/src/provision.js index 75cc4f56c..993b47bb9 100644 --- a/src/provision.js +++ b/src/provision.js @@ -16,7 +16,7 @@ var appstore = require('./appstore.js'), assert = require('assert'), async = require('async'), backups = require('./backups.js'), - BackupsError = require('./backups.js').BackupsError, + BoxError = require('./boxerror.js'), constants = require('./constants.js'), clients = require('./clients.js'), cloudron = require('./cloudron.js'), @@ -266,8 +266,8 @@ function restore(backupConfig, backupId, version, auditSource, callback) { if (activated) return done(new ProvisionError(ProvisionError.ALREADY_PROVISIONED, 'Already activated. Restore with a fresh Cloudron installation.')); backups.testConfig(backupConfig, function (error) { - if (error && error.reason === BackupsError.BAD_FIELD) return done(new ProvisionError(ProvisionError.BAD_FIELD, error.message)); - if (error && error.reason === BackupsError.EXTERNAL_ERROR) return done(new ProvisionError(ProvisionError.EXTERNAL_ERROR, error.message)); + if (error && error.reason === BoxError.BAD_FIELD) return done(new ProvisionError(ProvisionError.BAD_FIELD, error.message)); + if (error && error.reason === BoxError.EXTERNAL_ERROR) return done(new ProvisionError(ProvisionError.EXTERNAL_ERROR, error.message)); if (error) return done(new ProvisionError(ProvisionError.INTERNAL_ERROR, error)); debug(`restore: restoring from ${backupId} from provider ${backupConfig.provider} with format ${backupConfig.format}`); diff --git a/src/routes/backups.js b/src/routes/backups.js index c744d7bdd..d8a19317c 100644 --- a/src/routes/backups.js +++ b/src/routes/backups.js @@ -9,10 +9,27 @@ exports = module.exports = { let auditSource = require('../auditsource.js'), backupdb = require('../backupdb.js'), backups = require('../backups.js'), - BackupsError = require('../backups.js').BackupsError, + BoxError = require('../boxerror.js'), HttpError = require('connect-lastmile').HttpError, HttpSuccess = require('connect-lastmile').HttpSuccess; +function toHttpError(error) { + switch (error.reason) { + case BoxError.NOT_FOUND: + return new HttpError(404, error); + case BoxError.BAD_STATE: + return new HttpError(409, error); + case BoxError.BAD_FIELD: + return new HttpError(400, error); + case BoxError.EXTERNAL_ERROR: + return new HttpError(424, error); + case BoxError.INTERNAL_ERROR: + case BoxError.DATABASE_ERROR: + default: + return new HttpError(500, error); + } +} + function list(req, res, next) { var page = typeof req.query.page !== 'undefined' ? parseInt(req.query.page) : 1; if (!page || page < 0) return next(new HttpError(400, 'page query param has to be a postive number')); @@ -21,8 +38,7 @@ function list(req, res, next) { if (!perPage || perPage < 0) return next(new HttpError(400, 'per_page query param has to be a postive number')); backups.getByStatePaged(backupdb.BACKUP_STATE_NORMAL, page, perPage, function (error, result) { - if (error && error.reason === BackupsError.EXTERNAL_ERROR) return next(new HttpError(424, error.message)); - if (error) return next(new HttpError(500, error)); + if (error) return next(toHttpError(error)); next(new HttpSuccess(200, { backups: result })); }); @@ -30,8 +46,7 @@ function list(req, res, next) { function startBackup(req, res, next) { backups.startBackupTask(auditSource.fromRequest(req), function (error, taskId) { - if (error && error.reason === BackupsError.BAD_STATE) return next(new HttpError(409, error.message)); - if (error) return next(new HttpError(500, error)); + if (error) return next(toHttpError(error)); next(new HttpSuccess(202, { taskId })); }); @@ -39,7 +54,7 @@ function startBackup(req, res, next) { function cleanup(req, res, next) { backups.startCleanupTask(auditSource.fromRequest(req), function (error, taskId) { - if (error) return next(new HttpError(500, error)); + if (error) return next(toHttpError(error)); next(new HttpSuccess(202, { taskId })); }); diff --git a/src/settings.js b/src/settings.js index 4efd1a379..4374358e5 100644 --- a/src/settings.js +++ b/src/settings.js @@ -91,7 +91,6 @@ exports = module.exports = { var addons = require('./addons.js'), assert = require('assert'), backups = require('./backups.js'), - BackupsError = backups.BackupsError, BoxError = require('./boxerror.js'), constants = require('./constants.js'), cron = require('./cron.js'), @@ -352,9 +351,7 @@ function setBackupConfig(backupConfig, callback) { backups.injectPrivateFields(backupConfig, curentConfig); backups.testConfig(backupConfig, function (error) { - if (error && error.reason === BackupsError.BAD_FIELD) return callback(new BoxError(BoxError.BAD_FIELD, error.message)); - if (error && error.reason === BackupsError.EXTERNAL_ERROR) return callback(new BoxError(BoxError.EXTERNAL_ERROR, error.message)); - if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); + if (error) return callback(error); backups.cleanupCacheFilesSync(); diff --git a/src/storage/filesystem.js b/src/storage/filesystem.js index 014ccb51c..81cf6ce75 100644 --- a/src/storage/filesystem.js +++ b/src/storage/filesystem.js @@ -17,7 +17,7 @@ exports = module.exports = { }; var assert = require('assert'), - BackupsError = require('../backups.js').BackupsError, + BoxError = require('../boxerror.js'), debug = require('debug')('box:storage/filesystem'), EventEmitter = require('events'), fs = require('fs'), @@ -35,7 +35,7 @@ function upload(apiConfig, backupFilePath, sourceStream, callback) { assert.strictEqual(typeof callback, 'function'); mkdirp(path.dirname(backupFilePath), function (error) { - if (error) return callback(new BackupsError(BackupsError.EXTERNAL_ERROR, error.message)); + if (error) return callback(new BoxError(BoxError.EXTERNAL_ERROR, error.message)); safe.fs.unlinkSync(backupFilePath); // remove any hardlink @@ -48,15 +48,15 @@ function upload(apiConfig, backupFilePath, sourceStream, callback) { fileStream.on('error', function (error) { debug('[%s] upload: out stream error.', backupFilePath, error); - callback(new BackupsError(BackupsError.EXTERNAL_ERROR, error.message)); + callback(new BoxError(BoxError.EXTERNAL_ERROR, error.message)); }); fileStream.on('finish', function () { // in test, upload() may or may not be called via sudo script const BACKUP_UID = parseInt(process.env.SUDO_UID, 10) || process.getuid(); - if (!safe.fs.chownSync(backupFilePath, BACKUP_UID, BACKUP_UID)) return callback(new BackupsError(BackupsError.EXTERNAL_ERROR, 'Unable to chown:' + safe.error.message)); - if (!safe.fs.chownSync(path.dirname(backupFilePath), BACKUP_UID, BACKUP_UID)) return callback(new BackupsError(BackupsError.EXTERNAL_ERROR, 'Unable to chown:' + safe.error.message)); + if (!safe.fs.chownSync(backupFilePath, BACKUP_UID, BACKUP_UID)) return callback(new BoxError(BoxError.EXTERNAL_ERROR, 'Unable to chown:' + safe.error.message)); + if (!safe.fs.chownSync(path.dirname(backupFilePath), BACKUP_UID, BACKUP_UID)) return callback(new BoxError(BoxError.EXTERNAL_ERROR, 'Unable to chown:' + safe.error.message)); debug('upload %s: done.', backupFilePath); @@ -72,7 +72,7 @@ function download(apiConfig, sourceFilePath, callback) { debug(`download: ${sourceFilePath}`); - if (!safe.fs.existsSync(sourceFilePath)) return callback(new BackupsError(BackupsError.NOT_FOUND, `File not found: ${sourceFilePath}`)); + if (!safe.fs.existsSync(sourceFilePath)) return callback(new BoxError(BoxError.NOT_FOUND, `File not found: ${sourceFilePath}`)); var fileStream = fs.createReadStream(sourceFilePath); callback(null, fileStream); @@ -116,14 +116,14 @@ function copy(apiConfig, oldFilePath, newFilePath) { var events = new EventEmitter(); mkdirp(path.dirname(newFilePath), function (error) { - if (error) return events.emit('done', new BackupsError(BackupsError.EXTERNAL_ERROR, error.message)); + if (error) return events.emit('done', new BoxError(BoxError.EXTERNAL_ERROR, error.message)); events.emit('progress', `Copying ${oldFilePath} to ${newFilePath}`); // this will hardlink backups saving space var cpOptions = apiConfig.noHardlinks ? '-a' : '-al'; shell.spawn('copy', '/bin/cp', [ cpOptions, oldFilePath, newFilePath ], { }, function (error) { - if (error) return events.emit('done', new BackupsError(BackupsError.EXTERNAL_ERROR, error.message)); + if (error) return events.emit('done', new BoxError(BoxError.EXTERNAL_ERROR, error.message)); events.emit('done', null); }); @@ -141,9 +141,9 @@ function remove(apiConfig, filename, callback) { if (!stat) return callback(); if (stat.isFile()) { - if (!safe.fs.unlinkSync(filename)) return callback(new BackupsError(BackupsError.EXTERNAL_ERROR, safe.error.message)); + if (!safe.fs.unlinkSync(filename)) return callback(new BoxError(BoxError.EXTERNAL_ERROR, safe.error.message)); } else if (stat.isDirectory()) { - if (!safe.fs.rmdirSync(filename)) return callback(new BackupsError(BackupsError.EXTERNAL_ERROR, safe.error.message)); + if (!safe.fs.rmdirSync(filename)) return callback(new BoxError(BoxError.EXTERNAL_ERROR, safe.error.message)); } callback(null); @@ -158,7 +158,7 @@ function removeDir(apiConfig, pathPrefix) { process.nextTick(() => events.emit('progress', `Removing directory ${pathPrefix}`)); shell.spawn('removeDir', '/bin/rm', [ '-rf', pathPrefix ], { }, function (error) { - if (error) return events.emit('done', new BackupsError(BackupsError.EXTERNAL_ERROR, error.message)); + if (error) return events.emit('done', new BoxError(BoxError.EXTERNAL_ERROR, error.message)); events.emit('done', null); }); @@ -170,21 +170,21 @@ function testConfig(apiConfig, callback) { assert.strictEqual(typeof apiConfig, 'object'); assert.strictEqual(typeof callback, 'function'); - if (typeof apiConfig.backupFolder !== 'string') return callback(new BackupsError(BackupsError.BAD_FIELD, 'backupFolder must be string')); + if (typeof apiConfig.backupFolder !== 'string') return callback(new BoxError(BoxError.BAD_FIELD, 'backupFolder must be string', { field: 'backupFolder' })); - if (!apiConfig.backupFolder) return callback(new BackupsError(BackupsError.BAD_FIELD, 'backupFolder is required')); + if (!apiConfig.backupFolder) return callback(new BoxError(BoxError.BAD_FIELD, 'backupFolder is required', { field: 'backupFolder' })); - if ('noHardlinks' in apiConfig && typeof apiConfig.noHardlinks !== 'boolean') return callback(new BackupsError(BackupsError.BAD_FIELD, 'noHardlinks must be boolean')); + if ('noHardlinks' in apiConfig && typeof apiConfig.noHardlinks !== 'boolean') return callback(new BoxError(BoxError.BAD_FIELD, 'noHardlinks must be boolean', { field: 'noHardLinks' })); - if ('externalDisk' in apiConfig && typeof apiConfig.externalDisk !== 'boolean') return callback(new BackupsError(BackupsError.BAD_FIELD, 'externalDisk must be boolean')); + if ('externalDisk' in apiConfig && typeof apiConfig.externalDisk !== 'boolean') return callback(new BoxError(BoxError.BAD_FIELD, 'externalDisk must be boolean', { field: 'externalDisk' })); fs.stat(apiConfig.backupFolder, function (error, result) { - if (error) return callback(new BackupsError(BackupsError.BAD_FIELD, 'Directory does not exist or cannot be accessed: ' + error.message)); - if (!result.isDirectory()) return callback(new BackupsError(BackupsError.BAD_FIELD, 'Backup location is not a directory')); + if (error) return callback(new BoxError(BoxError.BAD_FIELD, 'Directory does not exist or cannot be accessed: ' + error.message), { field: 'backupFolder' }); + if (!result.isDirectory()) return callback(new BoxError(BoxError.BAD_FIELD, 'Backup location is not a directory', { field: 'backupFolder' })); mkdirp(path.join(apiConfig.backupFolder, 'snapshot'), function (error) { - if (error && error.code === 'EACCES') return callback(new BackupsError(BackupsError.BAD_FIELD, `Access denied. Run "chown yellowtent:yellowtent ${apiConfig.backupFolder}" on the server`)); - if (error) return callback(new BackupsError(BackupsError.BAD_FIELD, error.message)); + if (error && error.code === 'EACCES') return callback(new BoxError(BoxError.BAD_FIELD, `Access denied. Run "chown yellowtent:yellowtent ${apiConfig.backupFolder}" on the server`, { field: 'backupFolder' })); + if (error) return callback(new BoxError(BoxError.BAD_FIELD, error.message, { field: 'backupFolder' })); callback(null); }); diff --git a/src/storage/gcs.js b/src/storage/gcs.js index 8151de7f1..552375148 100644 --- a/src/storage/gcs.js +++ b/src/storage/gcs.js @@ -22,7 +22,7 @@ exports = module.exports = { var assert = require('assert'), async = require('async'), backups = require('../backups.js'), - BackupsError = require('../backups.js').BackupsError, + BoxError = require('../boxerror.js'), debug = require('debug')('box:storage/gcs'), EventEmitter = require('events'), GCS = require('@google-cloud/storage').Storage, @@ -68,7 +68,7 @@ function upload(apiConfig, backupFilePath, sourceStream, callback) { function done(error) { if (error) { debug('[%s] upload: gcp upload error.', backupFilePath, error); - return callback(new BackupsError(BackupsError.EXTERNAL_ERROR, `Error uploading ${backupFilePath}. Message: ${error.message} HTTP Code: ${error.code}`)); + return callback(new BoxError(BoxError.EXTERNAL_ERROR, `Error uploading ${backupFilePath}. Message: ${error.message} HTTP Code: ${error.code}`)); } callback(null); @@ -95,10 +95,10 @@ function download(apiConfig, backupFilePath, callback) { var readStream = file.createReadStream() .on('error', function(error) { if (error && error.code == 404){ - ps.emit('error', new BackupsError(BackupsError.NOT_FOUND)); + ps.emit('error', new BoxError(BoxError.NOT_FOUND)); } else { debug('[%s] download: gcp stream error.', backupFilePath, error); - ps.emit('error', new BackupsError(BackupsError.EXTERNAL_ERROR, error)); + ps.emit('error', new BoxError(BoxError.EXTERNAL_ERROR, error)); } }) ; @@ -151,8 +151,8 @@ function copy(apiConfig, oldFilePath, newFilePath) { getBucket(apiConfig).file(entry.fullPath).copy(path.join(newFilePath, relativePath), function(error) { if (error) debug('copyBackup: gcs copy error', error); - if (error && error.code === 404) return iteratorCallback(new BackupsError(BackupsError.NOT_FOUND, 'Old backup not found')); - if (error) return iteratorCallback(new BackupsError(BackupsError.EXTERNAL_ERROR, error.message)); + if (error && error.code === 404) return iteratorCallback(new BoxError(BoxError.NOT_FOUND, 'Old backup not found')); + if (error) return iteratorCallback(new BoxError(BoxError.EXTERNAL_ERROR, error.message)); iteratorCallback(null); }); @@ -219,13 +219,13 @@ function testConfig(apiConfig, callback) { assert.strictEqual(typeof apiConfig, 'object'); assert.strictEqual(typeof callback, 'function'); - if (typeof apiConfig.projectId !== 'string') return callback(new BackupsError(BackupsError.BAD_FIELD, 'projectId must be a string')); - if (!apiConfig.credentials || typeof apiConfig.credentials !== 'object') return callback(new BackupsError(BackupsError.BAD_FIELD, 'credentials must be an object')); - if (typeof apiConfig.credentials.client_email !== 'string') return callback(new BackupsError(BackupsError.BAD_FIELD, 'credentials.client_email must be a string')); - if (typeof apiConfig.credentials.private_key !== 'string') return callback(new BackupsError(BackupsError.BAD_FIELD, 'credentials.private_key must be a string')); + if (typeof apiConfig.projectId !== 'string') return callback(new BoxError(BoxError.BAD_FIELD, 'projectId must be a string')); + if (!apiConfig.credentials || typeof apiConfig.credentials !== 'object') return callback(new BoxError(BoxError.BAD_FIELD, 'credentials must be an object')); + if (typeof apiConfig.credentials.client_email !== 'string') return callback(new BoxError(BoxError.BAD_FIELD, 'credentials.client_email must be a string')); + if (typeof apiConfig.credentials.private_key !== 'string') return callback(new BoxError(BoxError.BAD_FIELD, 'credentials.private_key must be a string')); - if (typeof apiConfig.bucket !== 'string') return callback(new BackupsError(BackupsError.BAD_FIELD, 'bucket must be a string')); - if (typeof apiConfig.prefix !== 'string') return callback(new BackupsError(BackupsError.BAD_FIELD, 'prefix must be a string')); + if (typeof apiConfig.bucket !== 'string') return callback(new BoxError(BoxError.BAD_FIELD, 'bucket must be a string')); + if (typeof apiConfig.prefix !== 'string') return callback(new BoxError(BoxError.BAD_FIELD, 'prefix must be a string')); // attempt to upload and delete a file with new credentials var bucket = getBucket(apiConfig); @@ -239,16 +239,16 @@ function testConfig(apiConfig, callback) { uploadStream.on('error', function(error) { debug('testConfig: failed uploading cloudron-testfile', error); if (error && error.code && (error.code == 403 || error.code == 404)) { - return callback(new BackupsError(BackupsError.BAD_FIELD, error.message)); + return callback(new BoxError(BoxError.BAD_FIELD, error.message)); } - return callback(new BackupsError(BackupsError.EXTERNAL_ERROR, error.message)); + return callback(new BoxError(BoxError.EXTERNAL_ERROR, error.message)); }); uploadStream.on('finish', function() { debug('testConfig: uploaded cloudron-testfile ' + JSON.stringify(arguments)); bucket.file(path.join(apiConfig.prefix, 'cloudron-testfile')).delete(function(error) { - if (error) return callback(new BackupsError(BackupsError.EXTERNAL_ERROR, error.message)); + if (error) return callback(new BoxError(BoxError.EXTERNAL_ERROR, error.message)); debug('testConfig: deleted cloudron-testfile'); callback(); }); diff --git a/src/storage/s3.js b/src/storage/s3.js index 5e938e881..fcd6ce046 100644 --- a/src/storage/s3.js +++ b/src/storage/s3.js @@ -23,7 +23,7 @@ var assert = require('assert'), async = require('async'), AWS = require('aws-sdk'), backups = require('../backups.js'), - BackupsError = require('../backups.js').BackupsError, + BoxError = require('../boxerror.js'), chunk = require('lodash.chunk'), debug = require('debug')('box:storage/s3'), EventEmitter = require('events'), @@ -101,7 +101,7 @@ function upload(apiConfig, backupFilePath, sourceStream, callback) { s3.upload(params, { partSize, queueSize: 1 }, function (error, data) { if (error) { debug('Error uploading [%s]: s3 upload error.', backupFilePath, error); - return callback(new BackupsError(BackupsError.EXTERNAL_ERROR, `Error uploading ${backupFilePath}. Message: ${error.message} HTTP Code: ${error.code}`)); + return callback(new BoxError(BoxError.EXTERNAL_ERROR, `Error uploading ${backupFilePath}. Message: ${error.message} HTTP Code: ${error.code}`)); } debug(`Uploaded ${backupFilePath}: ${JSON.stringify(data)}`); @@ -131,10 +131,10 @@ function download(apiConfig, backupFilePath, callback) { multipartDownload.on('error', function (error) { if (S3_NOT_FOUND(error)) { - ps.emit('error', new BackupsError(BackupsError.NOT_FOUND, `Backup not found: ${backupFilePath}`)); + ps.emit('error', new BoxError(BoxError.NOT_FOUND, `Backup not found: ${backupFilePath}`)); } else { debug(`download: ${apiConfig.bucket}:${backupFilePath} s3 stream error.`, error); - ps.emit('error', new BackupsError(BackupsError.EXTERNAL_ERROR, error.message || error.code)); // DO sets 'code' + ps.emit('error', new BoxError(BoxError.EXTERNAL_ERROR, error.message || error.code)); // DO sets 'code' } }); @@ -219,8 +219,8 @@ function copy(apiConfig, oldFilePath, newFilePath) { function done(error) { if (error) debug(`copy: s3 copy error when copying ${entry.fullPath}: ${error}`); - if (error && S3_NOT_FOUND(error)) return iteratorCallback(new BackupsError(BackupsError.NOT_FOUND, `Old backup not found: ${entry.fullPath}`)); - if (error) return iteratorCallback(new BackupsError(BackupsError.EXTERNAL_ERROR, `Error copying ${entry.fullPath} : ${error.code} ${error}`)); + if (error && S3_NOT_FOUND(error)) return iteratorCallback(new BoxError(BoxError.NOT_FOUND, `Old backup not found: ${entry.fullPath}`)); + if (error) return iteratorCallback(new BoxError(BoxError.EXTERNAL_ERROR, `Error copying ${entry.fullPath} : ${error.code} ${error}`)); iteratorCallback(null); } @@ -405,13 +405,13 @@ function testConfig(apiConfig, callback) { assert.strictEqual(typeof apiConfig, 'object'); assert.strictEqual(typeof callback, 'function'); - if (typeof apiConfig.accessKeyId !== 'string') return callback(new BackupsError(BackupsError.BAD_FIELD, 'accessKeyId must be a string')); - if (typeof apiConfig.secretAccessKey !== 'string') return callback(new BackupsError(BackupsError.BAD_FIELD, 'secretAccessKey must be a string')); + if (typeof apiConfig.accessKeyId !== 'string') return callback(new BoxError(BoxError.BAD_FIELD, 'accessKeyId must be a string', { field: 'accessKeyId' })); + if (typeof apiConfig.secretAccessKey !== 'string') return callback(new BoxError(BoxError.BAD_FIELD, 'secretAccessKey must be a string', { field: 'secretAccessKey' })); - if (typeof apiConfig.bucket !== 'string') return callback(new BackupsError(BackupsError.BAD_FIELD, 'bucket must be a string')); - if (typeof apiConfig.prefix !== 'string') return callback(new BackupsError(BackupsError.BAD_FIELD, 'prefix must be a string')); - if ('signatureVersion' in apiConfig && typeof apiConfig.signatureVersion !== 'string') return callback(new BackupsError(BackupsError.BAD_FIELD, 'signatureVersion must be a string')); - if ('endpoint' in apiConfig && typeof apiConfig.endpoint !== 'string') return callback(new BackupsError(BackupsError.BAD_FIELD, 'endpoint must be a string')); + if (typeof apiConfig.bucket !== 'string') return callback(new BoxError(BoxError.BAD_FIELD, 'bucket must be a string', { field: 'bucket' })); + if (typeof apiConfig.prefix !== 'string') return callback(new BoxError(BoxError.BAD_FIELD, 'prefix must be a string', { field: 'prefix' })); + if ('signatureVersion' in apiConfig && typeof apiConfig.signatureVersion !== 'string') return callback(new BoxError(BoxError.BAD_FIELD, 'signatureVersion must be a string', { field: 'signatureVersion' })); + if ('endpoint' in apiConfig && typeof apiConfig.endpoint !== 'string') return callback(new BoxError(BoxError.BAD_FIELD, 'endpoint must be a string', { field: 'endpoint' })); // attempt to upload and delete a file with new credentials getS3Config(apiConfig, function (error, credentials) { @@ -425,7 +425,7 @@ function testConfig(apiConfig, callback) { var s3 = new AWS.S3(credentials); s3.putObject(params, function (error) { - if (error) return callback(new BackupsError(BackupsError.EXTERNAL_ERROR, error.message || error.code)); // DO sets 'code' + if (error) return callback(new BoxError(BoxError.EXTERNAL_ERROR, error.message || error.code)); // DO sets 'code' var params = { Bucket: apiConfig.bucket, @@ -433,7 +433,7 @@ function testConfig(apiConfig, callback) { }; s3.deleteObject(params, function (error) { - if (error) return callback(new BackupsError(BackupsError.EXTERNAL_ERROR, error.message || error.code)); // DO sets 'code' + if (error) return callback(new BoxError(BoxError.EXTERNAL_ERROR, error.message || error.code)); // DO sets 'code' callback(); }); diff --git a/src/test/storage-test.js b/src/test/storage-test.js index 8982781dc..a5cb1ad87 100644 --- a/src/test/storage-test.js +++ b/src/test/storage-test.js @@ -5,7 +5,7 @@ 'use strict'; -var BackupsError = require('../backups.js').BackupsError, +var BoxError = require('../boxerror.js'), execSync = require('child_process').execSync, expect = require('expect.js'), filesystem = require('../storage/filesystem.js'), @@ -98,7 +98,7 @@ describe('Storage', function () { var sourceFile = gTmpFolder + '/uploadtest/missing'; filesystem.download(gBackupConfig, sourceFile, function (error) { - expect(error.reason).to.be(BackupsError.NOT_FOUND); + expect(error.reason).to.be(BoxError.NOT_FOUND); done(); }); });