diff --git a/CHANGES b/CHANGES index e306950b9..76f21c967 100644 --- a/CHANGES +++ b/CHANGES @@ -2891,4 +2891,5 @@ * gandi: add token type in the setup view * mail: fix issue with dkim signing * mail: fix crash in dns list plugin +* scheduler: create jobs with cloudron tz setting diff --git a/src/apptaskmanager.js b/src/apptaskmanager.js index 673653adc..b74049523 100644 --- a/src/apptaskmanager.js +++ b/src/apptaskmanager.js @@ -47,14 +47,14 @@ async function drain() { if (!fs.existsSync(path.dirname(logFile))) safe.fs.mkdirSync(path.dirname(logFile)); // ensure directory - scheduler.suspendJobs(appId); + scheduler.suspendAppJobs(appId); tasks.startTask(taskId, Object.assign(options, { logFile }), async function (error, result) { onFinished(error, result); delete gActiveTasks[appId]; await locks.release(`${locks.TYPE_APP_PREFIX}${appId}`); - scheduler.resumeJobs(appId); + scheduler.resumeAppJobs(appId); }); } diff --git a/src/cron.js b/src/cron.js index c8b25b6c4..4be4c99c0 100644 --- a/src/cron.js +++ b/src/cron.js @@ -202,6 +202,7 @@ async function handleTimeZoneChanged(tz) { debug('handleTimeZoneChanged: recreating all jobs'); await stopJobs(); + await scheduler.deleteJobs(); // have to re-create with new tz await startJobs(); } diff --git a/src/scheduler.js b/src/scheduler.js index f08c201bd..0f3f47569 100644 --- a/src/scheduler.js +++ b/src/scheduler.js @@ -2,13 +2,16 @@ exports = module.exports = { sync, - suspendJobs, - resumeJobs + deleteJobs, + + suspendAppJobs, + resumeAppJobs }; const apps = require('./apps.js'), assert = require('assert'), BoxError = require('./boxerror.js'), + cloudron = require('./cloudron.js'), constants = require('./constants.js'), { CronJob } = require('cron'), debug = require('debug')('box:scheduler'), @@ -20,13 +23,13 @@ const gState = {}; // appId -> { containerId, schedulerConfig (manifest+crontab) const gSuspendedAppIds = new Set(); // suspended because some apptask is running // TODO: this should probably also stop existing jobs to completely prevent race but the code is not re-entrant -function suspendJobs(appId) { - debug(`suspendJobs: ${appId}`); +function suspendAppJobs(appId) { + debug(`suspendAppJobs: ${appId}`); gSuspendedAppIds.add(appId); } -function resumeJobs(appId) { - debug(`resumeJobs: ${appId}`); +function resumeAppJobs(appId) { + debug(`resumeAppJobs: ${appId}`); gSuspendedAppIds.delete(appId); } @@ -60,6 +63,7 @@ async function createJobs(app, schedulerConfig) { const appId = app.id; const jobs = {}; + const tz = await cloudron.getTimeZone(); for (const taskName of Object.keys(schedulerConfig)) { const { schedule, command } = schedulerConfig[taskName]; @@ -87,7 +91,8 @@ async function createJobs(app, schedulerConfig) { const [error] = await safe(runTask(appId, taskName)); // put the app id in closure, so we don't use the outdated app object by mistake if (error) debug(`could not run task ${taskName} : ${error.message}`); }, - start: true + start: true, + timeZone: tz }); jobs[taskName] = cronJob; @@ -96,7 +101,7 @@ async function createJobs(app, schedulerConfig) { return jobs; } -async function stopJobs(appId, appState) { +async function deleteAppJobs(appId, appState) { assert.strictEqual(typeof appId, 'string'); assert.strictEqual(typeof appState, 'object'); @@ -107,7 +112,16 @@ async function stopJobs(appId, appState) { const containerName = `${appId}-${taskName}`; const [error] = await safe(docker.deleteContainer(containerName)); - if (error) debug(`stopJobs: failed to delete task container with name ${containerName} : ${error.message}`); + if (error) debug(`deleteAppJobs: failed to delete task container with name ${containerName} : ${error.message}`); + } +} + +async function deleteJobs() { + for (const appId of Object.keys(gState)) { + debug(`deleteJobs: removing jobs of ${appId}`); + const [error] = await safe(deleteAppJobs(appId, gState[appId])); + if (error) debug(`deleteJobs: error stopping jobs of removed app ${appId}: ${error.message}`); + delete gState[appId]; } } @@ -122,7 +136,7 @@ async function sync() { for (const appId of removedAppIds) { debug(`sync: removing jobs of ${appId}`); - const [error] = await safe(stopJobs(appId, gState[appId])); + const [error] = await safe(deleteAppJobs(appId, gState[appId])); if (error) debug(`sync: error stopping jobs of removed app ${appId}: ${error.message}`); delete gState[appId]; } @@ -138,7 +152,7 @@ async function sync() { debug(`sync: clearing jobs of ${app.id} (${app.fqdn})`); - const [error] = await safe(stopJobs(app.id, appState)); + const [error] = await safe(deleteAppJobs(app.id, appState)); if (error) debug(`sync: error stopping jobs of ${app.id} : ${error.message}`); if (!schedulerConfig) { // updated app version removed scheduler addon