test_state.py 60 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667
  1. # -*- coding: utf-8 -*-
  2. """
  3. :codeauthor: Rahul Handay <rahulha@saltstack.com>
  4. """
  5. from __future__ import absolute_import, print_function, unicode_literals
  6. import copy
  7. import os
  8. import shutil
  9. import tempfile
  10. import textwrap
  11. import time
  12. import salt.config
  13. import salt.loader
  14. import salt.modules.config as config
  15. import salt.modules.state as state
  16. import salt.state
  17. import salt.utils.args
  18. import salt.utils.files
  19. import salt.utils.hashutils
  20. import salt.utils.json
  21. import salt.utils.odict
  22. import salt.utils.platform
  23. import salt.utils.state
  24. from salt.exceptions import CommandExecutionError, SaltInvocationError
  25. from salt.ext import six
  26. from tests.support.helpers import slowTest
  27. from tests.support.mixins import LoaderModuleMockMixin
  28. from tests.support.mock import MagicMock, Mock, mock_open, patch
  29. from tests.support.runtests import RUNTIME_VARS
  30. from tests.support.unit import TestCase
  31. class MockState(object):
  32. """
  33. Mock class
  34. """
  35. def __init__(self):
  36. pass
  37. class State(object):
  38. """
  39. Mock state class
  40. """
  41. flag = None
  42. def __init__(
  43. self, opts, pillar_override=False, pillar_enc=None, initial_pillar=None
  44. ):
  45. pass
  46. def verify_data(self, data):
  47. """
  48. Mock verify_data method
  49. """
  50. if self.flag:
  51. return True
  52. else:
  53. return False
  54. @staticmethod
  55. def call(data):
  56. """
  57. Mock call method
  58. """
  59. return list
  60. @staticmethod
  61. def call_high(data, orchestration_jid=None):
  62. """
  63. Mock call_high method
  64. """
  65. return True
  66. @staticmethod
  67. def call_template_str(data):
  68. """
  69. Mock call_template_str method
  70. """
  71. return True
  72. @staticmethod
  73. def _mod_init(data):
  74. """
  75. Mock _mod_init method
  76. """
  77. return True
  78. def verify_high(self, data):
  79. """
  80. Mock verify_high method
  81. """
  82. if self.flag:
  83. return True
  84. else:
  85. return -1
  86. @staticmethod
  87. def compile_high_data(data):
  88. """
  89. Mock compile_high_data
  90. """
  91. return [{"__id__": "ABC"}]
  92. @staticmethod
  93. def call_chunk(data, data1, data2):
  94. """
  95. Mock call_chunk method
  96. """
  97. return {"": "ABC"}
  98. @staticmethod
  99. def call_chunks(data):
  100. """
  101. Mock call_chunks method
  102. """
  103. return True
  104. @staticmethod
  105. def call_listen(data, ret):
  106. """
  107. Mock call_listen method
  108. """
  109. return True
  110. def requisite_in(self, data): # pylint: disable=unused-argument
  111. return data, []
  112. class HighState(object):
  113. """
  114. Mock HighState class
  115. """
  116. flag = False
  117. opts = {"state_top": "", "pillar": {}}
  118. def __init__(self, opts, pillar_override=None, *args, **kwargs):
  119. self.building_highstate = salt.utils.odict.OrderedDict
  120. self.state = MockState.State(opts, pillar_override=pillar_override)
  121. def render_state(self, sls, saltenv, mods, matches, local=False):
  122. """
  123. Mock render_state method
  124. """
  125. if self.flag:
  126. return {}, True
  127. else:
  128. return {}, False
  129. @staticmethod
  130. def get_top():
  131. """
  132. Mock get_top method
  133. """
  134. return "_top"
  135. def verify_tops(self, data):
  136. """
  137. Mock verify_tops method
  138. """
  139. if self.flag:
  140. return ["a", "b"]
  141. else:
  142. return []
  143. @staticmethod
  144. def top_matches(data):
  145. """
  146. Mock top_matches method
  147. """
  148. return ["a", "b", "c"]
  149. @staticmethod
  150. def push_active():
  151. """
  152. Mock push_active method
  153. """
  154. return True
  155. @staticmethod
  156. def compile_highstate():
  157. """
  158. Mock compile_highstate method
  159. """
  160. return "A"
  161. @staticmethod
  162. def compile_state_usage():
  163. """
  164. Mock compile_state_usage method
  165. """
  166. return "A"
  167. @staticmethod
  168. def pop_active():
  169. """
  170. Mock pop_active method
  171. """
  172. return True
  173. @staticmethod
  174. def compile_low_chunks():
  175. """
  176. Mock compile_low_chunks method
  177. """
  178. return [{"__id__": "ABC", "__sls__": "abc"}]
  179. def render_highstate(self, data):
  180. """
  181. Mock render_highstate method
  182. """
  183. if self.flag:
  184. return ["a", "b"], True
  185. else:
  186. return ["a", "b"], False
  187. @staticmethod
  188. def call_highstate(
  189. exclude,
  190. cache,
  191. cache_name,
  192. force=None,
  193. whitelist=None,
  194. orchestration_jid=None,
  195. ):
  196. """
  197. Mock call_highstate method
  198. """
  199. return True
  200. class MockSerial(object):
  201. """
  202. Mock Class
  203. """
  204. def __init__(self):
  205. pass
  206. class Serial(object):
  207. """
  208. Mock Serial class
  209. """
  210. def __init__(self, data):
  211. pass
  212. @staticmethod
  213. def load(data):
  214. """
  215. Mock load method
  216. """
  217. return {"A": "B"}
  218. @staticmethod
  219. def dump(data, data1):
  220. """
  221. Mock dump method
  222. """
  223. return True
  224. class MockTarFile(object):
  225. """
  226. Mock tarfile class
  227. """
  228. path = os.sep + "tmp"
  229. def __init__(self):
  230. pass
  231. @staticmethod
  232. def open(data, data1):
  233. """
  234. Mock open method
  235. """
  236. return MockTarFile
  237. @staticmethod
  238. def getmembers():
  239. """
  240. Mock getmembers method
  241. """
  242. return [MockTarFile]
  243. @staticmethod
  244. def extractall(data):
  245. """
  246. Mock extractall method
  247. """
  248. return True
  249. @staticmethod
  250. def close():
  251. """
  252. Mock close method
  253. """
  254. return True
  255. class StateTestCase(TestCase, LoaderModuleMockMixin):
  256. """
  257. Test case for salt.modules.state
  258. """
  259. def setup_loader_modules(self):
  260. utils = salt.loader.utils(
  261. salt.config.DEFAULT_MINION_OPTS.copy(),
  262. whitelist=["state", "args", "systemd", "path", "platform"],
  263. )
  264. utils.keys()
  265. patcher = patch("salt.modules.state.salt.state", MockState())
  266. patcher.start()
  267. self.addCleanup(patcher.stop)
  268. return {
  269. state: {
  270. "__opts__": {"cachedir": "/D", "saltenv": None, "__cli": "salt"},
  271. "__utils__": utils,
  272. "__salt__": {
  273. "config.get": config.get,
  274. "config.option": MagicMock(return_value=""),
  275. },
  276. },
  277. config: {"__opts__": {}, "__pillar__": {}},
  278. }
  279. def test_running(self):
  280. """
  281. Test of checking i fthe state function is already running
  282. """
  283. self.assertEqual(state.running(True), [])
  284. mock = MagicMock(
  285. side_effect=[
  286. [
  287. {
  288. "fun": "state.running",
  289. "pid": "4126",
  290. "jid": "20150325123407204096",
  291. }
  292. ],
  293. [],
  294. ]
  295. )
  296. with patch.dict(state.__salt__, {"saltutil.is_running": mock}):
  297. self.assertListEqual(
  298. state.running(),
  299. [
  300. 'The function "state.running"'
  301. " is running as PID 4126 and "
  302. "was started at 2015, Mar 25 12:34:07."
  303. "204096 with jid 20150325123407204096"
  304. ],
  305. )
  306. self.assertListEqual(state.running(), [])
  307. def test_low(self):
  308. """
  309. Test of executing a single low data call
  310. """
  311. mock = MagicMock(side_effect=[False, None, None])
  312. with patch.object(state, "_check_queue", mock):
  313. self.assertFalse(
  314. state.low({"state": "pkg", "fun": "installed", "name": "vi"})
  315. )
  316. MockState.State.flag = False
  317. self.assertEqual(
  318. state.low({"state": "pkg", "fun": "installed", "name": "vi"}), list
  319. )
  320. MockState.State.flag = True
  321. self.assertTrue(
  322. state.low({"state": "pkg", "fun": "installed", "name": "vi"})
  323. )
  324. def test_high(self):
  325. """
  326. Test for checking the state system
  327. """
  328. mock = MagicMock(side_effect=[False, None])
  329. with patch.object(state, "_check_queue", mock):
  330. self.assertFalse(state.high({"vim": {"pkg": ["installed"]}}))
  331. mock = MagicMock(return_value={"test": True})
  332. with patch.object(salt.utils.state, "get_sls_opts", mock):
  333. self.assertTrue(state.high({"vim": {"pkg": ["installed"]}}))
  334. def test_template(self):
  335. """
  336. Test of executing the information
  337. stored in a template file on the minion
  338. """
  339. mock = MagicMock(side_effect=[False, None, None])
  340. with patch.object(state, "_check_queue", mock):
  341. self.assertFalse(state.template("/home/salt/salt.sls"))
  342. MockState.HighState.flag = True
  343. self.assertTrue(state.template("/home/salt/salt.sls"))
  344. MockState.HighState.flag = False
  345. self.assertTrue(state.template("/home/salt/salt.sls"))
  346. def test_template_str(self):
  347. """
  348. Test for Executing the information
  349. stored in a string from an sls template
  350. """
  351. mock = MagicMock(side_effect=[False, None])
  352. with patch.object(state, "_check_queue", mock):
  353. self.assertFalse(state.template_str("Template String"))
  354. self.assertTrue(state.template_str("Template String"))
  355. def test_apply_(self):
  356. """
  357. Test to apply states
  358. """
  359. mock = MagicMock(return_value=True)
  360. with patch.object(state, "sls", mock):
  361. self.assertTrue(state.apply_(True))
  362. with patch.object(state, "highstate", mock):
  363. self.assertTrue(state.apply_(None))
  364. def test_test(self):
  365. """
  366. Test to apply states in test mode
  367. """
  368. with patch.dict(state.__opts__, {"test": False}):
  369. mock = MagicMock(return_value=True)
  370. with patch.object(state, "sls", mock):
  371. self.assertTrue(state.test(True))
  372. mock.assert_called_once_with(True, test=True)
  373. self.assertEqual(state.__opts__["test"], False)
  374. mock = MagicMock(return_value=True)
  375. with patch.object(state, "highstate", mock):
  376. self.assertTrue(state.test(None))
  377. mock.assert_called_once_with(test=True)
  378. self.assertEqual(state.__opts__["test"], False)
  379. def test_list_disabled(self):
  380. """
  381. Test to list disabled states
  382. """
  383. mock = MagicMock(return_value=["A", "B", "C"])
  384. with patch.dict(state.__salt__, {"grains.get": mock}):
  385. self.assertListEqual(state.list_disabled(), ["A", "B", "C"])
  386. def test_enable(self):
  387. """
  388. Test to Enable state function or sls run
  389. """
  390. mock = MagicMock(return_value=["A", "B"])
  391. with patch.dict(state.__salt__, {"grains.get": mock}):
  392. mock = MagicMock(return_value=[])
  393. with patch.dict(state.__salt__, {"grains.setval": mock}):
  394. mock = MagicMock(return_value=[])
  395. with patch.dict(state.__salt__, {"saltutil.refresh_modules": mock}):
  396. self.assertDictEqual(
  397. state.enable("A"),
  398. {"msg": "Info: A state enabled.", "res": True},
  399. )
  400. self.assertDictEqual(
  401. state.enable("Z"),
  402. {"msg": "Info: Z state already " "enabled.", "res": True},
  403. )
  404. def test_disable(self):
  405. """
  406. Test to disable state run
  407. """
  408. mock = MagicMock(return_value=["C", "D"])
  409. with patch.dict(state.__salt__, {"grains.get": mock}):
  410. mock = MagicMock(return_value=[])
  411. with patch.dict(state.__salt__, {"grains.setval": mock}):
  412. mock = MagicMock(return_value=[])
  413. with patch.dict(state.__salt__, {"saltutil.refresh_modules": mock}):
  414. self.assertDictEqual(
  415. state.disable("C"),
  416. {"msg": "Info: C state " "already disabled.", "res": True},
  417. )
  418. self.assertDictEqual(
  419. state.disable("Z"),
  420. {"msg": "Info: Z state " "disabled.", "res": True},
  421. )
  422. def test_clear_cache(self):
  423. """
  424. Test to clear out cached state file
  425. """
  426. mock = MagicMock(return_value=["A.cache.p", "B.cache.p", "C"])
  427. with patch.object(os, "listdir", mock):
  428. mock = MagicMock(return_value=True)
  429. with patch.object(os.path, "isfile", mock):
  430. mock = MagicMock(return_value=True)
  431. with patch.object(os, "remove", mock):
  432. self.assertEqual(state.clear_cache(), ["A.cache.p", "B.cache.p"])
  433. def test_single(self):
  434. """
  435. Test to execute single state function
  436. """
  437. ret = {"pkg_|-name=vim_|-name=vim_|-installed": list}
  438. mock = MagicMock(side_effect=["A", None, None, None, None])
  439. with patch.object(state, "_check_queue", mock):
  440. self.assertEqual(state.single("pkg.installed", " name=vim"), "A")
  441. self.assertEqual(state.single("pk", "name=vim"), "Invalid function passed")
  442. with patch.dict(state.__opts__, {"test": "install"}):
  443. mock = MagicMock(return_value={"test": ""})
  444. with patch.object(salt.utils.state, "get_sls_opts", mock):
  445. mock = MagicMock(return_value=True)
  446. with patch.object(salt.utils.args, "test_mode", mock):
  447. self.assertRaises(
  448. SaltInvocationError,
  449. state.single,
  450. "pkg.installed",
  451. "name=vim",
  452. pillar="A",
  453. )
  454. MockState.State.flag = True
  455. self.assertTrue(state.single("pkg.installed", "name=vim"))
  456. MockState.State.flag = False
  457. self.assertDictEqual(
  458. state.single("pkg.installed", "name=vim"), ret
  459. )
  460. def test_show_top(self):
  461. """
  462. Test to return the top data that the minion will use for a highstate
  463. """
  464. mock = MagicMock(side_effect=["A", None, None])
  465. with patch.object(state, "_check_queue", mock):
  466. self.assertEqual(state.show_top(), "A")
  467. MockState.HighState.flag = True
  468. self.assertListEqual(state.show_top(), ["a", "b"])
  469. MockState.HighState.flag = False
  470. self.assertListEqual(state.show_top(), ["a", "b", "c"])
  471. def test_run_request(self):
  472. """
  473. Test to Execute the pending state request
  474. """
  475. mock = MagicMock(
  476. side_effect=[{}, {"name": "A"}, {"name": {"mods": "A", "kwargs": {}}}]
  477. )
  478. with patch.object(state, "check_request", mock):
  479. self.assertDictEqual(state.run_request("A"), {})
  480. self.assertDictEqual(state.run_request("A"), {})
  481. mock = MagicMock(return_value=["True"])
  482. with patch.object(state, "apply_", mock):
  483. mock = MagicMock(return_value="")
  484. with patch.object(os, "remove", mock):
  485. self.assertListEqual(state.run_request("name"), ["True"])
  486. def test_show_highstate(self):
  487. """
  488. Test to retrieve the highstate data from the salt master
  489. """
  490. mock = MagicMock(side_effect=["A", None, None])
  491. with patch.object(state, "_check_queue", mock):
  492. self.assertEqual(state.show_highstate(), "A")
  493. self.assertRaises(SaltInvocationError, state.show_highstate, pillar="A")
  494. self.assertEqual(state.show_highstate(), "A")
  495. def test_show_lowstate(self):
  496. """
  497. Test to list out the low data that will be applied to this minion
  498. """
  499. mock = MagicMock(side_effect=["A", None])
  500. with patch.object(state, "_check_queue", mock):
  501. self.assertRaises(AssertionError, state.show_lowstate)
  502. self.assertTrue(state.show_lowstate())
  503. def test_show_state_usage(self):
  504. """
  505. Test to list out the state usage that will be applied to this minion
  506. """
  507. mock = MagicMock(side_effect=["A", None, None])
  508. with patch.object(state, "_check_queue", mock):
  509. self.assertEqual(state.show_state_usage(), "A")
  510. self.assertRaises(SaltInvocationError, state.show_state_usage, pillar="A")
  511. self.assertEqual(state.show_state_usage(), "A")
  512. def test_show_states(self):
  513. """
  514. Test to display the low data from a specific sls
  515. """
  516. mock = MagicMock(side_effect=["A", None])
  517. with patch.object(state, "_check_queue", mock):
  518. self.assertEqual(state.show_low_sls("foo"), "A")
  519. self.assertListEqual(state.show_states("foo"), ["abc"])
  520. def test_show_states_missing_sls(self):
  521. """
  522. Test state.show_states when a sls file defined
  523. in a top.sls file is missing
  524. """
  525. msg = ["No matching sls found for 'cloud' in evn 'base'"]
  526. chunks_mock = MagicMock(side_effect=[msg])
  527. mock = MagicMock(side_effect=["A", None])
  528. with patch.object(state, "_check_queue", mock), patch(
  529. "salt.state.HighState.compile_low_chunks", chunks_mock
  530. ):
  531. self.assertEqual(state.show_low_sls("foo"), "A")
  532. self.assertListEqual(state.show_states("foo"), [msg[0]])
  533. def test_sls_id(self):
  534. """
  535. Test to call a single ID from the
  536. named module(s) and handle all requisites
  537. """
  538. mock = MagicMock(side_effect=["A", None, None, None])
  539. with patch.object(state, "_check_queue", mock):
  540. self.assertEqual(state.sls_id("apache", "http"), "A")
  541. with patch.dict(state.__opts__, {"test": "A"}):
  542. mock = MagicMock(return_value={"test": True, "saltenv": None})
  543. with patch.object(salt.utils.state, "get_sls_opts", mock):
  544. mock = MagicMock(return_value=True)
  545. with patch.object(salt.utils.args, "test_mode", mock):
  546. MockState.State.flag = True
  547. MockState.HighState.flag = True
  548. self.assertEqual(state.sls_id("apache", "http"), 2)
  549. MockState.State.flag = False
  550. self.assertDictEqual(state.sls_id("ABC", "http"), {"": "ABC"})
  551. self.assertRaises(
  552. SaltInvocationError, state.sls_id, "DEF", "http"
  553. )
  554. def test_show_low_sls(self):
  555. """
  556. Test to display the low data from a specific sls
  557. """
  558. mock = MagicMock(side_effect=["A", None, None])
  559. with patch.object(state, "_check_queue", mock):
  560. self.assertEqual(state.show_low_sls("foo"), "A")
  561. with patch.dict(state.__opts__, {"test": "A"}):
  562. mock = MagicMock(return_value={"test": True, "saltenv": None})
  563. with patch.object(salt.utils.state, "get_sls_opts", mock):
  564. MockState.State.flag = True
  565. MockState.HighState.flag = True
  566. self.assertEqual(state.show_low_sls("foo"), 2)
  567. MockState.State.flag = False
  568. self.assertListEqual(state.show_low_sls("foo"), [{"__id__": "ABC"}])
  569. def test_show_sls(self):
  570. """
  571. Test to display the state data from a specific sls
  572. """
  573. mock = MagicMock(side_effect=["A", None, None, None])
  574. with patch.object(state, "_check_queue", mock):
  575. self.assertEqual(state.show_sls("foo"), "A")
  576. with patch.dict(state.__opts__, {"test": "A"}):
  577. mock = MagicMock(return_value={"test": True, "saltenv": None})
  578. with patch.object(salt.utils.state, "get_sls_opts", mock):
  579. mock = MagicMock(return_value=True)
  580. with patch.object(salt.utils.args, "test_mode", mock):
  581. self.assertRaises(
  582. SaltInvocationError, state.show_sls, "foo", pillar="A"
  583. )
  584. MockState.State.flag = True
  585. self.assertEqual(state.show_sls("foo"), 2)
  586. MockState.State.flag = False
  587. self.assertListEqual(state.show_sls("foo"), ["a", "b"])
  588. def test_sls_exists(self):
  589. """
  590. Test of sls_exists
  591. """
  592. test_state = {}
  593. test_missing_state = []
  594. mock = MagicMock(return_value=test_state)
  595. with patch.object(state, "show_sls", mock):
  596. self.assertTrue(state.sls_exists("state_name"))
  597. mock = MagicMock(return_value=test_missing_state)
  598. with patch.object(state, "show_sls", mock):
  599. self.assertFalse(state.sls_exists("missing_state"))
  600. def test_id_exists(self):
  601. """
  602. Test of id_exists
  603. """
  604. test_state = [
  605. {
  606. "key1": "value1",
  607. "name": "value1",
  608. "state": "file",
  609. "fun": "test",
  610. "__env__": "base",
  611. "__sls__": "test-sls",
  612. "order": 10000,
  613. "__id__": "state_id1",
  614. },
  615. {
  616. "key2": "value2",
  617. "name": "value2",
  618. "state": "file",
  619. "fun": "directory",
  620. "__env__": "base",
  621. "__sls__": "test-sls",
  622. "order": 10001,
  623. "__id__": "state_id2",
  624. },
  625. ]
  626. mock = MagicMock(return_value=test_state)
  627. with patch.object(state, "show_low_sls", mock):
  628. self.assertTrue(state.id_exists("state_id1,state_id2", "test-sls"))
  629. self.assertFalse(state.id_exists("invalid", "state_name"))
  630. def test_top(self):
  631. """
  632. Test to execute a specific top file
  633. """
  634. ret = ["Pillar failed to render with the following messages:", "E"]
  635. mock = MagicMock(side_effect=["A", None, None, None])
  636. with patch.object(state, "_check_queue", mock):
  637. self.assertEqual(state.top("reverse_top.sls"), "A")
  638. mock = MagicMock(side_effect=[["E"], None, None])
  639. with patch.object(state, "_get_pillar_errors", mock):
  640. with patch.dict(state.__pillar__, {"_errors": ["E"]}):
  641. self.assertListEqual(state.top("reverse_top.sls"), ret)
  642. with patch.dict(state.__opts__, {"test": "A"}):
  643. mock = MagicMock(return_value={"test": True})
  644. with patch.object(salt.utils.state, "get_sls_opts", mock):
  645. mock = MagicMock(return_value=True)
  646. with patch.object(salt.utils.args, "test_mode", mock):
  647. self.assertRaises(
  648. SaltInvocationError,
  649. state.top,
  650. "reverse_top.sls",
  651. pillar="A",
  652. )
  653. mock = MagicMock(return_value="salt://reverse_top.sls")
  654. with patch.object(os.path, "join", mock):
  655. mock = MagicMock(return_value=True)
  656. with patch.object(state, "_set_retcode", mock):
  657. self.assertTrue(
  658. state.top(
  659. "reverse_top.sls " "exclude=exclude.sls"
  660. )
  661. )
  662. def test_highstate(self):
  663. """
  664. Test to retrieve the state data from the
  665. salt master for the minion and execute it
  666. """
  667. arg = "whitelist=sls1.sls"
  668. mock = MagicMock(side_effect=[True, False, False, False])
  669. with patch.object(state, "_disabled", mock):
  670. self.assertDictEqual(
  671. state.highstate("whitelist=sls1.sls"),
  672. {
  673. "comment": "Disabled",
  674. "name": "Salt highstate run is disabled. "
  675. "To re-enable, run state.enable highstate",
  676. "result": "False",
  677. },
  678. )
  679. mock = MagicMock(side_effect=["A", None, None])
  680. with patch.object(state, "_check_queue", mock):
  681. self.assertEqual(state.highstate("whitelist=sls1.sls"), "A")
  682. with patch.dict(state.__opts__, {"test": "A"}):
  683. mock = MagicMock(return_value={"test": True})
  684. with patch.object(salt.utils.state, "get_sls_opts", mock):
  685. self.assertRaises(
  686. SaltInvocationError,
  687. state.highstate,
  688. "whitelist=sls1.sls",
  689. pillar="A",
  690. )
  691. mock = MagicMock(return_value="A")
  692. with patch.object(state, "_filter_running", mock):
  693. mock = MagicMock(return_value=True)
  694. with patch.object(state, "_filter_running", mock):
  695. mock = MagicMock(return_value=True)
  696. with patch.object(salt.payload, "Serial", mock):
  697. with patch.object(os.path, "join", mock):
  698. with patch.object(
  699. state, "_set" "_retcode", mock
  700. ):
  701. self.assertTrue(state.highstate(arg))
  702. def test_clear_request(self):
  703. """
  704. Test to clear out the state execution request without executing it
  705. """
  706. mock = MagicMock(return_value=True)
  707. with patch.object(salt.payload, "Serial", mock):
  708. mock = MagicMock(side_effect=[False, True, True])
  709. with patch.object(os.path, "isfile", mock):
  710. self.assertTrue(state.clear_request("A"))
  711. mock = MagicMock(return_value=True)
  712. with patch.object(os, "remove", mock):
  713. self.assertTrue(state.clear_request())
  714. mock = MagicMock(return_value={})
  715. with patch.object(state, "check_request", mock):
  716. self.assertFalse(state.clear_request("A"))
  717. def test_check_request(self):
  718. """
  719. Test to return the state request information
  720. """
  721. with patch("salt.modules.state.salt.payload", MockSerial):
  722. mock = MagicMock(side_effect=[True, True, False])
  723. with patch.object(os.path, "isfile", mock):
  724. with patch("salt.utils.files.fopen", mock_open()):
  725. self.assertDictEqual(state.check_request(), {"A": "B"})
  726. with patch("salt.utils.files.fopen", mock_open()):
  727. self.assertEqual(state.check_request("A"), "B")
  728. self.assertDictEqual(state.check_request(), {})
  729. def test_request(self):
  730. """
  731. Test to request the local admin execute a state run
  732. """
  733. mock = MagicMock(return_value=True)
  734. with patch.object(state, "apply_", mock):
  735. mock = MagicMock(return_value=True)
  736. with patch.object(os.path, "join", mock):
  737. mock = MagicMock(
  738. return_value={"test_run": "", "mods": "", "kwargs": ""}
  739. )
  740. with patch.object(state, "check_request", mock):
  741. mock = MagicMock(return_value=True)
  742. with patch.object(os, "umask", mock):
  743. with patch.object(salt.utils.platform, "is_windows", mock):
  744. with patch.dict(state.__salt__, {"cmd.run": mock}):
  745. with patch("salt.utils.files.fopen", mock_open()):
  746. mock = MagicMock(return_value=True)
  747. with patch.object(os, "umask", mock):
  748. self.assertTrue(state.request("A"))
  749. def test_sls(self):
  750. """
  751. Test to execute a set list of state files from an environment
  752. """
  753. arg = "core,edit.vim dev"
  754. ret = ["Pillar failed to render with the following messages:", "E", "1"]
  755. mock = MagicMock(return_value=True)
  756. with patch.object(state, "running", mock):
  757. with patch.dict(state.__context__, {"retcode": 1}):
  758. self.assertEqual(state.sls("core,edit.vim dev"), True)
  759. mock = MagicMock(side_effect=[True, True, True, True, True, True])
  760. with patch.object(state, "_wait", mock):
  761. mock = MagicMock(side_effect=[["A"], [], [], [], [], []])
  762. with patch.object(state, "_disabled", mock):
  763. with patch.dict(state.__context__, {"retcode": 1}):
  764. self.assertEqual(
  765. state.sls("core,edit.vim dev", None, None, True), ["A"]
  766. )
  767. mock = MagicMock(side_effect=[["E", "1"], None, None, None, None])
  768. with patch.object(state, "_get_pillar_errors", mock):
  769. with patch.dict(state.__context__, {"retcode": 5}):
  770. with patch.dict(state.__pillar__, {"_errors": ["E", "1"]}):
  771. self.assertListEqual(
  772. state.sls("core,edit.vim dev", None, None, True), ret
  773. )
  774. with patch.dict(state.__opts__, {"test": None}):
  775. mock = MagicMock(return_value={"test": "", "saltenv": None})
  776. with patch.object(salt.utils.state, "get_sls_opts", mock):
  777. mock = MagicMock(return_value=True)
  778. with patch.object(salt.utils.args, "test_mode", mock):
  779. self.assertRaises(
  780. SaltInvocationError,
  781. state.sls,
  782. "core,edit.vim dev",
  783. None,
  784. None,
  785. True,
  786. pillar="A",
  787. )
  788. mock = MagicMock(return_value="/D/cache.cache.p")
  789. with patch.object(os.path, "join", mock):
  790. mock = MagicMock(return_value=True)
  791. with patch.object(os.path, "isfile", mock):
  792. with patch(
  793. "salt.utils.files.fopen", mock_open(b"")
  794. ):
  795. self.assertTrue(
  796. state.sls(
  797. arg, None, None, True, cache=True
  798. )
  799. )
  800. MockState.HighState.flag = True
  801. self.assertTrue(
  802. state.sls(
  803. "core,edit" ".vim dev", None, None, True
  804. )
  805. )
  806. MockState.HighState.flag = False
  807. mock = MagicMock(return_value=True)
  808. with patch.object(
  809. state, "_filter_" "running", mock
  810. ):
  811. self.sub_test_sls()
  812. def test_get_test_value(self):
  813. """
  814. Test _get_test_value when opts contains different values
  815. """
  816. test_arg = "test"
  817. with patch.dict(state.__opts__, {test_arg: True}):
  818. self.assertTrue(
  819. state._get_test_value(test=None),
  820. msg="Failure when {0} is True in __opts__".format(test_arg),
  821. )
  822. with patch.dict(config.__pillar__, {test_arg: "blah"}):
  823. self.assertFalse(
  824. state._get_test_value(test=None),
  825. msg="Failure when {0} is blah in __opts__".format(test_arg),
  826. )
  827. with patch.dict(config.__pillar__, {test_arg: "true"}):
  828. self.assertFalse(
  829. state._get_test_value(test=None),
  830. msg="Failure when {0} is true in __opts__".format(test_arg),
  831. )
  832. with patch.dict(config.__opts__, {test_arg: False}):
  833. self.assertFalse(
  834. state._get_test_value(test=None),
  835. msg="Failure when {0} is False in __opts__".format(test_arg),
  836. )
  837. with patch.dict(config.__opts__, {}):
  838. self.assertFalse(
  839. state._get_test_value(test=None),
  840. msg="Failure when {0} does not exist in __opts__".format(test_arg),
  841. )
  842. with patch.dict(config.__pillar__, {test_arg: None}):
  843. self.assertEqual(
  844. state._get_test_value(test=None),
  845. None,
  846. msg="Failure when {0} is None in __opts__".format(test_arg),
  847. )
  848. with patch.dict(config.__pillar__, {test_arg: True}):
  849. self.assertTrue(
  850. state._get_test_value(test=None),
  851. msg="Failure when {0} is True in __pillar__".format(test_arg),
  852. )
  853. with patch.dict(config.__pillar__, {"master": {test_arg: True}}):
  854. self.assertTrue(
  855. state._get_test_value(test=None),
  856. msg="Failure when {0} is True in master __pillar__".format(test_arg),
  857. )
  858. with patch.dict(config.__pillar__, {"master": {test_arg: False}}):
  859. with patch.dict(config.__pillar__, {test_arg: True}):
  860. self.assertTrue(
  861. state._get_test_value(test=None),
  862. msg="Failure when {0} is False in master __pillar__ and True in pillar".format(
  863. test_arg
  864. ),
  865. )
  866. with patch.dict(config.__pillar__, {"master": {test_arg: True}}):
  867. with patch.dict(config.__pillar__, {test_arg: False}):
  868. self.assertFalse(
  869. state._get_test_value(test=None),
  870. msg="Failure when {0} is True in master __pillar__ and False in pillar".format(
  871. test_arg
  872. ),
  873. )
  874. with patch.dict(state.__opts__, {"test": False}):
  875. self.assertFalse(
  876. state._get_test_value(test=None),
  877. msg="Failure when {0} is False in __opts__".format(test_arg),
  878. )
  879. with patch.dict(state.__opts__, {"test": False}):
  880. with patch.dict(config.__pillar__, {"master": {test_arg: True}}):
  881. self.assertTrue(
  882. state._get_test_value(test=None),
  883. msg="Failure when {0} is False in __opts__".format(test_arg),
  884. )
  885. with patch.dict(state.__opts__, {}):
  886. self.assertTrue(
  887. state._get_test_value(test=True), msg="Failure when test is True as arg"
  888. )
  889. def sub_test_sls(self):
  890. """
  891. Sub function of test_sls
  892. """
  893. mock = MagicMock(return_value=True)
  894. with patch.object(os.path, "join", mock):
  895. with patch.object(os, "umask", mock):
  896. mock = MagicMock(return_value=False)
  897. with patch.object(salt.utils.platform, "is_windows", mock):
  898. mock = MagicMock(return_value=True)
  899. with patch.object(os, "umask", mock):
  900. with patch.object(state, "_set_retcode", mock):
  901. with patch.dict(state.__opts__, {"test": True}):
  902. with patch("salt.utils.files.fopen", mock_open()):
  903. self.assertTrue(
  904. state.sls(
  905. "core,edit" ".vim dev", None, None, True
  906. )
  907. )
  908. def test_sls_sync(self):
  909. """
  910. Test test.sls with the sync argument
  911. We're only mocking the sync functions we expect to sync. If any other
  912. sync functions are run then they will raise a KeyError, which we want
  913. as it will tell us that we are syncing things we shouldn't.
  914. """
  915. mock_empty_list = MagicMock(return_value=[])
  916. with patch.object(state, "running", mock_empty_list), patch.object(
  917. state, "_disabled", mock_empty_list
  918. ), patch.object(state, "_get_pillar_errors", mock_empty_list):
  919. sync_mocks = {
  920. "saltutil.sync_modules": Mock(),
  921. "saltutil.sync_states": Mock(),
  922. }
  923. with patch.dict(state.__salt__, sync_mocks):
  924. state.sls("foo", sync_mods="modules,states")
  925. for key in sync_mocks:
  926. call_count = sync_mocks[key].call_count
  927. expected = 1
  928. assert (
  929. call_count == expected
  930. ), "{0} called {1} time(s) (expected: {2})".format(
  931. key, call_count, expected
  932. )
  933. # Test syncing all
  934. sync_mocks = {"saltutil.sync_all": Mock()}
  935. with patch.dict(state.__salt__, sync_mocks):
  936. state.sls("foo", sync_mods="all")
  937. for key in sync_mocks:
  938. call_count = sync_mocks[key].call_count
  939. expected = 1
  940. assert (
  941. call_count == expected
  942. ), "{0} called {1} time(s) (expected: {2})".format(
  943. key, call_count, expected
  944. )
  945. # sync_mods=True should be interpreted as sync_mods=all
  946. sync_mocks = {"saltutil.sync_all": Mock()}
  947. with patch.dict(state.__salt__, sync_mocks):
  948. state.sls("foo", sync_mods=True)
  949. for key in sync_mocks:
  950. call_count = sync_mocks[key].call_count
  951. expected = 1
  952. assert (
  953. call_count == expected
  954. ), "{0} called {1} time(s) (expected: {2})".format(
  955. key, call_count, expected
  956. )
  957. # Test syncing all when "all" is passed along with module types.
  958. # This tests that we *only* run a sync_all and avoid unnecessary
  959. # extra syncing.
  960. sync_mocks = {"saltutil.sync_all": Mock()}
  961. with patch.dict(state.__salt__, sync_mocks):
  962. state.sls("foo", sync_mods="modules,all")
  963. for key in sync_mocks:
  964. call_count = sync_mocks[key].call_count
  965. expected = 1
  966. assert (
  967. call_count == expected
  968. ), "{0} called {1} time(s) (expected: {2})".format(
  969. key, call_count, expected
  970. )
  971. def test_pkg(self):
  972. """
  973. Test to execute a packaged state run
  974. """
  975. tar_file = os.sep + os.path.join("tmp", "state_pkg.tgz")
  976. mock = MagicMock(
  977. side_effect=[
  978. False,
  979. True,
  980. True,
  981. True,
  982. True,
  983. True,
  984. True,
  985. True,
  986. True,
  987. True,
  988. True,
  989. ]
  990. )
  991. mock_json_loads_true = MagicMock(return_value=[True])
  992. mock_json_loads_dictlist = MagicMock(return_value=[{"test": ""}])
  993. with patch.object(os.path, "isfile", mock), patch(
  994. "salt.modules.state.tarfile", MockTarFile
  995. ), patch.object(salt.utils, "json", mock_json_loads_dictlist):
  996. self.assertEqual(state.pkg(tar_file, "", "md5"), {})
  997. mock = MagicMock(side_effect=[False, 0, 0, 0, 0])
  998. with patch.object(salt.utils.hashutils, "get_hash", mock):
  999. # Verify hash
  1000. self.assertDictEqual(state.pkg(tar_file, "", "md5"), {})
  1001. # Verify file outside intended root
  1002. self.assertDictEqual(state.pkg(tar_file, 0, "md5"), {})
  1003. MockTarFile.path = ""
  1004. with patch("salt.utils.files.fopen", mock_open()), patch.object(
  1005. salt.utils.json, "loads", mock_json_loads_true
  1006. ), patch.object(state, "_format_cached_grains", MagicMock()):
  1007. self.assertEqual(state.pkg(tar_file, 0, "md5"), True)
  1008. state._format_cached_grains.assert_called_once()
  1009. MockTarFile.path = ""
  1010. if six.PY2:
  1011. with patch("salt.utils.files.fopen", mock_open()), patch.dict(
  1012. state.__utils__,
  1013. {"state.check_result": MagicMock(return_value=True)},
  1014. ):
  1015. self.assertTrue(state.pkg(tar_file, 0, "md5"))
  1016. else:
  1017. with patch("salt.utils.files.fopen", mock_open()):
  1018. self.assertTrue(state.pkg(tar_file, 0, "md5"))
  1019. def test_lock_saltenv(self):
  1020. """
  1021. Tests lock_saltenv in each function which accepts saltenv on the CLI
  1022. """
  1023. lock_msg = "lock_saltenv is enabled, saltenv cannot be changed"
  1024. empty_list_mock = MagicMock(return_value=[])
  1025. with patch.dict(state.__opts__, {"lock_saltenv": True}), patch.dict(
  1026. state.__salt__, {"grains.get": empty_list_mock}
  1027. ), patch.object(state, "running", empty_list_mock):
  1028. # Test high
  1029. with self.assertRaisesRegex(CommandExecutionError, lock_msg):
  1030. state.high([{"vim": {"pkg": ["installed"]}}], saltenv="base")
  1031. # Test template
  1032. with self.assertRaisesRegex(CommandExecutionError, lock_msg):
  1033. state.template("foo", saltenv="base")
  1034. # Test template_str
  1035. with self.assertRaisesRegex(CommandExecutionError, lock_msg):
  1036. state.template_str("foo", saltenv="base")
  1037. # Test apply_ with SLS
  1038. with self.assertRaisesRegex(CommandExecutionError, lock_msg):
  1039. state.apply_("foo", saltenv="base")
  1040. # Test apply_ with Highstate
  1041. with self.assertRaisesRegex(CommandExecutionError, lock_msg):
  1042. state.apply_(saltenv="base")
  1043. # Test "test" with SLS
  1044. with self.assertRaisesRegex(CommandExecutionError, lock_msg):
  1045. state.test("foo", saltenv="base")
  1046. # Test "test" with Highstate
  1047. with self.assertRaisesRegex(CommandExecutionError, lock_msg):
  1048. state.test(saltenv="base")
  1049. # Test highstate
  1050. with self.assertRaisesRegex(CommandExecutionError, lock_msg):
  1051. state.highstate(saltenv="base")
  1052. # Test sls
  1053. with self.assertRaisesRegex(CommandExecutionError, lock_msg):
  1054. state.sls("foo", saltenv="base")
  1055. # Test top
  1056. with self.assertRaisesRegex(CommandExecutionError, lock_msg):
  1057. state.top("foo.sls", saltenv="base")
  1058. # Test show_highstate
  1059. with self.assertRaisesRegex(CommandExecutionError, lock_msg):
  1060. state.show_highstate(saltenv="base")
  1061. # Test show_lowstate
  1062. with self.assertRaisesRegex(CommandExecutionError, lock_msg):
  1063. state.show_lowstate(saltenv="base")
  1064. # Test sls_id
  1065. with self.assertRaisesRegex(CommandExecutionError, lock_msg):
  1066. state.sls_id("foo", "bar", saltenv="base")
  1067. # Test show_low_sls
  1068. with self.assertRaisesRegex(CommandExecutionError, lock_msg):
  1069. state.show_low_sls("foo", saltenv="base")
  1070. # Test show_sls
  1071. with self.assertRaisesRegex(CommandExecutionError, lock_msg):
  1072. state.show_sls("foo", saltenv="base")
  1073. # Test show_top
  1074. with self.assertRaisesRegex(CommandExecutionError, lock_msg):
  1075. state.show_top(saltenv="base")
  1076. # Test single
  1077. with self.assertRaisesRegex(CommandExecutionError, lock_msg):
  1078. state.single("foo.bar", name="baz", saltenv="base")
  1079. # Test pkg
  1080. with self.assertRaisesRegex(CommandExecutionError, lock_msg):
  1081. state.pkg(
  1082. "/tmp/salt_state.tgz",
  1083. "760a9353810e36f6d81416366fc426dc",
  1084. "md5",
  1085. saltenv="base",
  1086. )
  1087. def test_get_pillar_errors_CC(self):
  1088. """
  1089. Test _get_pillar_errors function.
  1090. CC: External clean, Internal clean
  1091. :return:
  1092. """
  1093. for int_pillar, ext_pillar in [
  1094. ({"foo": "bar"}, {"fred": "baz"}),
  1095. ({"foo": "bar"}, None),
  1096. ({}, {"fred": "baz"}),
  1097. ]:
  1098. with patch("salt.modules.state.__pillar__", int_pillar):
  1099. for opts, res in [
  1100. ({"force": True}, None),
  1101. ({"force": False}, None),
  1102. ({}, None),
  1103. ]:
  1104. assert res == state._get_pillar_errors(
  1105. kwargs=opts, pillar=ext_pillar
  1106. )
  1107. def test_get_pillar_errors_EC(self):
  1108. """
  1109. Test _get_pillar_errors function.
  1110. EC: External erroneous, Internal clean
  1111. :return:
  1112. """
  1113. errors = ["failure", "everywhere"]
  1114. for int_pillar, ext_pillar in [
  1115. ({"foo": "bar"}, {"fred": "baz", "_errors": errors}),
  1116. ({}, {"fred": "baz", "_errors": errors}),
  1117. ]:
  1118. with patch("salt.modules.state.__pillar__", int_pillar):
  1119. for opts, res in [
  1120. ({"force": True}, None),
  1121. ({"force": False}, errors),
  1122. ({}, errors),
  1123. ]:
  1124. assert res == state._get_pillar_errors(
  1125. kwargs=opts, pillar=ext_pillar
  1126. )
  1127. def test_get_pillar_errors_EE(self):
  1128. """
  1129. Test _get_pillar_errors function.
  1130. CC: External erroneous, Internal erroneous
  1131. :return:
  1132. """
  1133. errors = ["failure", "everywhere"]
  1134. for int_pillar, ext_pillar in [
  1135. ({"foo": "bar", "_errors": errors}, {"fred": "baz", "_errors": errors})
  1136. ]:
  1137. with patch("salt.modules.state.__pillar__", int_pillar):
  1138. for opts, res in [
  1139. ({"force": True}, None),
  1140. ({"force": False}, errors),
  1141. ({}, errors),
  1142. ]:
  1143. assert res == state._get_pillar_errors(
  1144. kwargs=opts, pillar=ext_pillar
  1145. )
  1146. def test_get_pillar_errors_CE(self):
  1147. """
  1148. Test _get_pillar_errors function.
  1149. CC: External clean, Internal erroneous
  1150. :return:
  1151. """
  1152. errors = ["failure", "everywhere"]
  1153. for int_pillar, ext_pillar in [
  1154. ({"foo": "bar", "_errors": errors}, {"fred": "baz"}),
  1155. ({"foo": "bar", "_errors": errors}, None),
  1156. ]:
  1157. with patch("salt.modules.state.__pillar__", int_pillar):
  1158. for opts, res in [
  1159. ({"force": True}, None),
  1160. ({"force": False}, errors),
  1161. ({}, errors),
  1162. ]:
  1163. assert res == state._get_pillar_errors(
  1164. kwargs=opts, pillar=ext_pillar
  1165. )
  1166. class TopFileMergingCase(TestCase, LoaderModuleMockMixin):
  1167. def setup_loader_modules(self):
  1168. return {
  1169. state: {
  1170. "__opts__": salt.config.minion_config(
  1171. os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "minion")
  1172. ),
  1173. "__salt__": {"saltutil.is_running": MagicMock(return_value=[])},
  1174. },
  1175. }
  1176. def setUp(self):
  1177. self.cachedir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
  1178. self.fileserver_root = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
  1179. self.addCleanup(shutil.rmtree, self.cachedir, ignore_errors=True)
  1180. self.addCleanup(shutil.rmtree, self.fileserver_root, ignore_errors=True)
  1181. self.saltenvs = ["base", "foo", "bar", "baz"]
  1182. self.saltenv_roots = {
  1183. x: os.path.join(self.fileserver_root, x)
  1184. for x in ("base", "foo", "bar", "baz")
  1185. }
  1186. self.base_top_file = os.path.join(self.saltenv_roots["base"], "top.sls")
  1187. self.dunder_opts = salt.utils.yaml.safe_load(
  1188. textwrap.dedent(
  1189. """\
  1190. file_client: local
  1191. default_top: base
  1192. file_roots:
  1193. base:
  1194. - {base}
  1195. foo:
  1196. - {foo}
  1197. bar:
  1198. - {bar}
  1199. baz:
  1200. - {baz}
  1201. """.format(
  1202. **self.saltenv_roots
  1203. )
  1204. )
  1205. )
  1206. self.dunder_opts["env_order"] = self.saltenvs
  1207. # Write top files for all but the "baz" environment
  1208. for saltenv in self.saltenv_roots:
  1209. os.makedirs(self.saltenv_roots[saltenv])
  1210. if saltenv == "baz":
  1211. continue
  1212. top_file = os.path.join(self.saltenv_roots[saltenv], "top.sls")
  1213. with salt.utils.files.fopen(top_file, "w") as fp_:
  1214. # Add a section for every environment to each top file, with
  1215. # the SLS target prefixed with the current saltenv.
  1216. for env_name in self.saltenvs:
  1217. fp_.write(
  1218. textwrap.dedent(
  1219. """\
  1220. {env_name}:
  1221. '*':
  1222. - {saltenv}_{env_name}
  1223. """.format(
  1224. env_name=env_name, saltenv=saltenv
  1225. )
  1226. )
  1227. )
  1228. def tearDown(self):
  1229. time.sleep(1)
  1230. os.remove(self.base_top_file)
  1231. def show_top(self, **kwargs):
  1232. local_opts = copy.deepcopy(self.dunder_opts)
  1233. local_opts.update(kwargs)
  1234. with patch.dict(state.__opts__, local_opts), patch.object(
  1235. salt.state.State, "_gather_pillar", MagicMock(return_value={})
  1236. ):
  1237. ret = state.show_top()
  1238. # Lazy way of converting ordered dicts to regular dicts. We don't
  1239. # care about dict ordering for these tests.
  1240. return salt.utils.json.loads(salt.utils.json.dumps(ret))
  1241. def use_limited_base_top_file(self):
  1242. """
  1243. Overwrites the base top file so that it only contains sections for its
  1244. own saltenv.
  1245. """
  1246. with salt.utils.files.fopen(self.base_top_file, "w") as fp_:
  1247. fp_.write(
  1248. textwrap.dedent(
  1249. """\
  1250. base:
  1251. '*':
  1252. - base_base
  1253. """
  1254. )
  1255. )
  1256. time.sleep(1)
  1257. @slowTest
  1258. def test_merge_strategy_merge(self):
  1259. """
  1260. Base overrides everything
  1261. """
  1262. ret = self.show_top(top_file_merging_strategy="merge")
  1263. assert ret == {
  1264. "base": ["base_base"],
  1265. "foo": ["base_foo"],
  1266. "bar": ["base_bar"],
  1267. "baz": ["base_baz"],
  1268. }, ret
  1269. @slowTest
  1270. def test_merge_strategy_merge_limited_base(self):
  1271. """
  1272. Test with a "base" top file containing only a "base" section. The "baz"
  1273. saltenv should not be in the return data because that env doesn't have
  1274. its own top file and there will be no "baz" section in the "base" env's
  1275. top file.
  1276. Next, append a "baz" section to the rewritten top file and we should
  1277. get results for that saltenv in the return data.
  1278. """
  1279. self.use_limited_base_top_file()
  1280. ret = self.show_top(top_file_merging_strategy="merge")
  1281. assert ret == {
  1282. "base": ["base_base"],
  1283. "foo": ["foo_foo"],
  1284. "bar": ["bar_bar"],
  1285. }, ret
  1286. # Add a "baz" section
  1287. with salt.utils.files.fopen(self.base_top_file, "a") as fp_:
  1288. fp_.write(
  1289. textwrap.dedent(
  1290. """\
  1291. baz:
  1292. '*':
  1293. - base_baz
  1294. """
  1295. )
  1296. )
  1297. ret = self.show_top(top_file_merging_strategy="merge")
  1298. assert ret == {
  1299. "base": ["base_base"],
  1300. "foo": ["foo_foo"],
  1301. "bar": ["bar_bar"],
  1302. "baz": ["base_baz"],
  1303. }, ret
  1304. @slowTest
  1305. def test_merge_strategy_merge_state_top_saltenv_base(self):
  1306. """
  1307. This tests with state_top_saltenv=base, which should pull states *only*
  1308. from the base saltenv.
  1309. """
  1310. ret = self.show_top(top_file_merging_strategy="merge", state_top_saltenv="base")
  1311. assert ret == {
  1312. "base": ["base_base"],
  1313. "foo": ["base_foo"],
  1314. "bar": ["base_bar"],
  1315. "baz": ["base_baz"],
  1316. }, ret
  1317. @slowTest
  1318. def test_merge_strategy_merge_state_top_saltenv_foo(self):
  1319. """
  1320. This tests with state_top_saltenv=foo, which should pull states *only*
  1321. from the foo saltenv. Since that top file is only authoritative for
  1322. its own saltenv, *only* the foo saltenv's matches from the foo top file
  1323. should be in the return data.
  1324. """
  1325. ret = self.show_top(top_file_merging_strategy="merge", state_top_saltenv="foo")
  1326. assert ret == {"foo": ["foo_foo"]}, ret
  1327. @slowTest
  1328. def test_merge_strategy_merge_all(self):
  1329. """
  1330. Include everything in every top file
  1331. """
  1332. ret = self.show_top(top_file_merging_strategy="merge_all")
  1333. assert ret == {
  1334. "base": ["base_base", "foo_base", "bar_base"],
  1335. "foo": ["base_foo", "foo_foo", "bar_foo"],
  1336. "bar": ["base_bar", "foo_bar", "bar_bar"],
  1337. "baz": ["base_baz", "foo_baz", "bar_baz"],
  1338. }, ret
  1339. @slowTest
  1340. def test_merge_strategy_merge_all_alternate_env_order(self):
  1341. """
  1342. Use an alternate env_order. This should change the order in which the
  1343. SLS targets appear in the result.
  1344. """
  1345. ret = self.show_top(
  1346. top_file_merging_strategy="merge_all", env_order=["bar", "foo", "base"]
  1347. )
  1348. assert ret == {
  1349. "base": ["bar_base", "foo_base", "base_base"],
  1350. "foo": ["bar_foo", "foo_foo", "base_foo"],
  1351. "bar": ["bar_bar", "foo_bar", "base_bar"],
  1352. "baz": ["bar_baz", "foo_baz", "base_baz"],
  1353. }, ret
  1354. @slowTest
  1355. def test_merge_strategy_merge_all_state_top_saltenv_base(self):
  1356. """
  1357. This tests with state_top_saltenv=base, which should pull states *only*
  1358. from the base saltenv. Since we are using the "merge_all" strategy, all
  1359. the states from that top file should be in the return data.
  1360. """
  1361. ret = self.show_top(
  1362. top_file_merging_strategy="merge_all", state_top_saltenv="base"
  1363. )
  1364. assert ret == {
  1365. "base": ["base_base"],
  1366. "foo": ["base_foo"],
  1367. "bar": ["base_bar"],
  1368. "baz": ["base_baz"],
  1369. }, ret
  1370. @slowTest
  1371. def test_merge_strategy_merge_all_state_top_saltenv_foo(self):
  1372. """
  1373. This tests with state_top_saltenv=foo, which should pull states *only*
  1374. from the foo saltenv. Since we are using the "merge_all" strategy, all
  1375. the states from that top file should be in the return data.
  1376. """
  1377. ret = self.show_top(
  1378. top_file_merging_strategy="merge_all", state_top_saltenv="foo"
  1379. )
  1380. assert ret == {
  1381. "base": ["foo_base"],
  1382. "foo": ["foo_foo"],
  1383. "bar": ["foo_bar"],
  1384. "baz": ["foo_baz"],
  1385. }, ret
  1386. @slowTest
  1387. def test_merge_strategy_same(self):
  1388. """
  1389. Each env should get its SLS targets from its own top file, with the
  1390. "baz" env pulling from "base" since default_top=base and there is no
  1391. top file in the "baz" saltenv.
  1392. """
  1393. ret = self.show_top(top_file_merging_strategy="same")
  1394. assert ret == {
  1395. "base": ["base_base"],
  1396. "foo": ["foo_foo"],
  1397. "bar": ["bar_bar"],
  1398. "baz": ["base_baz"],
  1399. }, ret
  1400. @slowTest
  1401. def test_merge_strategy_same_limited_base(self):
  1402. """
  1403. Each env should get its SLS targets from its own top file, with the
  1404. "baz" env pulling from "base" since default_top=base and there is no
  1405. top file in the "baz" saltenv.
  1406. """
  1407. self.use_limited_base_top_file()
  1408. ret = self.show_top(top_file_merging_strategy="same")
  1409. assert ret == {
  1410. "base": ["base_base"],
  1411. "foo": ["foo_foo"],
  1412. "bar": ["bar_bar"],
  1413. }, ret
  1414. @slowTest
  1415. def test_merge_strategy_same_default_top_foo(self):
  1416. """
  1417. Each env should get its SLS targets from its own top file, with the
  1418. "baz" env pulling from "foo" since default_top=foo and there is no top
  1419. file in the "baz" saltenv.
  1420. """
  1421. ret = self.show_top(top_file_merging_strategy="same", default_top="foo")
  1422. assert ret == {
  1423. "base": ["base_base"],
  1424. "foo": ["foo_foo"],
  1425. "bar": ["bar_bar"],
  1426. "baz": ["foo_baz"],
  1427. }, ret
  1428. @slowTest
  1429. def test_merge_strategy_same_state_top_saltenv_base(self):
  1430. """
  1431. Test the state_top_saltenv parameter to load states exclusively from
  1432. the base saltenv, with the "same" merging strategy. This should
  1433. result in just the base environment's states from the base top file
  1434. being in the merged result.
  1435. """
  1436. ret = self.show_top(top_file_merging_strategy="same", state_top_saltenv="base")
  1437. assert ret == {"base": ["base_base"]}, ret
  1438. @slowTest
  1439. def test_merge_strategy_same_state_top_saltenv_foo(self):
  1440. """
  1441. Test the state_top_saltenv parameter to load states exclusively from
  1442. the foo saltenv, with the "same" merging strategy. This should
  1443. result in just the foo environment's states from the foo top file
  1444. being in the merged result.
  1445. """
  1446. ret = self.show_top(top_file_merging_strategy="same", state_top_saltenv="foo")
  1447. assert ret == {"foo": ["foo_foo"]}, ret
  1448. @slowTest
  1449. def test_merge_strategy_same_state_top_saltenv_baz(self):
  1450. """
  1451. Test the state_top_saltenv parameter to load states exclusively from
  1452. the baz saltenv, with the "same" merging strategy. This should
  1453. result in an empty dictionary since there is no top file in that
  1454. environment.
  1455. """
  1456. ret = self.show_top(top_file_merging_strategy="same", state_top_saltenv="baz")
  1457. assert ret == {}, ret