class ScheduleTestCase(TestCase): ''' Unit tests for salt.utils.schedule module ''' def setUp(self): with patch('salt.utils.schedule.clean_proc_dir', MagicMock(return_value=None)): self.schedule = Schedule(copy.deepcopy(DEFAULT_CONFIG), {}, returners={}) # delete_job tests def test_delete_job_exists(self): ''' Tests ensuring the job exists and deleting it ''' self.schedule.opts.update({'schedule': {'foo': 'bar'}, 'pillar': {}}) self.assertIn('foo', self.schedule.opts['schedule']) self.schedule.delete_job('foo') self.assertNotIn('foo', self.schedule.opts['schedule']) def test_delete_job_in_pillar(self): ''' Tests ignoring deletion job from pillar ''' self.schedule.opts.update({ 'pillar': { 'schedule': { 'foo': 'bar' } }, 'schedule': {} }) self.assertIn('foo', self.schedule.opts['pillar']['schedule']) self.schedule.delete_job('foo') self.assertIn('foo', self.schedule.opts['pillar']['schedule']) def test_delete_job_intervals(self): ''' Tests removing job from intervals ''' self.schedule.opts.update({'pillar': {}, 'schedule': {}}) self.schedule.intervals = {'foo': 'bar'} self.schedule.delete_job('foo') self.assertNotIn('foo', self.schedule.intervals) def test_delete_job_prefix(self): ''' Tests ensuring jobs exists and deleting them by prefix ''' self.schedule.opts.update({ 'schedule': { 'foobar': 'bar', 'foobaz': 'baz', 'fooboo': 'boo' }, 'pillar': {} }) ret = copy.deepcopy(self.schedule.opts) del ret['schedule']['foobar'] del ret['schedule']['foobaz'] self.schedule.delete_job_prefix('fooba') self.assertEqual(self.schedule.opts, ret) def test_delete_job_prefix_in_pillar(self): ''' Tests ignoring deletion jobs by prefix from pillar ''' self.schedule.opts.update({ 'pillar': { 'schedule': { 'foobar': 'bar', 'foobaz': 'baz', 'fooboo': 'boo' } }, 'schedule': {} }) ret = copy.deepcopy(self.schedule.opts) self.schedule.delete_job_prefix('fooba') self.assertEqual(self.schedule.opts, ret) # add_job tests def test_add_job_data_not_dict(self): ''' Tests if data is a dictionary ''' data = 'foo' self.assertRaises(ValueError, Schedule.add_job, self.schedule, data) def test_add_job_multiple_jobs(self): ''' Tests if more than one job is scheduled at a time ''' data = {'key1': 'value1', 'key2': 'value2'} self.assertRaises(ValueError, Schedule.add_job, self.schedule, data) def test_add_job(self): ''' Tests adding a job to the schedule ''' data = {'foo': {'bar': 'baz'}} ret = copy.deepcopy(self.schedule.opts) ret.update({ 'schedule': { 'foo': { 'bar': 'baz', 'enabled': True }, 'hello': { 'world': 'peace', 'enabled': True } }, 'pillar': {} }) self.schedule.opts.update({ 'schedule': { 'hello': { 'world': 'peace', 'enabled': True } }, 'pillar': {} }) Schedule.add_job(self.schedule, data) self.assertEqual(self.schedule.opts, ret) # enable_job tests def test_enable_job(self): ''' Tests enabling a job ''' self.schedule.opts.update({'schedule': {'name': {'enabled': 'foo'}}}) Schedule.enable_job(self.schedule, 'name') self.assertTrue(self.schedule.opts['schedule']['name']['enabled']) def test_enable_job_pillar(self): ''' Tests ignoring enable a job from pillar ''' self.schedule.opts.update( {'pillar': { 'schedule': { 'name': { 'enabled': False } } }}) Schedule.enable_job(self.schedule, 'name', persist=False) self.assertFalse( self.schedule.opts['pillar']['schedule']['name']['enabled']) # disable_job tests def test_disable_job(self): ''' Tests disabling a job ''' self.schedule.opts.update({ 'schedule': { 'name': { 'enabled': 'foo' } }, 'pillar': {} }) Schedule.disable_job(self.schedule, 'name') self.assertFalse(self.schedule.opts['schedule']['name']['enabled']) def test_disable_job_pillar(self): ''' Tests ignoring disable a job in pillar ''' self.schedule.opts.update({ 'pillar': { 'schedule': { 'name': { 'enabled': True } } }, 'schedule': {} }) Schedule.disable_job(self.schedule, 'name', persist=False) self.assertTrue( self.schedule.opts['pillar']['schedule']['name']['enabled']) # modify_job tests def test_modify_job(self): ''' Tests modifying a job in the scheduler ''' schedule = {'foo': 'bar'} self.schedule.opts.update({'schedule': {'name': 'baz'}, 'pillar': {}}) ret = copy.deepcopy(self.schedule.opts) ret.update({'schedule': {'name': {'foo': 'bar'}}}) Schedule.modify_job(self.schedule, 'name', schedule) self.assertEqual(self.schedule.opts, ret) def test_modify_job_not_exists(self): ''' Tests modifying a job in the scheduler if jobs not exists ''' schedule = {'foo': 'bar'} self.schedule.opts.update({'schedule': {}, 'pillar': {}}) ret = copy.deepcopy(self.schedule.opts) ret.update({'schedule': {'name': {'foo': 'bar'}}}) Schedule.modify_job(self.schedule, 'name', schedule) self.assertEqual(self.schedule.opts, ret) def test_modify_job_pillar(self): ''' Tests ignoring modification of job from pillar ''' schedule = {'foo': 'bar'} self.schedule.opts.update({ 'schedule': {}, 'pillar': { 'schedule': { 'name': 'baz' } } }) ret = copy.deepcopy(self.schedule.opts) Schedule.modify_job(self.schedule, 'name', schedule, persist=False) self.assertEqual(self.schedule.opts, ret) maxDiff = None # enable_schedule tests def test_enable_schedule(self): ''' Tests enabling the scheduler ''' self.schedule.opts.update({ 'schedule': { 'enabled': 'foo' }, 'pillar': {} }) Schedule.enable_schedule(self.schedule) self.assertTrue(self.schedule.opts['schedule']['enabled']) # disable_schedule tests def test_disable_schedule(self): ''' Tests disabling the scheduler ''' self.schedule.opts.update({ 'schedule': { 'enabled': 'foo' }, 'pillar': {} }) Schedule.disable_schedule(self.schedule) self.assertFalse(self.schedule.opts['schedule']['enabled']) # reload tests def test_reload_update_schedule_key(self): ''' Tests reloading the schedule from saved schedule where both the saved schedule and self.schedule.opts contain a schedule key ''' saved = {'schedule': {'foo': 'bar'}} ret = copy.deepcopy(self.schedule.opts) ret.update({'schedule': {'foo': 'bar', 'hello': 'world'}}) self.schedule.opts.update({'schedule': {'hello': 'world'}}) Schedule.reload(self.schedule, saved) self.assertEqual(self.schedule.opts, ret) def test_reload_update_schedule_no_key(self): ''' Tests reloading the schedule from saved schedule that does not contain a schedule key but self.schedule.opts does ''' saved = {'foo': 'bar'} ret = copy.deepcopy(self.schedule.opts) ret.update({'schedule': {'foo': 'bar', 'hello': 'world'}}) self.schedule.opts.update({'schedule': {'hello': 'world'}}) Schedule.reload(self.schedule, saved) self.assertEqual(self.schedule.opts, ret) def test_reload_no_schedule_in_opts(self): ''' Tests reloading the schedule from saved schedule that does not contain a schedule key and neither does self.schedule.opts ''' saved = {'foo': 'bar'} ret = copy.deepcopy(self.schedule.opts) ret['schedule'] = {'foo': 'bar'} self.schedule.opts.pop('schedule', None) Schedule.reload(self.schedule, saved) self.assertEqual(self.schedule.opts, ret) def test_reload_schedule_in_saved_but_not_opts(self): ''' Tests reloading the schedule from saved schedule that contains a schedule key, but self.schedule.opts does not ''' saved = {'schedule': {'foo': 'bar'}} ret = copy.deepcopy(self.schedule.opts) ret['schedule'] = {'foo': 'bar'} self.schedule.opts.pop('schedule', None) Schedule.reload(self.schedule, saved) self.assertEqual(self.schedule.opts, ret) # eval tests def test_eval_schedule_is_not_dict(self): ''' Tests eval if the schedule is not a dictionary ''' self.schedule.opts.update({'schedule': '', 'pillar': {'schedule': {}}}) self.assertRaises(ValueError, Schedule.eval, self.schedule) def test_eval_schedule_is_not_dict_in_pillar(self): ''' Tests eval if the schedule from pillar is not a dictionary ''' 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 = datetime.datetime.now() 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 = datetime.datetime.now() self.schedule.eval() self.assertTrue((self.schedule.opts['schedule']['testjob']['_splay'] - now).total_seconds() > 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 = datetime.datetime.now() 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'])
class ScheduleTestCase(TestCase): """ Unit tests for salt.utils.schedule module """ @classmethod def setUpClass(cls): root_dir = os.path.join(RUNTIME_VARS.TMP, "schedule-unit-tests") default_config = salt.config.minion_config(None) default_config["conf_dir"] = default_config["root_dir"] = root_dir default_config["sock_dir"] = os.path.join(root_dir, "test-socks") default_config["pki_dir"] = os.path.join(root_dir, "pki") default_config["cachedir"] = os.path.join(root_dir, "cache") cls.default_config = default_config @classmethod def tearDownClass(cls): delattr(cls, "default_config") def setUp(self): with patch("salt.utils.schedule.clean_proc_dir", MagicMock(return_value=None)): self.schedule = Schedule(copy.deepcopy(self.default_config), {}, returners={}) self.addCleanup(delattr, self, "schedule") # delete_job tests @skipIf(True, "SLOWTEST skip") def test_delete_job_exists(self): """ Tests ensuring the job exists and deleting it """ self.schedule.opts.update({"schedule": {"foo": "bar"}, "pillar": {}}) self.assertIn("foo", self.schedule.opts["schedule"]) self.schedule.delete_job("foo") self.assertNotIn("foo", self.schedule.opts["schedule"]) @skipIf(True, "SLOWTEST skip") def test_delete_job_in_pillar(self): """ Tests ignoring deletion job from pillar """ self.schedule.opts.update({ "pillar": { "schedule": { "foo": "bar" } }, "schedule": {} }) self.assertIn("foo", self.schedule.opts["pillar"]["schedule"]) self.schedule.delete_job("foo") self.assertIn("foo", self.schedule.opts["pillar"]["schedule"]) @skipIf(True, "SLOWTEST skip") def test_delete_job_intervals(self): """ Tests removing job from intervals """ self.schedule.opts.update({"pillar": {}, "schedule": {}}) self.schedule.intervals = {"foo": "bar"} self.schedule.delete_job("foo") self.assertNotIn("foo", self.schedule.intervals) @skipIf(True, "SLOWTEST skip") def test_delete_job_prefix(self): """ Tests ensuring jobs exists and deleting them by prefix """ self.schedule.opts.update({ "schedule": { "foobar": "bar", "foobaz": "baz", "fooboo": "boo" }, "pillar": {}, }) ret = copy.deepcopy(self.schedule.opts) del ret["schedule"]["foobar"] del ret["schedule"]["foobaz"] self.schedule.delete_job_prefix("fooba") self.assertEqual(self.schedule.opts, ret) @skipIf(True, "SLOWTEST skip") def test_delete_job_prefix_in_pillar(self): """ Tests ignoring deletion jobs by prefix from pillar """ self.schedule.opts.update({ "pillar": { "schedule": { "foobar": "bar", "foobaz": "baz", "fooboo": "boo" } }, "schedule": {}, }) ret = copy.deepcopy(self.schedule.opts) self.schedule.delete_job_prefix("fooba") self.assertEqual(self.schedule.opts, ret) # add_job tests def test_add_job_data_not_dict(self): """ Tests if data is a dictionary """ data = "foo" self.assertRaises(ValueError, Schedule.add_job, self.schedule, data) def test_add_job_multiple_jobs(self): """ Tests if more than one job is scheduled at a time """ data = {"key1": "value1", "key2": "value2"} self.assertRaises(ValueError, Schedule.add_job, self.schedule, data) @skipIf(True, "SLOWTEST skip") def test_add_job(self): """ Tests adding a job to the schedule """ data = {"foo": {"bar": "baz"}} ret = copy.deepcopy(self.schedule.opts) ret.update({ "schedule": { "foo": { "bar": "baz", "enabled": True }, "hello": { "world": "peace", "enabled": True }, }, "pillar": {}, }) self.schedule.opts.update({ "schedule": { "hello": { "world": "peace", "enabled": True } }, "pillar": {} }) Schedule.add_job(self.schedule, data) self.assertEqual(self.schedule.opts, ret) # enable_job tests @skipIf(True, "SLOWTEST skip") def test_enable_job(self): """ Tests enabling a job """ self.schedule.opts.update({"schedule": {"name": {"enabled": "foo"}}}) Schedule.enable_job(self.schedule, "name") self.assertTrue(self.schedule.opts["schedule"]["name"]["enabled"]) @skipIf(True, "SLOWTEST skip") def test_enable_job_pillar(self): """ Tests ignoring enable a job from pillar """ self.schedule.opts.update( {"pillar": { "schedule": { "name": { "enabled": False } } }}) Schedule.enable_job(self.schedule, "name", persist=False) self.assertFalse( self.schedule.opts["pillar"]["schedule"]["name"]["enabled"]) # disable_job tests @skipIf(True, "SLOWTEST skip") def test_disable_job(self): """ Tests disabling a job """ self.schedule.opts.update({ "schedule": { "name": { "enabled": "foo" } }, "pillar": {} }) Schedule.disable_job(self.schedule, "name") self.assertFalse(self.schedule.opts["schedule"]["name"]["enabled"]) @skipIf(True, "SLOWTEST skip") def test_disable_job_pillar(self): """ Tests ignoring disable a job in pillar """ self.schedule.opts.update({ "pillar": { "schedule": { "name": { "enabled": True } } }, "schedule": {} }) Schedule.disable_job(self.schedule, "name", persist=False) self.assertTrue( self.schedule.opts["pillar"]["schedule"]["name"]["enabled"]) # modify_job tests @skipIf(True, "SLOWTEST skip") def test_modify_job(self): """ Tests modifying a job in the scheduler """ schedule = {"foo": "bar"} self.schedule.opts.update({"schedule": {"name": "baz"}, "pillar": {}}) ret = copy.deepcopy(self.schedule.opts) ret.update({"schedule": {"name": {"foo": "bar"}}}) Schedule.modify_job(self.schedule, "name", schedule) self.assertEqual(self.schedule.opts, ret) def test_modify_job_not_exists(self): """ Tests modifying a job in the scheduler if jobs not exists """ schedule = {"foo": "bar"} self.schedule.opts.update({"schedule": {}, "pillar": {}}) ret = copy.deepcopy(self.schedule.opts) ret.update({"schedule": {"name": {"foo": "bar"}}}) Schedule.modify_job(self.schedule, "name", schedule) self.assertEqual(self.schedule.opts, ret) def test_modify_job_pillar(self): """ Tests ignoring modification of job from pillar """ schedule = {"foo": "bar"} self.schedule.opts.update({ "schedule": {}, "pillar": { "schedule": { "name": "baz" } } }) ret = copy.deepcopy(self.schedule.opts) Schedule.modify_job(self.schedule, "name", schedule, persist=False) self.assertEqual(self.schedule.opts, ret) maxDiff = None # enable_schedule tests @skipIf(True, "SLOWTEST skip") def test_enable_schedule(self): """ Tests enabling the scheduler """ with patch("salt.utils.schedule.Schedule.persist", MagicMock(return_value=None)) as persist_mock: self.schedule.opts.update({ "schedule": { "enabled": "foo" }, "pillar": {} }) Schedule.enable_schedule(self.schedule) self.assertTrue(self.schedule.opts["schedule"]["enabled"]) persist_mock.assert_called() # disable_schedule tests @skipIf(True, "SLOWTEST skip") def test_disable_schedule(self): """ Tests disabling the scheduler """ with patch("salt.utils.schedule.Schedule.persist", MagicMock(return_value=None)) as persist_mock: self.schedule.opts.update({ "schedule": { "enabled": "foo" }, "pillar": {} }) Schedule.disable_schedule(self.schedule) self.assertFalse(self.schedule.opts["schedule"]["enabled"]) persist_mock.assert_called() # reload tests def test_reload_update_schedule_key(self): """ Tests reloading the schedule from saved schedule where both the saved schedule and self.schedule.opts contain a schedule key """ saved = {"schedule": {"foo": "bar"}} ret = copy.deepcopy(self.schedule.opts) ret.update({"schedule": {"foo": "bar", "hello": "world"}}) self.schedule.opts.update({"schedule": {"hello": "world"}}) Schedule.reload(self.schedule, saved) self.assertEqual(self.schedule.opts, ret) def test_reload_update_schedule_no_key(self): """ Tests reloading the schedule from saved schedule that does not contain a schedule key but self.schedule.opts does """ saved = {"foo": "bar"} ret = copy.deepcopy(self.schedule.opts) ret.update({"schedule": {"foo": "bar", "hello": "world"}}) self.schedule.opts.update({"schedule": {"hello": "world"}}) Schedule.reload(self.schedule, saved) self.assertEqual(self.schedule.opts, ret) def test_reload_no_schedule_in_opts(self): """ Tests reloading the schedule from saved schedule that does not contain a schedule key and neither does self.schedule.opts """ saved = {"foo": "bar"} ret = copy.deepcopy(self.schedule.opts) ret["schedule"] = {"foo": "bar"} self.schedule.opts.pop("schedule", None) Schedule.reload(self.schedule, saved) self.assertEqual(self.schedule.opts, ret) def test_reload_schedule_in_saved_but_not_opts(self): """ Tests reloading the schedule from saved schedule that contains a schedule key, but self.schedule.opts does not """ saved = {"schedule": {"foo": "bar"}} ret = copy.deepcopy(self.schedule.opts) ret["schedule"] = {"foo": "bar"} self.schedule.opts.pop("schedule", None) Schedule.reload(self.schedule, saved) self.assertEqual(self.schedule.opts, ret) # eval tests def test_eval_schedule_is_not_dict(self): """ Tests eval if the schedule is not a dictionary """ self.schedule.opts.update({"schedule": "", "pillar": {"schedule": {}}}) self.assertRaises(ValueError, Schedule.eval, self.schedule) def test_eval_schedule_is_not_dict_in_pillar(self): """ Tests eval if the schedule from pillar is not a dictionary """ 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 = datetime.datetime.now() 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 = datetime.datetime.now() self.schedule.eval() self.assertTrue(self.schedule.opts["schedule"]["testjob"]["_splay"] - now > datetime.timedelta(seconds=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 = datetime.datetime.now() 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"]) @skipIf(True, "SLOWTEST skip") def test_handle_func_schedule_minion_blackout(self): """ Tests eval if the schedule from pillar is not a dictionary """ self.schedule.opts.update({"pillar": {"schedule": {}}}) self.schedule.opts.update({"grains": {"minion_blackout": True}}) self.schedule.opts.update({ "schedule": { "testjob": { "function": "test.true", "seconds": 60 } } }) data = { "function": "test.true", "_next_scheduled_fire_time": datetime.datetime(2018, 11, 21, 14, 9, 53, 903438), "run": True, "name": "testjob", "seconds": 60, "_splay": None, "_seconds": 60, "jid_include": True, "maxrunning": 1, "_next_fire_time": datetime.datetime(2018, 11, 21, 14, 8, 53, 903438), } with patch.object(salt.utils.schedule, "log") as log_mock: with patch("salt.utils.process.daemonize"), patch( "sys.platform", "linux2"): self.schedule.handle_func(False, "test.ping", data) self.assertTrue(log_mock.exception.called)
class ScheduleTestCase(TestCase): ''' Unit tests for salt.utils.schedule module ''' @classmethod def setUpClass(cls): root_dir = os.path.join(RUNTIME_VARS.TMP, 'schedule-unit-tests') default_config = salt.config.minion_config(None) default_config['conf_dir'] = default_config['root_dir'] = root_dir default_config['sock_dir'] = os.path.join(root_dir, 'test-socks') default_config['pki_dir'] = os.path.join(root_dir, 'pki') default_config['cachedir'] = os.path.join(root_dir, 'cache') cls.default_config = default_config @classmethod def tearDownClass(cls): delattr(cls, 'default_config') def setUp(self): with patch('salt.utils.schedule.clean_proc_dir', MagicMock(return_value=None)): self.schedule = Schedule(copy.deepcopy(self.default_config), {}, returners={}) self.addCleanup(delattr, self, 'schedule') # delete_job tests def test_delete_job_exists(self): ''' Tests ensuring the job exists and deleting it ''' self.schedule.opts.update({'schedule': {'foo': 'bar'}, 'pillar': {}}) self.assertIn('foo', self.schedule.opts['schedule']) self.schedule.delete_job('foo') self.assertNotIn('foo', self.schedule.opts['schedule']) def test_delete_job_in_pillar(self): ''' Tests ignoring deletion job from pillar ''' self.schedule.opts.update({ 'pillar': { 'schedule': { 'foo': 'bar' } }, 'schedule': {} }) self.assertIn('foo', self.schedule.opts['pillar']['schedule']) self.schedule.delete_job('foo') self.assertIn('foo', self.schedule.opts['pillar']['schedule']) def test_delete_job_intervals(self): ''' Tests removing job from intervals ''' self.schedule.opts.update({'pillar': {}, 'schedule': {}}) self.schedule.intervals = {'foo': 'bar'} self.schedule.delete_job('foo') self.assertNotIn('foo', self.schedule.intervals) def test_delete_job_prefix(self): ''' Tests ensuring jobs exists and deleting them by prefix ''' self.schedule.opts.update({ 'schedule': { 'foobar': 'bar', 'foobaz': 'baz', 'fooboo': 'boo' }, 'pillar': {} }) ret = copy.deepcopy(self.schedule.opts) del ret['schedule']['foobar'] del ret['schedule']['foobaz'] self.schedule.delete_job_prefix('fooba') self.assertEqual(self.schedule.opts, ret) def test_delete_job_prefix_in_pillar(self): ''' Tests ignoring deletion jobs by prefix from pillar ''' self.schedule.opts.update({ 'pillar': { 'schedule': { 'foobar': 'bar', 'foobaz': 'baz', 'fooboo': 'boo' } }, 'schedule': {} }) ret = copy.deepcopy(self.schedule.opts) self.schedule.delete_job_prefix('fooba') self.assertEqual(self.schedule.opts, ret) # add_job tests def test_add_job_data_not_dict(self): ''' Tests if data is a dictionary ''' data = 'foo' self.assertRaises(ValueError, Schedule.add_job, self.schedule, data) def test_add_job_multiple_jobs(self): ''' Tests if more than one job is scheduled at a time ''' data = {'key1': 'value1', 'key2': 'value2'} self.assertRaises(ValueError, Schedule.add_job, self.schedule, data) def test_add_job(self): ''' Tests adding a job to the schedule ''' data = {'foo': {'bar': 'baz'}} ret = copy.deepcopy(self.schedule.opts) ret.update({ 'schedule': { 'foo': { 'bar': 'baz', 'enabled': True }, 'hello': { 'world': 'peace', 'enabled': True } }, 'pillar': {} }) self.schedule.opts.update({ 'schedule': { 'hello': { 'world': 'peace', 'enabled': True } }, 'pillar': {} }) Schedule.add_job(self.schedule, data) self.assertEqual(self.schedule.opts, ret) # enable_job tests def test_enable_job(self): ''' Tests enabling a job ''' self.schedule.opts.update({'schedule': {'name': {'enabled': 'foo'}}}) Schedule.enable_job(self.schedule, 'name') self.assertTrue(self.schedule.opts['schedule']['name']['enabled']) def test_enable_job_pillar(self): ''' Tests ignoring enable a job from pillar ''' self.schedule.opts.update( {'pillar': { 'schedule': { 'name': { 'enabled': False } } }}) Schedule.enable_job(self.schedule, 'name', persist=False) self.assertFalse( self.schedule.opts['pillar']['schedule']['name']['enabled']) # disable_job tests def test_disable_job(self): ''' Tests disabling a job ''' self.schedule.opts.update({ 'schedule': { 'name': { 'enabled': 'foo' } }, 'pillar': {} }) Schedule.disable_job(self.schedule, 'name') self.assertFalse(self.schedule.opts['schedule']['name']['enabled']) def test_disable_job_pillar(self): ''' Tests ignoring disable a job in pillar ''' self.schedule.opts.update({ 'pillar': { 'schedule': { 'name': { 'enabled': True } } }, 'schedule': {} }) Schedule.disable_job(self.schedule, 'name', persist=False) self.assertTrue( self.schedule.opts['pillar']['schedule']['name']['enabled']) # modify_job tests def test_modify_job(self): ''' Tests modifying a job in the scheduler ''' schedule = {'foo': 'bar'} self.schedule.opts.update({'schedule': {'name': 'baz'}, 'pillar': {}}) ret = copy.deepcopy(self.schedule.opts) ret.update({'schedule': {'name': {'foo': 'bar'}}}) Schedule.modify_job(self.schedule, 'name', schedule) self.assertEqual(self.schedule.opts, ret) def test_modify_job_not_exists(self): ''' Tests modifying a job in the scheduler if jobs not exists ''' schedule = {'foo': 'bar'} self.schedule.opts.update({'schedule': {}, 'pillar': {}}) ret = copy.deepcopy(self.schedule.opts) ret.update({'schedule': {'name': {'foo': 'bar'}}}) Schedule.modify_job(self.schedule, 'name', schedule) self.assertEqual(self.schedule.opts, ret) def test_modify_job_pillar(self): ''' Tests ignoring modification of job from pillar ''' schedule = {'foo': 'bar'} self.schedule.opts.update({ 'schedule': {}, 'pillar': { 'schedule': { 'name': 'baz' } } }) ret = copy.deepcopy(self.schedule.opts) Schedule.modify_job(self.schedule, 'name', schedule, persist=False) self.assertEqual(self.schedule.opts, ret) maxDiff = None # enable_schedule tests def test_enable_schedule(self): ''' Tests enabling the scheduler ''' self.schedule.opts.update({ 'schedule': { 'enabled': 'foo' }, 'pillar': {} }) Schedule.enable_schedule(self.schedule) self.assertTrue(self.schedule.opts['schedule']['enabled']) # disable_schedule tests def test_disable_schedule(self): ''' Tests disabling the scheduler ''' self.schedule.opts.update({ 'schedule': { 'enabled': 'foo' }, 'pillar': {} }) Schedule.disable_schedule(self.schedule) self.assertFalse(self.schedule.opts['schedule']['enabled']) # reload tests def test_reload_update_schedule_key(self): ''' Tests reloading the schedule from saved schedule where both the saved schedule and self.schedule.opts contain a schedule key ''' saved = {'schedule': {'foo': 'bar'}} ret = copy.deepcopy(self.schedule.opts) ret.update({'schedule': {'foo': 'bar', 'hello': 'world'}}) self.schedule.opts.update({'schedule': {'hello': 'world'}}) Schedule.reload(self.schedule, saved) self.assertEqual(self.schedule.opts, ret) def test_reload_update_schedule_no_key(self): ''' Tests reloading the schedule from saved schedule that does not contain a schedule key but self.schedule.opts does ''' saved = {'foo': 'bar'} ret = copy.deepcopy(self.schedule.opts) ret.update({'schedule': {'foo': 'bar', 'hello': 'world'}}) self.schedule.opts.update({'schedule': {'hello': 'world'}}) Schedule.reload(self.schedule, saved) self.assertEqual(self.schedule.opts, ret) def test_reload_no_schedule_in_opts(self): ''' Tests reloading the schedule from saved schedule that does not contain a schedule key and neither does self.schedule.opts ''' saved = {'foo': 'bar'} ret = copy.deepcopy(self.schedule.opts) ret['schedule'] = {'foo': 'bar'} self.schedule.opts.pop('schedule', None) Schedule.reload(self.schedule, saved) self.assertEqual(self.schedule.opts, ret) def test_reload_schedule_in_saved_but_not_opts(self): ''' Tests reloading the schedule from saved schedule that contains a schedule key, but self.schedule.opts does not ''' saved = {'schedule': {'foo': 'bar'}} ret = copy.deepcopy(self.schedule.opts) ret['schedule'] = {'foo': 'bar'} self.schedule.opts.pop('schedule', None) Schedule.reload(self.schedule, saved) self.assertEqual(self.schedule.opts, ret) # eval tests def test_eval_schedule_is_not_dict(self): ''' Tests eval if the schedule is not a dictionary ''' self.schedule.opts.update({'schedule': '', 'pillar': {'schedule': {}}}) self.assertRaises(ValueError, Schedule.eval, self.schedule) def test_eval_schedule_is_not_dict_in_pillar(self): ''' Tests eval if the schedule from pillar is not a dictionary ''' 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 = datetime.datetime.now() 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 = datetime.datetime.now() self.schedule.eval() self.assertTrue(self.schedule.opts['schedule']['testjob']['_splay'] - now > datetime.timedelta(seconds=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 = datetime.datetime.now() 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']) def test_handle_func_schedule_minion_blackout(self): ''' Tests eval if the schedule from pillar is not a dictionary ''' self.schedule.opts.update({'pillar': {'schedule': {}}}) self.schedule.opts.update({'grains': {'minion_blackout': True}}) self.schedule.opts.update({ 'schedule': { 'testjob': { 'function': 'test.true', 'seconds': 60 } } }) data = { 'function': 'test.true', '_next_scheduled_fire_time': datetime.datetime(2018, 11, 21, 14, 9, 53, 903438), 'run': True, 'name': 'testjob', 'seconds': 60, '_splay': None, '_seconds': 60, 'jid_include': True, 'maxrunning': 1, '_next_fire_time': datetime.datetime(2018, 11, 21, 14, 8, 53, 903438) } with patch.object(salt.utils.schedule, 'log') as log_mock: with patch('salt.utils.process.daemonize'), \ patch('sys.platform', 'linux2'): self.schedule.handle_func(False, 'test.ping', data) self.assertTrue(log_mock.exception.called)
class ScheduleTestCase(TestCase): ''' Unit tests for salt.utils.schedule module ''' def setUp(self): with patch('salt.utils.schedule.clean_proc_dir', MagicMock(return_value=None)): functions = { 'test.ping': test_ping, 'test.true': test_true, 'status.time': status_time, 'cmd.run': cmd_run } self.schedule = Schedule(copy.deepcopy(DEFAULT_CONFIG), functions, returners={}) # delete_job tests def test_delete_job_exists(self): ''' Tests ensuring the job exists and deleting it ''' self.schedule.opts.update({'schedule': {'foo': 'bar'}, 'pillar': {}}) self.assertIn('foo', self.schedule.opts['schedule']) self.schedule.delete_job('foo') self.assertNotIn('foo', self.schedule.opts['schedule']) def test_delete_job_in_pillar(self): ''' Tests ignoring deletion job from pillar ''' self.schedule.opts.update({ 'pillar': { 'schedule': { 'foo': 'bar' } }, 'schedule': {} }) self.assertIn('foo', self.schedule.opts['pillar']['schedule']) self.schedule.delete_job('foo') self.assertIn('foo', self.schedule.opts['pillar']['schedule']) def test_delete_job_intervals(self): ''' Tests removing job from intervals ''' self.schedule.opts.update({'pillar': {}, 'schedule': {}}) self.schedule.intervals = {'foo': 'bar'} self.schedule.delete_job('foo') self.assertNotIn('foo', self.schedule.intervals) def test_delete_job_prefix(self): ''' Tests ensuring jobs exists and deleting them by prefix ''' self.schedule.opts.update({ 'schedule': { 'foobar': 'bar', 'foobaz': 'baz', 'fooboo': 'boo' }, 'pillar': {} }) ret = copy.deepcopy(self.schedule.opts) del ret['schedule']['foobar'] del ret['schedule']['foobaz'] self.schedule.delete_job_prefix('fooba') self.assertEqual(self.schedule.opts, ret) def test_delete_job_prefix_in_pillar(self): ''' Tests ignoring deletion jobs by prefix from pillar ''' self.schedule.opts.update({ 'pillar': { 'schedule': { 'foobar': 'bar', 'foobaz': 'baz', 'fooboo': 'boo' } }, 'schedule': {} }) ret = copy.deepcopy(self.schedule.opts) self.schedule.delete_job_prefix('fooba') self.assertEqual(self.schedule.opts, ret) # add_job tests def test_add_job_data_not_dict(self): ''' Tests if data is a dictionary ''' data = 'foo' self.assertRaises(ValueError, Schedule.add_job, self.schedule, data) def test_add_job_multiple_jobs(self): ''' Tests if more than one job is scheduled at a time ''' data = {'key1': 'value1', 'key2': 'value2'} self.assertRaises(ValueError, Schedule.add_job, self.schedule, data) def test_add_job(self): ''' Tests adding a job to the schedule ''' data = {'foo': {'bar': 'baz'}} ret = copy.deepcopy(self.schedule.opts) ret.update({ 'schedule': { 'foo': { 'bar': 'baz', 'enabled': True }, 'hello': { 'world': 'peace', 'enabled': True } }, 'pillar': {} }) self.schedule.opts.update({ 'schedule': { 'hello': { 'world': 'peace', 'enabled': True } }, 'pillar': {} }) Schedule.add_job(self.schedule, data) self.assertEqual(self.schedule.opts, ret) # enable_job tests def test_enable_job(self): ''' Tests enabling a job ''' self.schedule.opts.update({'schedule': {'name': {'enabled': 'foo'}}}) Schedule.enable_job(self.schedule, 'name') self.assertTrue(self.schedule.opts['schedule']['name']['enabled']) def test_enable_job_pillar(self): ''' Tests ignoring enable a job from pillar ''' self.schedule.opts.update( {'pillar': { 'schedule': { 'name': { 'enabled': False } } }}) Schedule.enable_job(self.schedule, 'name', persist=False) self.assertFalse( self.schedule.opts['pillar']['schedule']['name']['enabled']) # disable_job tests def test_disable_job(self): ''' Tests disabling a job ''' self.schedule.opts.update({ 'schedule': { 'name': { 'enabled': 'foo' } }, 'pillar': {} }) Schedule.disable_job(self.schedule, 'name') self.assertFalse(self.schedule.opts['schedule']['name']['enabled']) def test_disable_job_pillar(self): ''' Tests ignoring disable a job in pillar ''' self.schedule.opts.update({ 'pillar': { 'schedule': { 'name': { 'enabled': True } } }, 'schedule': {} }) Schedule.disable_job(self.schedule, 'name', persist=False) self.assertTrue( self.schedule.opts['pillar']['schedule']['name']['enabled']) # modify_job tests def test_modify_job(self): ''' Tests modifying a job in the scheduler ''' schedule = {'foo': 'bar'} self.schedule.opts.update({'schedule': {'name': 'baz'}, 'pillar': {}}) ret = copy.deepcopy(self.schedule.opts) ret.update({'schedule': {'name': {'foo': 'bar'}}}) Schedule.modify_job(self.schedule, 'name', schedule) self.assertEqual(self.schedule.opts, ret) def test_modify_job_not_exists(self): ''' Tests modifying a job in the scheduler if jobs not exists ''' schedule = {'foo': 'bar'} self.schedule.opts.update({'schedule': {}, 'pillar': {}}) ret = copy.deepcopy(self.schedule.opts) ret.update({'schedule': {'name': {'foo': 'bar'}}}) Schedule.modify_job(self.schedule, 'name', schedule) self.assertEqual(self.schedule.opts, ret) def test_modify_job_pillar(self): ''' Tests ignoring modification of job from pillar ''' schedule = {'foo': 'bar'} self.schedule.opts.update({ 'schedule': {}, 'pillar': { 'schedule': { 'name': 'baz' } } }) ret = copy.deepcopy(self.schedule.opts) Schedule.modify_job(self.schedule, 'name', schedule, persist=False) self.assertEqual(self.schedule.opts, ret) maxDiff = None # enable_schedule tests def test_enable_schedule(self): ''' Tests enabling the scheduler ''' self.schedule.opts.update({ 'schedule': { 'enabled': 'foo' }, 'pillar': {} }) Schedule.enable_schedule(self.schedule) self.assertTrue(self.schedule.opts['schedule']['enabled']) # disable_schedule tests def test_disable_schedule(self): ''' Tests disabling the scheduler ''' self.schedule.opts.update({ 'schedule': { 'enabled': 'foo' }, 'pillar': {} }) Schedule.disable_schedule(self.schedule) self.assertFalse(self.schedule.opts['schedule']['enabled']) # reload tests def test_reload_update_schedule_key(self): ''' Tests reloading the schedule from saved schedule where both the saved schedule and self.schedule.opts contain a schedule key ''' saved = {'schedule': {'foo': 'bar'}} ret = copy.deepcopy(self.schedule.opts) ret.update({'schedule': {'foo': 'bar', 'hello': 'world'}}) self.schedule.opts.update({'schedule': {'hello': 'world'}}) Schedule.reload(self.schedule, saved) self.assertEqual(self.schedule.opts, ret) def test_reload_update_schedule_no_key(self): ''' Tests reloading the schedule from saved schedule that does not contain a schedule key but self.schedule.opts does ''' saved = {'foo': 'bar'} ret = copy.deepcopy(self.schedule.opts) ret.update({'schedule': {'foo': 'bar', 'hello': 'world'}}) self.schedule.opts.update({'schedule': {'hello': 'world'}}) Schedule.reload(self.schedule, saved) self.assertEqual(self.schedule.opts, ret) def test_reload_no_schedule_in_opts(self): ''' Tests reloading the schedule from saved schedule that does not contain a schedule key and neither does self.schedule.opts ''' saved = {'foo': 'bar'} ret = copy.deepcopy(self.schedule.opts) ret['schedule'] = {'foo': 'bar'} self.schedule.opts.pop('schedule', None) Schedule.reload(self.schedule, saved) self.assertEqual(self.schedule.opts, ret) def test_reload_schedule_in_saved_but_not_opts(self): ''' Tests reloading the schedule from saved schedule that contains a schedule key, but self.schedule.opts does not ''' saved = {'schedule': {'foo': 'bar'}} ret = copy.deepcopy(self.schedule.opts) ret['schedule'] = {'foo': 'bar'} self.schedule.opts.pop('schedule', None) Schedule.reload(self.schedule, saved) self.assertEqual(self.schedule.opts, ret) # eval tests def test_eval_schedule_is_not_dict(self): ''' Tests eval if the schedule is not a dictionary ''' self.schedule.opts.update({'schedule': '', 'pillar': {'schedule': {}}}) self.assertRaises(ValueError, Schedule.eval, self.schedule) def test_eval_schedule_is_not_dict_in_pillar(self): ''' Tests eval if the schedule from pillar is not a dictionary ''' 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 = datetime.datetime.now() 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 = datetime.datetime.now() self.schedule.eval() self.assertTrue(self.schedule.opts['schedule']['testjob']['_splay'] - now > datetime.timedelta(seconds=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 = datetime.datetime.now() 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']) def test_handle_func_schedule_minion_blackout(self): ''' Tests eval if the schedule from pillar is not a dictionary ''' self.schedule.opts.update({'pillar': {'schedule': {}}}) self.schedule.opts.update({'grains': {'minion_blackout': True}}) self.schedule.opts.update({ 'schedule': { 'testjob': { 'function': 'test.true', 'seconds': 60 } } }) data = { 'function': 'test.true', '_next_scheduled_fire_time': datetime.datetime(2018, 11, 21, 14, 9, 53, 903438), 'run': True, 'name': 'testjob', 'seconds': 60, '_splay': None, '_seconds': 60, 'jid_include': True, 'maxrunning': 1, '_next_fire_time': datetime.datetime(2018, 11, 21, 14, 8, 53, 903438) } with patch.object(salt.utils.schedule, 'log') as log_mock: with patch('salt.utils.process.daemonize'), \ patch('sys.platform', 'linux2'): self.schedule.handle_func(False, 'test.ping', data) self.assertTrue(log_mock.exception.called) def test_eval_schedule_compound_function(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': ['cmd.run', 'status.time'], 'args': [["data"], []], 'seconds': 60 } } }) now = datetime.datetime.now() self.schedule.eval() self.assertTrue( self.schedule.opts['schedule']['testjob']['_next_fire_time'] > now) def test_eval_schedule_invalid_arguments(self): ''' Tests eval if the schedule if data contains error ''' self.schedule.opts.update({'pillar': {'schedule': {}}}) self.schedule.opts.update({ 'schedule': { 'testjob': { 'function': ['cmd.run', 'status.time'], 'args': [["data"]], 'seconds': 60 } } }) now = datetime.datetime.now() # Run eval one to prime the scheduler self.schedule.eval() # Run in "60 seconds" and we should receive the error self.schedule.eval(now + datetime.timedelta(seconds=60)) self.assertIn('_error', self.schedule.opts['schedule']['testjob']) _expected = 'Number of arguments is less than the number of functions. Ignoring job.' self.assertEqual(self.schedule.opts['schedule']['testjob']['_error'], _expected)