test_spm.py 7.7 KB

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