diff --git a/src/apps.js b/src/apps.js index 9718e1b5b..c715925fc 100644 --- a/src/apps.js +++ b/src/apps.js @@ -1348,9 +1348,10 @@ function getLogs(appId, options, callback) { }); } +// does a re-configure when called from most states. for install/clone errors, it re-installs with an optional manifest function repair(appId, data, auditSource, callback) { assert.strictEqual(typeof appId, 'string'); - assert.strictEqual(typeof data, 'object'); + assert.strictEqual(typeof data, 'object'); // { manifest } assert.strictEqual(typeof auditSource, 'object'); assert.strictEqual(typeof callback, 'function'); @@ -1359,35 +1360,33 @@ function repair(appId, data, auditSource, callback) { get(appId, function (error, app) { if (error) return callback(error); - const appError = app.error || {}; // repair can always be called - const newState = appError.installationState ? appError.installationState : exports.ISTATE_PENDING_CONFIGURE; + const errorState = (app.error && app.error.installationState) || exports.ISTATE_PENDING_CONFIGURE; - debug(`Repairing app with error: ${JSON.stringify(error)} and state: ${newState}`); + const task = { + args: {}, + values: {}, + requiredState: null + }; - let values = _.pick(data, 'location', 'domain', 'alternateDomains'); + if (errorState === exports.ISTATE_PENDING_INSTALL || errorState === exports.ISTATE_PENDING_CLONE) { + if (data.manifest) { + error = manifestFormat.parse(data.manifest); + if (error) return callback(new BoxError(BoxError.BAD_FIELD, `manifest error: ${error.message}`)); - const locations = (values.location ? [ { subdomain: values.location, domain: values.domain } ] : []).concat(values.alternateDomains || []); - validateLocations(locations, function (error, domainObjectMap) { + error = checkManifestConstraints(data.manifest); + if (error) return callback(error); + + task.values.manifest = data.manifest; + task.args.oldManifest = app.manifest; + } + } + + addTask(appId, errorState, task, function (error, result) { if (error) return callback(error); - tasks.get(appError.taskId || '', function (error, task) { - let args = !error ? task.args[1] : {}; // pick args for the failed task. the first argument is the app id + eventlog.add(eventlog.ACTION_APP_REPAIR, auditSource, { taskId: result.taskId, app }); - if ('backupId' in data) { - args.restoreConfig = data.backupId ? { backupId: data.backupId, backupFormat: data.backupFormat, oldManifest: app.manifest } : null; // when null, apptask simply reinstalls - } - args.overwriteDns = 'overwriteDns' in data ? data.overwriteDns : false; - - // create a new task instead of updating the old one, since it helps tracking - addTask(appId, newState, { args, values, requiredState: null }, function (error, result) { - if (error && error.reason === BoxError.ALREADY_EXISTS) error = getDuplicateErrorDetails(error.message, locations, domainObjectMap, { /* portBindings */}); - if (error) return callback(error); - - eventlog.add(eventlog.ACTION_APP_REPAIR, auditSource, { taskId: result.taskId, app, newState }); - - callback(null, { taskId: result.taskId }); - }); - }); + callback(null, { taskId: result.taskId }); }); }); } diff --git a/src/routes/apps.js b/src/routes/apps.js index 6e47da83a..674321e31 100644 --- a/src/routes/apps.js +++ b/src/routes/apps.js @@ -349,19 +349,10 @@ function repairApp(req, res, next) { const data = req.body; - if (data.backupId && typeof data.backupId !== 'string') return next(new HttpError(400, 'backupId must be string or null')); - if (data.backupFormat && typeof data.backupFormat !== 'string') return next(new HttpError(400, 'backupFormat must be string or null')); - - if (data.location && typeof data.location !== 'string') return next(new HttpError(400, 'location is required')); - if (data.domain && typeof data.domain !== 'string') return next(new HttpError(400, 'domain is required')); - - if ('alternateDomains' in data) { - if (!Array.isArray(data.alternateDomains)) return next(new HttpError(400, 'alternateDomains must be an array')); - if (data.alternateDomains.some(function (d) { return (typeof d.domain !== 'string' || typeof d.subdomain !== 'string'); })) return next(new HttpError(400, 'alternateDomains array must contain objects with domain and subdomain strings')); + if ('manifest' in data) { + if (!data.manifest || typeof data.manifest !== 'object') return next(new HttpError(400, 'backupId must be an object')); } - if ('overwriteDns' in req.body && typeof req.body.overwriteDns !== 'boolean') return next(new HttpError(400, 'overwriteDns must be boolean')); - apps.repair(req.params.id, data, auditSource.fromRequest(req), function (error, result) { if (error) return next(BoxError.toHttpError(error));