test_pillar.py 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829
  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 tempfile
  11. # Import Salt Testing libs
  12. from tests.support.unit import skipIf, TestCase
  13. from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch
  14. from tests.support.paths import TMP
  15. # Import salt libs
  16. import salt.pillar
  17. import salt.utils.stringutils
  18. import salt.exceptions
  19. @skipIf(NO_MOCK, NO_MOCK_REASON)
  20. class PillarTestCase(TestCase):
  21. def tearDown(self):
  22. for attrname in ('generic_file', 'generic_minion_file', 'ssh_file', 'ssh_minion_file', 'top_file'):
  23. try:
  24. delattr(self, attrname)
  25. except AttributeError:
  26. continue
  27. def test_pillarenv_from_saltenv(self):
  28. with patch('salt.pillar.compile_template') as compile_template:
  29. opts = {
  30. 'optimization_order': [0, 1, 2],
  31. 'renderer': 'json',
  32. 'renderer_blacklist': [],
  33. 'renderer_whitelist': [],
  34. 'state_top': '',
  35. 'pillar_roots': {
  36. 'dev': [],
  37. 'base': []
  38. },
  39. 'file_roots': {
  40. 'dev': [],
  41. 'base': []
  42. },
  43. 'extension_modules': '',
  44. 'pillarenv_from_saltenv': True
  45. }
  46. grains = {
  47. 'os': 'Ubuntu',
  48. }
  49. pillar = salt.pillar.Pillar(opts, grains, 'mocked-minion', 'dev')
  50. self.assertEqual(pillar.opts['saltenv'], 'dev')
  51. self.assertEqual(pillar.opts['pillarenv'], 'dev')
  52. def test_ext_pillar_no_extra_minion_data_val_dict(self):
  53. opts = {
  54. 'optimization_order': [0, 1, 2],
  55. 'renderer': 'json',
  56. 'renderer_blacklist': [],
  57. 'renderer_whitelist': [],
  58. 'state_top': '',
  59. 'pillar_roots': {
  60. 'dev': [],
  61. 'base': []
  62. },
  63. 'file_roots': {
  64. 'dev': [],
  65. 'base': []
  66. },
  67. 'extension_modules': '',
  68. 'pillarenv_from_saltenv': True
  69. }
  70. mock_ext_pillar_func = MagicMock()
  71. with patch('salt.loader.pillars',
  72. MagicMock(return_value={'fake_ext_pillar':
  73. mock_ext_pillar_func})):
  74. pillar = salt.pillar.Pillar(opts, {}, 'mocked-minion', 'dev')
  75. # ext pillar function doesn't have the extra_minion_data arg
  76. with patch('salt.utils.args.get_function_argspec',
  77. MagicMock(return_value=MagicMock(args=[]))):
  78. pillar._external_pillar_data('fake_pillar', {'arg': 'foo'},
  79. 'fake_ext_pillar')
  80. mock_ext_pillar_func.assert_called_once_with('mocked-minion',
  81. 'fake_pillar',
  82. arg='foo')
  83. # ext pillar function has the extra_minion_data arg
  84. mock_ext_pillar_func.reset_mock()
  85. with patch('salt.utils.args.get_function_argspec',
  86. MagicMock(return_value=MagicMock(args=['extra_minion_data']))):
  87. pillar._external_pillar_data('fake_pillar', {'arg': 'foo'},
  88. 'fake_ext_pillar')
  89. mock_ext_pillar_func.assert_called_once_with('mocked-minion',
  90. 'fake_pillar',
  91. arg='foo')
  92. def test_ext_pillar_no_extra_minion_data_val_list(self):
  93. opts = {
  94. 'optimization_order': [0, 1, 2],
  95. 'renderer': 'json',
  96. 'renderer_blacklist': [],
  97. 'renderer_whitelist': [],
  98. 'state_top': '',
  99. 'pillar_roots': {
  100. 'dev': [],
  101. 'base': []
  102. },
  103. 'file_roots': {
  104. 'dev': [],
  105. 'base': []
  106. },
  107. 'extension_modules': '',
  108. 'pillarenv_from_saltenv': True
  109. }
  110. mock_ext_pillar_func = MagicMock()
  111. with patch('salt.loader.pillars',
  112. MagicMock(return_value={'fake_ext_pillar':
  113. mock_ext_pillar_func})):
  114. pillar = salt.pillar.Pillar(opts, {}, 'mocked-minion', 'dev')
  115. # ext pillar function doesn't have the extra_minion_data arg
  116. with patch('salt.utils.args.get_function_argspec',
  117. MagicMock(return_value=MagicMock(args=[]))):
  118. pillar._external_pillar_data('fake_pillar', ['foo'],
  119. 'fake_ext_pillar')
  120. mock_ext_pillar_func.assert_called_once_with('mocked-minion',
  121. 'fake_pillar',
  122. 'foo')
  123. # ext pillar function has the extra_minion_data arg
  124. mock_ext_pillar_func.reset_mock()
  125. with patch('salt.utils.args.get_function_argspec',
  126. MagicMock(return_value=MagicMock(args=['extra_minion_data']))):
  127. pillar._external_pillar_data('fake_pillar', ['foo'],
  128. 'fake_ext_pillar')
  129. mock_ext_pillar_func.assert_called_once_with('mocked-minion',
  130. 'fake_pillar',
  131. 'foo')
  132. def test_ext_pillar_no_extra_minion_data_val_elem(self):
  133. opts = {
  134. 'optimization_order': [0, 1, 2],
  135. 'renderer': 'json',
  136. 'renderer_blacklist': [],
  137. 'renderer_whitelist': [],
  138. 'state_top': '',
  139. 'pillar_roots': {
  140. 'dev': [],
  141. 'base': []
  142. },
  143. 'file_roots': {
  144. 'dev': [],
  145. 'base': []
  146. },
  147. 'extension_modules': '',
  148. 'pillarenv_from_saltenv': True
  149. }
  150. mock_ext_pillar_func = MagicMock()
  151. with patch('salt.loader.pillars',
  152. MagicMock(return_value={'fake_ext_pillar':
  153. mock_ext_pillar_func})):
  154. pillar = salt.pillar.Pillar(opts, {}, 'mocked-minion', 'dev')
  155. # ext pillar function doesn't have the extra_minion_data arg
  156. with patch('salt.utils.args.get_function_argspec',
  157. MagicMock(return_value=MagicMock(args=[]))):
  158. pillar._external_pillar_data('fake_pillar', 'fake_val',
  159. 'fake_ext_pillar')
  160. mock_ext_pillar_func.assert_called_once_with('mocked-minion',
  161. 'fake_pillar', 'fake_val')
  162. # ext pillar function has the extra_minion_data arg
  163. mock_ext_pillar_func.reset_mock()
  164. with patch('salt.utils.args.get_function_argspec',
  165. MagicMock(return_value=MagicMock(args=['extra_minion_data']))):
  166. pillar._external_pillar_data('fake_pillar', 'fake_val',
  167. 'fake_ext_pillar')
  168. mock_ext_pillar_func.assert_called_once_with('mocked-minion',
  169. 'fake_pillar', 'fake_val')
  170. def test_ext_pillar_with_extra_minion_data_val_dict(self):
  171. opts = {
  172. 'optimization_order': [0, 1, 2],
  173. 'renderer': 'json',
  174. 'renderer_blacklist': [],
  175. 'renderer_whitelist': [],
  176. 'state_top': '',
  177. 'pillar_roots': {
  178. 'dev': [],
  179. 'base': []
  180. },
  181. 'file_roots': {
  182. 'dev': [],
  183. 'base': []
  184. },
  185. 'extension_modules': '',
  186. 'pillarenv_from_saltenv': True
  187. }
  188. mock_ext_pillar_func = MagicMock()
  189. with patch('salt.loader.pillars',
  190. MagicMock(return_value={'fake_ext_pillar':
  191. mock_ext_pillar_func})):
  192. pillar = salt.pillar.Pillar(opts, {}, 'mocked-minion', 'dev',
  193. extra_minion_data={'fake_key': 'foo'})
  194. # ext pillar function doesn't have the extra_minion_data arg
  195. with patch('salt.utils.args.get_function_argspec',
  196. MagicMock(return_value=MagicMock(args=[]))):
  197. pillar._external_pillar_data('fake_pillar', {'arg': 'foo'},
  198. 'fake_ext_pillar')
  199. mock_ext_pillar_func.assert_called_once_with(
  200. 'mocked-minion', 'fake_pillar', arg='foo')
  201. # ext pillar function has the extra_minion_data arg
  202. mock_ext_pillar_func.reset_mock()
  203. with patch('salt.utils.args.get_function_argspec',
  204. MagicMock(return_value=MagicMock(args=['extra_minion_data']))):
  205. pillar._external_pillar_data('fake_pillar', {'arg': 'foo'},
  206. 'fake_ext_pillar')
  207. mock_ext_pillar_func.assert_called_once_with(
  208. 'mocked-minion', 'fake_pillar', arg='foo',
  209. extra_minion_data={'fake_key': 'foo'})
  210. def test_ext_pillar_with_extra_minion_data_val_list(self):
  211. opts = {
  212. 'optimization_order': [0, 1, 2],
  213. 'renderer': 'json',
  214. 'renderer_blacklist': [],
  215. 'renderer_whitelist': [],
  216. 'state_top': '',
  217. 'pillar_roots': {
  218. 'dev': [],
  219. 'base': []
  220. },
  221. 'file_roots': {
  222. 'dev': [],
  223. 'base': []
  224. },
  225. 'extension_modules': '',
  226. 'pillarenv_from_saltenv': True
  227. }
  228. mock_ext_pillar_func = MagicMock()
  229. with patch('salt.loader.pillars',
  230. MagicMock(return_value={'fake_ext_pillar':
  231. mock_ext_pillar_func})):
  232. pillar = salt.pillar.Pillar(opts, {}, 'mocked-minion', 'dev',
  233. extra_minion_data={'fake_key': 'foo'})
  234. # ext pillar function doesn't have the extra_minion_data arg
  235. with patch('salt.utils.args.get_function_argspec',
  236. MagicMock(return_value=MagicMock(args=[]))):
  237. pillar._external_pillar_data('fake_pillar', ['bar'],
  238. 'fake_ext_pillar')
  239. mock_ext_pillar_func.assert_called_once_with(
  240. 'mocked-minion', 'fake_pillar', 'bar')
  241. # ext pillar function has the extra_minion_data arg
  242. mock_ext_pillar_func.reset_mock()
  243. with patch('salt.utils.args.get_function_argspec',
  244. MagicMock(return_value=MagicMock(args=['extra_minion_data']))):
  245. pillar._external_pillar_data('fake_pillar', ['bar'],
  246. 'fake_ext_pillar')
  247. mock_ext_pillar_func.assert_called_once_with(
  248. 'mocked-minion', 'fake_pillar', 'bar',
  249. extra_minion_data={'fake_key': 'foo'})
  250. def test_ext_pillar_with_extra_minion_data_val_elem(self):
  251. opts = {
  252. 'optimization_order': [0, 1, 2],
  253. 'renderer': 'json',
  254. 'renderer_blacklist': [],
  255. 'renderer_whitelist': [],
  256. 'state_top': '',
  257. 'pillar_roots': {
  258. 'dev': [],
  259. 'base': []
  260. },
  261. 'file_roots': {
  262. 'dev': [],
  263. 'base': []
  264. },
  265. 'extension_modules': '',
  266. 'pillarenv_from_saltenv': True
  267. }
  268. mock_ext_pillar_func = MagicMock()
  269. with patch('salt.loader.pillars',
  270. MagicMock(return_value={'fake_ext_pillar':
  271. mock_ext_pillar_func})):
  272. pillar = salt.pillar.Pillar(opts, {}, 'mocked-minion', 'dev',
  273. extra_minion_data={'fake_key': 'foo'})
  274. # ext pillar function doesn't have the extra_minion_data arg
  275. with patch('salt.utils.args.get_function_argspec',
  276. MagicMock(return_value=MagicMock(args=[]))):
  277. pillar._external_pillar_data('fake_pillar', 'bar',
  278. 'fake_ext_pillar')
  279. mock_ext_pillar_func.assert_called_once_with(
  280. 'mocked-minion', 'fake_pillar', 'bar')
  281. # ext pillar function has the extra_minion_data arg
  282. mock_ext_pillar_func.reset_mock()
  283. with patch('salt.utils.args.get_function_argspec',
  284. MagicMock(return_value=MagicMock(args=['extra_minion_data']))):
  285. pillar._external_pillar_data('fake_pillar', 'bar',
  286. 'fake_ext_pillar')
  287. mock_ext_pillar_func.assert_called_once_with(
  288. 'mocked-minion', 'fake_pillar', 'bar',
  289. extra_minion_data={'fake_key': 'foo'})
  290. def test_dynamic_pillarenv(self):
  291. opts = {
  292. 'optimization_order': [0, 1, 2],
  293. 'renderer': 'json',
  294. 'renderer_blacklist': [],
  295. 'renderer_whitelist': [],
  296. 'state_top': '',
  297. 'pillar_roots': {'__env__': ['/srv/pillar/__env__'], 'base': ['/srv/pillar/base']},
  298. 'file_roots': {'base': ['/srv/salt/base'], 'dev': ['/svr/salt/dev']},
  299. 'extension_modules': '',
  300. }
  301. pillar = salt.pillar.Pillar(opts, {}, 'mocked-minion', 'base', pillarenv='dev')
  302. self.assertEqual(pillar.opts['pillar_roots'],
  303. {'base': ['/srv/pillar/base'], 'dev': ['/srv/pillar/__env__']})
  304. def test_ignored_dynamic_pillarenv(self):
  305. opts = {
  306. 'optimization_order': [0, 1, 2],
  307. 'renderer': 'json',
  308. 'renderer_blacklist': [],
  309. 'renderer_whitelist': [],
  310. 'state_top': '',
  311. 'pillar_roots': {'__env__': ['/srv/pillar/__env__'], 'base': ['/srv/pillar/base']},
  312. 'file_roots': {'base': ['/srv/salt/base'], 'dev': ['/svr/salt/dev']},
  313. 'extension_modules': '',
  314. }
  315. pillar = salt.pillar.Pillar(opts, {}, 'mocked-minion', 'base', pillarenv='base')
  316. self.assertEqual(pillar.opts['pillar_roots'], {'base': ['/srv/pillar/base']})
  317. def test_malformed_pillar_sls(self):
  318. with patch('salt.pillar.compile_template') as compile_template:
  319. opts = {
  320. 'optimization_order': [0, 1, 2],
  321. 'renderer': 'json',
  322. 'renderer_blacklist': [],
  323. 'renderer_whitelist': [],
  324. 'state_top': '',
  325. 'pillar_roots': [],
  326. 'file_roots': [],
  327. 'extension_modules': ''
  328. }
  329. grains = {
  330. 'os': 'Ubuntu',
  331. 'os_family': 'Debian',
  332. 'oscodename': 'raring',
  333. 'osfullname': 'Ubuntu',
  334. 'osrelease': '13.04',
  335. 'kernel': 'Linux'
  336. }
  337. pillar = salt.pillar.Pillar(opts, grains, 'mocked-minion', 'base')
  338. # Mock getting the proper template files
  339. pillar.client.get_state = MagicMock(
  340. return_value={
  341. 'dest': '/path/to/pillar/files/foo.sls',
  342. 'source': 'salt://foo.sls'
  343. }
  344. )
  345. # Template compilation returned a string
  346. compile_template.return_value = 'BAHHH'
  347. self.assertEqual(
  348. pillar.render_pillar({'base': ['foo.sls']}),
  349. ({}, ['SLS \'foo.sls\' does not render to a dictionary'])
  350. )
  351. # Template compilation returned a list
  352. compile_template.return_value = ['BAHHH']
  353. self.assertEqual(
  354. pillar.render_pillar({'base': ['foo.sls']}),
  355. ({}, ['SLS \'foo.sls\' does not render to a dictionary'])
  356. )
  357. # Template compilation returned a dictionary, which is what's expected
  358. compile_template.return_value = {'foo': 'bar'}
  359. self.assertEqual(
  360. pillar.render_pillar({'base': ['foo.sls']}),
  361. ({'foo': 'bar'}, [])
  362. )
  363. # Test improper includes
  364. compile_template.side_effect = [
  365. {'foo': 'bar', 'include': 'blah'},
  366. {'foo2': 'bar2'}
  367. ]
  368. self.assertEqual(
  369. pillar.render_pillar({'base': ['foo.sls']}),
  370. ({'foo': 'bar', 'include': 'blah'},
  371. ["Include Declaration in SLS 'foo.sls' is not formed as a list"])
  372. )
  373. # Test includes as a list, which is what's expected
  374. compile_template.side_effect = [
  375. {'foo': 'bar', 'include': ['blah']},
  376. {'foo2': 'bar2'}
  377. ]
  378. self.assertEqual(
  379. pillar.render_pillar({'base': ['foo.sls']}),
  380. ({'foo': 'bar', 'foo2': 'bar2'}, [])
  381. )
  382. # Test includes as a list overriding data
  383. compile_template.side_effect = [
  384. {'foo': 'bar', 'include': ['blah']},
  385. {'foo': 'bar2'}
  386. ]
  387. self.assertEqual(
  388. pillar.render_pillar({'base': ['foo.sls']}),
  389. ({'foo': 'bar'}, [])
  390. )
  391. # Test includes using empty key directive
  392. compile_template.side_effect = [
  393. {'foo': 'bar', 'include': [{'blah': {'key': ''}}]},
  394. {'foo': 'bar2'}
  395. ]
  396. self.assertEqual(
  397. pillar.render_pillar({'base': ['foo.sls']}),
  398. ({'foo': 'bar'}, [])
  399. )
  400. # Test includes using simple non-nested key
  401. compile_template.side_effect = [
  402. {'foo': 'bar', 'include': [{'blah': {'key': 'nested'}}]},
  403. {'foo': 'bar2'}
  404. ]
  405. self.assertEqual(
  406. pillar.render_pillar({'base': ['foo.sls']}),
  407. ({'foo': 'bar', 'nested': {'foo': 'bar2'}}, [])
  408. )
  409. # Test includes using nested key
  410. compile_template.side_effect = [
  411. {'foo': 'bar', 'include': [{'blah': {'key': 'nested:level'}}]},
  412. {'foo': 'bar2'}
  413. ]
  414. self.assertEqual(
  415. pillar.render_pillar({'base': ['foo.sls']}),
  416. ({'foo': 'bar', 'nested': {'level': {'foo': 'bar2'}}}, [])
  417. )
  418. def test_includes_override_sls(self):
  419. opts = {
  420. 'optimization_order': [0, 1, 2],
  421. 'renderer': 'json',
  422. 'renderer_blacklist': [],
  423. 'renderer_whitelist': [],
  424. 'state_top': '',
  425. 'pillar_roots': {},
  426. 'file_roots': {},
  427. 'extension_modules': ''
  428. }
  429. grains = {
  430. 'os': 'Ubuntu',
  431. 'os_family': 'Debian',
  432. 'oscodename': 'raring',
  433. 'osfullname': 'Ubuntu',
  434. 'osrelease': '13.04',
  435. 'kernel': 'Linux'
  436. }
  437. with patch('salt.pillar.compile_template') as compile_template:
  438. # Test with option set to True
  439. opts['pillar_includes_override_sls'] = True
  440. pillar = salt.pillar.Pillar(opts, grains, 'mocked-minion', 'base')
  441. # Mock getting the proper template files
  442. pillar.client.get_state = MagicMock(
  443. return_value={
  444. 'dest': '/path/to/pillar/files/foo.sls',
  445. 'source': 'salt://foo.sls'
  446. }
  447. )
  448. compile_template.side_effect = [
  449. {'foo': 'bar', 'include': ['blah']},
  450. {'foo': 'bar2'}
  451. ]
  452. self.assertEqual(
  453. pillar.render_pillar({'base': ['foo.sls']}),
  454. ({'foo': 'bar2'}, [])
  455. )
  456. # Test with option set to False
  457. opts['pillar_includes_override_sls'] = False
  458. pillar = salt.pillar.Pillar(opts, grains, 'mocked-minion', 'base')
  459. # Mock getting the proper template files
  460. pillar.client.get_state = MagicMock(
  461. return_value={
  462. 'dest': '/path/to/pillar/files/foo.sls',
  463. 'source': 'salt://foo.sls'
  464. }
  465. )
  466. compile_template.side_effect = [
  467. {'foo': 'bar', 'include': ['blah']},
  468. {'foo': 'bar2'}
  469. ]
  470. self.assertEqual(
  471. pillar.render_pillar({'base': ['foo.sls']}),
  472. ({'foo': 'bar'}, [])
  473. )
  474. def test_topfile_order(self):
  475. with patch('salt.pillar.salt.fileclient.get_file_client', autospec=True) as get_file_client, \
  476. patch('salt.pillar.salt.minion.Matcher') as Matcher: # autospec=True disabled due to py3 mock bug
  477. opts = {
  478. 'optimization_order': [0, 1, 2],
  479. 'renderer': 'yaml',
  480. 'renderer_blacklist': [],
  481. 'renderer_whitelist': [],
  482. 'state_top': '',
  483. 'pillar_roots': [],
  484. 'extension_modules': '',
  485. 'saltenv': 'base',
  486. 'file_roots': [],
  487. }
  488. grains = {
  489. 'os': 'Ubuntu',
  490. 'os_family': 'Debian',
  491. 'oscodename': 'raring',
  492. 'osfullname': 'Ubuntu',
  493. 'osrelease': '13.04',
  494. 'kernel': 'Linux'
  495. }
  496. # glob match takes precedence
  497. self._setup_test_topfile_mocks(Matcher, get_file_client, 1, 2)
  498. pillar = salt.pillar.Pillar(opts, grains, 'mocked-minion', 'base')
  499. self.assertEqual(pillar.compile_pillar()['ssh'], 'bar')
  500. # nodegroup match takes precedence
  501. self._setup_test_topfile_mocks(Matcher, get_file_client, 2, 1)
  502. pillar = salt.pillar.Pillar(opts, grains, 'mocked-minion', 'base')
  503. self.assertEqual(pillar.compile_pillar()['ssh'], 'foo')
  504. def _setup_test_topfile_mocks(self, Matcher, get_file_client,
  505. nodegroup_order, glob_order):
  506. # Write a simple topfile and two pillar state files
  507. self.top_file = tempfile.NamedTemporaryFile(dir=TMP, delete=False)
  508. s = '''
  509. base:
  510. group:
  511. - match: nodegroup
  512. - order: {nodegroup_order}
  513. - ssh
  514. - generic
  515. '*':
  516. - generic
  517. minion:
  518. - order: {glob_order}
  519. - ssh.minion
  520. - generic.minion
  521. '''.format(nodegroup_order=nodegroup_order, glob_order=glob_order)
  522. self.top_file.write(salt.utils.stringutils.to_bytes(s))
  523. self.top_file.flush()
  524. self.ssh_file = tempfile.NamedTemporaryFile(dir=TMP, delete=False)
  525. self.ssh_file.write(b'''
  526. ssh:
  527. foo
  528. ''')
  529. self.ssh_file.flush()
  530. self.ssh_minion_file = tempfile.NamedTemporaryFile(dir=TMP, delete=False)
  531. self.ssh_minion_file.write(b'''
  532. ssh:
  533. bar
  534. ''')
  535. self.ssh_minion_file.flush()
  536. self.generic_file = tempfile.NamedTemporaryFile(dir=TMP, delete=False)
  537. self.generic_file.write(b'''
  538. generic:
  539. key1:
  540. - value1
  541. - value2
  542. key2:
  543. sub_key1: []
  544. ''')
  545. self.generic_file.flush()
  546. self.generic_minion_file = tempfile.NamedTemporaryFile(dir=TMP, delete=False)
  547. self.generic_minion_file.write(b'''
  548. generic:
  549. key1:
  550. - value3
  551. key2:
  552. sub_key2: []
  553. ''')
  554. self.generic_minion_file.flush()
  555. # Setup Matcher mock
  556. matcher = Matcher.return_value
  557. matcher.confirm_top.return_value = True
  558. # Setup fileclient mock
  559. client = get_file_client.return_value
  560. client.cache_file.return_value = self.top_file.name
  561. def get_state(sls, env):
  562. return {
  563. 'ssh': {'path': '', 'dest': self.ssh_file.name},
  564. 'ssh.minion': {'path': '', 'dest': self.ssh_minion_file.name},
  565. 'generic': {'path': '', 'dest': self.generic_file.name},
  566. 'generic.minion': {'path': '', 'dest': self.generic_minion_file.name},
  567. }[sls]
  568. client.get_state.side_effect = get_state
  569. def _setup_test_include_mocks(self, Matcher, get_file_client):
  570. self.top_file = top_file = tempfile.NamedTemporaryFile(dir=TMP, delete=False)
  571. top_file.write(b'''
  572. base:
  573. '*':
  574. - order: 1
  575. - test.sub2
  576. minion:
  577. - order: 2
  578. - test
  579. ''')
  580. top_file.flush()
  581. self.init_sls = init_sls = tempfile.NamedTemporaryFile(dir=TMP, delete=False)
  582. init_sls.write(b'''
  583. include:
  584. - test.sub1
  585. - test.sub2
  586. ''')
  587. init_sls.flush()
  588. self.sub1_sls = sub1_sls = tempfile.NamedTemporaryFile(dir=TMP, delete=False)
  589. sub1_sls.write(b'''
  590. p1:
  591. - value1_1
  592. - value1_2
  593. ''')
  594. sub1_sls.flush()
  595. self.sub2_sls = sub2_sls = tempfile.NamedTemporaryFile(dir=TMP, delete=False)
  596. sub2_sls.write(b'''
  597. p1:
  598. - value1_3
  599. p2:
  600. - value2_1
  601. - value2_2
  602. ''')
  603. sub2_sls.flush()
  604. # Setup Matcher mock
  605. matcher = Matcher.return_value
  606. matcher.confirm_top.return_value = True
  607. # Setup fileclient mock
  608. client = get_file_client.return_value
  609. client.cache_file.return_value = self.top_file.name
  610. def get_state(sls, env):
  611. return {
  612. 'test': {'path': '', 'dest': init_sls.name},
  613. 'test.sub1': {'path': '', 'dest': sub1_sls.name},
  614. 'test.sub2': {'path': '', 'dest': sub2_sls.name},
  615. }[sls]
  616. client.get_state.side_effect = get_state
  617. @skipIf(NO_MOCK, NO_MOCK_REASON)
  618. @patch('salt.transport.Channel.factory', MagicMock())
  619. class RemotePillarTestCase(TestCase):
  620. '''
  621. Tests for instantiating a RemotePillar in salt.pillar
  622. '''
  623. def setUp(self):
  624. self.grains = {}
  625. def tearDown(self):
  626. for attr in ('grains',):
  627. try:
  628. delattr(self, attr)
  629. except AttributeError:
  630. continue
  631. def test_get_opts_in_pillar_override_call(self):
  632. mock_get_extra_minion_data = MagicMock(return_value={})
  633. with patch(
  634. 'salt.pillar.RemotePillarMixin.get_ext_pillar_extra_minion_data',
  635. mock_get_extra_minion_data):
  636. salt.pillar.RemotePillar({}, self.grains, 'mocked-minion', 'dev')
  637. mock_get_extra_minion_data.assert_called_once_with(
  638. {'saltenv': 'dev'})
  639. def test_multiple_keys_in_opts_added_to_pillar(self):
  640. opts = {
  641. 'renderer': 'json',
  642. 'path_to_add': 'fake_data',
  643. 'path_to_add2': {'fake_data2': ['fake_data3', 'fake_data4']},
  644. 'pass_to_ext_pillars': ['path_to_add', 'path_to_add2']
  645. }
  646. pillar = salt.pillar.RemotePillar(opts, self.grains,
  647. 'mocked-minion', 'dev')
  648. self.assertEqual(pillar.extra_minion_data,
  649. {'path_to_add': 'fake_data',
  650. 'path_to_add2': {'fake_data2': ['fake_data3',
  651. 'fake_data4']}})
  652. def test_subkey_in_opts_added_to_pillar(self):
  653. opts = {
  654. 'renderer': 'json',
  655. 'path_to_add': 'fake_data',
  656. 'path_to_add2': {'fake_data5': 'fake_data6',
  657. 'fake_data2': ['fake_data3', 'fake_data4']},
  658. 'pass_to_ext_pillars': ['path_to_add2:fake_data5']
  659. }
  660. pillar = salt.pillar.RemotePillar(opts, self.grains,
  661. 'mocked-minion', 'dev')
  662. self.assertEqual(pillar.extra_minion_data,
  663. {'path_to_add2': {'fake_data5': 'fake_data6'}})
  664. def test_non_existent_leaf_opt_in_add_to_pillar(self):
  665. opts = {
  666. 'renderer': 'json',
  667. 'path_to_add': 'fake_data',
  668. 'path_to_add2': {'fake_data5': 'fake_data6',
  669. 'fake_data2': ['fake_data3', 'fake_data4']},
  670. 'pass_to_ext_pillars': ['path_to_add2:fake_data_non_exist']
  671. }
  672. pillar = salt.pillar.RemotePillar(opts, self.grains,
  673. 'mocked-minion', 'dev')
  674. self.assertEqual(pillar.pillar_override, {})
  675. def test_non_existent_intermediate_opt_in_add_to_pillar(self):
  676. opts = {
  677. 'renderer': 'json',
  678. 'path_to_add': 'fake_data',
  679. 'path_to_add2': {'fake_data5': 'fake_data6',
  680. 'fake_data2': ['fake_data3', 'fake_data4']},
  681. 'pass_to_ext_pillars': ['path_to_add_no_exist']
  682. }
  683. pillar = salt.pillar.RemotePillar(opts, self.grains,
  684. 'mocked-minion', 'dev')
  685. self.assertEqual(pillar.pillar_override, {})
  686. def test_malformed_add_to_pillar(self):
  687. opts = {
  688. 'renderer': 'json',
  689. 'path_to_add': 'fake_data',
  690. 'path_to_add2': {'fake_data5': 'fake_data6',
  691. 'fake_data2': ['fake_data3', 'fake_data4']},
  692. 'pass_to_ext_pillars': MagicMock()
  693. }
  694. with self.assertRaises(salt.exceptions.SaltClientError) as excinfo:
  695. salt.pillar.RemotePillar(opts, self.grains, 'mocked-minion', 'dev')
  696. self.assertEqual(excinfo.exception.strerror,
  697. '\'pass_to_ext_pillars\' config is malformed.')
  698. def test_pillar_send_extra_minion_data_from_config(self):
  699. opts = {
  700. 'renderer': 'json',
  701. 'pillarenv': 'fake_pillar_env',
  702. 'path_to_add': 'fake_data',
  703. 'path_to_add2': {'fake_data5': 'fake_data6',
  704. 'fake_data2': ['fake_data3', 'fake_data4']},
  705. 'pass_to_ext_pillars': ['path_to_add']}
  706. mock_channel = MagicMock(
  707. crypted_transfer_decode_dictentry=MagicMock(return_value={}))
  708. with patch('salt.transport.Channel.factory',
  709. MagicMock(return_value=mock_channel)):
  710. pillar = salt.pillar.RemotePillar(opts, self.grains,
  711. 'mocked_minion', 'fake_env')
  712. ret = pillar.compile_pillar()
  713. self.assertEqual(pillar.channel, mock_channel)
  714. mock_channel.crypted_transfer_decode_dictentry.assert_called_once_with(
  715. {'cmd': '_pillar', 'ver': '2',
  716. 'id': 'mocked_minion',
  717. 'grains': {},
  718. 'saltenv': 'fake_env',
  719. 'pillarenv': 'fake_pillar_env',
  720. 'pillar_override': {},
  721. 'extra_minion_data': {'path_to_add': 'fake_data'}},
  722. dictkey='pillar')
  723. @skipIf(NO_MOCK, NO_MOCK_REASON)
  724. @patch('salt.transport.client.AsyncReqChannel.factory', MagicMock())
  725. class AsyncRemotePillarTestCase(TestCase):
  726. '''
  727. Tests for instantiating a AsyncRemotePillar in salt.pillar
  728. '''
  729. def setUp(self):
  730. self.grains = {}
  731. def tearDown(self):
  732. for attr in ('grains',):
  733. try:
  734. delattr(self, attr)
  735. except AttributeError:
  736. continue
  737. def test_get_opts_in_pillar_override_call(self):
  738. mock_get_extra_minion_data = MagicMock(return_value={})
  739. with patch(
  740. 'salt.pillar.RemotePillarMixin.get_ext_pillar_extra_minion_data',
  741. mock_get_extra_minion_data):
  742. salt.pillar.RemotePillar({}, self.grains, 'mocked-minion', 'dev')
  743. mock_get_extra_minion_data.assert_called_once_with(
  744. {'saltenv': 'dev'})
  745. def test_pillar_send_extra_minion_data_from_config(self):
  746. opts = {
  747. 'renderer': 'json',
  748. 'pillarenv': 'fake_pillar_env',
  749. 'path_to_add': 'fake_data',
  750. 'path_to_add2': {'fake_data5': 'fake_data6',
  751. 'fake_data2': ['fake_data3', 'fake_data4']},
  752. 'pass_to_ext_pillars': ['path_to_add']}
  753. mock_channel = MagicMock(
  754. crypted_transfer_decode_dictentry=MagicMock(return_value={}))
  755. with patch('salt.transport.client.AsyncReqChannel.factory',
  756. MagicMock(return_value=mock_channel)):
  757. pillar = salt.pillar.RemotePillar(opts, self.grains,
  758. 'mocked_minion', 'fake_env')
  759. ret = pillar.compile_pillar()
  760. mock_channel.crypted_transfer_decode_dictentry.assert_called_once_with(
  761. {'cmd': '_pillar', 'ver': '2',
  762. 'id': 'mocked_minion',
  763. 'grains': {},
  764. 'saltenv': 'fake_env',
  765. 'pillarenv': 'fake_pillar_env',
  766. 'pillar_override': {},
  767. 'extra_minion_data': {'path_to_add': 'fake_data'}},
  768. dictkey='pillar')