Merge pull request #44640 from vutny/fix-cron-schedule-splay

Fix #44583: splay with cron-like scheduled jobs
This commit is contained in:
Nicole Thomas 2017-11-30 10:30:40 -05:00 committed by GitHub
commit 06fb80b69c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 77 additions and 13 deletions

View file

@ -1244,8 +1244,27 @@ class Schedule(object):
run = False
seconds = data['_next_fire_time'] - now
if data['_splay']:
seconds = data['_splay'] - now
if 'splay' in data:
# Got "splay" configured, make decision to run a job based on that
if not data['_splay']:
# Try to add "splay" time only if next job fire time is
# still in the future. We should trigger job run
# immediately otherwise.
splay = _splay(data['splay'])
if now < data['_next_fire_time'] + splay:
log.debug('schedule.handle_func: Adding splay of '
'{0} seconds to next run.'.format(splay))
data['_splay'] = data['_next_fire_time'] + splay
if 'when' in data:
data['_run'] = True
else:
run = True
if data['_splay']:
# The "splay" configuration has been already processed, just use it
seconds = data['_splay'] - now
if seconds <= 0:
if '_seconds' in data:
run = True
@ -1264,16 +1283,6 @@ class Schedule(object):
run = True
data['_run_on_start'] = False
elif run:
if 'splay' in data and not data['_splay']:
splay = _splay(data['splay'])
if now < data['_next_fire_time'] + splay:
log.debug('schedule.handle_func: Adding splay of '
'{0} seconds to next run.'.format(splay))
run = False
data['_splay'] = data['_next_fire_time'] + splay
if 'when' in data:
data['_run'] = True
if 'range' in data:
if not _RANGE_SUPPORTED:
log.error('Missing python-dateutil. Ignoring job {0}'.format(job))

View file

@ -5,8 +5,9 @@
# Import python libs
from __future__ import absolute_import
import os
import copy
import os
import time
# Import Salt Testing Libs
from tests.support.unit import skipIf, TestCase
@ -17,6 +18,15 @@ import tests.integration as integration
import salt.config
from salt.utils.schedule import Schedule
# pylint: disable=import-error,unused-import
try:
import croniter
_CRON_SUPPORTED = True
except ImportError:
_CRON_SUPPORTED = False
# pylint: enable=import-error
ROOT_DIR = os.path.join(integration.TMP, 'schedule-unit-tests')
SOCK_DIR = os.path.join(ROOT_DIR, 'test-socks')
@ -28,6 +38,7 @@ DEFAULT_CONFIG['pki_dir'] = os.path.join(ROOT_DIR, 'pki')
DEFAULT_CONFIG['cachedir'] = os.path.join(ROOT_DIR, 'cache')
# pylint: disable=too-many-public-methods,invalid-name
@skipIf(NO_MOCK, NO_MOCK_REASON)
class ScheduleTestCase(TestCase):
'''
@ -276,3 +287,47 @@ class ScheduleTestCase(TestCase):
'''
self.schedule.opts.update({'schedule': {}, 'pillar': {'schedule': ''}})
self.assertRaises(ValueError, Schedule.eval, self.schedule)
def test_eval_schedule_time(self):
'''
Tests eval if the schedule setting time is in the future
'''
self.schedule.opts.update({'pillar': {'schedule': {}}})
self.schedule.opts.update({'schedule': {'testjob': {'function': 'test.true', 'seconds': 60}}})
now = int(time.time())
self.schedule.eval()
self.assertTrue(self.schedule.opts['schedule']['testjob']['_next_fire_time'] > now)
def test_eval_schedule_time_eval(self):
'''
Tests eval if the schedule setting time is in the future plus splay
'''
self.schedule.opts.update({'pillar': {'schedule': {}}})
self.schedule.opts.update(
{'schedule': {'testjob': {'function': 'test.true', 'seconds': 60, 'splay': 5}}})
now = int(time.time())
self.schedule.eval()
self.assertTrue(self.schedule.opts['schedule']['testjob']['_splay'] - now > 60)
@skipIf(not _CRON_SUPPORTED, 'croniter module not installed')
def test_eval_schedule_cron(self):
'''
Tests eval if the schedule is defined with cron expression
'''
self.schedule.opts.update({'pillar': {'schedule': {}}})
self.schedule.opts.update({'schedule': {'testjob': {'function': 'test.true', 'cron': '* * * * *'}}})
now = int(time.time())
self.schedule.eval()
self.assertTrue(self.schedule.opts['schedule']['testjob']['_next_fire_time'] > now)
@skipIf(not _CRON_SUPPORTED, 'croniter module not installed')
def test_eval_schedule_cron_splay(self):
'''
Tests eval if the schedule is defined with cron expression plus splay
'''
self.schedule.opts.update({'pillar': {'schedule': {}}})
self.schedule.opts.update(
{'schedule': {'testjob': {'function': 'test.true', 'cron': '* * * * *', 'splay': 5}}})
self.schedule.eval()
self.assertTrue(self.schedule.opts['schedule']['testjob']['_splay'] >
self.schedule.opts['schedule']['testjob']['_next_fire_time'])