diff --git a/src/cron.js b/src/cron.js index 1694a9c27..24290258f 100644 --- a/src/cron.js +++ b/src/cron.js @@ -12,6 +12,7 @@ var appHealthMonitor = require('./apphealthmonitor.js'), apps = require('./apps.js'), appstore = require('./appstore.js'), assert = require('assert'), + async = require('async'), auditSource = require('./auditsource.js'), backups = require('./backups.js'), cloudron = require('./cloudron.js'), @@ -59,148 +60,141 @@ var NOOP_CALLBACK = function (error) { if (error) debug(error); }; function startJobs(callback) { assert.strictEqual(typeof callback, 'function'); - var randomHourMinute = Math.floor(60*Math.random()); + const randomMinute = Math.floor(60*Math.random()); gJobs.alive = new CronJob({ - cronTime: '00 ' + randomHourMinute + ' * * * *', // every hour on a random minute + cronTime: '00 ' + randomMinute + ' * * * *', // every hour on a random minute onTick: appstore.sendAliveStatus, start: true }); + gJobs.systemChecks = new CronJob({ + cronTime: '00 30 * * * *', // every 30 minutes. if you change this interval, change the notification messages with correct duration + onTick: () => cloudron.runSystemChecks(NOOP_CALLBACK), + start: true + }); + + gJobs.diskSpaceChecker = new CronJob({ + cronTime: '00 30 * * * *', // every 30 minutes. if you change this interval, change the notification messages with correct duration + onTick: () => system.checkDiskSpace(NOOP_CALLBACK), + start: true + }); + + gJobs.boxUpdateCheckerJob = new CronJob({ + cronTime: '00 ' + randomMinute + ' * * * *', // once an hour + onTick: () => updateChecker.checkBoxUpdates(NOOP_CALLBACK), + start: true + }); + + gJobs.appUpdateChecker = new CronJob({ + cronTime: '00 ' + randomMinute + ' * * * *', // once an hour + onTick: () => updateChecker.checkAppUpdates(NOOP_CALLBACK), + start: true + }); + + gJobs.cleanupTokens = new CronJob({ + cronTime: '00 */30 * * * *', // every 30 minutes + onTick: janitor.cleanupTokens, + start: true + }); + + gJobs.cleanupBackups = new CronJob({ + cronTime: '00 45 1,3,5,23 * * *', // every 6 hours. try not to overlap with ensureBackup job + onTick: backups.startCleanupTask.bind(null, auditSource.CRON, NOOP_CALLBACK), + start: true + }); + + gJobs.cleanupEventlog = new CronJob({ + cronTime: '00 */30 * * * *', // every 30 minutes + onTick: eventlog.cleanup, + start: true + }); + + gJobs.dockerVolumeCleaner = new CronJob({ + cronTime: '00 00 */12 * * *', // every 12 hours + onTick: janitor.cleanupDockerVolumes, + start: true + }); + + gJobs.schedulerSync = new CronJob({ + cronTime: constants.TEST ? '*/10 * * * * *' : '00 */1 * * * *', // every minute + onTick: scheduler.sync, + start: true + }); + + gJobs.certificateRenew = new CronJob({ + cronTime: '00 00 */12 * * *', // every 12 hours + onTick: cloudron.renewCerts.bind(null, {}, auditSource.CRON, NOOP_CALLBACK), + start: true + }); + + gJobs.appHealthMonitor = new CronJob({ + cronTime: '*/10 * * * * *', // every 10 seconds + onTick: appHealthMonitor.run.bind(null, 10, NOOP_CALLBACK), + start: true + }); + settings.getAll(function (error, allSettings) { if (error) return callback(error); - recreateJobs(allSettings[settings.TIME_ZONE_KEY]); - appAutoupdatePatternChanged(allSettings[settings.APP_AUTOUPDATE_PATTERN_KEY]); - boxAutoupdatePatternChanged(allSettings[settings.BOX_AUTOUPDATE_PATTERN_KEY]); + 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); dynamicDnsChanged(allSettings[settings.DYNAMIC_DNS_KEY]); callback(); }); } +// eslint-disable-next-line no-unused-vars function handleSettingsChanged(key, value) { assert.strictEqual(typeof key, 'string'); - // value is a variant + switch (key) { - case settings.TIME_ZONE_KEY: recreateJobs(value); break; - case settings.APP_AUTOUPDATE_PATTERN_KEY: appAutoupdatePatternChanged(value); break; - case settings.BOX_AUTOUPDATE_PATTERN_KEY: boxAutoupdatePatternChanged(value); break; - case settings.DYNAMIC_DNS_KEY: dynamicDnsChanged(value); break; - default: break; + case settings.TIME_ZONE_KEY: + case settings.BACKUP_CONFIG_KEY: + case settings.APP_AUTOUPDATE_PATTERN_KEY: + case settings.BOX_AUTOUPDATE_PATTERN_KEY: + case settings.DYNAMIC_DNS_KEY: + debug('handleSettingsChanged: recreating all jobs'); + async.series([ + stopJobs, + startJobs + ], NOOP_CALLBACK); + break; + default: + break; } } -function recreateJobs(tz) { +function backupConfigChanged(value, tz) { + assert.strictEqual(typeof value, 'object'); assert.strictEqual(typeof tz, 'string'); - debug('Creating jobs with timezone %s', tz); + debug(`backupConfigChanged: interval ${value.intervalSecs} (${tz})`); if (gJobs.backup) gJobs.backup.stop(); + let pattern; + if (value.intervalSecs <= 6 * 60 * 60) { + pattern = '00 00 1,7,13,19 * * *'; // no option but to backup in the middle of the day + } else { + pattern = '00 00 1,3,5,23 * * *'; // avoid middle of the day backups + } + gJobs.backup = new CronJob({ - cronTime: '00 00 1,3,5,23 * * *', + cronTime: pattern, onTick: backups.ensureBackup.bind(null, auditSource.CRON, NOOP_CALLBACK), start: true, timeZone: tz }); - - if (gJobs.systemChecks) gJobs.systemChecks.stop(); - gJobs.systemChecks = new CronJob({ - cronTime: '00 30 * * * *', // every 30 minutes. if you change this interval, change the notification messages with correct duration - onTick: () => cloudron.runSystemChecks(NOOP_CALLBACK), - start: true, - timeZone: tz - }); - - if (gJobs.diskSpaceChecker) gJobs.diskSpaceChecker.stop(); - gJobs.diskSpaceChecker = new CronJob({ - cronTime: '00 30 * * * *', // every 30 minutes. if you change this interval, change the notification messages with correct duration - onTick: () => system.checkDiskSpace(NOOP_CALLBACK), - start: true, - timeZone: tz - }); - - // randomized pattern per cloudron every hour - var randomMinute = Math.floor(60*Math.random()); - - if (gJobs.boxUpdateCheckerJob) gJobs.boxUpdateCheckerJob.stop(); - gJobs.boxUpdateCheckerJob = new CronJob({ - cronTime: '00 ' + randomMinute + ' * * * *', // once an hour - onTick: () => updateChecker.checkBoxUpdates(NOOP_CALLBACK), - start: true, - timeZone: tz - }); - - if (gJobs.appUpdateChecker) gJobs.appUpdateChecker.stop(); - gJobs.appUpdateChecker = new CronJob({ - cronTime: '00 ' + randomMinute + ' * * * *', // once an hour - onTick: () => updateChecker.checkAppUpdates(NOOP_CALLBACK), - start: true, - timeZone: tz - }); - - if (gJobs.cleanupTokens) gJobs.cleanupTokens.stop(); - gJobs.cleanupTokens = new CronJob({ - cronTime: '00 */30 * * * *', // every 30 minutes - onTick: janitor.cleanupTokens, - start: true, - timeZone: tz - }); - - if (gJobs.cleanupBackups) gJobs.cleanupBackups.stop(); - gJobs.cleanupBackups = new CronJob({ - cronTime: '00 45 1,3,5,23 * * *', // every 6 hours. try not to overlap with ensureBackup job - onTick: backups.startCleanupTask.bind(null, auditSource.CRON, NOOP_CALLBACK), - start: true, - timeZone: tz - }); - - if (gJobs.cleanupEventlog) gJobs.cleanupEventlog.stop(); - gJobs.cleanupEventlog = new CronJob({ - cronTime: '00 */30 * * * *', // every 30 minutes - onTick: eventlog.cleanup, - start: true, - timeZone: tz - }); - - if (gJobs.dockerVolumeCleaner) gJobs.dockerVolumeCleaner.stop(); - gJobs.dockerVolumeCleaner = new CronJob({ - cronTime: '00 00 */12 * * *', // every 12 hours - onTick: janitor.cleanupDockerVolumes, - start: true, - timeZone: tz - }); - - if (gJobs.schedulerSync) gJobs.schedulerSync.stop(); - gJobs.schedulerSync = new CronJob({ - cronTime: constants.TEST ? '*/10 * * * * *' : '00 */1 * * * *', // every minute - onTick: scheduler.sync, - start: true, - timeZone: tz - }); - - if (gJobs.certificateRenew) gJobs.certificateRenew.stop(); - gJobs.certificateRenew = new CronJob({ - cronTime: '00 00 */12 * * *', // every 12 hours - onTick: cloudron.renewCerts.bind(null, {}, auditSource.CRON, NOOP_CALLBACK), - start: true, - timeZone: tz - }); - - if (gJobs.appHealthMonitor) gJobs.appHealthMonitor.stop(); - gJobs.appHealthMonitor = new CronJob({ - cronTime: '*/10 * * * * *', // every 10 seconds - onTick: appHealthMonitor.run.bind(null, 10, NOOP_CALLBACK), - start: true, - timeZone: tz - }); } -function boxAutoupdatePatternChanged(pattern) { +function boxAutoupdatePatternChanged(pattern, tz) { assert.strictEqual(typeof pattern, 'string'); - assert(gJobs.boxUpdateCheckerJob); + assert.strictEqual(typeof tz, 'string'); - debug('Box auto update pattern changed to %s', pattern); + debug(`boxAutoupdatePatternChanged: pattern - ${pattern} (${tz})`); if (gJobs.boxAutoUpdater) gJobs.boxAutoUpdater.stop(); @@ -218,15 +212,15 @@ function boxAutoupdatePatternChanged(pattern) { } }, start: true, - timeZone: gJobs.boxUpdateCheckerJob.cronTime.zone // hack + timeZone: tz }); } -function appAutoupdatePatternChanged(pattern) { +function appAutoupdatePatternChanged(pattern, tz) { assert.strictEqual(typeof pattern, 'string'); - assert(gJobs.boxUpdateCheckerJob); + assert.strictEqual(typeof tz, 'string'); - debug('Apps auto update pattern changed to %s', pattern); + debug(`appAutoupdatePatternChanged: pattern ${pattern} (${tz})`); if (gJobs.appAutoUpdater) gJobs.appAutoUpdater.stop(); @@ -244,13 +238,12 @@ function appAutoupdatePatternChanged(pattern) { } }, start: true, - timeZone: gJobs.boxUpdateCheckerJob.cronTime.zone // hack + timeZone: tz }); } function dynamicDnsChanged(enabled) { assert.strictEqual(typeof enabled, 'boolean'); - assert(gJobs.boxUpdateCheckerJob); debug('Dynamic DNS setting changed to %s', enabled); @@ -258,8 +251,7 @@ function dynamicDnsChanged(enabled) { gJobs.dynamicDns = new CronJob({ cronTime: '5 * * * * *', // we only update the records if the ip has changed. onTick: dyndns.sync.bind(null, auditSource.CRON, NOOP_CALLBACK), - start: true, - timeZone: gJobs.boxUpdateCheckerJob.cronTime.zone // hack + start: true }); } else { if (gJobs.dynamicDns) gJobs.dynamicDns.stop();