test_boto_cloudwatch_event.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. # -*- coding: utf-8 -*-
  2. # Import Python libs
  3. from __future__ import absolute_import, print_function, unicode_literals
  4. import random
  5. import string
  6. import logging
  7. # Import Salt Testing libs
  8. from tests.support.mixins import LoaderModuleMockMixin
  9. from tests.support.unit import skipIf, TestCase
  10. from tests.support.mock import patch, MagicMock
  11. # Import Salt libs
  12. import salt.config
  13. import salt.loader
  14. import salt.states.boto_cloudwatch_event as boto_cloudwatch_event
  15. # pylint: disable=import-error,no-name-in-module
  16. from tests.unit.modules.test_boto_cloudwatch_event import BotoCloudWatchEventTestCaseMixin
  17. # pylint: disable=unused-import
  18. # Import 3rd-party libs
  19. try:
  20. import boto3
  21. from botocore.exceptions import ClientError
  22. HAS_BOTO = True
  23. except ImportError:
  24. HAS_BOTO = False
  25. # pylint: enable=unused-import
  26. from salt.ext.six.moves import range
  27. # pylint: enable=import-error,no-name-in-module
  28. region = 'us-east-1'
  29. access_key = 'GKTADJGHEIQSXMKKRBJ08H'
  30. secret_key = 'askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs'
  31. conn_parameters = {'region': region, 'key': access_key, 'keyid': secret_key, 'profile': {}}
  32. error_message = 'An error occurred (101) when calling the {0} operation: Test-defined error'
  33. error_content = {
  34. 'Error': {
  35. 'Code': 101,
  36. 'Message': "Test-defined error"
  37. }
  38. }
  39. if HAS_BOTO:
  40. not_found_error = ClientError({
  41. 'Error': {
  42. 'Code': 'ResourceNotFoundException',
  43. 'Message': "Test-defined error"
  44. }
  45. }, 'msg')
  46. rule_name = 'test_thing_type'
  47. rule_desc = 'test_thing_type_desc'
  48. rule_sched = 'rate(20 min)'
  49. rule_arn = 'arn:::::rule/arn'
  50. rule_ret = dict(
  51. Arn=rule_arn,
  52. Description=rule_desc,
  53. EventPattern=None,
  54. Name=rule_name,
  55. RoleArn=None,
  56. ScheduleExpression=rule_sched,
  57. State='ENABLED'
  58. )
  59. log = logging.getLogger(__name__)
  60. def _has_required_boto():
  61. '''
  62. Returns True/False boolean depending on if Boto is installed and correct
  63. version.
  64. '''
  65. if not HAS_BOTO:
  66. return False
  67. else:
  68. return True
  69. class BotoCloudWatchEventStateTestCaseBase(TestCase, LoaderModuleMockMixin):
  70. conn = None
  71. def setup_loader_modules(self):
  72. ctx = {}
  73. utils = salt.loader.utils(
  74. self.opts,
  75. whitelist=['boto3', 'args', 'systemd', 'path', 'platform'],
  76. context=ctx)
  77. serializers = salt.loader.serializers(self.opts)
  78. self.funcs = funcs = salt.loader.minion_mods(self.opts, context=ctx, utils=utils, whitelist=['boto_cloudwatch_event'])
  79. self.salt_states = salt.loader.states(opts=self.opts, functions=funcs, utils=utils, whitelist=['boto_cloudwatch_event'],
  80. serializers=serializers)
  81. return {
  82. boto_cloudwatch_event: {
  83. '__opts__': self.opts,
  84. '__salt__': funcs,
  85. '__utils__': utils,
  86. '__states__': self.salt_states,
  87. '__serializers__': serializers,
  88. }
  89. }
  90. @classmethod
  91. def setUpClass(cls):
  92. cls.opts = salt.config.DEFAULT_MINION_OPTS.copy()
  93. cls.opts['grains'] = salt.loader.grains(cls.opts)
  94. @classmethod
  95. def tearDownClass(cls):
  96. del cls.opts
  97. def setUp(self):
  98. self.addCleanup(delattr, self, 'funcs')
  99. self.addCleanup(delattr, self, 'salt_states')
  100. # Set up MagicMock to replace the boto3 session
  101. # connections keep getting cached from prior tests, can't find the
  102. # correct context object to clear it. So randomize the cache key, to prevent any
  103. # cache hits
  104. conn_parameters['key'] = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(50))
  105. self.patcher = patch('boto3.session.Session')
  106. self.addCleanup(self.patcher.stop)
  107. self.addCleanup(delattr, self, 'patcher')
  108. mock_session = self.patcher.start()
  109. session_instance = mock_session.return_value
  110. self.conn = MagicMock()
  111. self.addCleanup(delattr, self, 'conn')
  112. session_instance.client.return_value = self.conn
  113. @skipIf(HAS_BOTO is False, 'The boto module must be installed.')
  114. class BotoCloudWatchEventTestCase(BotoCloudWatchEventStateTestCaseBase, BotoCloudWatchEventTestCaseMixin):
  115. def test_present_when_failing_to_describe_rule(self):
  116. '''
  117. Tests exceptions when checking rule existence
  118. '''
  119. self.conn.list_rules.side_effect = ClientError(error_content, 'error on list rules')
  120. result = self.salt_states['boto_cloudwatch_event.present'](
  121. name='test present',
  122. Name=rule_name,
  123. Description=rule_desc,
  124. ScheduleExpression=rule_sched,
  125. Targets=[{
  126. 'Id': 'target1',
  127. 'Arn': 'arn::::::*',
  128. }],
  129. **conn_parameters)
  130. self.assertEqual(result.get('result'), False)
  131. self.assertTrue('error on list rules' in result.get('comment', {}))
  132. def test_present_when_failing_to_create_a_new_rule(self):
  133. '''
  134. Tests present on a rule name that doesn't exist and
  135. an error is thrown on creation.
  136. '''
  137. self.conn.list_rules.return_value = {'Rules': []}
  138. self.conn.put_rule.side_effect = ClientError(error_content, 'put_rule')
  139. result = self.salt_states['boto_cloudwatch_event.present'](
  140. name='test present',
  141. Name=rule_name,
  142. Description=rule_desc,
  143. ScheduleExpression=rule_sched,
  144. Targets=[{
  145. 'Id': 'target1',
  146. 'Arn': 'arn::::::*',
  147. }],
  148. **conn_parameters)
  149. self.assertEqual(result.get('result'), False)
  150. self.assertTrue('put_rule' in result.get('comment', ''))
  151. def test_present_when_failing_to_describe_the_new_rule(self):
  152. '''
  153. Tests present on a rule name that doesn't exist and
  154. an error is thrown when adding targets.
  155. '''
  156. self.conn.list_rules.return_value = {'Rules': []}
  157. self.conn.put_rule.return_value = rule_ret
  158. self.conn.describe_rule.side_effect = ClientError(error_content, 'describe_rule')
  159. result = self.salt_states['boto_cloudwatch_event.present'](
  160. name='test present',
  161. Name=rule_name,
  162. Description=rule_desc,
  163. ScheduleExpression=rule_sched,
  164. Targets=[{
  165. 'Id': 'target1',
  166. 'Arn': 'arn::::::*',
  167. }],
  168. **conn_parameters)
  169. self.assertEqual(result.get('result'), False)
  170. self.assertTrue('describe_rule' in result.get('comment', ''))
  171. def test_present_when_failing_to_create_a_new_rules_targets(self):
  172. '''
  173. Tests present on a rule name that doesn't exist and
  174. an error is thrown when adding targets.
  175. '''
  176. self.conn.list_rules.return_value = {'Rules': []}
  177. self.conn.put_rule.return_value = rule_ret
  178. self.conn.describe_rule.return_value = rule_ret
  179. self.conn.put_targets.side_effect = ClientError(error_content, 'put_targets')
  180. result = self.salt_states['boto_cloudwatch_event.present'](
  181. name='test present',
  182. Name=rule_name,
  183. Description=rule_desc,
  184. ScheduleExpression=rule_sched,
  185. Targets=[{
  186. 'Id': 'target1',
  187. 'Arn': 'arn::::::*',
  188. }],
  189. **conn_parameters)
  190. self.assertEqual(result.get('result'), False)
  191. self.assertTrue('put_targets' in result.get('comment', ''))
  192. def test_present_when_rule_does_not_exist(self):
  193. '''
  194. Tests the successful case of creating a new rule, and updating its
  195. targets
  196. '''
  197. self.conn.list_rules.return_value = {'Rules': []}
  198. self.conn.put_rule.return_value = rule_ret
  199. self.conn.describe_rule.return_value = rule_ret
  200. self.conn.put_targets.return_value = {'FailedEntryCount': 0}
  201. result = self.salt_states['boto_cloudwatch_event.present'](
  202. name='test present',
  203. Name=rule_name,
  204. Description=rule_desc,
  205. ScheduleExpression=rule_sched,
  206. Targets=[{
  207. 'Id': 'target1',
  208. 'Arn': 'arn::::::*',
  209. }],
  210. **conn_parameters)
  211. self.assertEqual(result.get('result'), True)
  212. def test_present_when_failing_to_update_an_existing_rule(self):
  213. '''
  214. Tests present on an existing rule where an error is thrown on updating the pool properties.
  215. '''
  216. self.conn.list_rules.return_value = {'Rules': [rule_ret]}
  217. self.conn.describe_rule.side_effect = ClientError(error_content, 'describe_rule')
  218. result = self.salt_states['boto_cloudwatch_event.present'](
  219. name='test present',
  220. Name=rule_name,
  221. Description=rule_desc,
  222. ScheduleExpression=rule_sched,
  223. Targets=[{
  224. 'Id': 'target1',
  225. 'Arn': 'arn::::::*',
  226. }],
  227. **conn_parameters)
  228. self.assertEqual(result.get('result'), False)
  229. self.assertTrue('describe_rule' in result.get('comment', ''))
  230. def test_present_when_failing_to_get_targets(self):
  231. '''
  232. Tests present on an existing rule where put_rule succeeded, but an error
  233. is thrown on getting targets
  234. '''
  235. self.conn.list_rules.return_value = {'Rules': [rule_ret]}
  236. self.conn.put_rule.return_value = rule_ret
  237. self.conn.describe_rule.return_value = rule_ret
  238. self.conn.list_targets_by_rule.side_effect = ClientError(error_content, 'list_targets')
  239. result = self.salt_states['boto_cloudwatch_event.present'](
  240. name='test present',
  241. Name=rule_name,
  242. Description=rule_desc,
  243. ScheduleExpression=rule_sched,
  244. Targets=[{
  245. 'Id': 'target1',
  246. 'Arn': 'arn::::::*',
  247. }],
  248. **conn_parameters)
  249. self.assertEqual(result.get('result'), False)
  250. self.assertTrue('list_targets' in result.get('comment', ''))
  251. def test_present_when_failing_to_put_targets(self):
  252. '''
  253. Tests present on an existing rule where put_rule succeeded, but an error
  254. is thrown on putting targets
  255. '''
  256. self.conn.list_rules.return_value = {'Rules': []}
  257. self.conn.put_rule.return_value = rule_ret
  258. self.conn.describe_rule.return_value = rule_ret
  259. self.conn.list_targets.return_value = {'Targets': []}
  260. self.conn.put_targets.side_effect = ClientError(error_content, 'put_targets')
  261. result = self.salt_states['boto_cloudwatch_event.present'](
  262. name='test present',
  263. Name=rule_name,
  264. Description=rule_desc,
  265. ScheduleExpression=rule_sched,
  266. Targets=[{
  267. 'Id': 'target1',
  268. 'Arn': 'arn::::::*',
  269. }],
  270. **conn_parameters)
  271. self.assertEqual(result.get('result'), False)
  272. self.assertTrue('put_targets' in result.get('comment', ''))
  273. def test_present_when_putting_targets(self):
  274. '''
  275. Tests present on an existing rule where put_rule succeeded, and targets
  276. must be added
  277. '''
  278. self.conn.list_rules.return_value = {'Rules': []}
  279. self.conn.put_rule.return_value = rule_ret
  280. self.conn.describe_rule.return_value = rule_ret
  281. self.conn.list_targets.return_value = {'Targets': []}
  282. self.conn.put_targets.return_value = {'FailedEntryCount': 0}
  283. result = self.salt_states['boto_cloudwatch_event.present'](
  284. name='test present',
  285. Name=rule_name,
  286. Description=rule_desc,
  287. ScheduleExpression=rule_sched,
  288. Targets=[{
  289. 'Id': 'target1',
  290. 'Arn': 'arn::::::*',
  291. }],
  292. **conn_parameters)
  293. self.assertEqual(result.get('result'), True)
  294. def test_present_when_removing_targets(self):
  295. '''
  296. Tests present on an existing rule where put_rule succeeded, and targets
  297. must be removed
  298. '''
  299. self.conn.list_rules.return_value = {'Rules': []}
  300. self.conn.put_rule.return_value = rule_ret
  301. self.conn.describe_rule.return_value = rule_ret
  302. self.conn.list_targets.return_value = {'Targets': [{'Id': 'target1'}, {'Id': 'target2'}]}
  303. self.conn.put_targets.return_value = {'FailedEntryCount': 0}
  304. result = self.salt_states['boto_cloudwatch_event.present'](
  305. name='test present',
  306. Name=rule_name,
  307. Description=rule_desc,
  308. ScheduleExpression=rule_sched,
  309. Targets=[{
  310. 'Id': 'target1',
  311. 'Arn': 'arn::::::*',
  312. }],
  313. **conn_parameters)
  314. self.assertEqual(result.get('result'), True)
  315. def test_absent_when_failing_to_describe_rule(self):
  316. '''
  317. Tests exceptions when checking rule existence
  318. '''
  319. self.conn.list_rules.side_effect = ClientError(error_content, 'error on list rules')
  320. result = self.salt_states['boto_cloudwatch_event.absent'](
  321. name='test present',
  322. Name=rule_name,
  323. **conn_parameters)
  324. self.assertEqual(result.get('result'), False)
  325. self.assertTrue('error on list rules' in result.get('comment', {}))
  326. def test_absent_when_rule_does_not_exist(self):
  327. '''
  328. Tests absent on an non-existing rule
  329. '''
  330. self.conn.list_rules.return_value = {'Rules': []}
  331. result = self.salt_states['boto_cloudwatch_event.absent'](
  332. name='test absent',
  333. Name=rule_name,
  334. **conn_parameters)
  335. self.assertEqual(result.get('result'), True)
  336. self.assertEqual(result['changes'], {})
  337. def test_absent_when_failing_to_list_targets(self):
  338. '''
  339. Tests absent on an rule when the list_targets call fails
  340. '''
  341. self.conn.list_rules.return_value = {'Rules': [rule_ret]}
  342. self.conn.list_targets_by_rule.side_effect = ClientError(error_content, 'list_targets')
  343. result = self.salt_states['boto_cloudwatch_event.absent'](
  344. name='test absent',
  345. Name=rule_name,
  346. **conn_parameters)
  347. self.assertEqual(result.get('result'), False)
  348. self.assertTrue('list_targets' in result.get('comment', ''))
  349. def test_absent_when_failing_to_remove_targets_exception(self):
  350. '''
  351. Tests absent on an rule when the remove_targets call fails
  352. '''
  353. self.conn.list_rules.return_value = {'Rules': [rule_ret]}
  354. self.conn.list_targets_by_rule.return_value = {'Targets': [{'Id': 'target1'}]}
  355. self.conn.remove_targets.side_effect = ClientError(error_content, 'remove_targets')
  356. result = self.salt_states['boto_cloudwatch_event.absent'](
  357. name='test absent',
  358. Name=rule_name,
  359. **conn_parameters)
  360. self.assertEqual(result.get('result'), False)
  361. self.assertTrue('remove_targets' in result.get('comment', ''))
  362. def test_absent_when_failing_to_remove_targets_nonexception(self):
  363. '''
  364. Tests absent on an rule when the remove_targets call fails
  365. '''
  366. self.conn.list_rules.return_value = {'Rules': [rule_ret]}
  367. self.conn.list_targets_by_rule.return_value = {'Targets': [{'Id': 'target1'}]}
  368. self.conn.remove_targets.return_value = {'FailedEntryCount': 1}
  369. result = self.salt_states['boto_cloudwatch_event.absent'](
  370. name='test absent',
  371. Name=rule_name,
  372. **conn_parameters)
  373. self.assertEqual(result.get('result'), False)
  374. def test_absent_when_failing_to_delete_rule(self):
  375. '''
  376. Tests absent on an rule when the delete_rule call fails
  377. '''
  378. self.conn.list_rules.return_value = {'Rules': [rule_ret]}
  379. self.conn.list_targets_by_rule.return_value = {'Targets': [{'Id': 'target1'}]}
  380. self.conn.remove_targets.return_value = {'FailedEntryCount': 0}
  381. self.conn.delete_rule.side_effect = ClientError(error_content, 'delete_rule')
  382. result = self.salt_states['boto_cloudwatch_event.absent'](
  383. name='test absent',
  384. Name=rule_name,
  385. **conn_parameters)
  386. self.assertEqual(result.get('result'), False)
  387. self.assertTrue('delete_rule' in result.get('comment', ''))
  388. def test_absent(self):
  389. '''
  390. Tests absent on an rule
  391. '''
  392. self.conn.list_rules.return_value = {'Rules': [rule_ret]}
  393. self.conn.list_targets_by_rule.return_value = {'Targets': [{'Id': 'target1'}]}
  394. self.conn.remove_targets.return_value = {'FailedEntryCount': 0}
  395. result = self.salt_states['boto_cloudwatch_event.absent'](
  396. name='test absent',
  397. Name=rule_name,
  398. **conn_parameters)
  399. self.assertEqual(result.get('result'), True)