test_pillar.py 38 KB

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