test_spm.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. # coding: utf-8
  2. # Import Python libs
  3. from __future__ import absolute_import
  4. import os
  5. import shutil
  6. import tempfile
  7. # Import Salt Testing libs
  8. from tests.support.unit import TestCase
  9. from tests.support.mock import patch, MagicMock
  10. from tests.support.helpers import destructiveTest
  11. from tests.support.mixins import AdaptedConfigurationTestCaseMixin
  12. import salt.config
  13. import salt.utils.files
  14. import salt.spm
  15. _F1 = {
  16. 'definition': {
  17. 'name': 'formula1',
  18. 'version': '1.2',
  19. 'release': '2',
  20. 'summary': 'test',
  21. 'description': 'testing, nothing to see here',
  22. }
  23. }
  24. _F1['contents'] = (
  25. ('FORMULA', ('name: {name}\n'
  26. 'version: {version}\n'
  27. 'release: {release}\n'
  28. 'summary: {summary}\n'
  29. 'description: {description}').format(**_F1['definition'])),
  30. ('modules/mod1.py', '# mod1.py'),
  31. ('modules/mod2.py', '# mod2.py'),
  32. ('states/state1.sls', '# state1.sls'),
  33. ('states/state2.sls', '# state2.sls'),
  34. )
  35. @destructiveTest
  36. class SPMTestUserInterface(salt.spm.SPMUserInterface):
  37. '''
  38. Unit test user interface to SPMClient
  39. '''
  40. def __init__(self):
  41. self._status = []
  42. self._confirm = []
  43. self._error = []
  44. def status(self, msg):
  45. self._status.append(msg)
  46. def confirm(self, action):
  47. self._confirm.append(action)
  48. def error(self, msg):
  49. self._error.append(msg)
  50. class SPMTest(TestCase, AdaptedConfigurationTestCaseMixin):
  51. def setUp(self):
  52. self._tmp_spm = tempfile.mkdtemp()
  53. self.addCleanup(shutil.rmtree, self._tmp_spm, ignore_errors=True)
  54. minion_config = self.get_temp_config('minion', **{
  55. 'spm_logfile': os.path.join(self._tmp_spm, 'log'),
  56. 'spm_repos_config': os.path.join(self._tmp_spm, 'etc', 'spm.repos'),
  57. 'spm_cache_dir': os.path.join(self._tmp_spm, 'cache'),
  58. 'spm_build_dir': os.path.join(self._tmp_spm, 'build'),
  59. 'spm_build_exclude': ['.git'],
  60. 'spm_db_provider': 'sqlite3',
  61. 'spm_files_provider': 'local',
  62. 'spm_db': os.path.join(self._tmp_spm, 'packages.db'),
  63. 'extension_modules': os.path.join(self._tmp_spm, 'modules'),
  64. 'file_roots': {'base': [self._tmp_spm, ]},
  65. 'formula_path': os.path.join(self._tmp_spm, 'spm'),
  66. 'pillar_path': os.path.join(self._tmp_spm, 'pillar'),
  67. 'reactor_path': os.path.join(self._tmp_spm, 'reactor'),
  68. 'assume_yes': True,
  69. 'force': False,
  70. 'verbose': False,
  71. 'cache': 'localfs',
  72. 'cachedir': os.path.join(self._tmp_spm, 'cache'),
  73. 'spm_repo_dups': 'ignore',
  74. 'spm_share_dir': os.path.join(self._tmp_spm, 'share'),
  75. })
  76. self.ui = SPMTestUserInterface()
  77. self.client = salt.spm.SPMClient(self.ui, minion_config)
  78. self.minion_config = minion_config
  79. for attr in ('client', 'ui', '_tmp_spm', 'minion_config'):
  80. self.addCleanup(delattr, self, attr)
  81. def _create_formula_files(self, formula):
  82. fdir = os.path.join(self._tmp_spm, formula['definition']['name'])
  83. shutil.rmtree(fdir, ignore_errors=True)
  84. os.mkdir(fdir)
  85. for path, contents in formula['contents']:
  86. path = os.path.join(fdir, path)
  87. dirname, _ = os.path.split(path)
  88. if not os.path.exists(dirname):
  89. os.makedirs(dirname)
  90. with salt.utils.files.fopen(path, 'w') as f:
  91. f.write(contents)
  92. return fdir
  93. def test_build_install(self):
  94. # Build package
  95. fdir = self._create_formula_files(_F1)
  96. with patch('salt.client.Caller', MagicMock(return_value=self.minion_opts)):
  97. with patch('salt.client.get_local_client', MagicMock(return_value=self.minion_opts['conf_file'])):
  98. self.client.run(['build', fdir])
  99. pkgpath = self.ui._status[-1].split()[-1]
  100. assert os.path.exists(pkgpath)
  101. # Install package
  102. with patch('salt.client.Caller', MagicMock(return_value=self.minion_opts)):
  103. with patch('salt.client.get_local_client', MagicMock(return_value=self.minion_opts['conf_file'])):
  104. self.client.run(['local', 'install', pkgpath])
  105. # Check filesystem
  106. for path, contents in _F1['contents']:
  107. path = os.path.join(self.minion_config['file_roots']['base'][0], _F1['definition']['name'], path)
  108. assert os.path.exists(path)
  109. with salt.utils.files.fopen(path, 'r') as rfh:
  110. assert rfh.read() == contents
  111. # Check database
  112. with patch('salt.client.Caller', MagicMock(return_value=self.minion_opts)):
  113. with patch('salt.client.get_local_client', MagicMock(return_value=self.minion_opts['conf_file'])):
  114. self.client.run(['info', _F1['definition']['name']])
  115. lines = self.ui._status[-1].split('\n')
  116. for key, line in (
  117. ('name', 'Name: {0}'),
  118. ('version', 'Version: {0}'),
  119. ('release', 'Release: {0}'),
  120. ('summary', 'Summary: {0}')):
  121. assert line.format(_F1['definition'][key]) in lines
  122. # Reinstall with force=False, should fail
  123. self.ui._error = []
  124. with patch('salt.client.Caller', MagicMock(return_value=self.minion_opts)):
  125. with patch('salt.client.get_local_client', MagicMock(return_value=self.minion_opts['conf_file'])):
  126. self.client.run(['local', 'install', pkgpath])
  127. assert self.ui._error
  128. # Reinstall with force=True, should succeed
  129. with patch.dict(self.minion_config, {'force': True}):
  130. self.ui._error = []
  131. with patch('salt.client.Caller', MagicMock(return_value=self.minion_opts)):
  132. with patch('salt.client.get_local_client', MagicMock(return_value=self.minion_opts['conf_file'])):
  133. self.client.run(['local', 'install', pkgpath])
  134. assert not self.ui._error
  135. def test_failure_paths(self):
  136. fail_args = (
  137. ['bogus', 'command'],
  138. ['create_repo'],
  139. ['build'],
  140. ['build', '/nonexistent/path'],
  141. ['info'],
  142. ['info', 'not_installed'],
  143. ['files'],
  144. ['files', 'not_installed'],
  145. ['install'],
  146. ['install', 'nonexistent.spm'],
  147. ['remove'],
  148. ['remove', 'not_installed'],
  149. ['local', 'bogus', 'command'],
  150. ['local', 'info'],
  151. ['local', 'info', '/nonexistent/path/junk.spm'],
  152. ['local', 'files'],
  153. ['local', 'files', '/nonexistent/path/junk.spm'],
  154. ['local', 'install'],
  155. ['local', 'install', '/nonexistent/path/junk.spm'],
  156. ['local', 'list'],
  157. ['local', 'list', '/nonexistent/path/junk.spm'],
  158. # XXX install failure due to missing deps
  159. # XXX install failure due to missing field
  160. )
  161. for args in fail_args:
  162. self.ui._error = []
  163. with patch('salt.client.Caller', MagicMock(return_value=self.minion_opts)):
  164. with patch('salt.client.get_local_client', MagicMock(return_value=self.minion_opts['conf_file'])):
  165. self.client.run(args)
  166. assert self.ui._error