diff --git a/CHANGES b/CHANGES index 01da355f4..9f4ee6036 100644 --- a/CHANGES +++ b/CHANGES @@ -1661,4 +1661,5 @@ * Check disk space before various operations like install, update, backup etc * Collect per app du information * Set Cloudron specific UA for healthchecks +* Show message why an app task is 'pending' diff --git a/src/apps.js b/src/apps.js index d9c6c4a4c..d734809af 100644 --- a/src/apps.js +++ b/src/apps.js @@ -1182,7 +1182,7 @@ function uninstall(appId, auditSource, callback) { assert.strictEqual(typeof auditSource, 'object'); assert.strictEqual(typeof callback, 'function'); - debug('Will uninstall app with id:%s', appId); + debug(`Will uninstall app with id : ${appId}`); get(appId, function (error, app) { if (error) return callback(error); @@ -1194,7 +1194,7 @@ 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)); - appdb.setInstallationCommand(appId, appdb.ISTATE_PENDING_UNINSTALL, function (error) { + appdb.update(appId, { installationState: appdb.ISTATE_PENDING_UNINSTALL }, function (error) { if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new AppsError(AppsError.NOT_FOUND, 'No such app')); if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error)); diff --git a/src/apptaskmanager.js b/src/apptaskmanager.js index f77d00490..e8d350703 100644 --- a/src/apptaskmanager.js +++ b/src/apptaskmanager.js @@ -22,6 +22,14 @@ let gPendingTasks = [ ]; const TASK_CONCURRENCY = 3; const NOOP_CALLBACK = function (error) { if (error) debug(error); }; +function waitText(lockOperation) { + if (lockOperation === locker.OP_BOX_UPDATE) return 'Waiting for Cloudron to finish updating. See the Settings view'; + if (lockOperation === locker.OP_PLATFORM_START) return 'Waiting for Cloudron to initialize'; + if (lockOperation === locker.OP_FULL_BACKUP) return 'Wait for Cloudron to finish backup. See the Backups view'; + + return ''; // cannot happen +} + // callback is called when task is finished function scheduleTask(appId, taskId, callback) { assert.strictEqual(typeof appId, 'string'); @@ -34,6 +42,7 @@ function scheduleTask(appId, taskId, callback) { if (Object.keys(gActiveTasks).length >= TASK_CONCURRENCY) { debug(`Reached concurrency limit, queueing task id ${taskId}`); + tasks.update(taskId, { percent: 0, message: 'Waiting for other app tasks to complete' }, NOOP_CALLBACK); gPendingTasks.push({ appId, taskId, callback }); return; } @@ -41,7 +50,8 @@ function scheduleTask(appId, taskId, callback) { var lockError = locker.recursiveLock(locker.OP_APPTASK); if (lockError) { - debug(`Locked for another operation, queueing task id ${taskId}`); + debug(`Could not get lock. ${lockError.message}, queueing task id ${taskId}`); + tasks.update(taskId, { percent: 0, message: waitText(lockError.operation) }, NOOP_CALLBACK); gPendingTasks.push({ appId, taskId, callback }); return; } diff --git a/src/locker.js b/src/locker.js index c40db92ed..369e355c3 100644 --- a/src/locker.js +++ b/src/locker.js @@ -22,7 +22,11 @@ Locker.prototype.OP_APPTASK = 'apptask'; Locker.prototype.lock = function (operation) { assert.strictEqual(typeof operation, 'string'); - if (this._operation !== null) return new Error('Already locked for ' + this._operation); + if (this._operation !== null) { + let error = new Error(`Locked for ${this._operation}`); + error.operation = this._operation; + return error; + } this._operation = operation; ++this._lockDepth;