Files
cloudron-box/src/apptaskmanager.js
Girish Ramakrishnan b1380819ba debug taskId
2019-09-03 16:06:28 -07:00

104 lines
3.4 KiB
JavaScript

'use strict';
exports = module.exports = {
resumeTasks: resumeTasks,
scheduleTask: scheduleTask
};
let apps = require('./apps.js'),
assert = require('assert'),
debug = require('debug')('box:taskmanager'),
fs = require('fs'),
locker = require('./locker.js'),
safe = require('safetydance'),
path = require('path'),
paths = require('./paths.js'),
tasks = require('./tasks.js');
let gActiveTasks = { }; // indexed by app id
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');
assert.strictEqual(typeof taskId, 'string');
assert.strictEqual(typeof callback, 'function');
if (appId in gActiveTasks) {
return callback(new Error(`Task for %s is already active: ${appId}`));
}
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;
}
var lockError = locker.recursiveLock(locker.OP_APPTASK);
if (lockError) {
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;
}
gActiveTasks[appId] = {};
const logFile = path.join(paths.LOG_DIR, appId, 'apptask.log');
if (!fs.existsSync(path.dirname(logFile))) safe.fs.mkdirSync(path.dirname(logFile)); // ensure directory
tasks.startTask(taskId, { logFile }, function (error, result) {
callback(error, result);
delete gActiveTasks[appId];
locker.unlock(locker.OP_APPTASK); // unlock event will trigger next task
});
}
function startNextTask() {
if (gPendingTasks.length === 0) return;
assert(Object.keys(gActiveTasks).length < TASK_CONCURRENCY);
const t = gPendingTasks.shift();
scheduleTask(t.appId, t.taskId, t.callback);
}
// resume app tasks when platform is ready or after a crash
function resumeTasks(callback) {
assert.strictEqual(typeof callback, 'function');
debug('resuming tasks');
locker.on('unlocked', startNextTask);
apps.getAll(function (error, result) {
if (error) return callback(error);
result.forEach(function (app) {
if (app.installationState === apps.ISTATE_INSTALLED && app.runState === apps.RSTATE_RUNNING) return;
if (app.installationState === apps.ISTATE_ERROR) return;
debug(`resumeTask: schedule task for ${app.fqdn} ${app.id}: state=${app.installationState},taskId=${app.taskId}`);
scheduleTask(app.id, app.taskId, NOOP_CALLBACK);
});
callback(null);
});
}