Fix platform update logic

This commit is contained in:
Girish Ramakrishnan
2019-09-24 20:29:01 -07:00
parent 00fd9e5b7f
commit 85c13cae58
9 changed files with 80 additions and 50 deletions

View File

@@ -54,7 +54,7 @@ exports = module.exports = {
restoreInstalledApps: restoreInstalledApps,
configureInstalledApps: configureInstalledApps,
resumeTasks: resumeTasks,
schedulePendingTasks: schedulePendingTasks,
getDataDir: getDataDir,
getIconPath: getIconPath,
@@ -655,16 +655,18 @@ function addTask(appId, installationState, task, callback) {
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;
const scheduleNow = 'scheduleNow' in task ? task.scheduleNow : true;
const requireNullTaskId = 'requireNullTaskId' in task ? task.requireNullTaskId : true;
tasks.add(tasks.TASK_APP, [ appId, args ], function (error, taskId) {
if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error));
appdb.setTask(appId, _.extend({ installationState, taskId, error: null }, values), requiredState, function (error) {
appdb.setTask(appId, _.extend({ installationState, taskId, error: null }, values), { requiredState, requireNullTaskId }, 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));
scheduleTask(appId, installationState, taskId, NOOP_CALLBACK);
if (scheduleNow) scheduleTask(appId, installationState, taskId, NOOP_CALLBACK);
callback(null, { taskId });
});
@@ -1843,21 +1845,25 @@ function restoreInstalledApps(callback) {
getAll(function (error, apps) {
if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error));
apps = apps.filter(app => app.installationState !== exports.ISTATE_ERROR); // remove errored apps. let them be 'repaired' by hand
apps = apps.filter(app => app.installationState !== exports.ISTATE_PENDING_RESTORE); // safeguard against tasks being created non-stop if we crash on startup
async.map(apps, function (app, iteratorDone) {
async.eachSeries(apps, function (app, iteratorDone) {
backups.getByAppIdPaged(1, 1, app.id, function (error, results) {
const restoreConfig = !error && results.length ? { backupId: results[0].id, backupFormat: results[0].format, oldManifest: app.manifest } : null;
const task = {
args: { restoreConfig, overwriteDns: true },
values: {},
scheduleNow: false, // task will be scheduled by autoRestartTasks when platform is ready
requireNullTaskId: false // ignore existing stale taskId
};
debug(`marking ${app.fqdn} for restore using restore config ${JSON.stringify(restoreConfig)}`);
debug(`restoreInstalledApps: marking ${app.fqdn} for restore using restore config ${JSON.stringify(restoreConfig)}`);
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)}`);
addTask(app.id, exports.ISTATE_PENDING_RESTORE, task, function (error, result) {
if (error) debug(`restoreInstalledApps: error marking ${app.fqdn} for restore: ${JSON.stringify(error)}`);
else debug(`restoreInstalledApps: marked ${app.id} for restore with taskId ${result.taskId}`);
const task = {
args: { restoreConfig, overwriteDns: true },
values: {}
};
addTask(app.id, exports.ISTATE_PENDING_RESTORE, task, () => iteratorDone()); // always succeed
iteratorDone(); // ignore error
});
});
}, callback);
@@ -1870,31 +1876,34 @@ function configureInstalledApps(callback) {
getAll(function (error, apps) {
if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error));
apps = apps.filter(app => app.installationState !== exports.ISTATE_ERROR); // remove errored apps. let them be 'repaired' by hand
apps = apps.filter(app => app.installationState !== exports.ISTATE_PENDING_CONFIGURE); // safeguard against tasks being created non-stop if we crash on startup
async.map(apps, function (app, iteratorDone) {
debug(`marking ${app.fqdn} for reconfigure`);
async.eachSeries(apps, function (app, iteratorDone) {
debug(`configureInstalledApps: marking ${app.fqdn} for reconfigure`);
appdb.update(app.id, { taskId: null, error: null }, function (error) { // clear any stale taskId
if (error) debug(`Error marking ${app.fqdn} for reconfigure: ${JSON.stringify(error)}`);
const oldConfig = _.pick(app, 'location', 'domain', 'fqdn', 'alternateDomains', 'portBindings');
const task = {
args: { oldConfig },
values: {},
scheduleNow: false, // task will be scheduled by autoRestartTasks when platform is ready
requireNullTaskId: false // ignore existing stale taskId
};
const oldConfig = _.pick(app, 'location', 'domain', 'fqdn', 'alternateDomains', 'portBindings');
const task = {
args: { oldConfig },
values: {}
};
addTask(app.id, exports.ISTATE_PENDING_CONFIGURE, task, () => iteratorDone()); // always succeed
addTask(app.id, exports.ISTATE_PENDING_CONFIGURE, task, function (error, result) {
if (error) debug(`configureInstalledApps: error marking ${app.fqdn} for configure: ${JSON.stringify(error)}`);
else debug(`configureInstalledApps: marked ${app.id} for re-configure with taskId ${result.taskId}`);
iteratorDone(); // ignore error
});
}, callback);
});
}
// resume app tasks when platform is ready or after a crash
function resumeTasks(callback) {
// auto-restart app tasks after a crash
function schedulePendingTasks(callback) {
assert.strictEqual(typeof callback, 'function');
debug('resuming tasks');
appTaskManager.initializeSync();
debug('schedulePendingTasks: scheduling app tasks');
getAll(function (error, result) {
if (error) return callback(error);
@@ -1902,7 +1911,7 @@ function resumeTasks(callback) {
result.forEach(function (app) {
if (!app.taskId) return; // if not in any pending state, do nothing
debug(`resumeTask: schedule task for ${app.fqdn} ${app.id}: state=${app.installationState},taskId=${app.taskId}`);
debug(`schedulePendingTasks: schedule task for ${app.fqdn} ${app.id}: state=${app.installationState},taskId=${app.taskId}`);
scheduleTask(app.id, app.installationState, app.taskId, NOOP_CALLBACK);
});