From 81d06374838d2e667745ef308e1e13c3b2dec516 Mon Sep 17 00:00:00 2001 From: Girish Ramakrishnan Date: Wed, 19 Aug 2020 21:39:58 -0700 Subject: [PATCH] Allow box auto update pattern to be configurable We just use the current app auto update pattern as the default. There is now only one pattern for box and app updates. Fixes #727 --- ...50259-settings-merge-autoupdate-pattern.js | 22 +++++ src/cron.js | 55 ++++------- src/routes/settings.js | 34 ++----- src/routes/test/settings-test.js | 99 +++---------------- src/settings.js | 54 ++-------- 5 files changed, 69 insertions(+), 195 deletions(-) create mode 100644 migrations/20200820050259-settings-merge-autoupdate-pattern.js diff --git a/migrations/20200820050259-settings-merge-autoupdate-pattern.js b/migrations/20200820050259-settings-merge-autoupdate-pattern.js new file mode 100644 index 000000000..e6f68bd48 --- /dev/null +++ b/migrations/20200820050259-settings-merge-autoupdate-pattern.js @@ -0,0 +1,22 @@ +'use strict'; + +var async = require('async'); + +exports.up = function(db, callback) { + db.runSql('SELECT * FROM settings WHERE name=?', ['app_autoupdate_pattern'], function (error, results) { + if (error || results.length === 0) return callback(error); // will use defaults from box code + + var updatePattern = results[0].value; // use app auto update patter for the box as well + + async.series([ + db.runSql.bind(db, 'START TRANSACTION;'), + db.runSql.bind(db, 'DELETE FROM settings WHERE name=? OR name=?', ['app_autoupdate_pattern', 'box_autoupdate_pattern']), + db.runSql.bind(db, 'INSERT settings (name, value) VALUES(?, ?)', ['autoupdate_pattern', updatePattern]), + db.runSql.bind(db, 'COMMIT') + ], callback); + }); +}; + +exports.down = function(db, callback) { + callback(); +}; diff --git a/src/cron.js b/src/cron.js index 377452b07..18428c6e8 100644 --- a/src/cron.js +++ b/src/cron.js @@ -4,8 +4,7 @@ // If the patterns overlap all the time, then the task may not ever get a chance to run! // If you change this change dashboard patterns in settings.html const DEFAULT_CLEANUP_BACKUPS_PATTERN = '00 30 1,3,5,23 * * *', - DEFAULT_BOX_AUTOUPDATE_PATTERN = '00 00 1,3,5,23 * * *', - DEFAULT_APP_AUTOUPDATE_PATTERN = '00 15 1,3,5,23 * * *'; + DEFAULT_AUTOUPDATE_PATTERN = '00 00 1,3,5,23 * * *'; exports = module.exports = { startJobs, @@ -14,8 +13,7 @@ exports = module.exports = { handleSettingsChanged, - DEFAULT_BOX_AUTOUPDATE_PATTERN, - DEFAULT_APP_AUTOUPDATE_PATTERN + DEFAULT_AUTOUPDATE_PATTERN, }; var appHealthMonitor = require('./apphealthmonitor.js'), @@ -38,8 +36,7 @@ var appHealthMonitor = require('./apphealthmonitor.js'), updateChecker = require('./updatechecker.js'); var gJobs = { - appAutoUpdater: null, - boxAutoUpdater: null, + autoUpdater: null, backup: null, updateChecker: null, systemChecks: null, @@ -134,8 +131,7 @@ function startJobs(callback) { const tz = allSettings[settings.TIME_ZONE_KEY]; backupConfigChanged(allSettings[settings.BACKUP_CONFIG_KEY], tz); - appAutoupdatePatternChanged(allSettings[settings.APP_AUTOUPDATE_PATTERN_KEY], tz); - boxAutoupdatePatternChanged(allSettings[settings.BOX_AUTOUPDATE_PATTERN_KEY], tz); + autoupdatePatternChanged(allSettings[settings.AUTOUPDATE_PATTERN_KEY], tz); dynamicDnsChanged(allSettings[settings.DYNAMIC_DNS_KEY]); callback(); @@ -150,8 +146,7 @@ function handleSettingsChanged(key, value) { switch (key) { case settings.TIME_ZONE_KEY: case settings.BACKUP_CONFIG_KEY: - case settings.APP_AUTOUPDATE_PATTERN_KEY: - case settings.BOX_AUTOUPDATE_PATTERN_KEY: + case settings.AUTOUPDATE_PATTERN_KEY: case settings.DYNAMIC_DNS_KEY: debug('handleSettingsChanged: recreating all jobs'); async.series([ @@ -180,44 +175,27 @@ function backupConfigChanged(value, tz) { }); } -function boxAutoupdatePatternChanged(pattern, tz) { +function autoupdatePatternChanged(pattern, tz) { assert.strictEqual(typeof pattern, 'string'); assert.strictEqual(typeof tz, 'string'); - debug(`boxAutoupdatePatternChanged: pattern - ${pattern} (${tz})`); + debug(`autoupdatePatternChanged: pattern - ${pattern} (${tz})`); - if (gJobs.boxAutoUpdater) gJobs.boxAutoUpdater.stop(); + if (gJobs.autoUpdater) gJobs.autoUpdater.stop(); if (pattern === constants.AUTOUPDATE_PATTERN_NEVER) return; - gJobs.boxAutoUpdater = new CronJob({ + gJobs.autoUpdater = new CronJob({ cronTime: pattern, onTick: function() { - var updateInfo = updateChecker.getUpdateInfo(); - if (!updateInfo.box) return debug('No box auto updates available'); - if (updateInfo.box.unstable) return debug('Will not auto-update to unstable release'); - debug('Starting autoupdate to %j', updateInfo.box); - updater.updateToLatest({ skipBackup: false }, auditSource.CRON, NOOP_CALLBACK); - }, - start: true, - timeZone: tz - }); -} + const updateInfo = updateChecker.getUpdateInfo(); + // do box before app updates. for the off chance that the box logic fixes some app update logic issue + if (updateInfo.box && !updateInfo.box.unstable) { + debug('Starting box autoupdate to %j', updateInfo.box); + updater.updateToLatest({ skipBackup: false }, auditSource.CRON, NOOP_CALLBACK); + return; + } -function appAutoupdatePatternChanged(pattern, tz) { - assert.strictEqual(typeof pattern, 'string'); - assert.strictEqual(typeof tz, 'string'); - - debug(`appAutoupdatePatternChanged: pattern ${pattern} (${tz})`); - - if (gJobs.appAutoUpdater) gJobs.appAutoUpdater.stop(); - - if (pattern === constants.AUTOUPDATE_PATTERN_NEVER) return; - - gJobs.appAutoUpdater = new CronJob({ - cronTime: pattern, - onTick: function() { - var updateInfo = updateChecker.getUpdateInfo(); if (updateInfo.apps) { debug('Starting app update to %j', updateInfo.apps); apps.autoupdateApps(updateInfo.apps, auditSource.CRON, NOOP_CALLBACK); @@ -225,6 +203,7 @@ function appAutoupdatePatternChanged(pattern, tz) { debug('No app auto updates available'); } }, + start: true, timeZone: tz }); diff --git a/src/routes/settings.js b/src/routes/settings.js index 2c91ac767..4f2b7b068 100644 --- a/src/routes/settings.js +++ b/src/routes/settings.js @@ -17,40 +17,20 @@ var assert = require('assert'), HttpSuccess = require('connect-lastmile').HttpSuccess, settings = require('../settings.js'); -function getAppAutoupdatePattern(req, res, next) { - settings.getAppAutoupdatePattern(function (error, pattern) { +function getAutoupdatePattern(req, res, next) { + settings.getAutoupdatePattern(function (error, pattern) { if (error) return next(BoxError.toHttpError(error)); next(new HttpSuccess(200, { pattern: pattern })); }); } -function setAppAutoupdatePattern(req, res, next) { +function setAutoupdatePattern(req, res, next) { assert.strictEqual(typeof req.body, 'object'); if (typeof req.body.pattern !== 'string') return next(new HttpError(400, 'pattern is required')); - settings.setAppAutoupdatePattern(req.body.pattern, function (error) { - if (error) return next(BoxError.toHttpError(error)); - - next(new HttpSuccess(200, {})); - }); -} - -function getBoxAutoupdatePattern(req, res, next) { - settings.getBoxAutoupdatePattern(function (error, pattern) { - if (error) return next(BoxError.toHttpError(error)); - - next(new HttpSuccess(200, { pattern: pattern })); - }); -} - -function setBoxAutoupdatePattern(req, res, next) { - assert.strictEqual(typeof req.body, 'object'); - - if (typeof req.body.pattern !== 'string') return next(new HttpError(400, 'pattern is required')); - - settings.setBoxAutoupdatePattern(req.body.pattern, function (error) { + settings.setAutoupdatePattern(req.body.pattern, function (error) { if (error) return next(BoxError.toHttpError(error)); next(new HttpSuccess(200, {})); @@ -305,8 +285,7 @@ function get(req, res, next) { case settings.REGISTRY_CONFIG_KEY: return getRegistryConfig(req, res, next); case settings.SYSINFO_CONFIG_KEY: return getSysinfoConfig(req, res, next); - case settings.APP_AUTOUPDATE_PATTERN_KEY: return getAppAutoupdatePattern(req, res, next); - case settings.BOX_AUTOUPDATE_PATTERN_KEY: return getBoxAutoupdatePattern(req, res, next); + case settings.AUTOUPDATE_PATTERN_KEY: return getAutoupdatePattern(req, res, next); case settings.TIME_ZONE_KEY: return getTimeZone(req, res, next); case settings.DIRECTORY_CONFIG_KEY: return getDirectoryConfig(req, res, next); @@ -327,8 +306,7 @@ function set(req, res, next) { case settings.REGISTRY_CONFIG_KEY: return setRegistryConfig(req, res, next); case settings.SYSINFO_CONFIG_KEY: return setSysinfoConfig(req, res, next); - case settings.APP_AUTOUPDATE_PATTERN_KEY: return setAppAutoupdatePattern(req, res, next); - case settings.BOX_AUTOUPDATE_PATTERN_KEY: return setBoxAutoupdatePattern(req, res, next); + case settings.AUTOUPDATE_PATTERN_KEY: return setAutoupdatePattern(req, res, next); case settings.TIME_ZONE_KEY: return setTimeZone(req, res, next); case settings.DIRECTORY_CONFIG_KEY: return setDirectoryConfig(req, res, next); diff --git a/src/routes/test/settings-test.js b/src/routes/test/settings-test.js index b2bf1aea8..a731fed67 100644 --- a/src/routes/test/settings-test.js +++ b/src/routes/test/settings-test.js @@ -58,9 +58,9 @@ describe('Settings API', function () { before(setup); after(cleanup); - describe('app_autoupdate_pattern', function () { + describe('autoupdate_pattern', function () { it('can get app auto update pattern (default)', function (done) { - superagent.get(SERVER_URL + '/api/v1/settings/app_autoupdate_pattern') + superagent.get(SERVER_URL + '/api/v1/settings/autoupdate_pattern') .query({ access_token: token }) .end(function (err, res) { expect(res.statusCode).to.equal(200); @@ -69,8 +69,8 @@ describe('Settings API', function () { }); }); - it('cannot set app_autoupdate_pattern without pattern', function (done) { - superagent.post(SERVER_URL + '/api/v1/settings/app_autoupdate_pattern') + it('cannot set autoupdate_pattern without pattern', function (done) { + superagent.post(SERVER_URL + '/api/v1/settings/autoupdate_pattern') .query({ access_token: token }) .end(function (err, res) { expect(res.statusCode).to.equal(400); @@ -78,8 +78,8 @@ describe('Settings API', function () { }); }); - it('can set app_autoupdate_pattern', function (done) { - superagent.post(SERVER_URL + '/api/v1/settings/app_autoupdate_pattern') + it('can set autoupdate_pattern', function (done) { + superagent.post(SERVER_URL + '/api/v1/settings/autoupdate_pattern') .query({ access_token: token }) .send({ pattern: '00 30 11 * * 1-5' }) .end(function (err, res) { @@ -88,8 +88,8 @@ describe('Settings API', function () { }); }); - it('can get app auto update pattern', function (done) { - superagent.get(SERVER_URL + '/api/v1/settings/app_autoupdate_pattern') + it('can get auto update pattern', function (done) { + superagent.get(SERVER_URL + '/api/v1/settings/autoupdate_pattern') .query({ access_token: token }) .end(function (err, res) { expect(res.statusCode).to.equal(200); @@ -98,8 +98,8 @@ describe('Settings API', function () { }); }); - it('can set app_autoupdate_pattern to never', function (done) { - superagent.post(SERVER_URL + '/api/v1/settings/app_autoupdate_pattern') + it('can set autoupdate_pattern to never', function (done) { + superagent.post(SERVER_URL + '/api/v1/settings/autoupdate_pattern') .query({ access_token: token }) .send({ pattern: constants.AUTOUPDATE_PATTERN_NEVER }) .end(function (err, res) { @@ -108,8 +108,8 @@ describe('Settings API', function () { }); }); - it('can get app auto update pattern', function (done) { - superagent.get(SERVER_URL + '/api/v1/settings/app_autoupdate_pattern') + it('can get auto update pattern', function (done) { + superagent.get(SERVER_URL + '/api/v1/settings/autoupdate_pattern') .query({ access_token: token }) .end(function (err, res) { expect(res.statusCode).to.equal(200); @@ -118,79 +118,8 @@ describe('Settings API', function () { }); }); - it('cannot set invalid app_autoupdate_pattern', function (done) { - superagent.post(SERVER_URL + '/api/v1/settings/app_autoupdate_pattern') - .query({ access_token: token }) - .send({ pattern: '1 3 x 5 6' }) - .end(function (err, res) { - expect(res.statusCode).to.equal(400); - done(); - }); - }); - }); - - describe('box_autoupdate_pattern', function () { - it('can get app auto update pattern (default)', function (done) { - superagent.get(SERVER_URL + '/api/v1/settings/box_autoupdate_pattern') - .query({ access_token: token }) - .end(function (err, res) { - expect(res.statusCode).to.equal(200); - expect(res.body.pattern).to.be.ok(); - done(); - }); - }); - - it('cannot set box_autoupdate_pattern without pattern', function (done) { - superagent.post(SERVER_URL + '/api/v1/settings/box_autoupdate_pattern') - .query({ access_token: token }) - .end(function (err, res) { - expect(res.statusCode).to.equal(400); - done(); - }); - }); - - it('can set box_autoupdate_pattern', function (done) { - superagent.post(SERVER_URL + '/api/v1/settings/box_autoupdate_pattern') - .query({ access_token: token }) - .send({ pattern: '00 30 11 * * 1-5' }) - .end(function (err, res) { - expect(res.statusCode).to.equal(200); - done(); - }); - }); - - it('can get app auto update pattern', function (done) { - superagent.get(SERVER_URL + '/api/v1/settings/box_autoupdate_pattern') - .query({ access_token: token }) - .end(function (err, res) { - expect(res.statusCode).to.equal(200); - expect(res.body.pattern).to.be('00 30 11 * * 1-5'); - done(); - }); - }); - - it('can set box_autoupdate_pattern to never', function (done) { - superagent.post(SERVER_URL + '/api/v1/settings/box_autoupdate_pattern') - .query({ access_token: token }) - .send({ pattern: constants.AUTOUPDATE_PATTERN_NEVER }) - .end(function (err, res) { - expect(res.statusCode).to.equal(200); - done(); - }); - }); - - it('can get app auto update pattern', function (done) { - superagent.get(SERVER_URL + '/api/v1/settings/box_autoupdate_pattern') - .query({ access_token: token }) - .end(function (err, res) { - expect(res.statusCode).to.equal(200); - expect(res.body.pattern).to.be(constants.AUTOUPDATE_PATTERN_NEVER); - done(); - }); - }); - - it('cannot set invalid box_autoupdate_pattern', function (done) { - superagent.post(SERVER_URL + '/api/v1/settings/box_autoupdate_pattern') + it('cannot set invalid autoupdate_pattern', function (done) { + superagent.post(SERVER_URL + '/api/v1/settings/autoupdate_pattern') .query({ access_token: token }) .send({ pattern: '1 3 x 5 6' }) .end(function (err, res) { diff --git a/src/settings.js b/src/settings.js index 0e8e4b782..9bbff7522 100644 --- a/src/settings.js +++ b/src/settings.js @@ -1,11 +1,8 @@ 'use strict'; exports = module.exports = { - getAppAutoupdatePattern, - setAppAutoupdatePattern, - - getBoxAutoupdatePattern, - setBoxAutoupdatePattern, + getAutoupdatePattern, + setAutoupdatePattern, getTimeZone, setTimeZone, @@ -92,8 +89,7 @@ exports = module.exports = { DIRECTORY_CONFIG_KEY: 'directory_config', // strings - APP_AUTOUPDATE_PATTERN_KEY: 'app_autoupdate_pattern', - BOX_AUTOUPDATE_PATTERN_KEY: 'box_autoupdate_pattern', + AUTOUPDATE_PATTERN_KEY: 'autoupdate_pattern', TIME_ZONE_KEY: 'time_zone', CLOUDRON_NAME_KEY: 'cloudron_name', LICENSE_KEY: 'license_key', @@ -137,8 +133,7 @@ var addons = require('./addons.js'), let gDefaults = (function () { var result = { }; - result[exports.APP_AUTOUPDATE_PATTERN_KEY] = cron.DEFAULT_APP_AUTOUPDATE_PATTERN; - result[exports.BOX_AUTOUPDATE_PATTERN_KEY] = cron.DEFAULT_BOX_AUTOUPDATE_PATTERN; + result[exports.AUTOUPDATE_PATTERN_KEY] = cron.DEFAULT_AUTOUPDATE_PATTERN; result[exports.TIME_ZONE_KEY] = 'America/Los_Angeles'; result[exports.CLOUDRON_NAME_KEY] = 'Cloudron'; result[exports.DYNAMIC_DNS_KEY] = false; @@ -206,7 +201,7 @@ function notifyChange(key, value) { cron.handleSettingsChanged(key, value); } -function setAppAutoupdatePattern(pattern, callback) { +function setAutoupdatePattern(pattern, callback) { assert.strictEqual(typeof pattern, 'string'); assert.strictEqual(typeof callback, 'function'); @@ -215,49 +210,20 @@ function setAppAutoupdatePattern(pattern, callback) { if (!job) return callback(new BoxError(BoxError.BAD_FIELD, 'Invalid pattern', { field: 'pattern' })); } - settingsdb.set(exports.APP_AUTOUPDATE_PATTERN_KEY, pattern, function (error) { + settingsdb.set(exports.AUTOUPDATE_PATTERN_KEY, pattern, function (error) { if (error) return callback(error); - notifyChange(exports.APP_AUTOUPDATE_PATTERN_KEY, pattern); + notifyChange(exports.AUTOUPDATE_PATTERN_KEY, pattern); return callback(null); }); } -function getAppAutoupdatePattern(callback) { +function getAutoupdatePattern(callback) { assert.strictEqual(typeof callback, 'function'); - settingsdb.get(exports.APP_AUTOUPDATE_PATTERN_KEY, function (error, pattern) { - if (error && error.reason === BoxError.NOT_FOUND) return callback(null, gDefaults[exports.APP_AUTOUPDATE_PATTERN_KEY]); - if (error) return callback(error); - - callback(null, pattern); - }); -} - -function setBoxAutoupdatePattern(pattern, callback) { - assert.strictEqual(typeof pattern, 'string'); - assert.strictEqual(typeof callback, 'function'); - - if (pattern !== constants.AUTOUPDATE_PATTERN_NEVER) { // check if pattern is valid - var job = safe.safeCall(function () { return new CronJob(pattern); }); - if (!job) return callback(new BoxError(BoxError.BAD_FIELD, 'Invalid pattern', { field: 'pattern' })); - } - - settingsdb.set(exports.BOX_AUTOUPDATE_PATTERN_KEY, pattern, function (error) { - if (error) return callback(error); - - notifyChange(exports.BOX_AUTOUPDATE_PATTERN_KEY, pattern); - - return callback(null); - }); -} - -function getBoxAutoupdatePattern(callback) { - assert.strictEqual(typeof callback, 'function'); - - settingsdb.get(exports.BOX_AUTOUPDATE_PATTERN_KEY, function (error, pattern) { - if (error && error.reason === BoxError.NOT_FOUND) return callback(null, gDefaults[exports.BOX_AUTOUPDATE_PATTERN_KEY]); + settingsdb.get(exports.AUTOUPDATE_PATTERN_KEY, function (error, pattern) { + if (error && error.reason === BoxError.NOT_FOUND) return callback(null, gDefaults[exports.AUTOUPDATE_PATTERN_KEY]); if (error) return callback(error); callback(null, pattern);