test_boto_cloudfront.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. # -*- coding: utf-8 -*-
  2. '''
  3. Unit tests for the boto_cloudfront state module.
  4. '''
  5. # Import Python libs
  6. from __future__ import absolute_import, print_function, unicode_literals
  7. import copy
  8. import textwrap
  9. # Import Salt Testing Libs
  10. from tests.support.mixins import LoaderModuleMockMixin
  11. from tests.support.unit import TestCase
  12. from tests.support.mock import MagicMock, patch
  13. # Import Salt Libs
  14. import salt.config
  15. import salt.loader
  16. import salt.states.boto_cloudfront as boto_cloudfront
  17. class BotoCloudfrontTestCase(TestCase, LoaderModuleMockMixin):
  18. '''
  19. Test cases for salt.states.boto_cloudfront
  20. '''
  21. def setup_loader_modules(self):
  22. utils = salt.loader.utils(
  23. self.opts,
  24. whitelist=['boto3', 'dictdiffer', 'yaml', 'args', 'systemd', 'path', 'platform'],
  25. context={})
  26. return {
  27. boto_cloudfront: {
  28. '__utils__': utils,
  29. }
  30. }
  31. @classmethod
  32. def setUpClass(cls):
  33. cls.opts = salt.config.DEFAULT_MINION_OPTS.copy()
  34. cls.name = 'my_distribution'
  35. cls.base_ret = {'name': cls.name, 'changes': {}}
  36. # Most attributes elided since there are so many required ones
  37. cls.config = {'Enabled': True, 'HttpVersion': 'http2'}
  38. cls.tags = {'test_tag1': 'value1'}
  39. @classmethod
  40. def tearDownClass(cls):
  41. del cls.opts
  42. del cls.name
  43. del cls.base_ret
  44. del cls.config
  45. del cls.tags
  46. def base_ret_with(self, extra_ret):
  47. new_ret = copy.deepcopy(self.base_ret)
  48. new_ret.update(extra_ret)
  49. return new_ret
  50. def test_present_distribution_retrieval_error(self):
  51. '''
  52. Test for boto_cloudfront.present when we cannot get the distribution.
  53. '''
  54. mock_get = MagicMock(return_value={'error': 'get_distribution error'})
  55. with patch.multiple(boto_cloudfront,
  56. __salt__={'boto_cloudfront.get_distribution': mock_get},
  57. __opts__={'test': False},
  58. ):
  59. comment = 'Error checking distribution {0}: get_distribution error'
  60. self.assertDictEqual(
  61. boto_cloudfront.present(self.name, self.config, self.tags),
  62. self.base_ret_with({
  63. 'result': False,
  64. 'comment': comment.format(self.name),
  65. }),
  66. )
  67. def test_present_from_scratch(self):
  68. mock_get = MagicMock(return_value={'result': None})
  69. with patch.multiple(boto_cloudfront,
  70. __salt__={'boto_cloudfront.get_distribution': mock_get},
  71. __opts__={'test': True},
  72. ):
  73. comment = 'Distribution {0} set for creation.'.format(self.name)
  74. self.assertDictEqual(
  75. boto_cloudfront.present(self.name, self.config, self.tags),
  76. self.base_ret_with({
  77. 'result': None,
  78. 'comment': comment,
  79. 'changes': {'old': None, 'new': self.name},
  80. }),
  81. )
  82. mock_create_failure = MagicMock(return_value={'error': 'create error'})
  83. with patch.multiple(boto_cloudfront,
  84. __salt__={
  85. 'boto_cloudfront.get_distribution': mock_get,
  86. 'boto_cloudfront.create_distribution': mock_create_failure,
  87. },
  88. __opts__={'test': False},
  89. ):
  90. comment = 'Error creating distribution {0}: create error'
  91. self.assertDictEqual(
  92. boto_cloudfront.present(self.name, self.config, self.tags),
  93. self.base_ret_with({
  94. 'result': False,
  95. 'comment': comment.format(self.name),
  96. }),
  97. )
  98. mock_create_success = MagicMock(return_value={'result': True})
  99. with patch.multiple(boto_cloudfront,
  100. __salt__={
  101. 'boto_cloudfront.get_distribution': mock_get,
  102. 'boto_cloudfront.create_distribution': mock_create_success,
  103. },
  104. __opts__={'test': False},
  105. ):
  106. comment = 'Created distribution {0}.'
  107. self.assertDictEqual(
  108. boto_cloudfront.present(self.name, self.config, self.tags),
  109. self.base_ret_with({
  110. 'result': True,
  111. 'comment': comment.format(self.name),
  112. 'changes': {'old': None, 'new': self.name},
  113. }),
  114. )
  115. def test_present_correct_state(self):
  116. mock_get = MagicMock(return_value={'result': {
  117. 'distribution': {'DistributionConfig': self.config},
  118. 'tags': self.tags,
  119. 'etag': 'test etag',
  120. }})
  121. with patch.multiple(boto_cloudfront,
  122. __salt__={'boto_cloudfront.get_distribution': mock_get},
  123. __opts__={'test': False},
  124. ):
  125. comment = 'Distribution {0} has correct config.'
  126. self.assertDictEqual(
  127. boto_cloudfront.present(self.name, self.config, self.tags),
  128. self.base_ret_with({
  129. 'result': True,
  130. 'comment': comment.format(self.name),
  131. }),
  132. )
  133. def test_present_update_config_and_tags(self):
  134. mock_get = MagicMock(return_value={'result': {
  135. 'distribution': {'DistributionConfig': {
  136. 'Enabled': False,
  137. 'Comment': 'to be removed',
  138. }},
  139. 'tags': {'bad existing tag': 'also to be removed'},
  140. 'etag': 'test etag',
  141. }})
  142. diff = textwrap.dedent('''\
  143. ---
  144. +++
  145. @@ -1,5 +1,5 @@
  146. config:
  147. - Comment: to be removed
  148. - Enabled: false
  149. + Enabled: true
  150. + HttpVersion: http2
  151. tags:
  152. - bad existing tag: also to be removed
  153. + test_tag1: value1
  154. ''').splitlines()
  155. # Difflib adds a trailing space after the +++/--- lines,
  156. # programatically add them back here. Having them in the test file
  157. # itself is not feasible since a few popular plugins for vim will
  158. # remove trailing whitespace.
  159. for idx in (0, 1):
  160. diff[idx] += ' '
  161. diff = '\n'.join(diff)
  162. with patch.multiple(boto_cloudfront,
  163. __salt__={'boto_cloudfront.get_distribution': mock_get},
  164. __opts__={'test': True},
  165. ):
  166. header = 'Distribution {0} set for new config:'.format(self.name)
  167. self.assertDictEqual(
  168. boto_cloudfront.present(self.name, self.config, self.tags),
  169. self.base_ret_with({
  170. 'result': None,
  171. 'comment': '\n'.join([header, diff]),
  172. 'changes': {'diff': diff},
  173. }),
  174. )
  175. mock_update_failure = MagicMock(return_value={'error': 'update error'})
  176. with patch.multiple(boto_cloudfront,
  177. __salt__={
  178. 'boto_cloudfront.get_distribution': mock_get,
  179. 'boto_cloudfront.update_distribution': mock_update_failure,
  180. },
  181. __opts__={'test': False},
  182. ):
  183. comment = 'Error updating distribution {0}: update error'
  184. self.assertDictEqual(
  185. boto_cloudfront.present(self.name, self.config, self.tags),
  186. self.base_ret_with({
  187. 'result': False,
  188. 'comment': comment.format(self.name),
  189. }),
  190. )
  191. mock_update_success = MagicMock(return_value={'result': True})
  192. with patch.multiple(boto_cloudfront,
  193. __salt__={
  194. 'boto_cloudfront.get_distribution': mock_get,
  195. 'boto_cloudfront.update_distribution': mock_update_success,
  196. },
  197. __opts__={'test': False},
  198. ):
  199. self.assertDictEqual(
  200. boto_cloudfront.present(self.name, self.config, self.tags),
  201. self.base_ret_with({
  202. 'result': True,
  203. 'comment': 'Updated distribution {0}.'.format(self.name),
  204. 'changes': {'diff': diff},
  205. }),
  206. )