1
0

test_ec2.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. # -*- coding: utf-8 -*-
  2. '''
  3. :codeauthor: Nicole Thomas <nicole@saltstack.com>
  4. '''
  5. # Import Python Libs
  6. from __future__ import absolute_import, print_function, unicode_literals
  7. import os
  8. import yaml
  9. # Import Salt Libs
  10. from salt.config import cloud_providers_config
  11. import salt.utils.cloud
  12. import salt.utils.files
  13. import salt.utils.yaml
  14. # Import Salt Testing Libs
  15. from tests.support.case import ShellCase
  16. from tests.support.paths import FILES
  17. from tests.support.helpers import expensiveTest, generate_random_name
  18. from tests.support.unit import expectedFailure, skipIf
  19. from tests.support import win_installer
  20. # Create the cloud instance name to be used throughout the tests
  21. INSTANCE_NAME = generate_random_name('CLOUD-TEST-')
  22. PROVIDER_NAME = 'ec2'
  23. HAS_WINRM = salt.utils.cloud.HAS_WINRM and salt.utils.cloud.HAS_SMB
  24. TIMEOUT = 1200
  25. class EC2Test(ShellCase):
  26. '''
  27. Integration tests for the EC2 cloud provider in Salt-Cloud
  28. '''
  29. def _installer_name(self):
  30. '''
  31. Determine the downloaded installer name by searching the files
  32. directory for the firt file that loosk like an installer.
  33. '''
  34. for path, dirs, files in os.walk(FILES):
  35. for file in files:
  36. if file.startswith(win_installer.PREFIX):
  37. return file
  38. break
  39. return
  40. def _fetch_latest_installer(self):
  41. '''
  42. Download the latest Windows installer executable
  43. '''
  44. name = win_installer.latest_installer_name()
  45. path = os.path.join(FILES, name)
  46. with salt.utils.files.fopen(path, 'wb') as fp:
  47. win_installer.download_and_verify(fp, name)
  48. return name
  49. def _ensure_installer(self):
  50. '''
  51. Make sure the testing environment has a Windows installer executbale.
  52. '''
  53. name = self._installer_name()
  54. if name:
  55. return name
  56. return self._fetch_latest_installer()
  57. @expensiveTest
  58. def setUp(self):
  59. '''
  60. Sets up the test requirements
  61. '''
  62. super(EC2Test, self).setUp()
  63. # check if appropriate cloud provider and profile files are present
  64. profile_str = 'ec2-config'
  65. providers = self.run_cloud('--list-providers')
  66. if profile_str + ':' not in providers:
  67. self.skipTest(
  68. 'Configuration file for {0} was not found. Check {0}.conf files '
  69. 'in tests/integration/files/conf/cloud.*.d/ to run these tests.'
  70. .format(PROVIDER_NAME)
  71. )
  72. # check if id, key, keyname, securitygroup, private_key, location,
  73. # and provider are present
  74. config = cloud_providers_config(
  75. os.path.join(
  76. FILES,
  77. 'conf',
  78. 'cloud.providers.d',
  79. PROVIDER_NAME + '.conf'
  80. )
  81. )
  82. id_ = config[profile_str][PROVIDER_NAME]['id']
  83. key = config[profile_str][PROVIDER_NAME]['key']
  84. key_name = config[profile_str][PROVIDER_NAME]['keyname']
  85. sec_group = config[profile_str][PROVIDER_NAME]['securitygroupname'][0]
  86. private_key = config[profile_str][PROVIDER_NAME]['private_key']
  87. location = config[profile_str][PROVIDER_NAME]['location']
  88. conf_items = [id_, key, key_name, sec_group, private_key, location]
  89. missing_conf_item = []
  90. for item in conf_items:
  91. if item == '':
  92. missing_conf_item.append(item)
  93. if missing_conf_item:
  94. self.skipTest(
  95. 'An id, key, keyname, security group, private key, and location must '
  96. 'be provided to run these tests. One or more of these elements is '
  97. 'missing. Check tests/integration/files/conf/cloud.providers.d/{0}.conf'
  98. .format(PROVIDER_NAME)
  99. )
  100. self.INSTALLER = self._ensure_installer()
  101. def override_profile_config(self, name, data):
  102. conf_path = os.path.join(self.get_config_dir(), 'cloud.profiles.d', 'ec2.conf')
  103. with salt.utils.files.fopen(conf_path, 'r') as fp:
  104. conf = yaml.safe_load(fp)
  105. conf[name].update(data)
  106. with salt.utils.files.fopen(conf_path, 'w') as fp:
  107. salt.utils.yaml.safe_dump(conf, fp)
  108. def copy_file(self, name):
  109. '''
  110. Copy a file from tests/integration/files to a test's temporary
  111. configuration directory. The path to the file which is created will be
  112. returned.
  113. '''
  114. src = os.path.join(FILES, name)
  115. dst = os.path.join(self.get_config_dir(), name)
  116. with salt.utils.files.fopen(src, 'rb') as sfp:
  117. with salt.utils.files.fopen(dst, 'wb') as dfp:
  118. dfp.write(sfp.read())
  119. return dst
  120. def _test_instance(self, profile='ec2-test', debug=False, timeout=TIMEOUT):
  121. '''
  122. Tests creating and deleting an instance on EC2 (classic)
  123. '''
  124. # create the instance
  125. cmd = '-p {0}'.format(profile)
  126. if debug:
  127. cmd += ' -l debug'
  128. cmd += ' {0}'.format(INSTANCE_NAME)
  129. instance = self.run_cloud(cmd, timeout=timeout)
  130. ret_str = '{0}:'.format(INSTANCE_NAME)
  131. # check if instance returned with salt installed
  132. try:
  133. self.assertIn(ret_str, instance)
  134. except AssertionError:
  135. self.run_cloud('-d {0} --assume-yes'.format(INSTANCE_NAME), timeout=timeout)
  136. raise
  137. # delete the instance
  138. delete = self.run_cloud('-d {0} --assume-yes'.format(INSTANCE_NAME), timeout=timeout)
  139. ret_str = ' shutting-down'
  140. # check if deletion was performed appropriately
  141. try:
  142. self.assertIn(ret_str, delete)
  143. except AssertionError:
  144. raise
  145. def test_instance_rename(self):
  146. '''
  147. Tests creating and renaming an instance on EC2 (classic)
  148. '''
  149. # create the instance
  150. rename = INSTANCE_NAME + '-rename'
  151. instance = self.run_cloud('-p ec2-test {0} --no-deploy'.format(INSTANCE_NAME), timeout=TIMEOUT)
  152. ret_str = '{0}:'.format(INSTANCE_NAME)
  153. # check if instance returned
  154. try:
  155. self.assertIn(ret_str, instance)
  156. except AssertionError:
  157. self.run_cloud('-d {0} --assume-yes'.format(INSTANCE_NAME), timeout=TIMEOUT)
  158. raise
  159. change_name = self.run_cloud('-a rename {0} newname={1} --assume-yes'.format(INSTANCE_NAME, rename), timeout=TIMEOUT)
  160. check_rename = self.run_cloud('-a show_instance {0} --assume-yes'.format(rename), [rename])
  161. exp_results = [' {0}:'.format(rename), ' size:',
  162. ' architecture:']
  163. try:
  164. for result in exp_results:
  165. self.assertIn(result, check_rename[0])
  166. except AssertionError:
  167. self.run_cloud('-d {0} --assume-yes'.format(INSTANCE_NAME), timeout=TIMEOUT)
  168. raise
  169. # delete the instance
  170. delete = self.run_cloud('-d {0} --assume-yes'.format(rename), timeout=TIMEOUT)
  171. ret_str = ' shutting-down'
  172. # check if deletion was performed appropriately
  173. self.assertIn(ret_str, delete)
  174. def test_instance(self):
  175. '''
  176. Tests creating and deleting an instance on EC2 (classic)
  177. '''
  178. self._test_instance('ec2-test')
  179. @expectedFailure
  180. def test_win2012r2_winexe(self):
  181. '''
  182. Tests creating and deleting a Windows 2012r2instance on EC2 using
  183. winexe (classic)
  184. '''
  185. # TODO: winexe calls hang and the test fails by timing out. The same
  186. # same calls succeed when run outside of the test environment.
  187. self.override_profile_config(
  188. 'ec2-win2012r2-test',
  189. {
  190. 'use_winrm': False,
  191. 'userdata_file': self.copy_file('windows-firewall-winexe.ps1'),
  192. 'win_installer': self.copy_file(self.INSTALLER),
  193. },
  194. )
  195. self._test_instance('ec2-win2012r2-test', debug=True, timeout=TIMEOUT)
  196. @skipIf(not HAS_WINRM, 'Skip when winrm dependencies are missing')
  197. def test_win2012r2_winrm(self):
  198. '''
  199. Tests creating and deleting a Windows 2012r2 instance on EC2 using
  200. winrm (classic)
  201. '''
  202. self.override_profile_config(
  203. 'ec2-win2012r2-test',
  204. {
  205. 'userdata_file': self.copy_file('windows-firewall.ps1'),
  206. 'win_installer': self.copy_file(self.INSTALLER),
  207. 'winrm_ssl_verify': False,
  208. 'use_winrm': True,
  209. }
  210. )
  211. self._test_instance('ec2-win2012r2-test', debug=True, timeout=TIMEOUT)
  212. @expectedFailure
  213. def test_win2016_winexe(self):
  214. '''
  215. Tests creating and deleting a Windows 2016 instance on EC2 using winrm
  216. (classic)
  217. '''
  218. # TODO: winexe calls hang and the test fails by timing out. The same
  219. # same calls succeed when run outside of the test environment.
  220. self.override_profile_config(
  221. 'ec2-win2016-test',
  222. {
  223. 'use_winrm': False,
  224. 'userdata_file': self.copy_file('windows-firewall-winexe.ps1'),
  225. 'win_installer': self.copy_file(self.INSTALLER),
  226. },
  227. )
  228. self._test_instance('ec2-win2016-test', debug=True, timeout=TIMEOUT)
  229. @skipIf(not HAS_WINRM, 'Skip when winrm dependencies are missing')
  230. def test_win2016_winrm(self):
  231. '''
  232. Tests creating and deleting a Windows 2016 instance on EC2 using winrm
  233. (classic)
  234. '''
  235. self.override_profile_config(
  236. 'ec2-win2016-test',
  237. {
  238. 'userdata_file': self.copy_file('windows-firewall.ps1'),
  239. 'win_installer': self.copy_file(self.INSTALLER),
  240. 'winrm_ssl_verify': False,
  241. 'use_winrm': True,
  242. }
  243. )
  244. self._test_instance('ec2-win2016-test', debug=True, timeout=TIMEOUT)
  245. def tearDown(self):
  246. '''
  247. Clean up after tests
  248. '''
  249. query = self.run_cloud('--query')
  250. ret_str = ' {0}:'.format(INSTANCE_NAME)
  251. # if test instance is still present, delete it
  252. if ret_str in query:
  253. self.run_cloud('-d {0} --assume-yes'.format(INSTANCE_NAME), timeout=TIMEOUT)