diff --git a/src/appdb.js b/src/appdb.js index e122c5f5e..89a724d9d 100644 --- a/src/appdb.js +++ b/src/appdb.js @@ -238,8 +238,8 @@ function add(id, appStoreId, manifest, location, domain, portBindings, data, cal const accessRestriction = data.accessRestriction || null; const accessRestrictionJson = JSON.stringify(accessRestriction); const memoryLimit = data.memoryLimit || 0; - const installationState = data.installationState || 'pending_install'; // FIXME - const runState = data.runState || 'running'; // FIXME + const installationState = data.installationState; + const runState = data.runState; const sso = 'sso' in data ? data.sso : null; const robotsTxt = 'robotsTxt' in data ? data.robotsTxt : null; const debugModeJson = data.debugMode ? JSON.stringify(data.debugMode) : null; @@ -454,15 +454,16 @@ function setHealth(appId, health, healthTime, callback) { updateWithConstraints(appId, values, '', callback); } -function setTask(appId, values, callback) { +function setTask(appId, values, requiredState, callback) { assert.strictEqual(typeof appId, 'string'); assert.strictEqual(typeof values, 'object'); + assert(requiredState === null || typeof requiredState === 'string'); assert.strictEqual(typeof callback, 'function'); - if (values.installationState === 'pending_uninstall') { // FIXME + if (requiredState === null) { updateWithConstraints(appId, values, 'AND taskId IS NULL', callback); } else { - updateWithConstraints(appId, values, 'AND taskId IS NULL AND installationState != "error"', callback); + updateWithConstraints(appId, values, `AND taskId IS NULL AND installationState = "${requiredState}"`, callback); } } diff --git a/src/apps.js b/src/apps.js index f56f69719..1f7bfb137 100644 --- a/src/apps.js +++ b/src/apps.js @@ -629,13 +629,13 @@ function scheduleTask(appId, installationState, task, callback) { assert.strictEqual(typeof callback, 'function'); const { args, values } = task; + // by default, a task can only run on installed state. if it's null, it can be run on any state + const requiredState = 'requiredState' in task ? task.requiredState : exports.ISTATE_INSTALLED; tasks.add(tasks.TASK_APP, [ appId, args ], function (error, taskId) { if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error)); - values.error = null; - - appdb.setTask(appId, _.extend({ installationState, taskId }, values), function (error) { + appdb.setTask(appId, _.extend({ installationState, taskId, error: null }, values), requiredState, 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)); @@ -649,7 +649,7 @@ function scheduleTask(appId, installationState, task, callback) { boxError.details.stopped = error.code === tasks.ESTOPPED; boxError.details.task = { args, installationState }; // see also apptask makeTaskError appdb.update(appId, { installationState: exports.ISTATE_ERROR, error: boxError.toPlainObject(), taskId: null }, NOOP_CALLBACK); - } else if (installationState !== exports.ISTATE_PENDING_UNINSTALL) { // clear out taskId since it's done + } else if (!(installationState === exports.ISTATE_PENDING_UNINSTALL && !error)) { // clear out taskId except for successful uninstall appdb.update(appId, { taskId: null }, NOOP_CALLBACK); } }); @@ -786,7 +786,8 @@ function install(data, user, auditSource, callback) { env: env, label: label, tags: tags, - runState: exports.RSTATE_RUNNING + runState: exports.RSTATE_RUNNING, + installationState: exports.ISTATE_PENDING_INSTALL }; appdb.add(appId, appStoreId, manifest, location, domain, translatePortBindings(portBindings, manifest), data, function (error) { @@ -806,7 +807,8 @@ function install(data, user, auditSource, callback) { const restoreConfig = backupId ? { backupId: backupId, backupFormat: backupFormat } : null; const task = { args: { restoreConfig, overwriteDns }, - values: { } + values: { }, + requiredState: exports.ISTATE_PENDING_INSTALL }; scheduleTask(appId, exports.ISTATE_PENDING_INSTALL, task, function (error, result) { @@ -1384,7 +1386,7 @@ function repair(appId, data, auditSource, callback) { 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, newState, { args, values }, function (error, result) { + scheduleTask(appId, newState, { args, values, requiredState: exports.ISTATE_ERROR }, function (error, result) { if (error) return callback(error); eventlog.add(eventlog.ACTION_APP_REPAIR, auditSource, { taskId: result.taskId, app, newState }); @@ -1525,6 +1527,7 @@ function clone(appId, data, user, auditSource, callback) { var data = { installationState: exports.ISTATE_PENDING_CLONE, + runState: exports.RSTATE_RUNNING, memoryLimit: app.memoryLimit, accessRestriction: app.accessRestriction, sso: !!app.sso, @@ -1544,7 +1547,8 @@ function clone(appId, data, user, auditSource, callback) { const restoreConfig = { backupId: backupId, backupFormat: backupInfo.format }; const task = { args: { restoreConfig, overwriteDns }, - values: {} + values: {}, + requiredState: exports.ISTATE_PENDING_CLONE }; scheduleTask(newAppId, exports.ISTATE_PENDING_CLONE, task, function (error, result) { if (error) return callback(error); @@ -1583,7 +1587,8 @@ function uninstall(appId, auditSource, callback) { const task = { args: {}, - values: {} + values: {}, + requiredState: null // can run in any state, as long as no task is active }; scheduleTask(appId, exports.ISTATE_PENDING_UNINSTALL, task, function (error, result) { if (error) return callback(error);