From 2e130ef99d3f0ba6ff3bbe9e5e8911df8dc5eb3e Mon Sep 17 00:00:00 2001 From: Girish Ramakrishnan Date: Wed, 6 May 2020 16:50:27 -0700 Subject: [PATCH] Add automatic flag for update checks The appstore can then known if a user clicked the check for updates button manually or if it was done by the automatic updater. We will fix appstore so that updates are always provided for manual checks. automatic updates will follow our roll out plan. We do have one issue that the automatic update checker will reset the manual updates when it runs, but this is OK. --- CHANGES | 1 + src/appstore.js | 23 ++++++++++++++---- src/cron.js | 4 ++-- src/routes/cloudron.js | 4 ++-- src/test/updatechecker-test.js | 43 +++++++++++++++++----------------- src/updatechecker.js | 10 ++++---- 6 files changed, 51 insertions(+), 34 deletions(-) diff --git a/CHANGES b/CHANGES index f844d11a9..1e664415e 100644 --- a/CHANGES +++ b/CHANGES @@ -1927,4 +1927,5 @@ * Add per-app redis status and configuration to Services * spam: large emails were not scanned * mail relay: fix delivery event log +* manual update check always gets the latest updates diff --git a/src/appstore.js b/src/appstore.js index 04e186f43..fa2cef44c 100644 --- a/src/appstore.js +++ b/src/appstore.js @@ -340,7 +340,8 @@ function sendAliveStatus(callback) { }); } -function getBoxUpdate(callback) { +function getBoxUpdate(options, callback) { + assert.strictEqual(typeof options, 'object'); assert.strictEqual(typeof callback, 'function'); getCloudronToken(function (error, token) { @@ -348,7 +349,13 @@ function getBoxUpdate(callback) { const url = `${settings.apiServerOrigin()}/api/v1/boxupdate`; - superagent.get(url).query({ accessToken: token, boxVersion: constants.VERSION }).timeout(10 * 1000).end(function (error, result) { + const query = { + accessToken: token, + boxVersion: constants.VERSION, + automatic: options.automatic + }; + + superagent.get(url).query(query).timeout(10 * 1000).end(function (error, result) { if (error && !error.response) return callback(new BoxError(BoxError.NETWORK_ERROR, error.message)); if (result.statusCode === 401) return callback(new BoxError(BoxError.INVALID_CREDENTIALS)); if (result.statusCode === 422) return callback(new BoxError(BoxError.LICENSE_ERROR, result.body.message)); @@ -374,16 +381,24 @@ function getBoxUpdate(callback) { }); } -function getAppUpdate(app, callback) { +function getAppUpdate(app, options, callback) { assert.strictEqual(typeof app, 'object'); + assert.strictEqual(typeof options, 'object'); assert.strictEqual(typeof callback, 'function'); getCloudronToken(function (error, token) { if (error) return callback(error); const url = `${settings.apiServerOrigin()}/api/v1/appupdate`; + const query = { + accessToken: token, + boxVersion: constants.VERSION, + appId: app.appStoreId, + appVersion: app.manifest.version, + automatic: options.automatic + }; - superagent.get(url).query({ accessToken: token, boxVersion: constants.VERSION, appId: app.appStoreId, appVersion: app.manifest.version }).timeout(10 * 1000).end(function (error, result) { + superagent.get(url).query(query).timeout(10 * 1000).end(function (error, result) { if (error && !error.response) return callback(new BoxError(BoxError.NETWORK_ERROR, error)); if (result.statusCode === 401) return callback(new BoxError(BoxError.INVALID_CREDENTIALS)); if (result.statusCode === 422) return callback(new BoxError(BoxError.LICENSE_ERROR, result.body.message)); diff --git a/src/cron.js b/src/cron.js index 24290258f..3e2a0c1a8 100644 --- a/src/cron.js +++ b/src/cron.js @@ -81,13 +81,13 @@ function startJobs(callback) { gJobs.boxUpdateCheckerJob = new CronJob({ cronTime: '00 ' + randomMinute + ' * * * *', // once an hour - onTick: () => updateChecker.checkBoxUpdates(NOOP_CALLBACK), + onTick: () => updateChecker.checkBoxUpdates({ automatic: true }, NOOP_CALLBACK), start: true }); gJobs.appUpdateChecker = new CronJob({ cronTime: '00 ' + randomMinute + ' * * * *', // once an hour - onTick: () => updateChecker.checkAppUpdates(NOOP_CALLBACK), + onTick: () => updateChecker.checkAppUpdates({ automatic: true }, NOOP_CALLBACK), start: true }); diff --git a/src/routes/cloudron.js b/src/routes/cloudron.js index 4b71ce214..0c9a4ab65 100644 --- a/src/routes/cloudron.js +++ b/src/routes/cloudron.js @@ -218,8 +218,8 @@ function checkForUpdates(req, res, next) { req.clearTimeout(); async.series([ - updateChecker.checkAppUpdates, - updateChecker.checkBoxUpdates + (done) => updateChecker.checkAppUpdates({ automatic: false }, done), + (done) => updateChecker.checkBoxUpdates({ automatic: false }, done), ], function () { next(new HttpSuccess(200, { update: updateChecker.getUpdateInfo() })); }); diff --git a/src/test/updatechecker-test.js b/src/test/updatechecker-test.js index a18f60d66..481a59eac 100644 --- a/src/test/updatechecker-test.js +++ b/src/test/updatechecker-test.js @@ -13,7 +13,6 @@ var appdb = require('../appdb.js'), database = require('../database.js'), domains = require('../domains.js'), expect = require('expect.js'), - mail = require('../mail.js'), mailer = require('../mailer.js'), nock = require('nock'), paths = require('../paths.js'), @@ -95,10 +94,10 @@ describe('updatechecker - box - manual (email)', function () { var scope = nock('http://localhost:4444') .get('/api/v1/boxupdate') - .query({ boxVersion: constants.VERSION, accessToken: 'atoken' }) + .query({ boxVersion: constants.VERSION, accessToken: 'atoken', automatic: false }) .reply(204, { } ); - updatechecker.checkBoxUpdates(function (error) { + updatechecker.checkBoxUpdates({ automatic: false }, function (error) { expect(!error).to.be.ok(); expect(updatechecker.getUpdateInfo().box).to.be(null); expect(scope.isDone()).to.be.ok(); @@ -112,10 +111,10 @@ describe('updatechecker - box - manual (email)', function () { var scope = nock('http://localhost:4444') .get('/api/v1/boxupdate') - .query({ boxVersion: constants.VERSION, accessToken: 'atoken' }) + .query({ boxVersion: constants.VERSION, accessToken: 'atoken', automatic: false }) .reply(200, { version: UPDATE_VERSION, changelog: [''], sourceTarballUrl: 'box.tar.gz', sourceTarballSigUrl: 'box.tar.gz.sig', boxVersionsUrl: 'box.versions', boxVersionsSigUrl: 'box.versions.sig' } ); - updatechecker.checkBoxUpdates(function (error) { + updatechecker.checkBoxUpdates({ automatic: false }, function (error) { expect(!error).to.be.ok(); expect(updatechecker.getUpdateInfo().box.version).to.be(UPDATE_VERSION); expect(updatechecker.getUpdateInfo().box.sourceTarballUrl).to.be('box.tar.gz'); @@ -130,10 +129,10 @@ describe('updatechecker - box - manual (email)', function () { var scope = nock('http://localhost:4444') .get('/api/v1/boxupdate') - .query({ boxVersion: constants.VERSION, accessToken: 'atoken' }) + .query({ boxVersion: constants.VERSION, accessToken: 'atoken', automatic: false }) .reply(404, { version: '2.0.0-pre.0', changelog: [''], sourceTarballUrl: 'box-pre.tar.gz' } ); - updatechecker.checkBoxUpdates(function (error) { + updatechecker.checkBoxUpdates({ automatic: false }, function (error) { expect(error).to.be.ok(); expect(updatechecker.getUpdateInfo().box).to.be(null); expect(scope.isDone()).to.be.ok(); @@ -165,10 +164,10 @@ describe('updatechecker - box - automatic (no email)', function () { var scope = nock('http://localhost:4444') .get('/api/v1/boxupdate') - .query({ boxVersion: constants.VERSION, accessToken: 'atoken' }) + .query({ boxVersion: constants.VERSION, accessToken: 'atoken', automatic: false }) .reply(200, { version: UPDATE_VERSION, changelog: [''], sourceTarballUrl: 'box.tar.gz', sourceTarballSigUrl: 'box.tar.gz.sig', boxVersionsUrl: 'box.versions', boxVersionsSigUrl: 'box.versions.sig' } ); - updatechecker.checkBoxUpdates(function (error) { + updatechecker.checkBoxUpdates({ automatic: false }, function (error) { expect(!error).to.be.ok(); expect(updatechecker.getUpdateInfo().box.version).to.be(UPDATE_VERSION); expect(scope.isDone()).to.be.ok(); @@ -200,10 +199,10 @@ describe('updatechecker - box - automatic free (email)', function () { var scope = nock('http://localhost:4444') .get('/api/v1/boxupdate') - .query({ boxVersion: constants.VERSION, accessToken: 'atoken' }) + .query({ boxVersion: constants.VERSION, accessToken: 'atoken', automatic: false }) .reply(200, { version: UPDATE_VERSION, changelog: [''], sourceTarballUrl: 'box.tar.gz', sourceTarballSigUrl: 'box.tar.gz.sig', boxVersionsUrl: 'box.versions', boxVersionsSigUrl: 'box.versions.sig' } ); - updatechecker.checkBoxUpdates(function (error) { + updatechecker.checkBoxUpdates({ automatic: false }, function (error) { expect(!error).to.be.ok(); expect(updatechecker.getUpdateInfo().box.version).to.be(UPDATE_VERSION); expect(scope.isDone()).to.be.ok(); @@ -265,10 +264,10 @@ describe('updatechecker - app - manual (email)', function () { var scope = nock('http://localhost:4444') .get('/api/v1/appupdate') - .query({ boxVersion: constants.VERSION, accessToken: 'atoken', appId: APP_0.appStoreId, appVersion: APP_0.manifest.version }) + .query({ boxVersion: constants.VERSION, accessToken: 'atoken', appId: APP_0.appStoreId, appVersion: APP_0.manifest.version, automatic: false }) .reply(204, { } ); - updatechecker.checkAppUpdates(function (error) { + updatechecker.checkAppUpdates({ automatic: false }, function (error) { expect(!error).to.be.ok(); expect(updatechecker.getUpdateInfo().apps).to.eql({}); expect(scope.isDone()).to.be.ok(); @@ -282,10 +281,10 @@ describe('updatechecker - app - manual (email)', function () { var scope = nock('http://localhost:4444') .get('/api/v1/appupdate') - .query({ boxVersion: constants.VERSION, accessToken: 'atoken', appId: APP_0.appStoreId, appVersion: APP_0.manifest.version }) + .query({ boxVersion: constants.VERSION, accessToken: 'atoken', appId: APP_0.appStoreId, appVersion: APP_0.manifest.version, automatic: false }) .reply(500, { update: { manifest: { version: '1.0.0', changelog: '* some changes' } } } ); - updatechecker.checkAppUpdates(function (error) { + updatechecker.checkAppUpdates({ automatic: false }, function (error) { expect(!error).to.be.ok(); expect(updatechecker.getUpdateInfo().apps).to.eql({}); expect(scope.isDone()).to.be.ok(); @@ -299,10 +298,10 @@ describe('updatechecker - app - manual (email)', function () { var scope = nock('http://localhost:4444') .get('/api/v1/appupdate') - .query({ boxVersion: constants.VERSION, accessToken: 'atoken', appId: APP_0.appStoreId, appVersion: APP_0.manifest.version }) + .query({ boxVersion: constants.VERSION, accessToken: 'atoken', appId: APP_0.appStoreId, appVersion: APP_0.manifest.version, automatic: false }) .reply(200, { manifest: { version: '2.0.0', changelog: '* some changes' } } ); - updatechecker.checkAppUpdates(function (error) { + updatechecker.checkAppUpdates({ automatic: false }, function (error) { expect(!error).to.be.ok(); expect(updatechecker.getUpdateInfo().apps).to.eql({ 'appid-0': { manifest: { version: '2.0.0', changelog: '* some changes' } } }); expect(scope.isDone()).to.be.ok(); @@ -314,7 +313,7 @@ describe('updatechecker - app - manual (email)', function () { it('does not offer old version', function (done) { nock.cleanAll(); - updatechecker.checkAppUpdates(function (error) { + updatechecker.checkAppUpdates({ automatic: false }, function (error) { expect(!error).to.be.ok(); expect(updatechecker.getUpdateInfo().apps).to.eql({ }); checkMails(0, done); @@ -374,10 +373,10 @@ describe('updatechecker - app - automatic (no email)', function () { var scope = nock('http://localhost:4444') .get('/api/v1/appupdate') - .query({ boxVersion: constants.VERSION, accessToken: 'atoken', appId: APP_0.appStoreId, appVersion: APP_0.manifest.version }) + .query({ boxVersion: constants.VERSION, accessToken: 'atoken', appId: APP_0.appStoreId, appVersion: APP_0.manifest.version, automatic: false }) .reply(200, { manifest: { version: '2.0.0', changelog: 'c' } } ); - updatechecker.checkAppUpdates(function (error) { + updatechecker.checkAppUpdates({ automatic: false }, function (error) { expect(!error).to.be.ok(); expect(updatechecker.getUpdateInfo().apps).to.eql({ 'appid-0': { manifest: { version: '2.0.0', changelog: 'c' } } }); expect(scope.isDone()).to.be.ok(); @@ -439,10 +438,10 @@ describe('updatechecker - app - automatic free (email)', function () { var scope = nock('http://localhost:4444') .get('/api/v1/appupdate') - .query({ boxVersion: constants.VERSION, accessToken: 'atoken', appId: APP_0.appStoreId, appVersion: APP_0.manifest.version }) + .query({ boxVersion: constants.VERSION, accessToken: 'atoken', appId: APP_0.appStoreId, appVersion: APP_0.manifest.version, automatic: false }) .reply(200, { manifest: { version: '2.0.0', changelog: 'c' } } ); - updatechecker.checkAppUpdates(function (error) { + updatechecker.checkAppUpdates({ automatic: false }, function (error) { expect(!error).to.be.ok(); expect(updatechecker.getUpdateInfo().apps).to.eql({ 'appid-0': { manifest: { version: '2.0.0', changelog: 'c' } } }); expect(scope.isDone()).to.be.ok(); diff --git a/src/updatechecker.js b/src/updatechecker.js index 6f49a137f..3c1461475 100644 --- a/src/updatechecker.js +++ b/src/updatechecker.js @@ -62,7 +62,8 @@ function resetAppUpdateInfo(appId) { } } -function checkAppUpdates(callback) { +function checkAppUpdates(options, callback) { + assert.strictEqual(typeof options, 'object'); assert.strictEqual(typeof callback, 'function'); debug('Checking App Updates'); @@ -84,7 +85,7 @@ function checkAppUpdates(callback) { if (app.appStoreId === '') return iteratorDone(); // appStoreId can be '' for dev apps if (app.runState === apps.RSTATE_STOPPED) return iteratorDone(); // stopped apps won't run migration scripts and shouldn't be updated - appstore.getAppUpdate(app, function (error, updateInfo) { + appstore.getAppUpdate(app, options, function (error, updateInfo) { if (error) { debug('Error getting app update info for %s', app.id, error); return iteratorDone(); // continue to next @@ -136,14 +137,15 @@ function checkAppUpdates(callback) { }); } -function checkBoxUpdates(callback) { +function checkBoxUpdates(options, callback) { + assert.strictEqual(typeof options, 'object'); assert.strictEqual(typeof callback, 'function'); debug('Checking Box Updates'); gBoxUpdateInfo = null; - appstore.getBoxUpdate(function (error, updateInfo) { + appstore.getBoxUpdate(options, function (error, updateInfo) { if (error || !updateInfo) return callback(error); gBoxUpdateInfo = updateInfo;