1
0

test_pillar.py 38 KB

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