test_pillar.py 43 KB

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