diff --git a/src/apps.js b/src/apps.js index e4fed0c75..0b5e14e91 100644 --- a/src/apps.js +++ b/src/apps.js @@ -942,14 +942,16 @@ function update(appId, data, auditSource, callback) { if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new AppsError(AppsError.BAD_STATE)); // might be a bad guess if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error)); - taskmanager.restartAppTask(appId); + startAppTask(appId, function (error) { + if (error) return callback(error); - eventlog.add(eventlog.ACTION_APP_UPDATE, auditSource, { appId: appId, toManifest: manifest, fromManifest: app.manifest, force: data.force, app: app }); + eventlog.add(eventlog.ACTION_APP_UPDATE, auditSource, { appId: appId, toManifest: manifest, fromManifest: app.manifest, force: data.force, app: app }); - // clear update indicator, if update fails, it will come back through the update checker - updateChecker.resetAppUpdateInfo(appId); + // clear update indicator, if update fails, it will come back through the update checker + updateChecker.resetAppUpdateInfo(appId); - callback(null); + callback(null); + }); }); }); }); diff --git a/src/apptask.js b/src/apptask.js index 297fe92c2..ba84458b7 100644 --- a/src/apptask.js +++ b/src/apptask.js @@ -712,8 +712,9 @@ function configure(app, progressCallback, callback) { } // nginx configuration is skipped because app.httpPort is expected to be available -function update(app, callback) { +function update(app, progressCallback, callback) { assert.strictEqual(typeof app, 'object'); + assert.strictEqual(typeof progressCallback, 'function'); assert.strictEqual(typeof callback, 'function'); debugApp(app, `Updating to ${app.updateConfig.manifest.version}`); @@ -727,14 +728,14 @@ function update(app, callback) { async.series([ // this protects against the theoretical possibility of an app being marked for update from // a previous version of box code - updateApp.bind(null, app, { installationProgress: '0, Verify manifest' }), + progressCallback.bind(null, { percent: 0, message: 'Verify manifest' }), verifyManifest.bind(null, app.updateConfig.manifest), function (next) { if (FORCED_UPDATE) return next(null); async.series([ - updateApp.bind(null, app, { installationProgress: '15, Backing up app' }), + progressCallback.bind(null, { percent: 15, message: 'Backing up app' }), // preserve update backups for 3 weeks backups.backupApp.bind(null, app, { preserveSecs: 3*7*24*60*60 }, (progress) => updateApp(app, { installationProgress: `15, Backup - ${progress.message}` }, NOOP_CALLBACK)) ], function (error) { @@ -745,12 +746,12 @@ function update(app, callback) { // download new image before app is stopped. this is so we can reduce downtime // and also not remove the 'common' layers when the old image is deleted - updateApp.bind(null, app, { installationProgress: '25, Downloading image' }), + progressCallback.bind(null, { percent: 25, message: 'Downloading image' }), downloadImage.bind(null, app.updateConfig.manifest), // note: we cleanup first and then backup. this is done so that the app is not running should backup fail // we cannot easily 'recover' from backup failures because we have to revert manfest and portBindings - updateApp.bind(null, app, { installationProgress: '35, Cleaning up old install' }), + progressCallback.bind(null, { percent: 35, message: 'Cleaning up old install' }), removeCollectdProfile.bind(null, app), removeLogrotateConfig.bind(null, app), stopApp.bind(null, app), @@ -788,32 +789,29 @@ function update(app, callback) { // switch over to the new config. manifest, memoryLimit, portBindings, appstoreId are updated here updateApp.bind(null, app, app.updateConfig), - updateApp.bind(null, app, { installationProgress: '45, Downloading icon' }), + progressCallback.bind(null, { percent: 45, message: 'Downloading icon' }), downloadIcon.bind(null, app), - updateApp.bind(null, app, { installationProgress: '70, Updating addons' }), + progressCallback.bind(null, { percent: 70, message: 'Updating addons' }), addons.setupAddons.bind(null, app, app.updateConfig.manifest.addons), - updateApp.bind(null, app, { installationProgress: '80, Creating container' }), + progressCallback.bind(null, { percent: 80, message: 'Creating container' }), createContainer.bind(null, app), - updateApp.bind(null, app, { installationProgress: '85, Setting up logrotate config' }), + progressCallback.bind(null, { percent: 85, message: 'Setting up logrotate config' }), addLogrotateConfig.bind(null, app), - updateApp.bind(null, app, { installationProgress: '90, Add collectd profile' }), + progressCallback.bind(null, { percent: 90, message: 'Add collectd profile' }), addCollectdProfile.bind(null, app), runApp.bind(null, app), - // done! - function (callback) { - debugApp(app, 'updated'); - updateApp(app, { installationState: appdb.ISTATE_INSTALLED, installationProgress: '', health: null, updateConfig: null, updateTime: new Date() }, callback); - } + progressCallback.bind(null, { percent: 100, message: 'Done' }), + updateApp.bind(null, app, { installationState: appdb.ISTATE_INSTALLED, installationProgress: '', health: null, updateConfig: null, taskId: null, updateTime: new Date() }) ], function seriesDone(error) { if (error && error.backupError) { debugApp(app, 'update aborted because backup failed', error); - updateApp(app, { installationState: appdb.ISTATE_INSTALLED, installationProgress: '', health: null, updateConfig: null }, callback.bind(null, error)); + updateApp(app, { installationState: appdb.ISTATE_INSTALLED, installationProgress: '', health: null, updateConfig: null, taskId: null }, callback.bind(null, error)); } else if (error) { debugApp(app, 'Error updating app: %s', error); updateApp(app, { installationState: appdb.ISTATE_ERROR, installationProgress: error.message, updateTime: new Date() }, callback.bind(null, error)); @@ -968,6 +966,8 @@ function run(appId, progressCallback, callback) { case appdb.ISTATE_PENDING_UNINSTALL: return uninstall(app, progressCallback, callback); case appdb.ISTATE_PENDING_CLONE: return install(app, progressCallback, callback); case appdb.ISTATE_PENDING_RESTORE: return install(app, progressCallback, callback); + case appdb.ISTATE_PENDING_UPDATE: return update(app, progressCallback, callback); + case appdb.ISTATE_PENDING_FORCE_UPDATE: return update(app, progressCallback, callback); default: debugApp(app, 'apptask launched with invalid command');