diff --git a/src/apps.js b/src/apps.js index 1d428cba1..dcb531833 100644 --- a/src/apps.js +++ b/src/apps.js @@ -622,21 +622,20 @@ function mailboxNameForLocation(location, manifest) { return (location ? location : manifest.title.toLowerCase().replace(/[^a-zA-Z0-9]/g, '')) + '.app'; } -function scheduleTask(appId, args, values, callback) { +function scheduleTask(appId, installationState, task, callback) { assert.strictEqual(typeof appId, 'string'); - assert.strictEqual(typeof args, 'object'); - assert.strictEqual(typeof values, 'object'); + assert.strictEqual(typeof installationState, 'string'); + assert.strictEqual(typeof task, 'object'); // { args, values } assert.strictEqual(typeof callback, 'function'); - assert(values.installationState); + const { args, values } = task; tasks.add(tasks.TASK_APP, [ appId, args ], function (error, taskId) { if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error)); values.error = null; - values.taskId = taskId; - appdb.setTask(appId, values, function (error) { + appdb.setTask(appId, _.extend({ installationState, taskId }, values), function (error) { if (error && error.reason === DatabaseError.ALREADY_EXISTS) return callback(new AppsError(AppsError.ALREADY_EXISTS, error.message)); if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new AppsError(AppsError.BAD_STATE, 'Another task is scheduled for this app')); // could be because app went away OR a taskId exists if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error)); @@ -805,8 +804,12 @@ function install(data, user, auditSource, callback) { } const restoreConfig = backupId ? { backupId: backupId, backupFormat: backupFormat } : null; + const task = { + args: { restoreConfig, overwriteDns }, + values: { } + }; - scheduleTask(appId, { restoreConfig, overwriteDns }, { installationState: exports.ISTATE_PENDING_INSTALL }, function (error, result) { + scheduleTask(appId, exports.ISTATE_PENDING_INSTALL, task, function (error, result) { if (error) return callback(error); const newApp = _.extend({ fqdn: domains.fqdn(location, domainObject) }, data, { appStoreId, manifest, location, domain, portBindings }); @@ -929,7 +932,11 @@ function setMemoryLimit(appId, memoryLimit, auditSource, callback) { error = validateMemoryLimit(app.manifest, memoryLimit); if (error) return callback(error); - scheduleTask(appId, {}, { installationState: exports.ISTATE_PENDING_RESIZE, memoryLimit: memoryLimit }, function (error, result) { + const task = { + args: {}, + values: { memoryLimit } + }; + scheduleTask(appId, exports.ISTATE_PENDING_RESIZE, task, function (error, result) { if (error) return callback(error); eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId: appId, app: app, memoryLimit: memoryLimit, taskId: result.taskId }); @@ -954,7 +961,11 @@ function setEnvironment(appId, env, auditSource, callback) { error = validateEnv(env); if (error) return callback(error); - scheduleTask(appId, {}, { installationState: exports.ISTATE_PENDING_RECREATE_CONTAINER, env: env }, function (error, result) { + const task = { + args: {}, + values: { env } + }; + scheduleTask(appId, exports.ISTATE_PENDING_RECREATE_CONTAINER, task, function (error, result) { if (error) return callback(error); eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId: appId, app: app, env: env, taskId: result.taskId }); @@ -979,7 +990,11 @@ function setDebugMode(appId, debugMode, auditSource, callback) { error = validateDebugMode(debugMode); if (error) return callback(error); - scheduleTask(appId, {}, { installationState: exports.ISTATE_PENDING_DEBUG, debugMode: debugMode }, function (error, result) { + const task = { + args: {}, + values: { debugMode } + }; + scheduleTask(appId, exports.ISTATE_PENDING_DEBUG, task, function (error, result) { if (error) return callback(error); eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId: appId, app: app, debugMode: debugMode, taskId: result.taskId }); @@ -1008,7 +1023,11 @@ function setMailbox(appId, mailboxName, auditSource, callback) { mailboxName = mailboxNameForLocation(app.location, app.manifest); } - scheduleTask(appId, {}, { installationState: exports.ISTATE_PENDING_RECREATE_CONTAINER, mailboxName: mailboxName }, function (error, result) { + const task = { + args: {}, + values: { mailboxName } + }; + scheduleTask(appId, exports.ISTATE_PENDING_RECREATE_CONTAINER, task, function (error, result) { if (error) return callback(error); eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId: appId, app: app, mailboxName: mailboxName, taskId: result.taskId }); @@ -1127,7 +1146,6 @@ function setLocation(appId, data, auditSource, callback) { if (error) return callback(error); let values = { - installationState: exports.ISTATE_PENDING_LOCATION_CHANGE, // these are intentionally reset, if not set portBindings: null, alternateDomains: [] @@ -1158,11 +1176,14 @@ function setLocation(appId, data, auditSource, callback) { error = domains.validateHostname(values.location, domainObject); if (error) return callback(new AppsError(AppsError.BAD_FIELD, 'Bad location: ' + error.message, { field: 'location' })); - const args = { - oldConfig: _.pick(app, 'location', 'domain', 'fqdn', 'alternateDomains', 'portBindings'), - overwriteDns: !!data.overwriteDns + const task = { + args: { + oldConfig: _.pick(app, 'location', 'domain', 'fqdn', 'alternateDomains', 'portBindings'), + overwriteDns: !!data.overwriteDns + }, + values }; - scheduleTask(appId, args, values, function (error, result) { + scheduleTask(appId, exports.ISTATE_PENDING_LOCATION_CHANGE, task, function (error, result) { if (error && error.reason === AppsError.ALREADY_EXISTS) error = getDuplicateErrorDetails(error.message, values.location, domainObject, data.portBindings, app.alternateDomains); if (error) return callback(error); @@ -1189,7 +1210,11 @@ function setDataDir(appId, dataDir, auditSource, callback) { error = validateDataDir(dataDir); if (error) return callback(error); - scheduleTask(appId, { oldDataDir: app.dataDir }, { installationState: exports.ISTATE_PENDING_DATA_DIR_MIGRATION, dataDir: dataDir }, function (error, result) { + const task = { + args: { oldDataDir: app.dataDir }, + values: { dataDir: dataDir } + }; + scheduleTask(appId, exports.ISTATE_PENDING_DATA_DIR_MIGRATION, task, function (error, result) { if (error) return callback(error); eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId: appId, app: app, dataDir: dataDir, taskId: result.taskId }); @@ -1265,7 +1290,11 @@ function update(appId, data, auditSource, callback) { updateConfig.memoryLimit = updateConfig.manifest.memoryLimit; } - scheduleTask(appId, { updateConfig: updateConfig }, { installationState: exports.ISTATE_PENDING_UPDATE }, function (error, result) { + const task = { + args: { updateConfig }, + values: {} + }; + scheduleTask(appId, exports.ISTATE_PENDING_UPDATE, task, function (error, result) { if (error) return callback(error); eventlog.add(eventlog.ACTION_APP_UPDATE, auditSource, { appId: appId, toManifest: manifest, fromManifest: app.manifest, force: data.force, app: app, taskId: result.taskId }); @@ -1350,13 +1379,12 @@ function repair(appId, data, auditSource, callback) { debug(`Repairing app with error: ${JSON.stringify(error)} and state: ${newState}`); let values = _.pick(data, 'location', 'domain', 'alternateDomains'); // FIXME: validate - values.installationState = newState; let args = appError.task ? appError.task.args : {}; 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; - scheduleTask(appId, args, values, function (error, result) { + scheduleTask(appId, newState, { args, values }, function (error, result) { if (error) return callback(error); eventlog.add(eventlog.ACTION_APP_REPAIR, auditSource, { taskId: result.taskId, app, newState }); @@ -1394,13 +1422,18 @@ function restore(appId, data, auditSource, callback) { error = checkManifestConstraints(backupInfo.manifest); if (error) return callback(error); - let values = { - installationState: exports.ISTATE_PENDING_RESTORE, - manifest: backupInfo.manifest - }; - const restoreConfig = data.backupId ? { backupId: data.backupId, backupFormat: backupInfo.format, oldManifest: app.manifest } : null; // when null, apptask simply reinstalls - scheduleTask(appId, { restoreConfig, overwriteDns: true }, values, function (error, result) { + + const task = { + args: { + restoreConfig, + overwriteDns: true + }, + values: { + manifest: backupInfo.manifest + } + }; + scheduleTask(appId, exports.ISTATE_PENDING_RESTORE, task, function (error, result) { if (error) return callback(error); eventlog.add(eventlog.ACTION_APP_RESTORE, auditSource, { app: app, backupId: backupInfo.id, fromManifest: app.manifest, toManifest: backupInfo.manifest, taskId: result.taskId }); @@ -1509,8 +1542,11 @@ function clone(appId, data, user, auditSource, callback) { if (error) return callback(error); const restoreConfig = { backupId: backupId, backupFormat: backupInfo.format }; - - scheduleTask(newAppId, { restoreConfig, overwriteDns }, { installationState: exports.ISTATE_PENDING_CLONE }, function (error, result) { + const task = { + args: { restoreConfig, overwriteDns }, + values: {} + }; + scheduleTask(newAppId, exports.ISTATE_PENDING_CLONE, task, function (error, result) { if (error) return callback(error); const newApp = _.extend({}, data, { appStoreId, manifest, location, domain, portBindings }); @@ -1545,7 +1581,11 @@ function uninstall(appId, auditSource, callback) { if (error && error.reason === AppstoreError.EXTERNAL_ERROR) return callback(new AppsError(AppsError.EXTERNAL_ERROR, error.message)); if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error)); - scheduleTask(appId, { /* args */ }, { installationState: exports.ISTATE_PENDING_UNINSTALL }, function (error, result) { + const task = { + args: {}, + values: {} + }; + scheduleTask(appId, exports.ISTATE_PENDING_UNINSTALL, task, function (error, result) { if (error) return callback(error); eventlog.add(eventlog.ACTION_APP_UNINSTALL, auditSource, { appId: appId, app: app, taskId: result.taskId }); @@ -1568,7 +1608,15 @@ function start(appId, callback) { error = checkAppState(app, exports.ISTATE_PENDING_START); if (error) return callback(error); - scheduleTask(appId, { /* args */ }, { installationState: exports.ISTATE_PENDING_START, runState: exports.RSTATE_RUNNING }, callback); + const task = { + args: {}, + values: { runState: exports.RSTATE_RUNNING } + }; + scheduleTask(appId, exports.ISTATE_PENDING_START, task, function (error, result) { + if (error) return callback(error); + + callback(null, { taskId: result.taskId }); + }); }); } @@ -1584,7 +1632,15 @@ function stop(appId, callback) { error = checkAppState(app, exports.ISTATE_PENDING_STOP); if (error) return callback(error); - scheduleTask(appId, { /* args */ }, { installationState: exports.ISTATE_PENDING_STOP, runState: exports.RSTATE_STOPPED }, callback); + const task = { + args: {}, + values: { runState: exports.RSTATE_STOPPED } + }; + scheduleTask(appId, exports.ISTATE_PENDING_STOP, task, function (error, result) { + if (error) return callback(error); + + callback(null, { taskId: result.taskId }); + }); }); } @@ -1730,7 +1786,11 @@ function backup(appId, callback) { error = checkAppState(app, exports.ISTATE_PENDING_BACKUP); if (error) return callback(error); - scheduleTask(appId, { /* args */ }, { installationState: exports.ISTATE_PENDING_BACKUP }, (error, result) => { + const task = { + args: {}, + values: {} + }; + scheduleTask(appId, exports.ISTATE_PENDING_BACKUP, task, (error, result) => { if (error) return callback(error); callback(null, { taskId: result.taskId }); @@ -1773,7 +1833,11 @@ function restoreInstalledApps(callback) { appdb.update(app.id, { taskId: null, error: null }, function (error) { // clear any stale taskId if (error) debug(`Error marking ${app.fqdn} for restore: ${JSON.stringify(error)}`); - scheduleTask(app.id, { restoreConfig, overwriteDns: true }, { installationState: exports.ISTATE_PENDING_RESTORE }, () => iteratorDone()); // always succeed + const task = { + args: { restoreConfig, overwriteDns: true }, + values: {} + }; + scheduleTask(app.id, exports.ISTATE_PENDING_RESTORE, task, () => iteratorDone()); // always succeed }); }); }, callback); @@ -1794,7 +1858,11 @@ function configureInstalledApps(callback) { if (error) debug(`Error marking ${app.fqdn} for reconfigure: ${JSON.stringify(error)}`); const oldConfig = _.pick(app, 'location', 'domain', 'fqdn', 'alternateDomains', 'portBindings'); - scheduleTask(app.id, { oldConfig }, { installationState: exports.ISTATE_PENDING_CONFIGURE }, () => iteratorDone()); // always succeed + const task = { + args: { oldConfig }, + values: {} + }; + scheduleTask(app.id, exports.ISTATE_PENDING_CONFIGURE, task, () => iteratorDone()); // always succeed }); }, callback); });