fixtures.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. """
  2. tests.support.pytest.fixtures
  3. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  4. The purpose of this fixtures module is provide the same set of available fixture for the old unittest
  5. test suite under ``test/integration``, ``tests/multimaster`` and ``tests/unit``.
  6. Please refrain from adding fixtures to this module and instead add them to the appropriate
  7. ``conftest.py`` file.
  8. """
  9. import os
  10. import shutil
  11. import stat
  12. import pytest
  13. import salt.utils.files
  14. from salt.serializers import yaml
  15. from salt.utils.immutabletypes import freeze
  16. from tests.support.helpers import get_virtualenv_binary_path
  17. from tests.support.runtests import RUNTIME_VARS
  18. @pytest.fixture(scope="session")
  19. def integration_files_dir(salt_factories):
  20. """
  21. Fixture which returns the salt integration files directory path.
  22. Creates the directory if it does not yet exist.
  23. """
  24. dirname = salt_factories.root_dir.join("integration-files")
  25. dirname.ensure(dir=True)
  26. return dirname
  27. @pytest.fixture(scope="session")
  28. def state_tree_root_dir(integration_files_dir):
  29. """
  30. Fixture which returns the salt state tree root directory path.
  31. Creates the directory if it does not yet exist.
  32. """
  33. dirname = integration_files_dir.join("state-tree")
  34. dirname.ensure(dir=True)
  35. return dirname
  36. @pytest.fixture(scope="session")
  37. def pillar_tree_root_dir(integration_files_dir):
  38. """
  39. Fixture which returns the salt pillar tree root directory path.
  40. Creates the directory if it does not yet exist.
  41. """
  42. dirname = integration_files_dir.join("pillar-tree")
  43. dirname.ensure(dir=True)
  44. return dirname
  45. @pytest.fixture(scope="session")
  46. def base_env_state_tree_root_dir(state_tree_root_dir):
  47. """
  48. Fixture which returns the salt base environment state tree directory path.
  49. Creates the directory if it does not yet exist.
  50. """
  51. dirname = state_tree_root_dir.join("base")
  52. dirname.ensure(dir=True)
  53. RUNTIME_VARS.TMP_STATE_TREE = dirname.realpath().strpath
  54. RUNTIME_VARS.TMP_BASEENV_STATE_TREE = RUNTIME_VARS.TMP_STATE_TREE
  55. return dirname
  56. @pytest.fixture(scope="session")
  57. def prod_env_state_tree_root_dir(state_tree_root_dir):
  58. """
  59. Fixture which returns the salt prod environment state tree directory path.
  60. Creates the directory if it does not yet exist.
  61. """
  62. dirname = state_tree_root_dir.join("prod")
  63. dirname.ensure(dir=True)
  64. RUNTIME_VARS.TMP_PRODENV_STATE_TREE = dirname.realpath().strpath
  65. return dirname
  66. @pytest.fixture(scope="session")
  67. def base_env_pillar_tree_root_dir(pillar_tree_root_dir):
  68. """
  69. Fixture which returns the salt base environment pillar tree directory path.
  70. Creates the directory if it does not yet exist.
  71. """
  72. dirname = pillar_tree_root_dir.join("base")
  73. dirname.ensure(dir=True)
  74. RUNTIME_VARS.TMP_PILLAR_TREE = dirname.realpath().strpath
  75. RUNTIME_VARS.TMP_BASEENV_PILLAR_TREE = RUNTIME_VARS.TMP_PILLAR_TREE
  76. return dirname
  77. @pytest.fixture(scope="session")
  78. def prod_env_pillar_tree_root_dir(pillar_tree_root_dir):
  79. """
  80. Fixture which returns the salt prod environment pillar tree directory path.
  81. Creates the directory if it does not yet exist.
  82. """
  83. dirname = pillar_tree_root_dir.join("prod")
  84. dirname.ensure(dir=True)
  85. RUNTIME_VARS.TMP_PRODENV_PILLAR_TREE = dirname.realpath().strpath
  86. return dirname
  87. @pytest.fixture(scope="session")
  88. def salt_syndic_master_config(request, salt_factories):
  89. root_dir = salt_factories._get_root_dir_for_daemon("syndic_master")
  90. with salt.utils.files.fopen(
  91. os.path.join(RUNTIME_VARS.CONF_DIR, "syndic_master")
  92. ) as rfh:
  93. config_defaults = yaml.deserialize(rfh.read())
  94. tests_known_hosts_file = root_dir.join("salt_ssh_known_hosts").strpath
  95. with salt.utils.files.fopen(tests_known_hosts_file, "w") as known_hosts:
  96. known_hosts.write("")
  97. config_defaults["root_dir"] = root_dir.strpath
  98. config_defaults["known_hosts_file"] = tests_known_hosts_file
  99. config_defaults["syndic_master"] = "localhost"
  100. config_defaults["transport"] = request.config.getoption("--transport")
  101. config_overrides = {}
  102. ext_pillar = []
  103. if salt.utils.platform.is_windows():
  104. ext_pillar.append(
  105. {"cmd_yaml": "type {}".format(os.path.join(RUNTIME_VARS.FILES, "ext.yaml"))}
  106. )
  107. else:
  108. ext_pillar.append(
  109. {"cmd_yaml": "cat {}".format(os.path.join(RUNTIME_VARS.FILES, "ext.yaml"))}
  110. )
  111. # We need to copy the extension modules into the new master root_dir or
  112. # it will be prefixed by it
  113. extension_modules_path = root_dir.join("extension_modules").strpath
  114. if not os.path.exists(extension_modules_path):
  115. shutil.copytree(
  116. os.path.join(RUNTIME_VARS.FILES, "extension_modules"),
  117. extension_modules_path,
  118. )
  119. # Copy the autosign_file to the new master root_dir
  120. autosign_file_path = root_dir.join("autosign_file").strpath
  121. shutil.copyfile(
  122. os.path.join(RUNTIME_VARS.FILES, "autosign_file"), autosign_file_path
  123. )
  124. # all read, only owner write
  125. autosign_file_permissions = (
  126. stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IWUSR
  127. )
  128. os.chmod(autosign_file_path, autosign_file_permissions)
  129. config_overrides.update(
  130. {
  131. "ext_pillar": ext_pillar,
  132. "extension_modules": extension_modules_path,
  133. "file_roots": {
  134. "base": [
  135. RUNTIME_VARS.TMP_STATE_TREE,
  136. os.path.join(RUNTIME_VARS.FILES, "file", "base"),
  137. ],
  138. # Alternate root to test __env__ choices
  139. "prod": [
  140. RUNTIME_VARS.TMP_PRODENV_STATE_TREE,
  141. os.path.join(RUNTIME_VARS.FILES, "file", "prod"),
  142. ],
  143. },
  144. "pillar_roots": {
  145. "base": [
  146. RUNTIME_VARS.TMP_PILLAR_TREE,
  147. os.path.join(RUNTIME_VARS.FILES, "pillar", "base"),
  148. ],
  149. "prod": [RUNTIME_VARS.TMP_PRODENV_PILLAR_TREE],
  150. },
  151. }
  152. )
  153. return salt_factories.configure_master(
  154. request,
  155. "syndic_master",
  156. order_masters=True,
  157. config_defaults=config_defaults,
  158. config_overrides=config_overrides,
  159. )
  160. @pytest.fixture(scope="session")
  161. def salt_syndic_config(request, salt_factories, salt_syndic_master_config):
  162. return salt_factories.configure_syndic(
  163. request, "syndic", master_of_masters_id="syndic_master"
  164. )
  165. @pytest.fixture(scope="session")
  166. def salt_master_config(request, salt_factories, salt_syndic_master_config):
  167. root_dir = salt_factories._get_root_dir_for_daemon("master")
  168. conf_dir = root_dir.join("conf").ensure(dir=True)
  169. with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.CONF_DIR, "master")) as rfh:
  170. config_defaults = yaml.deserialize(rfh.read())
  171. tests_known_hosts_file = root_dir.join("salt_ssh_known_hosts").strpath
  172. with salt.utils.files.fopen(tests_known_hosts_file, "w") as known_hosts:
  173. known_hosts.write("")
  174. config_defaults["root_dir"] = root_dir.strpath
  175. config_defaults["known_hosts_file"] = tests_known_hosts_file
  176. config_defaults["syndic_master"] = "localhost"
  177. config_defaults["transport"] = request.config.getoption("--transport")
  178. config_defaults["reactor"] = [
  179. {"salt/test/reactor": [os.path.join(RUNTIME_VARS.FILES, "reactor-test.sls")]}
  180. ]
  181. config_overrides = {"interface": "0.0.0.0"}
  182. ext_pillar = []
  183. if salt.utils.platform.is_windows():
  184. ext_pillar.append(
  185. {"cmd_yaml": "type {}".format(os.path.join(RUNTIME_VARS.FILES, "ext.yaml"))}
  186. )
  187. else:
  188. ext_pillar.append(
  189. {"cmd_yaml": "cat {}".format(os.path.join(RUNTIME_VARS.FILES, "ext.yaml"))}
  190. )
  191. ext_pillar.append(
  192. {
  193. "file_tree": {
  194. "root_dir": os.path.join(RUNTIME_VARS.PILLAR_DIR, "base", "file_tree"),
  195. "follow_dir_links": False,
  196. "keep_newline": True,
  197. }
  198. }
  199. )
  200. config_overrides["pillar_opts"] = True
  201. # We need to copy the extension modules into the new master root_dir or
  202. # it will be prefixed by it
  203. extension_modules_path = root_dir.join("extension_modules").strpath
  204. if not os.path.exists(extension_modules_path):
  205. shutil.copytree(
  206. os.path.join(RUNTIME_VARS.FILES, "extension_modules"),
  207. extension_modules_path,
  208. )
  209. # Copy the autosign_file to the new master root_dir
  210. autosign_file_path = root_dir.join("autosign_file").strpath
  211. shutil.copyfile(
  212. os.path.join(RUNTIME_VARS.FILES, "autosign_file"), autosign_file_path
  213. )
  214. # all read, only owner write
  215. autosign_file_permissions = (
  216. stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IWUSR
  217. )
  218. os.chmod(autosign_file_path, autosign_file_permissions)
  219. config_overrides.update(
  220. {
  221. "ext_pillar": ext_pillar,
  222. "extension_modules": extension_modules_path,
  223. "file_roots": {
  224. "base": [
  225. RUNTIME_VARS.TMP_STATE_TREE,
  226. os.path.join(RUNTIME_VARS.FILES, "file", "base"),
  227. ],
  228. # Alternate root to test __env__ choices
  229. "prod": [
  230. RUNTIME_VARS.TMP_PRODENV_STATE_TREE,
  231. os.path.join(RUNTIME_VARS.FILES, "file", "prod"),
  232. ],
  233. },
  234. "pillar_roots": {
  235. "base": [
  236. RUNTIME_VARS.TMP_PILLAR_TREE,
  237. os.path.join(RUNTIME_VARS.FILES, "pillar", "base"),
  238. ],
  239. "prod": [RUNTIME_VARS.TMP_PRODENV_PILLAR_TREE],
  240. },
  241. }
  242. )
  243. # Let's copy over the test cloud config files and directories into the running master config directory
  244. for entry in os.listdir(RUNTIME_VARS.CONF_DIR):
  245. if not entry.startswith("cloud"):
  246. continue
  247. source = os.path.join(RUNTIME_VARS.CONF_DIR, entry)
  248. dest = conf_dir.join(entry).strpath
  249. if os.path.isdir(source):
  250. shutil.copytree(source, dest)
  251. else:
  252. shutil.copyfile(source, dest)
  253. return salt_factories.configure_master(
  254. request,
  255. "master",
  256. master_of_masters_id="syndic_master",
  257. config_defaults=config_defaults,
  258. config_overrides=config_overrides,
  259. )
  260. @pytest.fixture(scope="session")
  261. def salt_minion_config(request, salt_factories, salt_master_config):
  262. with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.CONF_DIR, "minion")) as rfh:
  263. config_defaults = yaml.deserialize(rfh.read())
  264. config_defaults["hosts.file"] = os.path.join(RUNTIME_VARS.TMP, "hosts")
  265. config_defaults["aliases.file"] = os.path.join(RUNTIME_VARS.TMP, "aliases")
  266. config_defaults["transport"] = request.config.getoption("--transport")
  267. config_overrides = {
  268. "file_roots": {
  269. "base": [
  270. RUNTIME_VARS.TMP_STATE_TREE,
  271. os.path.join(RUNTIME_VARS.FILES, "file", "base"),
  272. ],
  273. # Alternate root to test __env__ choices
  274. "prod": [
  275. RUNTIME_VARS.TMP_PRODENV_STATE_TREE,
  276. os.path.join(RUNTIME_VARS.FILES, "file", "prod"),
  277. ],
  278. },
  279. "pillar_roots": {
  280. "base": [
  281. RUNTIME_VARS.TMP_PILLAR_TREE,
  282. os.path.join(RUNTIME_VARS.FILES, "pillar", "base"),
  283. ],
  284. "prod": [RUNTIME_VARS.TMP_PRODENV_PILLAR_TREE],
  285. },
  286. }
  287. virtualenv_binary = get_virtualenv_binary_path()
  288. if virtualenv_binary:
  289. config_overrides["venv_bin"] = virtualenv_binary
  290. return salt_factories.configure_minion(
  291. request,
  292. "minion",
  293. master_id="master",
  294. config_defaults=config_defaults,
  295. config_overrides=config_overrides,
  296. )
  297. @pytest.fixture(scope="session")
  298. def salt_sub_minion_config(request, salt_factories, salt_master_config):
  299. with salt.utils.files.fopen(
  300. os.path.join(RUNTIME_VARS.CONF_DIR, "sub_minion")
  301. ) as rfh:
  302. config_defaults = yaml.deserialize(rfh.read())
  303. config_defaults["hosts.file"] = os.path.join(RUNTIME_VARS.TMP, "hosts")
  304. config_defaults["aliases.file"] = os.path.join(RUNTIME_VARS.TMP, "aliases")
  305. config_defaults["transport"] = request.config.getoption("--transport")
  306. config_overrides = {
  307. "file_roots": {
  308. "base": [
  309. RUNTIME_VARS.TMP_STATE_TREE,
  310. os.path.join(RUNTIME_VARS.FILES, "file", "base"),
  311. ],
  312. # Alternate root to test __env__ choices
  313. "prod": [
  314. RUNTIME_VARS.TMP_PRODENV_STATE_TREE,
  315. os.path.join(RUNTIME_VARS.FILES, "file", "prod"),
  316. ],
  317. },
  318. "pillar_roots": {
  319. "base": [
  320. RUNTIME_VARS.TMP_PILLAR_TREE,
  321. os.path.join(RUNTIME_VARS.FILES, "pillar", "base"),
  322. ],
  323. "prod": [RUNTIME_VARS.TMP_PRODENV_PILLAR_TREE],
  324. },
  325. }
  326. virtualenv_binary = get_virtualenv_binary_path()
  327. if virtualenv_binary:
  328. config_overrides["venv_bin"] = virtualenv_binary
  329. return salt_factories.configure_minion(
  330. request,
  331. "sub_minion",
  332. master_id="master",
  333. config_defaults=config_defaults,
  334. config_overrides=config_overrides,
  335. )
  336. @pytest.hookspec(firstresult=True)
  337. def pytest_saltfactories_syndic_configuration_defaults(
  338. request, factories_manager, root_dir, syndic_id, syndic_master_port
  339. ):
  340. """
  341. Hook which should return a dictionary tailored for the provided syndic_id with 3 keys:
  342. * `master`: The default config for the master running along with the syndic
  343. * `minion`: The default config for the master running along with the syndic
  344. * `syndic`: The default config for the master running along with the syndic
  345. Stops at the first non None result
  346. """
  347. factory_opts = {"master": None, "minion": None, "syndic": None}
  348. if syndic_id == "syndic":
  349. with salt.utils.files.fopen(
  350. os.path.join(RUNTIME_VARS.CONF_DIR, "syndic")
  351. ) as rfh:
  352. opts = yaml.deserialize(rfh.read())
  353. opts["hosts.file"] = os.path.join(RUNTIME_VARS.TMP, "hosts")
  354. opts["aliases.file"] = os.path.join(RUNTIME_VARS.TMP, "aliases")
  355. opts["transport"] = request.config.getoption("--transport")
  356. factory_opts["syndic"] = opts
  357. return factory_opts
  358. @pytest.hookspec(firstresult=True)
  359. def pytest_saltfactories_syndic_configuration_overrides(
  360. request, factories_manager, syndic_id, config_defaults
  361. ):
  362. """
  363. Hook which should return a dictionary tailored for the provided syndic_id.
  364. This dictionary will override the default_options dictionary.
  365. The returned dictionary should contain 3 keys:
  366. * `master`: The config overrides for the master running along with the syndic
  367. * `minion`: The config overrides for the master running along with the syndic
  368. * `syndic`: The config overridess for the master running along with the syndic
  369. The `default_options` parameter be None or have 3 keys, `master`, `minion`, `syndic`,
  370. while will contain the default options for each of the daemons.
  371. Stops at the first non None result
  372. """
  373. @pytest.fixture(scope="session", autouse=True)
  374. def bridge_pytest_and_runtests(
  375. reap_stray_processes,
  376. base_env_state_tree_root_dir,
  377. prod_env_state_tree_root_dir,
  378. base_env_pillar_tree_root_dir,
  379. prod_env_pillar_tree_root_dir,
  380. salt_factories,
  381. salt_syndic_master_config,
  382. salt_syndic_config,
  383. salt_master_config,
  384. salt_minion_config,
  385. salt_sub_minion_config,
  386. ):
  387. # Make sure unittest2 uses the pytest generated configuration
  388. RUNTIME_VARS.RUNTIME_CONFIGS["master"] = freeze(salt_master_config)
  389. RUNTIME_VARS.RUNTIME_CONFIGS["minion"] = freeze(salt_minion_config)
  390. RUNTIME_VARS.RUNTIME_CONFIGS["sub_minion"] = freeze(salt_sub_minion_config)
  391. RUNTIME_VARS.RUNTIME_CONFIGS["syndic_master"] = freeze(salt_syndic_master_config)
  392. RUNTIME_VARS.RUNTIME_CONFIGS["syndic"] = freeze(salt_syndic_config)
  393. RUNTIME_VARS.RUNTIME_CONFIGS["client_config"] = freeze(
  394. salt.config.client_config(salt_master_config["conf_file"])
  395. )
  396. # Make sure unittest2 classes know their paths
  397. RUNTIME_VARS.TMP_ROOT_DIR = salt_factories.root_dir.realpath().strpath
  398. RUNTIME_VARS.TMP_CONF_DIR = os.path.dirname(salt_master_config["conf_file"])
  399. RUNTIME_VARS.TMP_MINION_CONF_DIR = os.path.dirname(salt_minion_config["conf_file"])
  400. RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR = os.path.dirname(
  401. salt_sub_minion_config["conf_file"]
  402. )
  403. RUNTIME_VARS.TMP_SYNDIC_MASTER_CONF_DIR = os.path.dirname(
  404. salt_syndic_master_config["conf_file"]
  405. )
  406. RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR = os.path.dirname(
  407. salt_syndic_config["conf_file"]
  408. )
  409. # Only allow star importing the functions defined in this module
  410. __all__ = [
  411. name
  412. for (name, func) in locals().items()
  413. if getattr(func, "__module__", None) == __name__
  414. ]