test_cron.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. # -*- coding: utf-8 -*-
  2. '''
  3. :codeauthor: Mike Place <mp@saltstack.com>
  4. '''
  5. # Import python libs
  6. from __future__ import absolute_import, unicode_literals, print_function
  7. # Import Salt Testing libs
  8. from tests.support.mixins import LoaderModuleMockMixin
  9. from tests.support.unit import TestCase
  10. from tests.support.mock import MagicMock, patch
  11. from salt.ext.six.moves import StringIO
  12. import salt.modules.cron as cronmod
  13. import salt.states.cron as cron
  14. STUB_USER = 'root'
  15. STUB_PATH = '/tmp'
  16. STUB_CRON_TIMESTAMP = {
  17. 'minute': '1',
  18. 'hour': '2',
  19. 'daymonth': '3',
  20. 'month': '4',
  21. 'dayweek': '5'}
  22. STUB_SIMPLE_RAW_CRON = '5 0 * * * /tmp/no_script.sh'
  23. STUB_SIMPLE_CRON_DICT = {
  24. 'pre': ['5 0 * * * /tmp/no_script.sh'],
  25. 'crons': [],
  26. 'env': [],
  27. 'special': []}
  28. CRONTAB = StringIO()
  29. def get_crontab(*args, **kw):
  30. return CRONTAB.getvalue()
  31. def set_crontab(val):
  32. CRONTAB.seek(0)
  33. CRONTAB.truncate(0)
  34. CRONTAB.write(val)
  35. def write_crontab(*args, **kw):
  36. set_crontab('\n'.join(
  37. [a.strip() for a in args[1]]))
  38. return {
  39. 'retcode': False,
  40. }
  41. class CronTestCase(TestCase, LoaderModuleMockMixin):
  42. def setup_loader_modules(self):
  43. loader_gloabals = {
  44. '__low__': {'__id__': 'noo'},
  45. '__grains__': {
  46. 'os': 'Debian',
  47. 'os_family': 'Debian',
  48. },
  49. '__opts__': {'test': False},
  50. '__salt__': {
  51. 'cmd.run_all': MagicMock(return_value={
  52. 'pid': 5,
  53. 'retcode': 0,
  54. 'stderr': '',
  55. 'stdout': ''}),
  56. 'cron.list_tab': cronmod.list_tab,
  57. 'cron.rm_job': cronmod.rm_job,
  58. 'cron.set_job': cronmod.set_job,
  59. }
  60. }
  61. return {cron: loader_gloabals, cronmod: loader_gloabals}
  62. def setUp(self):
  63. super(CronTestCase, self).setUp()
  64. set_crontab('')
  65. for mod, mock in (('salt.modules.cron.raw_cron', MagicMock(side_effect=get_crontab)),
  66. ('salt.modules.cron._write_cron_lines', MagicMock(side_effect=write_crontab))):
  67. patcher = patch(mod, mock)
  68. patcher.start()
  69. self.addCleanup(patcher.stop)
  70. self.addCleanup(set_crontab, '')
  71. def test_present(self):
  72. cron.present(
  73. name='foo',
  74. hour='1',
  75. identifier='1',
  76. user='root')
  77. self.assertMultiLineEqual(
  78. get_crontab(),
  79. '# Lines below here are managed by Salt, do not edit\n'
  80. '# SALT_CRON_IDENTIFIER:1\n'
  81. '* 1 * * * foo')
  82. cron.present(
  83. name='foo',
  84. hour='2',
  85. identifier='1',
  86. user='root')
  87. self.assertMultiLineEqual(
  88. get_crontab(),
  89. '# Lines below here are managed by Salt, do not edit\n'
  90. '# SALT_CRON_IDENTIFIER:1\n'
  91. '* 2 * * * foo')
  92. cron.present(
  93. name='cmd1',
  94. minute='0',
  95. comment='Commented cron job',
  96. commented=True,
  97. identifier='commented_1',
  98. user='root')
  99. self.assertMultiLineEqual(
  100. get_crontab(),
  101. '# Lines below here are managed by Salt, do not edit\n'
  102. '# SALT_CRON_IDENTIFIER:1\n'
  103. '* 2 * * * foo\n'
  104. '# Commented cron job SALT_CRON_IDENTIFIER:commented_1\n'
  105. '#DISABLED#0 * * * * cmd1')
  106. cron.present(
  107. name='foo',
  108. hour='2',
  109. identifier='2',
  110. user='root')
  111. self.assertMultiLineEqual(
  112. get_crontab(),
  113. '# Lines below here are managed by Salt, do not edit\n'
  114. '# SALT_CRON_IDENTIFIER:1\n'
  115. '* 2 * * * foo\n'
  116. '# Commented cron job SALT_CRON_IDENTIFIER:commented_1\n'
  117. '#DISABLED#0 * * * * cmd1\n'
  118. '# SALT_CRON_IDENTIFIER:2\n'
  119. '* 2 * * * foo')
  120. cron.present(
  121. name='cmd2',
  122. commented=True,
  123. identifier='commented_2',
  124. user='root')
  125. self.assertMultiLineEqual(
  126. get_crontab(),
  127. '# Lines below here are managed by Salt, do not edit\n'
  128. '# SALT_CRON_IDENTIFIER:1\n'
  129. '* 2 * * * foo\n'
  130. '# Commented cron job SALT_CRON_IDENTIFIER:commented_1\n'
  131. '#DISABLED#0 * * * * cmd1\n'
  132. '# SALT_CRON_IDENTIFIER:2\n'
  133. '* 2 * * * foo\n'
  134. '# SALT_CRON_IDENTIFIER:commented_2\n'
  135. '#DISABLED#* * * * * cmd2')
  136. set_crontab(
  137. '# Lines below here are managed by Salt, do not edit\n'
  138. '# SALT_CRON_IDENTIFIER:1\n'
  139. '* 2 * * * foo\n'
  140. '# SALT_CRON_IDENTIFIER:2\n'
  141. '* 2 * * * foo\n'
  142. '* 2 * * * foo\n'
  143. )
  144. cron.present(
  145. name='foo',
  146. hour='2',
  147. user='root',
  148. identifier=None)
  149. self.assertEqual(
  150. get_crontab(),
  151. ('# Lines below here are managed by Salt, do not edit\n'
  152. '# SALT_CRON_IDENTIFIER:1\n'
  153. '* 2 * * * foo\n'
  154. '# SALT_CRON_IDENTIFIER:2\n'
  155. '* 2 * * * foo\n'
  156. '* 2 * * * foo'))
  157. def test_remove(self):
  158. with patch.dict(cron.__opts__, {'test': True}):
  159. set_crontab(
  160. '# Lines below here are managed by Salt, do not edit\n'
  161. '# SALT_CRON_IDENTIFIER:1\n'
  162. '* 1 * * * foo')
  163. result = cron.absent(name='bar', identifier='1')
  164. self.assertEqual(
  165. result,
  166. {'changes': {},
  167. 'comment': 'Cron bar is set to be removed',
  168. 'name': 'bar',
  169. 'result': None}
  170. )
  171. self.assertEqual(
  172. get_crontab(),
  173. '# Lines below here are managed by Salt, do not edit\n'
  174. '# SALT_CRON_IDENTIFIER:1\n'
  175. '* 1 * * * foo')
  176. with patch.dict(cron.__opts__, {'test': False}):
  177. set_crontab(
  178. '# Lines below here are managed by Salt, do not edit\n'
  179. '# SALT_CRON_IDENTIFIER:1\n'
  180. '* 1 * * * foo')
  181. cron.absent(name='bar', identifier='1')
  182. self.assertEqual(
  183. get_crontab(),
  184. '# Lines below here are managed by Salt, do not edit'
  185. )
  186. set_crontab(
  187. '# Lines below here are managed by Salt, do not edit\n'
  188. '* * * * * foo')
  189. cron.absent(name='bar', identifier='1')
  190. self.assertEqual(
  191. get_crontab(),
  192. '# Lines below here are managed by Salt, do not edit\n'
  193. '* * * * * foo'
  194. )
  195. # old behavior, do not remove with identifier set and
  196. # even if command match !
  197. set_crontab(
  198. '# Lines below here are managed by Salt, do not edit\n'
  199. '* * * * * foo')
  200. cron.absent(name='foo', identifier='1')
  201. self.assertEqual(
  202. get_crontab(),
  203. '# Lines below here are managed by Salt, do not edit'
  204. )
  205. # old behavior, remove if no identifier and command match
  206. set_crontab(
  207. '# Lines below here are managed by Salt, do not edit\n'
  208. '* * * * * foo')
  209. cron.absent(name='foo')
  210. self.assertEqual(
  211. get_crontab(),
  212. '# Lines below here are managed by Salt, do not edit'
  213. )
  214. def test_multiline_comments_are_updated(self):
  215. set_crontab(
  216. '# Lines below here are managed by Salt, do not edit\n'
  217. '# First crontab - single line comment SALT_CRON_IDENTIFIER:1\n'
  218. '* 1 * * * foo'
  219. )
  220. cron.present(
  221. name='foo',
  222. hour='1',
  223. comment='First crontab\nfirst multi-line comment\n',
  224. identifier='1',
  225. user='root')
  226. cron.present(
  227. name='foo',
  228. hour='1',
  229. comment='First crontab\nsecond multi-line comment\n',
  230. identifier='1',
  231. user='root')
  232. cron.present(
  233. name='foo',
  234. hour='1',
  235. comment='Second crontab\nmulti-line comment\n',
  236. identifier='2',
  237. user='root')
  238. self.assertEqual(
  239. get_crontab(),
  240. '# Lines below here are managed by Salt, do not edit\n'
  241. '# First crontab\n'
  242. '# second multi-line comment\n'
  243. '# SALT_CRON_IDENTIFIER:1\n'
  244. '* 1 * * * foo\n'
  245. '# Second crontab\n'
  246. '# multi-line comment\n'
  247. '# SALT_CRON_IDENTIFIER:2\n'
  248. '* 1 * * * foo')
  249. def test_existing_unmanaged_jobs_are_made_managed(self):
  250. set_crontab(
  251. '# Lines below here are managed by Salt, do not edit\n'
  252. '0 2 * * * foo'
  253. )
  254. ret = cron._check_cron('root', 'foo', hour='2', minute='0')
  255. self.assertEqual(ret, 'present')
  256. ret = cron.present('foo', 'root', minute='0', hour='2')
  257. self.assertEqual(ret['changes'], {'root': 'foo'})
  258. self.assertEqual(ret['comment'], 'Cron foo updated')
  259. self.assertEqual(
  260. get_crontab(),
  261. '# Lines below here are managed by Salt, do not edit\n'
  262. '# SALT_CRON_IDENTIFIER:foo\n'
  263. '0 2 * * * foo')
  264. ret = cron.present('foo', 'root', minute='0', hour='2')
  265. self.assertEqual(ret['changes'], {})
  266. self.assertEqual(ret['comment'], 'Cron foo already present')
  267. def test_existing_noid_jobs_are_updated_with_identifier(self):
  268. set_crontab(
  269. '# Lines below here are managed by Salt, do not edit\n'
  270. '# SALT_CRON_IDENTIFIER:NO ID SET\n'
  271. '1 * * * * foo'
  272. )
  273. ret = cron._check_cron('root', 'foo', minute=1)
  274. self.assertEqual(ret, 'present')
  275. ret = cron.present('foo', 'root', minute=1)
  276. self.assertEqual(ret['changes'], {'root': 'foo'})
  277. self.assertEqual(ret['comment'], 'Cron foo updated')
  278. self.assertEqual(
  279. get_crontab(),
  280. '# Lines below here are managed by Salt, do not edit\n'
  281. '# SALT_CRON_IDENTIFIER:foo\n'
  282. '1 * * * * foo')
  283. def test_existing_duplicate_unmanaged_jobs_are_merged_and_given_id(self):
  284. set_crontab(
  285. '# Lines below here are managed by Salt, do not edit\n'
  286. '0 2 * * * foo\n'
  287. '0 2 * * * foo'
  288. )
  289. ret = cron._check_cron('root', 'foo', hour='2', minute='0')
  290. self.assertEqual(ret, 'present')
  291. ret = cron.present('foo', 'root', minute='0', hour='2')
  292. self.assertEqual(ret['changes'], {'root': 'foo'})
  293. self.assertEqual(ret['comment'], 'Cron foo updated')
  294. self.assertEqual(
  295. get_crontab(),
  296. '# Lines below here are managed by Salt, do not edit\n'
  297. '# SALT_CRON_IDENTIFIER:foo\n'
  298. '0 2 * * * foo')
  299. ret = cron.present('foo', 'root', minute='0', hour='2')
  300. self.assertEqual(ret['changes'], {})
  301. self.assertEqual(ret['comment'], 'Cron foo already present')