Skip to content

Commit

Permalink
✨ Add job scheduler
Browse files Browse the repository at this point in the history
Co-authored-by: Mickael Alibert <mickael.alibert@pix.fr>
Co-authored-by: Emeric Martineau <eric.martineau@pix.fr>
  • Loading branch information
3 people committed Sep 7, 2023
1 parent a5964e1 commit b65b1f3
Show file tree
Hide file tree
Showing 5 changed files with 208 additions and 1 deletion.
6 changes: 5 additions & 1 deletion config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ function _getJSON(value) {
return JSON.parse(value);
}

function isFeatureEnabled(environmentVariable) {
return environmentVariable === 'true';
}

module.exports = (function () {
const config = {
port: _getNumber(process.env.PORT, 3000),
Expand Down Expand Up @@ -104,7 +108,7 @@ module.exports = (function () {
},

tasks: {
autoScaleEnabled: process.env.FT_AUTOSCALE_WEB || false,
autoScaleEnabled: isFeatureEnabled(process.env.FT_AUTOSCALE_WEB),
scheduleAutoScaleUp: process.env.SCHEDULE_AUTOSCALE_UP || '* 0 8 * * *',
scheduleAutoScaleDown: process.env.SCHEDULE_AUTOSCALE_DOWN || '* 0 19 * * *',
autoScaleApplicationName: process.env.SCHEDULE_AUTOSCALE_APP_NAME,
Expand Down
4 changes: 4 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ const githubServices = require('./common/services/github');
const { deploy } = require('./run/services/deploy');
const ecoModeService = require('./build/services/eco-mode-service');
const logger = require('./common/services/logger');
const taskScheluder = require('./run/services/task-scheduler');
const { tasks } = require('./run/services/tasks');

const init = async () => {
await ecoModeService.start();
Expand All @@ -21,6 +23,8 @@ const init = async () => {
config.pixSiteDeploy.schedule,
);

taskScheluder(tasks);

await server.start();

logger.info({
Expand Down
29 changes: 29 additions & 0 deletions run/services/task-scheduler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const parisTimezone = 'Europe/Paris';
const CronJob = require('cron').CronJob;
const logger = require('../../common/services/logger');

const taskScheluder = function (tasksList) {
const scheduleTask = ({ schedule, handler }) => {
new CronJob({
cronTime: schedule,
onTick: handler,
onComplete: null,
start: true,
timezone: parisTimezone,
});
};

tasksList.forEach(({ name, schedule, enabled, handler }) => {
if (enabled) {
logger.info({ event: 'task-scheduler', message: `task ${name} scheduled ${schedule}` });
scheduleTask({
schedule,
handler,
});
} else {
logger.info({ event: 'task-scheduler', message: `task ${name} not scheduled` });
}
});
};

module.exports = taskScheluder;
73 changes: 73 additions & 0 deletions sample.env
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,79 @@ SCALINGO_TOKEN_PRODUCTION=__CHANGE_ME__
# default: "https://api.osc-secnum-fr1.scalingo.com"
SCALINGO_API_URL_PRODUCTION=https://api.osc-secnum-fr1.scalingo.com

# ======================
# SCALINGO CONTAINERS AUTOSCALING SCHEDULED TASK
# ======================

# Enable web containers autoscaling tasks
#
# presence: optional
# type: text
# value: true to activate
# FT_AUTOSCALE_WEB

# Date time at which app web containers autoscaler must be upsized
#
# presence: optionnal
# type: String (RegExp)
# default: "* 0 8 * * *"
# SCHEDULE_AUTOSCALE_UP

# Date time at which app web containers autoscaler must be downsized
#
# presence: optionnal
# type: String (RegExp)
# default: "* 0 19 * * *"
# SCHEDULE_AUTOSCALE_DOWN

# Name of the application whose web containers have to be autoscaled
# If not present, the application will crash
#
# presence: required when autoscaling enabled
# type: text
# default: none
#SCHEDULE_AUTOSCALE_APP_NAME

# Application that has to be autoscaled name's region
# If not present, the application will crash
#
# presence: required when autoscaling enabled
# type: text
# default: none
# SCHEDULE_AUTOSCALE_REGION

# Minimum number of web containers with which start upsize autoscaling
# If not present, the application will crash
#
# presence: required when autoscaling enabled
# type: number
# default: 2
# SCHEDULE_AUTOSCALE_UP_SETTINGS_MIN

# Maximum number of web containers limit to upsize autoscaling
# If not present, the application will crash
#
# presence: required when autoscaling enabled
# type: number
# default: none
# SCHEDULE_AUTOSCALE_UP_SETTINGS_MAX

# Minimum number of web containers with which start donwsize autoscaling
# If not present, the application will crash
#
# presence: required when autoscaling enabled
# type: number
# default: 2
# SCHEDULE_AUTOSCALE_DOWN_SETTINGS_MIN

# Maximum number of web containers limit to downsize autoscaling
# If not present, the application will crash
#
# presence: required when autoscaling enabled
# type: number
# default: none
# SCHEDULE_AUTOSCALE_DOWN_SETTINGS_MAX

# ======================
# BUILD
# ======================
Expand Down
97 changes: 97 additions & 0 deletions test/integration/run/services/task-scheduler_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
const { expect, sinon } = require('../../../test-helper');
const logger = require('../../../../common/services/logger');
const taskScheluder = require('../../../../run/services/task-scheduler');

describe('Integration | Run | Services | schedule-task', function () {
let clock;
const ONE_SECOND = 1 * 10 ** 3;

beforeEach(function () {
clock = sinon.useFakeTimers();
});

afterEach(function () {
clock.restore();
sinon.restore();
});

describe('#ScheduleTask', function () {
it('should schedule enabled tasks', function () {
// given
const loggerInfoStub = sinon.stub(logger, 'info');

const runStub = sinon.stub();
const testTasks = [
{
name: 'task1',
enabled: true,
schedule: '* * * * * *',
handler: runStub,
},
];

// when
taskScheluder(testTasks);

// then
expect(runStub).to.not.have.been.called;

// when
clock.tick(ONE_SECOND - 1);

// then
expect(runStub).to.not.have.been.called;

// when
clock.tick(1);

// then
expect(runStub).to.have.been.calledOnce;

expect(loggerInfoStub.calledOnce).to.be.true;
expect(loggerInfoStub.firstCall.args[0]).to.deep.equal({
event: 'task-scheduler',
message: 'task task1 scheduled * * * * * *',
});
});

it('should not schedule tasks', function () {
// given
const loggerInfoStub = sinon.stub(logger, 'info');

const runStub = sinon.stub();
const testTasks = [
{
name: 'task1',
enabled: false,
schedule: '* * * * * *',
handler: runStub,
},
];

// when
taskScheluder(testTasks);
// then
expect(runStub).to.not.have.been.called;

// when
clock.tick(ONE_SECOND - 1);

// then
expect(runStub).to.not.have.been.called;

// when
clock.tick(1);

// then
expect(runStub).to.not.have.been.called;

expect(loggerInfoStub.calledOnce).to.be.true;

expect(loggerInfoStub.firstCall.args[0]).to.deep.equal({
event: 'task-scheduler',
message: 'task task1 not scheduled',
});
});
});
});

0 comments on commit b65b1f3

Please sign in to comment.