test_pillar.py 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080
  1. # -*- coding: utf-8 -*-
  2. """
  3. :codeauthor: Pedro Algarvio (pedro@algarvio.me)
  4. :codeauthor: Alexandru Bleotu (alexandru.bleotu@morganstanley.com)
  5. tests.unit.pillar_test
  6. ~~~~~~~~~~~~~~~~~~~~~~
  7. """
  8. # Import python libs
  9. from __future__ import absolute_import
  10. import shutil
  11. import tempfile
  12. import pytest
  13. import salt.exceptions
  14. # Import salt libs
  15. import salt.fileclient
  16. import salt.pillar
  17. import salt.utils.stringutils
  18. from tests.support.helpers import with_tempdir
  19. from tests.support.mock import MagicMock, patch
  20. # Import Salt Testing libs
  21. from tests.support.runtests import RUNTIME_VARS
  22. from tests.support.unit import TestCase
  23. class MockFileclient(object):
  24. def __init__(self, cache_file=None, get_state=None, list_states=None):
  25. if cache_file is not None:
  26. self.cache_file = lambda *x, **y: cache_file
  27. if get_state is not None:
  28. self.get_state = lambda sls, env: get_state[sls]
  29. if list_states is not None:
  30. self.list_states = lambda *x, **y: list_states
  31. # pylint: disable=unused-argument,no-method-argument,method-hidden
  32. def cache_file(*args, **kwargs):
  33. raise NotImplementedError()
  34. def get_state(*args, **kwargs):
  35. raise NotImplementedError()
  36. def list_states(*args, **kwargs):
  37. raise NotImplementedError()
  38. # pylint: enable=unused-argument,no-method-argument,method-hidden
  39. class PillarTestCase(TestCase):
  40. def tearDown(self):
  41. for attrname in (
  42. "generic_file",
  43. "generic_minion_file",
  44. "ssh_file",
  45. "ssh_minion_file",
  46. "top_file",
  47. ):
  48. try:
  49. delattr(self, attrname)
  50. except AttributeError:
  51. continue
  52. @pytest.mark.slow_test(seconds=1) # Test takes >0.1 and <=1 seconds
  53. def test_pillarenv_from_saltenv(self):
  54. with patch("salt.pillar.compile_template") as compile_template:
  55. opts = {
  56. "optimization_order": [0, 1, 2],
  57. "renderer": "json",
  58. "renderer_blacklist": [],
  59. "renderer_whitelist": [],
  60. "state_top": "",
  61. "pillar_roots": {"dev": [], "base": []},
  62. "file_roots": {"dev": [], "base": []},
  63. "extension_modules": "",
  64. "pillarenv_from_saltenv": True,
  65. }
  66. grains = {
  67. "os": "Ubuntu",
  68. }
  69. pillar = salt.pillar.Pillar(opts, grains, "mocked-minion", "dev")
  70. self.assertEqual(pillar.opts["saltenv"], "dev")
  71. self.assertEqual(pillar.opts["pillarenv"], "dev")
  72. def test_ext_pillar_no_extra_minion_data_val_dict(self):
  73. opts = {
  74. "optimization_order": [0, 1, 2],
  75. "renderer": "json",
  76. "renderer_blacklist": [],
  77. "renderer_whitelist": [],
  78. "state_top": "",
  79. "pillar_roots": {"dev": [], "base": []},
  80. "file_roots": {"dev": [], "base": []},
  81. "extension_modules": "",
  82. "pillarenv_from_saltenv": True,
  83. }
  84. mock_ext_pillar_func = MagicMock()
  85. with patch(
  86. "salt.loader.pillars",
  87. MagicMock(return_value={"fake_ext_pillar": mock_ext_pillar_func}),
  88. ):
  89. pillar = salt.pillar.Pillar(opts, {}, "mocked-minion", "dev")
  90. # ext pillar function doesn't have the extra_minion_data arg
  91. with patch(
  92. "salt.utils.args.get_function_argspec",
  93. MagicMock(return_value=MagicMock(args=[])),
  94. ):
  95. pillar._external_pillar_data(
  96. "fake_pillar", {"arg": "foo"}, "fake_ext_pillar"
  97. )
  98. mock_ext_pillar_func.assert_called_once_with(
  99. "mocked-minion", "fake_pillar", arg="foo"
  100. )
  101. # ext pillar function has the extra_minion_data arg
  102. mock_ext_pillar_func.reset_mock()
  103. with patch(
  104. "salt.utils.args.get_function_argspec",
  105. MagicMock(return_value=MagicMock(args=["extra_minion_data"])),
  106. ):
  107. pillar._external_pillar_data(
  108. "fake_pillar", {"arg": "foo"}, "fake_ext_pillar"
  109. )
  110. mock_ext_pillar_func.assert_called_once_with(
  111. "mocked-minion", "fake_pillar", arg="foo"
  112. )
  113. @pytest.mark.slow_test(seconds=1) # Test takes >0.1 and <=1 seconds
  114. def test_ext_pillar_no_extra_minion_data_val_list(self):
  115. opts = {
  116. "optimization_order": [0, 1, 2],
  117. "renderer": "json",
  118. "renderer_blacklist": [],
  119. "renderer_whitelist": [],
  120. "state_top": "",
  121. "pillar_roots": {"dev": [], "base": []},
  122. "file_roots": {"dev": [], "base": []},
  123. "extension_modules": "",
  124. "pillarenv_from_saltenv": True,
  125. }
  126. mock_ext_pillar_func = MagicMock()
  127. with patch(
  128. "salt.loader.pillars",
  129. MagicMock(return_value={"fake_ext_pillar": mock_ext_pillar_func}),
  130. ):
  131. pillar = salt.pillar.Pillar(opts, {}, "mocked-minion", "dev")
  132. # ext pillar function doesn't have the extra_minion_data arg
  133. with patch(
  134. "salt.utils.args.get_function_argspec",
  135. MagicMock(return_value=MagicMock(args=[])),
  136. ):
  137. pillar._external_pillar_data("fake_pillar", ["foo"], "fake_ext_pillar")
  138. mock_ext_pillar_func.assert_called_once_with(
  139. "mocked-minion", "fake_pillar", "foo"
  140. )
  141. # ext pillar function has the extra_minion_data arg
  142. mock_ext_pillar_func.reset_mock()
  143. with patch(
  144. "salt.utils.args.get_function_argspec",
  145. MagicMock(return_value=MagicMock(args=["extra_minion_data"])),
  146. ):
  147. pillar._external_pillar_data("fake_pillar", ["foo"], "fake_ext_pillar")
  148. mock_ext_pillar_func.assert_called_once_with(
  149. "mocked-minion", "fake_pillar", "foo"
  150. )
  151. @pytest.mark.slow_test(seconds=1) # Test takes >0.1 and <=1 seconds
  152. def test_ext_pillar_no_extra_minion_data_val_elem(self):
  153. opts = {
  154. "optimization_order": [0, 1, 2],
  155. "renderer": "json",
  156. "renderer_blacklist": [],
  157. "renderer_whitelist": [],
  158. "state_top": "",
  159. "pillar_roots": {"dev": [], "base": []},
  160. "file_roots": {"dev": [], "base": []},
  161. "extension_modules": "",
  162. "pillarenv_from_saltenv": True,
  163. }
  164. mock_ext_pillar_func = MagicMock()
  165. with patch(
  166. "salt.loader.pillars",
  167. MagicMock(return_value={"fake_ext_pillar": mock_ext_pillar_func}),
  168. ):
  169. pillar = salt.pillar.Pillar(opts, {}, "mocked-minion", "dev")
  170. # ext pillar function doesn't have the extra_minion_data arg
  171. with patch(
  172. "salt.utils.args.get_function_argspec",
  173. MagicMock(return_value=MagicMock(args=[])),
  174. ):
  175. pillar._external_pillar_data("fake_pillar", "fake_val", "fake_ext_pillar")
  176. mock_ext_pillar_func.assert_called_once_with(
  177. "mocked-minion", "fake_pillar", "fake_val"
  178. )
  179. # ext pillar function has the extra_minion_data arg
  180. mock_ext_pillar_func.reset_mock()
  181. with patch(
  182. "salt.utils.args.get_function_argspec",
  183. MagicMock(return_value=MagicMock(args=["extra_minion_data"])),
  184. ):
  185. pillar._external_pillar_data("fake_pillar", "fake_val", "fake_ext_pillar")
  186. mock_ext_pillar_func.assert_called_once_with(
  187. "mocked-minion", "fake_pillar", "fake_val"
  188. )
  189. def test_ext_pillar_with_extra_minion_data_val_dict(self):
  190. opts = {
  191. "optimization_order": [0, 1, 2],
  192. "renderer": "json",
  193. "renderer_blacklist": [],
  194. "renderer_whitelist": [],
  195. "state_top": "",
  196. "pillar_roots": {"dev": [], "base": []},
  197. "file_roots": {"dev": [], "base": []},
  198. "extension_modules": "",
  199. "pillarenv_from_saltenv": True,
  200. }
  201. mock_ext_pillar_func = MagicMock()
  202. with patch(
  203. "salt.loader.pillars",
  204. MagicMock(return_value={"fake_ext_pillar": mock_ext_pillar_func}),
  205. ):
  206. pillar = salt.pillar.Pillar(
  207. opts, {}, "mocked-minion", "dev", extra_minion_data={"fake_key": "foo"}
  208. )
  209. # ext pillar function doesn't have the extra_minion_data arg
  210. with patch(
  211. "salt.utils.args.get_function_argspec",
  212. MagicMock(return_value=MagicMock(args=[])),
  213. ):
  214. pillar._external_pillar_data(
  215. "fake_pillar", {"arg": "foo"}, "fake_ext_pillar"
  216. )
  217. mock_ext_pillar_func.assert_called_once_with(
  218. "mocked-minion", "fake_pillar", arg="foo"
  219. )
  220. # ext pillar function has the extra_minion_data arg
  221. mock_ext_pillar_func.reset_mock()
  222. with patch(
  223. "salt.utils.args.get_function_argspec",
  224. MagicMock(return_value=MagicMock(args=["extra_minion_data"])),
  225. ):
  226. pillar._external_pillar_data(
  227. "fake_pillar", {"arg": "foo"}, "fake_ext_pillar"
  228. )
  229. mock_ext_pillar_func.assert_called_once_with(
  230. "mocked-minion",
  231. "fake_pillar",
  232. arg="foo",
  233. extra_minion_data={"fake_key": "foo"},
  234. )
  235. def test_ext_pillar_with_extra_minion_data_val_list(self):
  236. opts = {
  237. "optimization_order": [0, 1, 2],
  238. "renderer": "json",
  239. "renderer_blacklist": [],
  240. "renderer_whitelist": [],
  241. "state_top": "",
  242. "pillar_roots": {"dev": [], "base": []},
  243. "file_roots": {"dev": [], "base": []},
  244. "extension_modules": "",
  245. "pillarenv_from_saltenv": True,
  246. }
  247. mock_ext_pillar_func = MagicMock()
  248. with patch(
  249. "salt.loader.pillars",
  250. MagicMock(return_value={"fake_ext_pillar": mock_ext_pillar_func}),
  251. ):
  252. pillar = salt.pillar.Pillar(
  253. opts, {}, "mocked-minion", "dev", extra_minion_data={"fake_key": "foo"}
  254. )
  255. # ext pillar function doesn't have the extra_minion_data arg
  256. with patch(
  257. "salt.utils.args.get_function_argspec",
  258. MagicMock(return_value=MagicMock(args=[])),
  259. ):
  260. pillar._external_pillar_data("fake_pillar", ["bar"], "fake_ext_pillar")
  261. mock_ext_pillar_func.assert_called_once_with(
  262. "mocked-minion", "fake_pillar", "bar"
  263. )
  264. # ext pillar function has the extra_minion_data arg
  265. mock_ext_pillar_func.reset_mock()
  266. with patch(
  267. "salt.utils.args.get_function_argspec",
  268. MagicMock(return_value=MagicMock(args=["extra_minion_data"])),
  269. ):
  270. pillar._external_pillar_data("fake_pillar", ["bar"], "fake_ext_pillar")
  271. mock_ext_pillar_func.assert_called_once_with(
  272. "mocked-minion", "fake_pillar", "bar", extra_minion_data={"fake_key": "foo"}
  273. )
  274. def test_ext_pillar_with_extra_minion_data_val_elem(self):
  275. opts = {
  276. "optimization_order": [0, 1, 2],
  277. "renderer": "json",
  278. "renderer_blacklist": [],
  279. "renderer_whitelist": [],
  280. "state_top": "",
  281. "pillar_roots": {"dev": [], "base": []},
  282. "file_roots": {"dev": [], "base": []},
  283. "extension_modules": "",
  284. "pillarenv_from_saltenv": True,
  285. }
  286. mock_ext_pillar_func = MagicMock()
  287. with patch(
  288. "salt.loader.pillars",
  289. MagicMock(return_value={"fake_ext_pillar": mock_ext_pillar_func}),
  290. ):
  291. pillar = salt.pillar.Pillar(
  292. opts, {}, "mocked-minion", "dev", extra_minion_data={"fake_key": "foo"}
  293. )
  294. # ext pillar function doesn't have the extra_minion_data arg
  295. with patch(
  296. "salt.utils.args.get_function_argspec",
  297. MagicMock(return_value=MagicMock(args=[])),
  298. ):
  299. pillar._external_pillar_data("fake_pillar", "bar", "fake_ext_pillar")
  300. mock_ext_pillar_func.assert_called_once_with(
  301. "mocked-minion", "fake_pillar", "bar"
  302. )
  303. # ext pillar function has the extra_minion_data arg
  304. mock_ext_pillar_func.reset_mock()
  305. with patch(
  306. "salt.utils.args.get_function_argspec",
  307. MagicMock(return_value=MagicMock(args=["extra_minion_data"])),
  308. ):
  309. pillar._external_pillar_data("fake_pillar", "bar", "fake_ext_pillar")
  310. mock_ext_pillar_func.assert_called_once_with(
  311. "mocked-minion", "fake_pillar", "bar", extra_minion_data={"fake_key": "foo"}
  312. )
  313. @pytest.mark.slow_test(seconds=1) # Test takes >0.1 and <=1 seconds
  314. def test_ext_pillar_first(self):
  315. """
  316. test when using ext_pillar and ext_pillar_first
  317. """
  318. opts = {
  319. "optimization_order": [0, 1, 2],
  320. "renderer": "yaml",
  321. "renderer_blacklist": [],
  322. "renderer_whitelist": [],
  323. "state_top": "",
  324. "pillar_roots": [],
  325. "extension_modules": "",
  326. "saltenv": "base",
  327. "file_roots": [],
  328. "ext_pillar_first": True,
  329. }
  330. grains = {
  331. "os": "Ubuntu",
  332. "os_family": "Debian",
  333. "oscodename": "raring",
  334. "osfullname": "Ubuntu",
  335. "osrelease": "13.04",
  336. "kernel": "Linux",
  337. }
  338. tempdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
  339. try:
  340. sls_files = self._setup_test_topfile_sls_pillar_match(tempdir,)
  341. fc_mock = MockFileclient(
  342. cache_file=sls_files["top"]["dest"],
  343. list_states=["top", "ssh", "ssh.minion", "generic", "generic.minion"],
  344. get_state=sls_files,
  345. )
  346. with patch.object(
  347. salt.fileclient, "get_file_client", MagicMock(return_value=fc_mock)
  348. ), patch(
  349. "salt.pillar.Pillar.ext_pillar",
  350. MagicMock(
  351. return_value=(
  352. {"id": "minion", "phase": "alpha", "role": "database"},
  353. [],
  354. )
  355. ),
  356. ):
  357. pillar = salt.pillar.Pillar(opts, grains, "mocked-minion", "base")
  358. self.assertEqual(pillar.compile_pillar()["generic"]["key1"], "value1")
  359. finally:
  360. shutil.rmtree(tempdir, ignore_errors=True)
  361. @pytest.mark.slow_test(seconds=1) # Test takes >0.1 and <=1 seconds
  362. def test_dynamic_pillarenv(self):
  363. opts = {
  364. "optimization_order": [0, 1, 2],
  365. "renderer": "json",
  366. "renderer_blacklist": [],
  367. "renderer_whitelist": [],
  368. "state_top": "",
  369. "pillar_roots": {
  370. "__env__": ["/srv/pillar/__env__"],
  371. "base": ["/srv/pillar/base"],
  372. },
  373. "file_roots": {"base": ["/srv/salt/base"], "dev": ["/svr/salt/dev"]},
  374. "extension_modules": "",
  375. }
  376. pillar = salt.pillar.Pillar(opts, {}, "mocked-minion", "base", pillarenv="dev")
  377. self.assertEqual(
  378. pillar.opts["pillar_roots"],
  379. {"base": ["/srv/pillar/base"], "dev": ["/srv/pillar/__env__"]},
  380. )
  381. def test_ignored_dynamic_pillarenv(self):
  382. opts = {
  383. "optimization_order": [0, 1, 2],
  384. "renderer": "json",
  385. "renderer_blacklist": [],
  386. "renderer_whitelist": [],
  387. "state_top": "",
  388. "pillar_roots": {
  389. "__env__": ["/srv/pillar/__env__"],
  390. "base": ["/srv/pillar/base"],
  391. },
  392. "file_roots": {"base": ["/srv/salt/base"], "dev": ["/svr/salt/dev"]},
  393. "extension_modules": "",
  394. }
  395. pillar = salt.pillar.Pillar(opts, {}, "mocked-minion", "base", pillarenv="base")
  396. self.assertEqual(pillar.opts["pillar_roots"], {"base": ["/srv/pillar/base"]})
  397. @patch("salt.fileclient.Client.list_states")
  398. @pytest.mark.slow_test(seconds=1) # Test takes >0.1 and <=1 seconds
  399. def test_malformed_pillar_sls(self, mock_list_states):
  400. with patch("salt.pillar.compile_template") as compile_template:
  401. opts = {
  402. "optimization_order": [0, 1, 2],
  403. "renderer": "json",
  404. "renderer_blacklist": [],
  405. "renderer_whitelist": [],
  406. "state_top": "",
  407. "pillar_roots": [],
  408. "file_roots": [],
  409. "extension_modules": "",
  410. }
  411. grains = {
  412. "os": "Ubuntu",
  413. "os_family": "Debian",
  414. "oscodename": "raring",
  415. "osfullname": "Ubuntu",
  416. "osrelease": "13.04",
  417. "kernel": "Linux",
  418. }
  419. mock_list_states.return_value = ["foo", "blah"]
  420. pillar = salt.pillar.Pillar(opts, grains, "mocked-minion", "base")
  421. # Mock getting the proper template files
  422. pillar.client.get_state = MagicMock(
  423. return_value={
  424. "dest": "/path/to/pillar/files/foo.sls",
  425. "source": "salt://foo.sls",
  426. }
  427. )
  428. # Template compilation returned a string
  429. compile_template.return_value = "BAHHH"
  430. self.assertEqual(
  431. pillar.render_pillar({"base": ["foo.sls"]}),
  432. ({}, ["SLS 'foo.sls' does not render to a dictionary"]),
  433. )
  434. # Template compilation returned a list
  435. compile_template.return_value = ["BAHHH"]
  436. self.assertEqual(
  437. pillar.render_pillar({"base": ["foo.sls"]}),
  438. ({}, ["SLS 'foo.sls' does not render to a dictionary"]),
  439. )
  440. # Template compilation returned a dictionary, which is what's expected
  441. compile_template.return_value = {"foo": "bar"}
  442. self.assertEqual(
  443. pillar.render_pillar({"base": ["foo.sls"]}), ({"foo": "bar"}, [])
  444. )
  445. # Test improper includes
  446. compile_template.side_effect = [
  447. {"foo": "bar", "include": "blah"},
  448. {"foo2": "bar2"},
  449. ]
  450. self.assertEqual(
  451. pillar.render_pillar({"base": ["foo.sls"]}),
  452. (
  453. {"foo": "bar", "include": "blah"},
  454. ["Include Declaration in SLS 'foo.sls' is not formed as a list"],
  455. ),
  456. )
  457. # Test includes as a list, which is what's expected
  458. compile_template.side_effect = [
  459. {"foo": "bar", "include": ["blah"]},
  460. {"foo2": "bar2"},
  461. ]
  462. self.assertEqual(
  463. pillar.render_pillar({"base": ["foo.sls"]}),
  464. ({"foo": "bar", "foo2": "bar2"}, []),
  465. )
  466. # Test includes as a list overriding data
  467. compile_template.side_effect = [
  468. {"foo": "bar", "include": ["blah"]},
  469. {"foo": "bar2"},
  470. ]
  471. self.assertEqual(
  472. pillar.render_pillar({"base": ["foo.sls"]}), ({"foo": "bar"}, [])
  473. )
  474. # Test includes using empty key directive
  475. compile_template.side_effect = [
  476. {"foo": "bar", "include": [{"blah": {"key": ""}}]},
  477. {"foo": "bar2"},
  478. ]
  479. self.assertEqual(
  480. pillar.render_pillar({"base": ["foo.sls"]}), ({"foo": "bar"}, [])
  481. )
  482. # Test includes using simple non-nested key
  483. compile_template.side_effect = [
  484. {"foo": "bar", "include": [{"blah": {"key": "nested"}}]},
  485. {"foo": "bar2"},
  486. ]
  487. self.assertEqual(
  488. pillar.render_pillar({"base": ["foo.sls"]}),
  489. ({"foo": "bar", "nested": {"foo": "bar2"}}, []),
  490. )
  491. # Test includes using nested key
  492. compile_template.side_effect = [
  493. {"foo": "bar", "include": [{"blah": {"key": "nested:level"}}]},
  494. {"foo": "bar2"},
  495. ]
  496. self.assertEqual(
  497. pillar.render_pillar({"base": ["foo.sls"]}),
  498. ({"foo": "bar", "nested": {"level": {"foo": "bar2"}}}, []),
  499. )
  500. @pytest.mark.slow_test(seconds=1) # Test takes >0.1 and <=1 seconds
  501. def test_includes_override_sls(self):
  502. opts = {
  503. "optimization_order": [0, 1, 2],
  504. "renderer": "json",
  505. "renderer_blacklist": [],
  506. "renderer_whitelist": [],
  507. "state_top": "",
  508. "pillar_roots": {},
  509. "file_roots": {},
  510. "extension_modules": "",
  511. }
  512. grains = {
  513. "os": "Ubuntu",
  514. "os_family": "Debian",
  515. "oscodename": "raring",
  516. "osfullname": "Ubuntu",
  517. "osrelease": "13.04",
  518. "kernel": "Linux",
  519. }
  520. with patch("salt.pillar.compile_template") as compile_template, patch.object(
  521. salt.pillar.Pillar,
  522. "_Pillar__gather_avail",
  523. MagicMock(return_value={"base": ["blah", "foo"]}),
  524. ):
  525. # Test with option set to True
  526. opts["pillar_includes_override_sls"] = True
  527. pillar = salt.pillar.Pillar(opts, grains, "mocked-minion", "base")
  528. # Mock getting the proper template files
  529. pillar.client.get_state = MagicMock(
  530. return_value={
  531. "dest": "/path/to/pillar/files/foo.sls",
  532. "source": "salt://foo.sls",
  533. }
  534. )
  535. compile_template.side_effect = [
  536. {"foo": "bar", "include": ["blah"]},
  537. {"foo": "bar2"},
  538. ]
  539. self.assertEqual(
  540. pillar.render_pillar({"base": ["foo.sls"]}), ({"foo": "bar2"}, [])
  541. )
  542. # Test with option set to False
  543. opts["pillar_includes_override_sls"] = False
  544. pillar = salt.pillar.Pillar(opts, grains, "mocked-minion", "base")
  545. # Mock getting the proper template files
  546. pillar.client.get_state = MagicMock(
  547. return_value={
  548. "dest": "/path/to/pillar/files/foo.sls",
  549. "source": "salt://foo.sls",
  550. }
  551. )
  552. compile_template.side_effect = [
  553. {"foo": "bar", "include": ["blah"]},
  554. {"foo": "bar2"},
  555. ]
  556. self.assertEqual(
  557. pillar.render_pillar({"base": ["foo.sls"]}), ({"foo": "bar"}, [])
  558. )
  559. @pytest.mark.slow_test(seconds=1) # Test takes >0.1 and <=1 seconds
  560. def test_topfile_order(self):
  561. opts = {
  562. "optimization_order": [0, 1, 2],
  563. "renderer": "yaml",
  564. "renderer_blacklist": [],
  565. "renderer_whitelist": [],
  566. "state_top": "",
  567. "pillar_roots": [],
  568. "extension_modules": "",
  569. "saltenv": "base",
  570. "file_roots": [],
  571. }
  572. grains = {
  573. "os": "Ubuntu",
  574. "os_family": "Debian",
  575. "oscodename": "raring",
  576. "osfullname": "Ubuntu",
  577. "osrelease": "13.04",
  578. "kernel": "Linux",
  579. }
  580. def _run_test(nodegroup_order, glob_order, expected):
  581. tempdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
  582. try:
  583. sls_files = self._setup_test_topfile_sls(
  584. tempdir, nodegroup_order, glob_order
  585. )
  586. fc_mock = MockFileclient(
  587. cache_file=sls_files["top"]["dest"],
  588. list_states=[
  589. "top",
  590. "ssh",
  591. "ssh.minion",
  592. "generic",
  593. "generic.minion",
  594. ],
  595. get_state=sls_files,
  596. )
  597. with patch.object(
  598. salt.fileclient, "get_file_client", MagicMock(return_value=fc_mock)
  599. ):
  600. pillar = salt.pillar.Pillar(opts, grains, "mocked-minion", "base")
  601. # Make sure that confirm_top.confirm_top returns True
  602. pillar.matchers["confirm_top.confirm_top"] = lambda *x, **y: True
  603. self.assertEqual(pillar.compile_pillar()["ssh"], expected)
  604. finally:
  605. shutil.rmtree(tempdir, ignore_errors=True)
  606. # test case where glob match happens second and therefore takes
  607. # precedence over nodegroup match.
  608. _run_test(nodegroup_order=1, glob_order=2, expected="bar")
  609. # test case where nodegroup match happens second and therefore takes
  610. # precedence over glob match.
  611. _run_test(nodegroup_order=2, glob_order=1, expected="foo")
  612. def _setup_test_topfile_sls_pillar_match(self, tempdir):
  613. # Write a simple topfile and two pillar state files
  614. top_file = tempfile.NamedTemporaryFile(dir=tempdir, delete=False)
  615. s = """
  616. base:
  617. 'phase:alpha':
  618. - match: pillar
  619. - generic
  620. """
  621. top_file.write(salt.utils.stringutils.to_bytes(s))
  622. top_file.flush()
  623. generic_file = tempfile.NamedTemporaryFile(dir=tempdir, delete=False)
  624. generic_file.write(
  625. b"""
  626. generic:
  627. key1: value1
  628. """
  629. )
  630. generic_file.flush()
  631. return {
  632. "top": {"path": "", "dest": top_file.name},
  633. "generic": {"path": "", "dest": generic_file.name},
  634. }
  635. def _setup_test_topfile_sls(self, tempdir, nodegroup_order, glob_order):
  636. # Write a simple topfile and two pillar state files
  637. top_file = tempfile.NamedTemporaryFile(dir=tempdir, delete=False)
  638. s = """
  639. base:
  640. group:
  641. - match: nodegroup
  642. - order: {nodegroup_order}
  643. - ssh
  644. - generic
  645. '*':
  646. - generic
  647. minion:
  648. - order: {glob_order}
  649. - ssh.minion
  650. - generic.minion
  651. """.format(
  652. nodegroup_order=nodegroup_order, glob_order=glob_order
  653. )
  654. top_file.write(salt.utils.stringutils.to_bytes(s))
  655. top_file.flush()
  656. ssh_file = tempfile.NamedTemporaryFile(dir=tempdir, delete=False)
  657. ssh_file.write(
  658. b"""
  659. ssh:
  660. foo
  661. """
  662. )
  663. ssh_file.flush()
  664. ssh_minion_file = tempfile.NamedTemporaryFile(dir=tempdir, delete=False)
  665. ssh_minion_file.write(
  666. b"""
  667. ssh:
  668. bar
  669. """
  670. )
  671. ssh_minion_file.flush()
  672. generic_file = tempfile.NamedTemporaryFile(dir=tempdir, delete=False)
  673. generic_file.write(
  674. b"""
  675. generic:
  676. key1:
  677. - value1
  678. - value2
  679. key2:
  680. sub_key1: []
  681. """
  682. )
  683. generic_file.flush()
  684. generic_minion_file = tempfile.NamedTemporaryFile(dir=tempdir, delete=False)
  685. generic_minion_file.write(
  686. b"""
  687. generic:
  688. key1:
  689. - value3
  690. key2:
  691. sub_key2: []
  692. """
  693. )
  694. generic_minion_file.flush()
  695. return {
  696. "top": {"path": "", "dest": top_file.name},
  697. "ssh": {"path": "", "dest": ssh_file.name},
  698. "ssh.minion": {"path": "", "dest": ssh_minion_file.name},
  699. "generic": {"path": "", "dest": generic_file.name},
  700. "generic.minion": {"path": "", "dest": generic_minion_file.name},
  701. }
  702. @with_tempdir()
  703. @pytest.mark.slow_test(seconds=1) # Test takes >0.1 and <=1 seconds
  704. def test_include(self, tempdir):
  705. opts = {
  706. "optimization_order": [0, 1, 2],
  707. "renderer": "yaml",
  708. "renderer_blacklist": [],
  709. "renderer_whitelist": [],
  710. "state_top": "",
  711. "pillar_roots": [],
  712. "extension_modules": "",
  713. "saltenv": "base",
  714. "file_roots": [],
  715. }
  716. grains = {
  717. "os": "Ubuntu",
  718. "os_family": "Debian",
  719. "oscodename": "raring",
  720. "osfullname": "Ubuntu",
  721. "osrelease": "13.04",
  722. "kernel": "Linux",
  723. }
  724. sls_files = self._setup_test_include_sls(tempdir)
  725. fc_mock = MockFileclient(
  726. cache_file=sls_files["top"]["dest"],
  727. get_state=sls_files,
  728. list_states=[
  729. "top",
  730. "test.init",
  731. "test.sub1",
  732. "test.sub2",
  733. "test.sub_wildcard_1",
  734. "test.sub_with_init_dot",
  735. "test.sub.with.slashes",
  736. ],
  737. )
  738. with patch.object(
  739. salt.fileclient, "get_file_client", MagicMock(return_value=fc_mock)
  740. ):
  741. pillar = salt.pillar.Pillar(opts, grains, "minion", "base")
  742. # Make sure that confirm_top.confirm_top returns True
  743. pillar.matchers["confirm_top.confirm_top"] = lambda *x, **y: True
  744. compiled_pillar = pillar.compile_pillar()
  745. self.assertEqual(compiled_pillar["foo_wildcard"], "bar_wildcard")
  746. self.assertEqual(compiled_pillar["foo1"], "bar1")
  747. self.assertEqual(compiled_pillar["foo2"], "bar2")
  748. self.assertEqual(compiled_pillar["sub_with_slashes"], "sub_slashes_worked")
  749. self.assertEqual(
  750. compiled_pillar["sub_init_dot"], "sub_with_init_dot_worked"
  751. )
  752. def _setup_test_include_sls(self, tempdir):
  753. top_file = tempfile.NamedTemporaryFile(dir=tempdir, delete=False)
  754. top_file.write(
  755. b"""
  756. base:
  757. '*':
  758. - order: 1
  759. - test.sub2
  760. minion:
  761. - order: 2
  762. - test
  763. """
  764. )
  765. top_file.flush()
  766. init_sls = tempfile.NamedTemporaryFile(dir=tempdir, delete=False)
  767. init_sls.write(
  768. b"""
  769. include:
  770. - test.sub1
  771. - test.sub_wildcard*
  772. - .test.sub_with_init_dot
  773. - test/sub/with/slashes
  774. """
  775. )
  776. init_sls.flush()
  777. sub1_sls = tempfile.NamedTemporaryFile(dir=tempdir, delete=False)
  778. sub1_sls.write(
  779. b"""
  780. foo1:
  781. bar1
  782. """
  783. )
  784. sub1_sls.flush()
  785. sub2_sls = tempfile.NamedTemporaryFile(dir=tempdir, delete=False)
  786. sub2_sls.write(
  787. b"""
  788. foo2:
  789. bar2
  790. """
  791. )
  792. sub2_sls.flush()
  793. sub_wildcard_1_sls = tempfile.NamedTemporaryFile(dir=tempdir, delete=False)
  794. sub_wildcard_1_sls.write(
  795. b"""
  796. foo_wildcard:
  797. bar_wildcard
  798. """
  799. )
  800. sub_wildcard_1_sls.flush()
  801. sub_with_init_dot_sls = tempfile.NamedTemporaryFile(dir=tempdir, delete=False)
  802. sub_with_init_dot_sls.write(
  803. b"""
  804. sub_init_dot:
  805. sub_with_init_dot_worked
  806. """
  807. )
  808. sub_with_init_dot_sls.flush()
  809. sub_with_slashes_sls = tempfile.NamedTemporaryFile(dir=tempdir, delete=False)
  810. sub_with_slashes_sls.write(
  811. b"""
  812. sub_with_slashes:
  813. sub_slashes_worked
  814. """
  815. )
  816. sub_with_slashes_sls.flush()
  817. return {
  818. "top": {"path": "", "dest": top_file.name},
  819. "test": {"path": "", "dest": init_sls.name},
  820. "test.sub1": {"path": "", "dest": sub1_sls.name},
  821. "test.sub2": {"path": "", "dest": sub2_sls.name},
  822. "test.sub_wildcard_1": {"path": "", "dest": sub_wildcard_1_sls.name},
  823. "test.sub_with_init_dot": {"path": "", "dest": sub_with_init_dot_sls.name},
  824. "test.sub.with.slashes": {"path": "", "dest": sub_with_slashes_sls.name},
  825. }
  826. @patch("salt.transport.client.ReqChannel.factory", MagicMock())
  827. class RemotePillarTestCase(TestCase):
  828. """
  829. Tests for instantiating a RemotePillar in salt.pillar
  830. """
  831. def setUp(self):
  832. self.grains = {}
  833. def tearDown(self):
  834. for attr in ("grains",):
  835. try:
  836. delattr(self, attr)
  837. except AttributeError:
  838. continue
  839. def test_get_opts_in_pillar_override_call(self):
  840. mock_get_extra_minion_data = MagicMock(return_value={})
  841. with patch(
  842. "salt.pillar.RemotePillarMixin.get_ext_pillar_extra_minion_data",
  843. mock_get_extra_minion_data,
  844. ):
  845. salt.pillar.RemotePillar({}, self.grains, "mocked-minion", "dev")
  846. mock_get_extra_minion_data.assert_called_once_with({"saltenv": "dev"})
  847. def test_multiple_keys_in_opts_added_to_pillar(self):
  848. opts = {
  849. "renderer": "json",
  850. "path_to_add": "fake_data",
  851. "path_to_add2": {"fake_data2": ["fake_data3", "fake_data4"]},
  852. "pass_to_ext_pillars": ["path_to_add", "path_to_add2"],
  853. }
  854. pillar = salt.pillar.RemotePillar(opts, self.grains, "mocked-minion", "dev")
  855. self.assertEqual(
  856. pillar.extra_minion_data,
  857. {
  858. "path_to_add": "fake_data",
  859. "path_to_add2": {"fake_data2": ["fake_data3", "fake_data4"]},
  860. },
  861. )
  862. def test_subkey_in_opts_added_to_pillar(self):
  863. opts = {
  864. "renderer": "json",
  865. "path_to_add": "fake_data",
  866. "path_to_add2": {
  867. "fake_data5": "fake_data6",
  868. "fake_data2": ["fake_data3", "fake_data4"],
  869. },
  870. "pass_to_ext_pillars": ["path_to_add2:fake_data5"],
  871. }
  872. pillar = salt.pillar.RemotePillar(opts, self.grains, "mocked-minion", "dev")
  873. self.assertEqual(
  874. pillar.extra_minion_data, {"path_to_add2": {"fake_data5": "fake_data6"}}
  875. )
  876. def test_non_existent_leaf_opt_in_add_to_pillar(self):
  877. opts = {
  878. "renderer": "json",
  879. "path_to_add": "fake_data",
  880. "path_to_add2": {
  881. "fake_data5": "fake_data6",
  882. "fake_data2": ["fake_data3", "fake_data4"],
  883. },
  884. "pass_to_ext_pillars": ["path_to_add2:fake_data_non_exist"],
  885. }
  886. pillar = salt.pillar.RemotePillar(opts, self.grains, "mocked-minion", "dev")
  887. self.assertEqual(pillar.pillar_override, {})
  888. def test_non_existent_intermediate_opt_in_add_to_pillar(self):
  889. opts = {
  890. "renderer": "json",
  891. "path_to_add": "fake_data",
  892. "path_to_add2": {
  893. "fake_data5": "fake_data6",
  894. "fake_data2": ["fake_data3", "fake_data4"],
  895. },
  896. "pass_to_ext_pillars": ["path_to_add_no_exist"],
  897. }
  898. pillar = salt.pillar.RemotePillar(opts, self.grains, "mocked-minion", "dev")
  899. self.assertEqual(pillar.pillar_override, {})
  900. def test_malformed_add_to_pillar(self):
  901. opts = {
  902. "renderer": "json",
  903. "path_to_add": "fake_data",
  904. "path_to_add2": {
  905. "fake_data5": "fake_data6",
  906. "fake_data2": ["fake_data3", "fake_data4"],
  907. },
  908. "pass_to_ext_pillars": MagicMock(),
  909. }
  910. with self.assertRaises(salt.exceptions.SaltClientError) as excinfo:
  911. salt.pillar.RemotePillar(opts, self.grains, "mocked-minion", "dev")
  912. self.assertEqual(
  913. excinfo.exception.strerror, "'pass_to_ext_pillars' config is malformed."
  914. )
  915. def test_pillar_send_extra_minion_data_from_config(self):
  916. opts = {
  917. "renderer": "json",
  918. "pillarenv": "fake_pillar_env",
  919. "path_to_add": "fake_data",
  920. "path_to_add2": {
  921. "fake_data5": "fake_data6",
  922. "fake_data2": ["fake_data3", "fake_data4"],
  923. },
  924. "pass_to_ext_pillars": ["path_to_add"],
  925. }
  926. mock_channel = MagicMock(
  927. crypted_transfer_decode_dictentry=MagicMock(return_value={})
  928. )
  929. with patch(
  930. "salt.transport.client.ReqChannel.factory",
  931. MagicMock(return_value=mock_channel),
  932. ):
  933. pillar = salt.pillar.RemotePillar(
  934. opts, self.grains, "mocked_minion", "fake_env"
  935. )
  936. ret = pillar.compile_pillar()
  937. self.assertEqual(pillar.channel, mock_channel)
  938. mock_channel.crypted_transfer_decode_dictentry.assert_called_once_with(
  939. {
  940. "cmd": "_pillar",
  941. "ver": "2",
  942. "id": "mocked_minion",
  943. "grains": {},
  944. "saltenv": "fake_env",
  945. "pillarenv": "fake_pillar_env",
  946. "pillar_override": {},
  947. "extra_minion_data": {"path_to_add": "fake_data"},
  948. },
  949. dictkey="pillar",
  950. )
  951. @patch("salt.transport.client.AsyncReqChannel.factory", MagicMock())
  952. class AsyncRemotePillarTestCase(TestCase):
  953. """
  954. Tests for instantiating a AsyncRemotePillar in salt.pillar
  955. """
  956. def setUp(self):
  957. self.grains = {}
  958. def tearDown(self):
  959. for attr in ("grains",):
  960. try:
  961. delattr(self, attr)
  962. except AttributeError:
  963. continue
  964. def test_get_opts_in_pillar_override_call(self):
  965. mock_get_extra_minion_data = MagicMock(return_value={})
  966. with patch(
  967. "salt.pillar.RemotePillarMixin.get_ext_pillar_extra_minion_data",
  968. mock_get_extra_minion_data,
  969. ):
  970. salt.pillar.RemotePillar({}, self.grains, "mocked-minion", "dev")
  971. mock_get_extra_minion_data.assert_called_once_with({"saltenv": "dev"})
  972. def test_pillar_send_extra_minion_data_from_config(self):
  973. opts = {
  974. "renderer": "json",
  975. "pillarenv": "fake_pillar_env",
  976. "path_to_add": "fake_data",
  977. "path_to_add2": {
  978. "fake_data5": "fake_data6",
  979. "fake_data2": ["fake_data3", "fake_data4"],
  980. },
  981. "pass_to_ext_pillars": ["path_to_add"],
  982. }
  983. mock_channel = MagicMock(
  984. crypted_transfer_decode_dictentry=MagicMock(return_value={})
  985. )
  986. with patch(
  987. "salt.transport.client.AsyncReqChannel.factory",
  988. MagicMock(return_value=mock_channel),
  989. ):
  990. pillar = salt.pillar.RemotePillar(
  991. opts, self.grains, "mocked_minion", "fake_env"
  992. )
  993. ret = pillar.compile_pillar()
  994. mock_channel.crypted_transfer_decode_dictentry.assert_called_once_with(
  995. {
  996. "cmd": "_pillar",
  997. "ver": "2",
  998. "id": "mocked_minion",
  999. "grains": {},
  1000. "saltenv": "fake_env",
  1001. "pillarenv": "fake_pillar_env",
  1002. "pillar_override": {},
  1003. "extra_minion_data": {"path_to_add": "fake_data"},
  1004. },
  1005. dictkey="pillar",
  1006. )