test_state.py 83 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099
  1. # -*- coding: utf-8 -*-
  2. # Import Python libs
  3. from __future__ import absolute_import, print_function, unicode_literals
  4. import logging
  5. import os
  6. import shutil
  7. import sys
  8. import tempfile
  9. import textwrap
  10. import threading
  11. import time
  12. # Import Salt Testing libs
  13. from tests.support.case import ModuleCase
  14. from tests.support.helpers import with_tempdir, flaky
  15. from tests.support.unit import skipIf
  16. from tests.support.paths import BASE_FILES, TMP, TMP_PILLAR_TREE
  17. from tests.support.mixins import SaltReturnAssertsMixin
  18. # Import Salt libs
  19. import salt.utils.atomicfile
  20. import salt.utils.files
  21. import salt.utils.path
  22. import salt.utils.platform
  23. import salt.utils.stringutils
  24. from salt.modules.virtualenv_mod import KNOWN_BINARY_NAMES
  25. # Import 3rd-party libs
  26. from salt.ext import six
  27. log = logging.getLogger(__name__)
  28. DEFAULT_ENDING = salt.utils.stringutils.to_bytes(os.linesep)
  29. def trim_line_end(line):
  30. '''
  31. Remove CRLF or LF from the end of line.
  32. '''
  33. if line[-2:] == salt.utils.stringutils.to_bytes('\r\n'):
  34. return line[:-2]
  35. elif line[-1:] == salt.utils.stringutils.to_bytes('\n'):
  36. return line[:-1]
  37. raise Exception("Invalid line ending")
  38. def reline(source, dest, force=False, ending=DEFAULT_ENDING):
  39. '''
  40. Normalize the line endings of a file.
  41. '''
  42. fp, tmp = tempfile.mkstemp()
  43. os.close(fp)
  44. with salt.utils.files.fopen(tmp, 'wb') as tmp_fd:
  45. with salt.utils.files.fopen(source, 'rb') as fd:
  46. lines = fd.readlines()
  47. for line in lines:
  48. line_noend = trim_line_end(line)
  49. tmp_fd.write(line_noend + ending)
  50. if os.path.exists(dest) and force:
  51. os.remove(dest)
  52. os.rename(tmp, dest)
  53. class StateModuleTest(ModuleCase, SaltReturnAssertsMixin):
  54. '''
  55. Validate the state module
  56. '''
  57. maxDiff = None
  58. @classmethod
  59. def setUpClass(cls):
  60. def _reline(path, ending=DEFAULT_ENDING):
  61. '''
  62. Normalize the line endings of a file.
  63. '''
  64. with salt.utils.files.fopen(path, 'rb') as fhr:
  65. lines = fhr.read().splitlines()
  66. with salt.utils.atomicfile.atomic_open(path, 'wb') as fhw:
  67. for line in lines:
  68. fhw.write(line + ending)
  69. destpath = os.path.join(BASE_FILES, 'testappend', 'firstif')
  70. _reline(destpath)
  71. destpath = os.path.join(BASE_FILES, 'testappend', 'secondif')
  72. _reline(destpath)
  73. cls.TIMEOUT = 600 if salt.utils.platform.is_windows() else 10
  74. def test_show_highstate(self):
  75. '''
  76. state.show_highstate
  77. '''
  78. high = self.run_function('state.show_highstate')
  79. destpath = os.path.join(TMP, 'testfile')
  80. self.assertTrue(isinstance(high, dict))
  81. self.assertTrue(destpath in high)
  82. self.assertEqual(high[destpath]['__env__'], 'base')
  83. def test_show_lowstate(self):
  84. '''
  85. state.show_lowstate
  86. '''
  87. low = self.run_function('state.show_lowstate')
  88. self.assertTrue(isinstance(low, list))
  89. self.assertTrue(isinstance(low[0], dict))
  90. def test_show_states(self):
  91. '''
  92. state.show_states
  93. '''
  94. states = self.run_function('state.show_states')
  95. self.assertTrue(isinstance(states, list))
  96. self.assertTrue(isinstance(states[0], six.string_types))
  97. states = self.run_function('state.show_states', sorted=False)
  98. self.assertTrue(isinstance(states, list))
  99. self.assertTrue(isinstance(states[0], six.string_types))
  100. def test_catch_recurse(self):
  101. '''
  102. state.show_sls used to catch a recursive ref
  103. '''
  104. err = self.run_function('state.sls', mods='recurse_fail')
  105. self.assertIn('recursive', err[0])
  106. def test_no_recurse(self):
  107. '''
  108. verify that a sls structure is NOT a recursive ref
  109. '''
  110. sls = self.run_function('state.show_sls', mods='recurse_ok')
  111. self.assertIn('snmpd', sls)
  112. def test_no_recurse_two(self):
  113. '''
  114. verify that a sls structure is NOT a recursive ref
  115. '''
  116. sls = self.run_function('state.show_sls', mods='recurse_ok_two')
  117. self.assertIn('/etc/nagios/nrpe.cfg', sls)
  118. def test_running_dictionary_consistency(self):
  119. '''
  120. Test the structure of the running dictionary so we don't change it
  121. without deprecating/documenting the change
  122. '''
  123. running_dict_fields = [
  124. '__id__',
  125. '__run_num__',
  126. '__sls__',
  127. 'changes',
  128. 'comment',
  129. 'duration',
  130. 'name',
  131. 'result',
  132. 'start_time',
  133. ]
  134. sls = self.run_function('state.single',
  135. fun='test.succeed_with_changes',
  136. name='gndn')
  137. for state, ret in sls.items():
  138. for field in running_dict_fields:
  139. self.assertIn(field, ret)
  140. def test_running_dictionary_key_sls(self):
  141. '''
  142. Ensure the __sls__ key is either null or a string
  143. '''
  144. sls1 = self.run_function('state.single',
  145. fun='test.succeed_with_changes',
  146. name='gndn')
  147. sls2 = self.run_function('state.sls', mods='gndn')
  148. for state, ret in sls1.items():
  149. self.assertTrue(isinstance(ret['__sls__'], type(None)))
  150. for state, ret in sls2.items():
  151. self.assertTrue(isinstance(ret['__sls__'], six.string_types))
  152. def _remove_request_cache_file(self):
  153. '''
  154. remove minion state request file
  155. '''
  156. cache_file = os.path.join(self.get_config('minion')['cachedir'], 'req_state.p')
  157. if os.path.exists(cache_file):
  158. os.remove(cache_file)
  159. def test_request(self):
  160. '''
  161. verify sending a state request to the minion(s)
  162. '''
  163. self._remove_request_cache_file()
  164. ret = self.run_function('state.request', mods='modules.state.requested')
  165. result = ret['cmd_|-count_root_dir_contents_|-ls -a / | wc -l_|-run']['result']
  166. self.assertEqual(result, None)
  167. def test_check_request(self):
  168. '''
  169. verify checking a state request sent to the minion(s)
  170. '''
  171. self._remove_request_cache_file()
  172. self.run_function('state.request', mods='modules.state.requested')
  173. ret = self.run_function('state.check_request')
  174. result = ret['default']['test_run']['cmd_|-count_root_dir_contents_|-ls -a / | wc -l_|-run']['result']
  175. self.assertEqual(result, None)
  176. def test_clear_request(self):
  177. '''
  178. verify clearing a state request sent to the minion(s)
  179. '''
  180. self._remove_request_cache_file()
  181. self.run_function('state.request', mods='modules.state.requested')
  182. ret = self.run_function('state.clear_request')
  183. self.assertTrue(ret)
  184. def test_run_request_succeeded(self):
  185. '''
  186. verify running a state request sent to the minion(s)
  187. '''
  188. self._remove_request_cache_file()
  189. if salt.utils.platform.is_windows():
  190. self.run_function('state.request', mods='modules.state.requested_win')
  191. else:
  192. self.run_function('state.request', mods='modules.state.requested')
  193. ret = self.run_function('state.run_request')
  194. if salt.utils.platform.is_windows():
  195. key = 'cmd_|-count_root_dir_contents_|-Get-ChildItem C:\\\\ | Measure-Object | %{$_.Count}_|-run'
  196. else:
  197. key = 'cmd_|-count_root_dir_contents_|-ls -a / | wc -l_|-run'
  198. result = ret[key]['result']
  199. self.assertTrue(result)
  200. def test_run_request_failed_no_request_staged(self):
  201. '''
  202. verify not running a state request sent to the minion(s)
  203. '''
  204. self._remove_request_cache_file()
  205. self.run_function('state.request', mods='modules.state.requested')
  206. self.run_function('state.clear_request')
  207. ret = self.run_function('state.run_request')
  208. self.assertEqual(ret, {})
  209. @with_tempdir()
  210. def test_issue_1896_file_append_source(self, base_dir):
  211. '''
  212. Verify that we can append a file's contents
  213. '''
  214. testfile = os.path.join(base_dir, 'test.append')
  215. ret = self.run_state('file.touch', name=testfile)
  216. self.assertSaltTrueReturn(ret)
  217. ret = self.run_state(
  218. 'file.append',
  219. name=testfile,
  220. source='salt://testappend/firstif')
  221. self.assertSaltTrueReturn(ret)
  222. ret = self.run_state(
  223. 'file.append',
  224. name=testfile,
  225. source='salt://testappend/secondif')
  226. self.assertSaltTrueReturn(ret)
  227. with salt.utils.files.fopen(testfile, 'r') as fp_:
  228. testfile_contents = salt.utils.stringutils.to_unicode(fp_.read())
  229. contents = textwrap.dedent('''\
  230. # set variable identifying the chroot you work in (used in the prompt below)
  231. if [ -z "$debian_chroot" ] && [ -r /etc/debian_chroot ]; then
  232. debian_chroot=$(cat /etc/debian_chroot)
  233. fi
  234. # enable bash completion in interactive shells
  235. if [ -f /etc/bash_completion ] && ! shopt -oq posix; then
  236. . /etc/bash_completion
  237. fi
  238. ''')
  239. if salt.utils.platform.is_windows():
  240. new_contents = contents.splitlines()
  241. contents = os.linesep.join(new_contents)
  242. contents += os.linesep
  243. self.assertMultiLineEqual(contents, testfile_contents)
  244. ret = self.run_state(
  245. 'file.append',
  246. name=testfile,
  247. source='salt://testappend/secondif')
  248. self.assertSaltTrueReturn(ret)
  249. ret = self.run_state(
  250. 'file.append',
  251. name=testfile,
  252. source='salt://testappend/firstif')
  253. self.assertSaltTrueReturn(ret)
  254. with salt.utils.files.fopen(testfile, 'r') as fp_:
  255. testfile_contents = salt.utils.stringutils.to_unicode(fp_.read())
  256. self.assertMultiLineEqual(contents, testfile_contents)
  257. def test_issue_1876_syntax_error(self):
  258. '''
  259. verify that we catch the following syntax error::
  260. /tmp/salttest/issue-1876:
  261. file:
  262. - managed
  263. - source: salt://testfile
  264. file.append:
  265. - text: foo
  266. '''
  267. testfile = os.path.join(TMP, 'issue-1876')
  268. sls = self.run_function('state.sls', mods='issue-1876')
  269. self.assertIn(
  270. 'ID \'{0}\' in SLS \'issue-1876\' contains multiple state '
  271. 'declarations of the same type'.format(testfile),
  272. sls
  273. )
  274. def test_issue_1879_too_simple_contains_check(self):
  275. expected = textwrap.dedent('''\
  276. # set variable identifying the chroot you work in (used in the prompt below)
  277. if [ -z "$debian_chroot" ] && [ -r /etc/debian_chroot ]; then
  278. debian_chroot=$(cat /etc/debian_chroot)
  279. fi
  280. # enable bash completion in interactive shells
  281. if [ -f /etc/bash_completion ] && ! shopt -oq posix; then
  282. . /etc/bash_completion
  283. fi
  284. ''')
  285. if salt.utils.platform.is_windows():
  286. new_contents = expected.splitlines()
  287. expected = os.linesep.join(new_contents)
  288. expected += os.linesep
  289. testfile = os.path.join(TMP, 'issue-1879')
  290. # Delete if exiting
  291. if os.path.isfile(testfile):
  292. os.unlink(testfile)
  293. # Create the file
  294. ret = self.run_function('state.sls', mods='issue-1879', timeout=120)
  295. self.assertSaltTrueReturn(ret)
  296. # The first append
  297. ret = self.run_function(
  298. 'state.sls', mods='issue-1879.step-1', timeout=120
  299. )
  300. self.assertSaltTrueReturn(ret)
  301. # The second append
  302. ret = self.run_function(
  303. 'state.sls', mods='issue-1879.step-2', timeout=120
  304. )
  305. self.assertSaltTrueReturn(ret)
  306. # Does it match?
  307. try:
  308. with salt.utils.files.fopen(testfile, 'r') as fp_:
  309. contents = salt.utils.stringutils.to_unicode(fp_.read())
  310. self.assertMultiLineEqual(expected, contents)
  311. # Make sure we don't re-append existing text
  312. ret = self.run_function(
  313. 'state.sls', mods='issue-1879.step-1', timeout=120
  314. )
  315. self.assertSaltTrueReturn(ret)
  316. ret = self.run_function(
  317. 'state.sls', mods='issue-1879.step-2', timeout=120
  318. )
  319. self.assertSaltTrueReturn(ret)
  320. with salt.utils.files.fopen(testfile, 'r') as fp_:
  321. contents = salt.utils.stringutils.to_unicode(fp_.read())
  322. self.assertMultiLineEqual(expected, contents)
  323. except Exception:
  324. if os.path.exists(testfile):
  325. shutil.copy(testfile, testfile + '.bak')
  326. raise
  327. finally:
  328. if os.path.exists(testfile):
  329. os.unlink(testfile)
  330. def test_include(self):
  331. tempdir = tempfile.mkdtemp(dir=TMP)
  332. self.addCleanup(shutil.rmtree, tempdir, ignore_errors=True)
  333. pillar = {}
  334. for path in ('include-test', 'to-include-test', 'exclude-test'):
  335. pillar[path] = os.path.join(tempdir, path)
  336. ret = self.run_function('state.sls', mods='include-test', pillar=pillar)
  337. self.assertSaltTrueReturn(ret)
  338. self.assertTrue(os.path.isfile(pillar['include-test']))
  339. self.assertTrue(os.path.isfile(pillar['to-include-test']))
  340. self.assertFalse(os.path.isfile(pillar['exclude-test']))
  341. def test_exclude(self):
  342. tempdir = tempfile.mkdtemp(dir=TMP)
  343. self.addCleanup(shutil.rmtree, tempdir, ignore_errors=True)
  344. pillar = {}
  345. for path in ('include-test', 'exclude-test', 'to-include-test'):
  346. pillar[path] = os.path.join(tempdir, path)
  347. ret = self.run_function('state.sls', mods='exclude-test', pillar=pillar)
  348. self.assertSaltTrueReturn(ret)
  349. self.assertTrue(os.path.isfile(pillar['include-test']))
  350. self.assertTrue(os.path.isfile(pillar['exclude-test']))
  351. self.assertFalse(os.path.isfile(pillar['to-include-test']))
  352. @skipIf(salt.utils.path.which_bin(KNOWN_BINARY_NAMES) is None, 'virtualenv not installed')
  353. def test_issue_2068_template_str(self):
  354. venv_dir = os.path.join(
  355. TMP, 'issue-2068-template-str'
  356. )
  357. try:
  358. ret = self.run_function(
  359. 'state.sls', mods='issue-2068-template-str-no-dot',
  360. timeout=120
  361. )
  362. self.assertSaltTrueReturn(ret)
  363. finally:
  364. if os.path.isdir(venv_dir):
  365. shutil.rmtree(venv_dir)
  366. # Let's load the template from the filesystem. If running this state
  367. # with state.sls works, so should using state.template_str
  368. template_path = os.path.join(
  369. os.path.dirname(os.path.dirname(__file__)),
  370. 'files', 'file', 'base', 'issue-2068-template-str-no-dot.sls'
  371. )
  372. with salt.utils.files.fopen(template_path, 'r') as fp_:
  373. template = salt.utils.stringutils.to_unicode(fp_.read())
  374. ret = self.run_function(
  375. 'state.template_str', [template], timeout=120
  376. )
  377. self.assertSaltTrueReturn(ret)
  378. # Now using state.template
  379. ret = self.run_function(
  380. 'state.template', [template_path], timeout=120
  381. )
  382. self.assertSaltTrueReturn(ret)
  383. # Now the problematic #2068 including dot's
  384. ret = self.run_function(
  385. 'state.sls', mods='issue-2068-template-str', timeout=120
  386. )
  387. self.assertSaltTrueReturn(ret)
  388. # Let's load the template from the filesystem. If running this state
  389. # with state.sls works, so should using state.template_str
  390. template_path = os.path.join(
  391. os.path.dirname(os.path.dirname(__file__)),
  392. 'files', 'file', 'base', 'issue-2068-template-str.sls'
  393. )
  394. with salt.utils.files.fopen(template_path, 'r') as fp_:
  395. template = salt.utils.stringutils.to_unicode(fp_.read())
  396. ret = self.run_function(
  397. 'state.template_str', [template], timeout=120
  398. )
  399. self.assertSaltTrueReturn(ret)
  400. # Now using state.template
  401. ret = self.run_function(
  402. 'state.template', [template_path], timeout=120
  403. )
  404. self.assertSaltTrueReturn(ret)
  405. def test_template_invalid_items(self):
  406. TEMPLATE = textwrap.dedent('''\
  407. {0}:
  408. - issue-2068-template-str
  409. /tmp/test-template-invalid-items:
  410. file:
  411. - managed
  412. - source: salt://testfile
  413. ''')
  414. for item in ('include', 'exclude', 'extends'):
  415. ret = self.run_function(
  416. 'state.template_str', [TEMPLATE.format(item)]
  417. )
  418. self.assertTrue(isinstance(ret, list))
  419. self.assertNotEqual(ret, [])
  420. self.assertEqual(
  421. ['The \'{0}\' declaration found on \'<template-str>\' is '
  422. 'invalid when rendering single templates'.format(item)],
  423. ret
  424. )
  425. def test_pydsl(self):
  426. '''
  427. Test the basics of the pydsl
  428. '''
  429. ret = self.run_function('state.sls', mods='pydsl-1')
  430. self.assertSaltTrueReturn(ret)
  431. def test_issues_7905_and_8174_sls_syntax_error(self):
  432. '''
  433. Call sls file with yaml syntax error.
  434. Ensure theses errors are detected and presented to the user without
  435. stack traces.
  436. '''
  437. ret = self.run_function('state.sls', mods='syntax.badlist')
  438. self.assertEqual(ret, [
  439. 'State \'A\' in SLS \'syntax.badlist\' is not formed as a list'
  440. ])
  441. ret = self.run_function('state.sls', mods='syntax.badlist2')
  442. self.assertEqual(ret, [
  443. 'State \'C\' in SLS \'syntax.badlist2\' is not formed as a list'
  444. ])
  445. def test_requisites_mixed_require_prereq_use(self):
  446. '''
  447. Call sls file containing several requisites.
  448. '''
  449. expected_simple_result = {
  450. 'cmd_|-A_|-echo A_|-run': {
  451. '__run_num__': 2,
  452. 'comment': 'Command "echo A" run',
  453. 'result': True,
  454. 'changes': True},
  455. 'cmd_|-B_|-echo B_|-run': {
  456. '__run_num__': 1,
  457. 'comment': 'Command "echo B" run',
  458. 'result': True,
  459. 'changes': True},
  460. 'cmd_|-C_|-echo C_|-run': {
  461. '__run_num__': 0,
  462. 'comment': 'Command "echo C" run',
  463. 'result': True,
  464. 'changes': True}
  465. }
  466. expected_result = {
  467. 'cmd_|-A_|-echo A fifth_|-run': {
  468. '__run_num__': 4,
  469. 'comment': 'Command "echo A fifth" run',
  470. 'result': True,
  471. 'changes': True},
  472. 'cmd_|-B_|-echo B third_|-run': {
  473. '__run_num__': 2,
  474. 'comment': 'Command "echo B third" run',
  475. 'result': True,
  476. 'changes': True},
  477. 'cmd_|-C_|-echo C second_|-run': {
  478. '__run_num__': 1,
  479. 'comment': 'Command "echo C second" run',
  480. 'result': True,
  481. 'changes': True},
  482. 'cmd_|-D_|-echo D first_|-run': {
  483. '__run_num__': 0,
  484. 'comment': 'Command "echo D first" run',
  485. 'result': True,
  486. 'changes': True},
  487. 'cmd_|-E_|-echo E fourth_|-run': {
  488. '__run_num__': 3,
  489. 'comment': 'Command "echo E fourth" run',
  490. 'result': True,
  491. 'changes': True}
  492. }
  493. expected_req_use_result = {
  494. 'cmd_|-A_|-echo A_|-run': {
  495. '__run_num__': 1,
  496. 'comment': 'Command "echo A" run',
  497. 'result': True,
  498. 'changes': True},
  499. 'cmd_|-B_|-echo B_|-run': {
  500. '__run_num__': 4,
  501. 'comment': 'Command "echo B" run',
  502. 'result': True,
  503. 'changes': True},
  504. 'cmd_|-C_|-echo C_|-run': {
  505. '__run_num__': 0,
  506. 'comment': 'Command "echo C" run',
  507. 'result': True,
  508. 'changes': True},
  509. 'cmd_|-D_|-echo D_|-run': {
  510. '__run_num__': 5,
  511. 'comment': 'Command "echo D" run',
  512. 'result': True,
  513. 'changes': True},
  514. 'cmd_|-E_|-echo E_|-run': {
  515. '__run_num__': 2,
  516. 'comment': 'Command "echo E" run',
  517. 'result': True,
  518. 'changes': True},
  519. 'cmd_|-F_|-echo F_|-run': {
  520. '__run_num__': 3,
  521. 'comment': 'Command "echo F" run',
  522. 'result': True,
  523. 'changes': True}
  524. }
  525. ret = self.run_function('state.sls', mods='requisites.mixed_simple')
  526. result = self.normalize_ret(ret)
  527. self.assertReturnNonEmptySaltType(ret)
  528. self.assertEqual(expected_simple_result, result)
  529. # test Traceback recursion prereq+require #8785
  530. # TODO: this is actually failing badly
  531. #ret = self.run_function('state.sls', mods='requisites.prereq_require_recursion_error2')
  532. #self.assertEqual(
  533. # ret,
  534. # ['A recursive requisite was found, SLS "requisites.prereq_require_recursion_error2" ID "B" ID "A"']
  535. #)
  536. # test Infinite recursion prereq+require #8785 v2
  537. # TODO: this is actually failing badly
  538. #ret = self.run_function('state.sls', mods='requisites.prereq_require_recursion_error3')
  539. #self.assertEqual(
  540. # ret,
  541. # ['A recursive requisite was found, SLS "requisites.prereq_require_recursion_error2" ID "B" ID "A"']
  542. #)
  543. # test Infinite recursion prereq+require #8785 v3
  544. # TODO: this is actually failing badly, and expected result is maybe not a recursion
  545. #ret = self.run_function('state.sls', mods='requisites.prereq_require_recursion_error4')
  546. #self.assertEqual(
  547. # ret,
  548. # ['A recursive requisite was found, SLS "requisites.prereq_require_recursion_error2" ID "B" ID "A"']
  549. #)
  550. # undetected infinite loopS prevents this test from running...
  551. # TODO: this is actually failing badly
  552. #ret = self.run_function('state.sls', mods='requisites.mixed_complex1')
  553. #result = self.normalize_ret(ret)
  554. #self.assertEqual(expected_result, result)
  555. def test_watch_in(self):
  556. '''
  557. test watch_in requisite when there is a success
  558. '''
  559. ret = self.run_function('state.sls', mods='requisites.watch_in')
  560. changes = 'test_|-return_changes_|-return_changes_|-succeed_with_changes'
  561. watch = 'test_|-watch_states_|-watch_states_|-succeed_without_changes'
  562. self.assertEqual(ret[changes]['__run_num__'], 0)
  563. self.assertEqual(ret[watch]['__run_num__'], 2)
  564. self.assertEqual('Watch statement fired.', ret[watch]['comment'])
  565. self.assertEqual('Something pretended to change',
  566. ret[changes]['changes']['testing']['new'])
  567. def test_watch_in_failure(self):
  568. '''
  569. test watch_in requisite when there is a failure
  570. '''
  571. ret = self.run_function('state.sls', mods='requisites.watch_in_failure')
  572. fail = 'test_|-return_changes_|-return_changes_|-fail_with_changes'
  573. watch = 'test_|-watch_states_|-watch_states_|-succeed_without_changes'
  574. self.assertEqual(False, ret[fail]['result'])
  575. self.assertEqual('One or more requisite failed: requisites.watch_in_failure.return_changes',
  576. ret[watch]['comment'])
  577. def normalize_ret(self, ret):
  578. '''
  579. Normalize the return to the format that we'll use for result checking
  580. '''
  581. result = {}
  582. for item, descr in six.iteritems(ret):
  583. result[item] = {
  584. '__run_num__': descr['__run_num__'],
  585. 'comment': descr['comment'],
  586. 'result': descr['result'],
  587. 'changes': descr['changes'] != {} # whether there where any changes
  588. }
  589. return result
  590. def test_requisites_require_ordering_and_errors(self):
  591. '''
  592. Call sls file containing several require_in and require.
  593. Ensure that some of them are failing and that the order is right.
  594. '''
  595. expected_result = {
  596. 'cmd_|-A_|-echo A fifth_|-run': {
  597. '__run_num__': 4,
  598. 'comment': 'Command "echo A fifth" run',
  599. 'result': True,
  600. 'changes': True,
  601. },
  602. 'cmd_|-B_|-echo B second_|-run': {
  603. '__run_num__': 1,
  604. 'comment': 'Command "echo B second" run',
  605. 'result': True,
  606. 'changes': True,
  607. },
  608. 'cmd_|-C_|-echo C third_|-run': {
  609. '__run_num__': 2,
  610. 'comment': 'Command "echo C third" run',
  611. 'result': True,
  612. 'changes': True,
  613. },
  614. 'cmd_|-D_|-echo D first_|-run': {
  615. '__run_num__': 0,
  616. 'comment': 'Command "echo D first" run',
  617. 'result': True,
  618. 'changes': True,
  619. },
  620. 'cmd_|-E_|-echo E fourth_|-run': {
  621. '__run_num__': 3,
  622. 'comment': 'Command "echo E fourth" run',
  623. 'result': True,
  624. 'changes': True,
  625. },
  626. 'cmd_|-F_|-echo F_|-run': {
  627. '__run_num__': 5,
  628. 'comment': 'The following requisites were not found:\n'
  629. + ' require:\n'
  630. + ' foobar: A\n',
  631. 'result': False,
  632. 'changes': False,
  633. },
  634. 'cmd_|-G_|-echo G_|-run': {
  635. '__run_num__': 6,
  636. 'comment': 'The following requisites were not found:\n'
  637. + ' require:\n'
  638. + ' cmd: Z\n',
  639. 'result': False,
  640. 'changes': False,
  641. },
  642. 'cmd_|-H_|-echo H_|-run': {
  643. '__run_num__': 7,
  644. 'comment': 'The following requisites were not found:\n'
  645. + ' require:\n'
  646. + ' cmd: Z\n',
  647. 'result': False,
  648. 'changes': False,
  649. }
  650. }
  651. ret = self.run_function('state.sls', mods='requisites.require')
  652. result = self.normalize_ret(ret)
  653. self.assertReturnNonEmptySaltType(ret)
  654. self.assertEqual(expected_result, result)
  655. ret = self.run_function('state.sls', mods='requisites.require_error1')
  656. self.assertEqual(ret, [
  657. "Cannot extend ID 'W' in 'base:requisites.require_error1'. It is not part of the high state.\nThis is likely due to a missing include statement or an incorrectly typed ID.\nEnsure that a state with an ID of 'W' is available\nin environment 'base' and to SLS 'requisites.require_error1'"
  658. ])
  659. # issue #8235
  660. # FIXME: Why is require enforcing list syntax while require_in does not?
  661. # And why preventing it?
  662. # Currently this state fails, should return C/B/A
  663. result = {}
  664. ret = self.run_function('state.sls', mods='requisites.require_simple_nolist')
  665. self.assertEqual(ret, [
  666. 'The require statement in state \'B\' in SLS '
  667. + '\'requisites.require_simple_nolist\' needs to be formed as a list'
  668. ])
  669. # commented until a fix is made for issue #8772
  670. # TODO: this test actually fails
  671. #ret = self.run_function('state.sls', mods='requisites.require_error2')
  672. #self.assertEqual(ret, [
  673. # 'Cannot extend state foobar for ID A in "base:requisites.require_error2".'
  674. # + ' It is not part of the high state.'
  675. #])
  676. ret = self.run_function('state.sls', mods='requisites.require_recursion_error1')
  677. self.assertEqual(
  678. ret,
  679. ['A recursive requisite was found, SLS "requisites.require_recursion_error1" ID "B" ID "A"']
  680. )
  681. def test_requisites_require_any(self):
  682. '''
  683. Call sls file containing several require_in and require.
  684. Ensure that some of them are failing and that the order is right.
  685. '''
  686. expected_result = {
  687. 'cmd_|-A_|-echo A_|-run': {
  688. '__run_num__': 3,
  689. 'comment': 'Command "echo A" run',
  690. 'result': True,
  691. 'changes': True,
  692. },
  693. 'cmd_|-B_|-echo B_|-run': {
  694. '__run_num__': 0,
  695. 'comment': 'Command "echo B" run',
  696. 'result': True,
  697. 'changes': True,
  698. },
  699. 'cmd_|-C_|-/bin/false_|-run': {
  700. '__run_num__': 1,
  701. 'comment': 'Command "/bin/false" run',
  702. 'result': False,
  703. 'changes': True,
  704. },
  705. 'cmd_|-D_|-echo D_|-run': {
  706. '__run_num__': 2,
  707. 'comment': 'Command "echo D" run',
  708. 'result': True,
  709. 'changes': True,
  710. },
  711. }
  712. ret = self.run_function('state.sls', mods='requisites.require_any')
  713. result = self.normalize_ret(ret)
  714. self.assertReturnNonEmptySaltType(ret)
  715. self.assertEqual(expected_result, result)
  716. def test_requisites_require_any_fail(self):
  717. '''
  718. Call sls file containing several require_in and require.
  719. Ensure that some of them are failing and that the order is right.
  720. '''
  721. ret = self.run_function('state.sls', mods='requisites.require_any_fail')
  722. result = self.normalize_ret(ret)
  723. self.assertReturnNonEmptySaltType(ret)
  724. self.assertIn('One or more requisite failed',
  725. result['cmd_|-D_|-echo D_|-run']['comment'])
  726. def test_requisites_watch_any(self):
  727. '''
  728. Call sls file containing several require_in and require.
  729. Ensure that some of them are failing and that the order is right.
  730. '''
  731. if salt.utils.platform.is_windows():
  732. cmd_true = 'exit'
  733. cmd_false = 'exit /B 1'
  734. else:
  735. cmd_true = 'true'
  736. cmd_false = 'false'
  737. expected_result = {
  738. 'cmd_|-A_|-{0}_|-wait'.format(cmd_true): {
  739. '__run_num__': 4,
  740. 'comment': 'Command "{0}" run'.format(cmd_true),
  741. 'result': True,
  742. 'changes': True,
  743. },
  744. 'cmd_|-B_|-{0}_|-run'.format(cmd_true): {
  745. '__run_num__': 0,
  746. 'comment': 'Command "{0}" run'.format(cmd_true),
  747. 'result': True,
  748. 'changes': True,
  749. },
  750. 'cmd_|-C_|-{0}_|-run'.format(cmd_false): {
  751. '__run_num__': 1,
  752. 'comment': 'Command "{0}" run'.format(cmd_false),
  753. 'result': False,
  754. 'changes': True,
  755. },
  756. 'cmd_|-D_|-{0}_|-run'.format(cmd_true): {
  757. '__run_num__': 2,
  758. 'comment': 'Command "{0}" run'.format(cmd_true),
  759. 'result': True,
  760. 'changes': True,
  761. },
  762. 'cmd_|-E_|-{0}_|-wait'.format(cmd_true): {
  763. '__run_num__': 9,
  764. 'comment': 'Command "{0}" run'.format(cmd_true),
  765. 'result': True,
  766. 'changes': True,
  767. },
  768. 'cmd_|-F_|-{0}_|-run'.format(cmd_true): {
  769. '__run_num__': 5,
  770. 'comment': 'Command "{0}" run'.format(cmd_true),
  771. 'result': True,
  772. 'changes': True,
  773. },
  774. 'cmd_|-G_|-{0}_|-run'.format(cmd_false): {
  775. '__run_num__': 6,
  776. 'comment': 'Command "{0}" run'.format(cmd_false),
  777. 'result': False,
  778. 'changes': True,
  779. },
  780. 'cmd_|-H_|-{0}_|-run'.format(cmd_false): {
  781. '__run_num__': 7,
  782. 'comment': 'Command "{0}" run'.format(cmd_false),
  783. 'result': False,
  784. 'changes': True,
  785. },
  786. }
  787. ret = self.run_function('state.sls', mods='requisites.watch_any')
  788. result = self.normalize_ret(ret)
  789. self.assertReturnNonEmptySaltType(ret)
  790. self.assertEqual(expected_result, result)
  791. def test_requisites_watch_any_fail(self):
  792. '''
  793. Call sls file containing several require_in and require.
  794. Ensure that some of them are failing and that the order is right.
  795. '''
  796. ret = self.run_function('state.sls', mods='requisites.watch_any_fail')
  797. result = self.normalize_ret(ret)
  798. self.assertReturnNonEmptySaltType(ret)
  799. self.assertIn('One or more requisite failed',
  800. result['cmd_|-A_|-true_|-wait']['comment'])
  801. def test_requisites_onchanges_any(self):
  802. '''
  803. Call sls file containing several require_in and require.
  804. Ensure that some of them are failing and that the order is right.
  805. '''
  806. expected_result = {
  807. 'cmd_|-another_changing_state_|-echo "Changed!"_|-run': {
  808. '__run_num__': 1,
  809. 'changes': True,
  810. 'comment': 'Command "echo "Changed!"" run',
  811. 'result': True
  812. },
  813. 'cmd_|-changing_state_|-echo "Changed!"_|-run': {
  814. '__run_num__': 0,
  815. 'changes': True,
  816. 'comment': 'Command "echo "Changed!"" run',
  817. 'result': True
  818. },
  819. 'cmd_|-test_one_changing_states_|-echo "Success!"_|-run': {
  820. '__run_num__': 4,
  821. 'changes': True,
  822. 'comment': 'Command "echo "Success!"" run',
  823. 'result': True
  824. },
  825. 'cmd_|-test_two_non_changing_states_|-echo "Should not run"_|-run': {
  826. '__run_num__': 5,
  827. 'changes': False,
  828. 'comment': 'State was not run because none of the onchanges reqs changed',
  829. 'result': True
  830. },
  831. 'pip_|-another_non_changing_state_|-mock_|-installed': {
  832. '__run_num__': 3,
  833. 'changes': False,
  834. 'comment': 'Python package mock was already installed\nAll specified packages are already installed',
  835. 'result': True
  836. },
  837. 'pip_|-non_changing_state_|-mock_|-installed': {
  838. '__run_num__': 2,
  839. 'changes': False,
  840. 'comment': 'Python package mock was already installed\nAll specified packages are already installed',
  841. 'result': True
  842. }
  843. }
  844. ret = self.run_function('state.sls', mods='requisites.onchanges_any')
  845. result = self.normalize_ret(ret)
  846. self.assertReturnNonEmptySaltType(ret)
  847. self.assertEqual(expected_result, result)
  848. def test_requisites_onfail_any(self):
  849. '''
  850. Call sls file containing several require_in and require.
  851. Ensure that some of them are failing and that the order is right.
  852. '''
  853. expected_result = {
  854. 'cmd_|-a_|-exit 0_|-run': {
  855. '__run_num__': 0,
  856. 'changes': True,
  857. 'comment': 'Command "exit 0" run',
  858. 'result': True
  859. },
  860. 'cmd_|-b_|-exit 1_|-run': {
  861. '__run_num__': 1,
  862. 'changes': True,
  863. 'comment': 'Command "exit 1" run',
  864. 'result': False
  865. },
  866. 'cmd_|-c_|-exit 0_|-run': {
  867. '__run_num__': 2,
  868. 'changes': True,
  869. 'comment': 'Command "exit 0" run',
  870. 'result': True
  871. },
  872. 'cmd_|-d_|-echo itworked_|-run': {
  873. '__run_num__': 3,
  874. 'changes': True,
  875. 'comment': 'Command "echo itworked" run',
  876. 'result': True},
  877. 'cmd_|-e_|-exit 0_|-run': {
  878. '__run_num__': 4,
  879. 'changes': True,
  880. 'comment': 'Command "exit 0" run',
  881. 'result': True
  882. },
  883. 'cmd_|-f_|-exit 0_|-run': {
  884. '__run_num__': 5,
  885. 'changes': True,
  886. 'comment': 'Command "exit 0" run',
  887. 'result': True
  888. },
  889. 'cmd_|-g_|-exit 0_|-run': {
  890. '__run_num__': 6,
  891. 'changes': True,
  892. 'comment': 'Command "exit 0" run',
  893. 'result': True
  894. },
  895. 'cmd_|-h_|-echo itworked_|-run': {
  896. '__run_num__': 7,
  897. 'changes': False,
  898. 'comment': 'State was not run because onfail req did not change',
  899. 'result': True
  900. }
  901. }
  902. ret = self.run_function('state.sls', mods='requisites.onfail_any')
  903. result = self.normalize_ret(ret)
  904. self.assertReturnNonEmptySaltType(ret)
  905. self.assertEqual(expected_result, result)
  906. def test_requisites_full_sls(self):
  907. '''
  908. Teste the sls special command in requisites
  909. '''
  910. expected_result = {
  911. 'cmd_|-A_|-echo A_|-run': {
  912. '__run_num__': 2,
  913. 'comment': 'Command "echo A" run',
  914. 'result': True,
  915. 'changes': True},
  916. 'cmd_|-B_|-echo B_|-run': {
  917. '__run_num__': 0,
  918. 'comment': 'Command "echo B" run',
  919. 'result': True,
  920. 'changes': True},
  921. 'cmd_|-C_|-echo C_|-run': {
  922. '__run_num__': 1,
  923. 'comment': 'Command "echo C" run',
  924. 'result': True,
  925. 'changes': True},
  926. }
  927. ret = self.run_function('state.sls', mods='requisites.fullsls_require')
  928. self.assertReturnNonEmptySaltType(ret)
  929. result = self.normalize_ret(ret)
  930. self.assertEqual(expected_result, result)
  931. # issue #8233: traceback on prereq sls
  932. # TODO: not done
  933. #ret = self.run_function('state.sls', mods='requisites.fullsls_prereq')
  934. #self.assertEqual(['sls command can only be used with require requisite'], ret)
  935. def test_requisites_require_no_state_module(self):
  936. '''
  937. Call sls file containing several require_in and require.
  938. Ensure that some of them are failing and that the order is right.
  939. '''
  940. expected_result = {
  941. 'cmd_|-A_|-echo A fifth_|-run': {
  942. '__run_num__': 4,
  943. 'comment': 'Command "echo A fifth" run',
  944. 'result': True,
  945. 'changes': True,
  946. },
  947. 'cmd_|-B_|-echo B second_|-run': {
  948. '__run_num__': 1,
  949. 'comment': 'Command "echo B second" run',
  950. 'result': True,
  951. 'changes': True,
  952. },
  953. 'cmd_|-C_|-echo C third_|-run': {
  954. '__run_num__': 2,
  955. 'comment': 'Command "echo C third" run',
  956. 'result': True,
  957. 'changes': True,
  958. },
  959. 'cmd_|-D_|-echo D first_|-run': {
  960. '__run_num__': 0,
  961. 'comment': 'Command "echo D first" run',
  962. 'result': True,
  963. 'changes': True,
  964. },
  965. 'cmd_|-E_|-echo E fourth_|-run': {
  966. '__run_num__': 3,
  967. 'comment': 'Command "echo E fourth" run',
  968. 'result': True,
  969. 'changes': True,
  970. },
  971. 'cmd_|-G_|-echo G_|-run': {
  972. '__run_num__': 5,
  973. 'comment': 'The following requisites were not found:\n'
  974. + ' require:\n'
  975. + ' id: Z\n',
  976. 'result': False,
  977. 'changes': False,
  978. },
  979. 'cmd_|-H_|-echo H_|-run': {
  980. '__run_num__': 6,
  981. 'comment': 'The following requisites were not found:\n'
  982. + ' require:\n'
  983. + ' id: Z\n',
  984. 'result': False,
  985. 'changes': False,
  986. }
  987. }
  988. ret = self.run_function('state.sls', mods='requisites.require_no_state_module')
  989. result = self.normalize_ret(ret)
  990. self.assertReturnNonEmptySaltType(ret)
  991. self.assertEqual(expected_result, result)
  992. def test_requisites_prereq_simple_ordering_and_errors(self):
  993. '''
  994. Call sls file containing several prereq_in and prereq.
  995. Ensure that some of them are failing and that the order is right.
  996. '''
  997. expected_result_simple = {
  998. 'cmd_|-A_|-echo A third_|-run': {
  999. '__run_num__': 2,
  1000. 'comment': 'Command "echo A third" run',
  1001. 'result': True,
  1002. 'changes': True},
  1003. 'cmd_|-B_|-echo B first_|-run': {
  1004. '__run_num__': 0,
  1005. 'comment': 'Command "echo B first" run',
  1006. 'result': True,
  1007. 'changes': True},
  1008. 'cmd_|-C_|-echo C second_|-run': {
  1009. '__run_num__': 1,
  1010. 'comment': 'Command "echo C second" run',
  1011. 'result': True,
  1012. 'changes': True},
  1013. 'cmd_|-I_|-echo I_|-run': {
  1014. '__run_num__': 3,
  1015. 'comment': 'The following requisites were not found:\n'
  1016. + ' prereq:\n'
  1017. + ' cmd: Z\n',
  1018. 'result': False,
  1019. 'changes': False},
  1020. 'cmd_|-J_|-echo J_|-run': {
  1021. '__run_num__': 4,
  1022. 'comment': 'The following requisites were not found:\n'
  1023. + ' prereq:\n'
  1024. + ' foobar: A\n',
  1025. 'result': False,
  1026. 'changes': False}
  1027. }
  1028. expected_result_simple_no_state_module = {
  1029. 'cmd_|-A_|-echo A third_|-run': {
  1030. '__run_num__': 2,
  1031. 'comment': 'Command "echo A third" run',
  1032. 'result': True,
  1033. 'changes': True},
  1034. 'cmd_|-B_|-echo B first_|-run': {
  1035. '__run_num__': 0,
  1036. 'comment': 'Command "echo B first" run',
  1037. 'result': True,
  1038. 'changes': True},
  1039. 'cmd_|-C_|-echo C second_|-run': {
  1040. '__run_num__': 1,
  1041. 'comment': 'Command "echo C second" run',
  1042. 'result': True,
  1043. 'changes': True},
  1044. 'cmd_|-I_|-echo I_|-run': {
  1045. '__run_num__': 3,
  1046. 'comment': 'The following requisites were not found:\n'
  1047. + ' prereq:\n'
  1048. + ' id: Z\n',
  1049. 'result': False,
  1050. 'changes': False}
  1051. }
  1052. expected_result_simple2 = {
  1053. 'cmd_|-A_|-echo A_|-run': {
  1054. '__run_num__': 1,
  1055. 'comment': 'Command "echo A" run',
  1056. 'result': True,
  1057. 'changes': True},
  1058. 'cmd_|-B_|-echo B_|-run': {
  1059. '__run_num__': 2,
  1060. 'comment': 'Command "echo B" run',
  1061. 'result': True,
  1062. 'changes': True},
  1063. 'cmd_|-C_|-echo C_|-run': {
  1064. '__run_num__': 0,
  1065. 'comment': 'Command "echo C" run',
  1066. 'result': True,
  1067. 'changes': True},
  1068. 'cmd_|-D_|-echo D_|-run': {
  1069. '__run_num__': 3,
  1070. 'comment': 'Command "echo D" run',
  1071. 'result': True,
  1072. 'changes': True},
  1073. 'cmd_|-E_|-echo E_|-run': {
  1074. '__run_num__': 4,
  1075. 'comment': 'Command "echo E" run',
  1076. 'result': True,
  1077. 'changes': True}
  1078. }
  1079. expected_result_simple3 = {
  1080. 'cmd_|-A_|-echo A first_|-run': {
  1081. '__run_num__': 0,
  1082. 'comment': 'Command "echo A first" run',
  1083. 'result': True,
  1084. 'changes': True,
  1085. },
  1086. 'cmd_|-B_|-echo B second_|-run': {
  1087. '__run_num__': 1,
  1088. 'comment': 'Command "echo B second" run',
  1089. 'result': True,
  1090. 'changes': True,
  1091. },
  1092. 'cmd_|-C_|-echo C third_|-wait': {
  1093. '__run_num__': 2,
  1094. 'comment': '',
  1095. 'result': True,
  1096. 'changes': False,
  1097. }
  1098. }
  1099. expected_result_complex = {
  1100. 'cmd_|-A_|-echo A fourth_|-run': {
  1101. '__run_num__': 3,
  1102. 'comment': 'Command "echo A fourth" run',
  1103. 'result': True,
  1104. 'changes': True},
  1105. 'cmd_|-B_|-echo B first_|-run': {
  1106. '__run_num__': 0,
  1107. 'comment': 'Command "echo B first" run',
  1108. 'result': True,
  1109. 'changes': True},
  1110. 'cmd_|-C_|-echo C second_|-run': {
  1111. '__run_num__': 1,
  1112. 'comment': 'Command "echo C second" run',
  1113. 'result': True,
  1114. 'changes': True},
  1115. 'cmd_|-D_|-echo D third_|-run': {
  1116. '__run_num__': 2,
  1117. 'comment': 'Command "echo D third" run',
  1118. 'result': True,
  1119. 'changes': True},
  1120. }
  1121. ret = self.run_function('state.sls', mods='requisites.prereq_simple')
  1122. self.assertReturnNonEmptySaltType(ret)
  1123. result = self.normalize_ret(ret)
  1124. self.assertEqual(expected_result_simple, result)
  1125. # same test, but not using lists in yaml syntax
  1126. # TODO: issue #8235, prereq ignored when not used in list syntax
  1127. # Currently fails badly with :
  1128. # TypeError encountered executing state.sls: string indices must be integers, not str.
  1129. #expected_result_simple.pop('cmd_|-I_|-echo I_|-run')
  1130. #expected_result_simple.pop('cmd_|-J_|-echo J_|-run')
  1131. #ret = self.run_function('state.sls', mods='requisites.prereq_simple_nolist')
  1132. #result = self.normalize_ret(ret)
  1133. #self.assertEqual(expected_result_simple, result)
  1134. ret = self.run_function('state.sls', mods='requisites.prereq_simple2')
  1135. result = self.normalize_ret(ret)
  1136. self.assertReturnNonEmptySaltType(ret)
  1137. self.assertEqual(expected_result_simple2, result)
  1138. ret = self.run_function('state.sls', mods='requisites.prereq_simple3')
  1139. result = self.normalize_ret(ret)
  1140. self.assertReturnNonEmptySaltType(ret)
  1141. self.assertEqual(expected_result_simple3, result)
  1142. #ret = self.run_function('state.sls', mods='requisites.prereq_error_nolist')
  1143. #self.assertEqual(
  1144. # ret,
  1145. # ['Cannot extend ID Z in "base:requisites.prereq_error_nolist".'
  1146. # + ' It is not part of the high state.']
  1147. #)
  1148. ret = self.run_function('state.sls', mods='requisites.prereq_compile_error1')
  1149. self.assertReturnNonEmptySaltType(ret)
  1150. self.assertEqual(
  1151. ret['cmd_|-B_|-echo B_|-run']['comment'],
  1152. 'The following requisites were not found:\n'
  1153. + ' prereq:\n'
  1154. + ' foobar: A\n'
  1155. )
  1156. ret = self.run_function('state.sls', mods='requisites.prereq_compile_error2')
  1157. self.assertReturnNonEmptySaltType(ret)
  1158. self.assertEqual(
  1159. ret['cmd_|-B_|-echo B_|-run']['comment'],
  1160. 'The following requisites were not found:\n'
  1161. + ' prereq:\n'
  1162. + ' foobar: C\n'
  1163. )
  1164. ret = self.run_function('state.sls', mods='requisites.prereq_complex')
  1165. result = self.normalize_ret(ret)
  1166. self.assertEqual(expected_result_complex, result)
  1167. # issue #8210 : prereq recursion undetected
  1168. # TODO: this test fails
  1169. #ret = self.run_function('state.sls', mods='requisites.prereq_recursion_error')
  1170. #self.assertEqual(
  1171. # ret,
  1172. # ['A recursive requisite was found, SLS "requisites.prereq_recursion_error" ID "B" ID "A"']
  1173. #)
  1174. ret = self.run_function('state.sls', mods='requisites.prereq_simple_no_state_module')
  1175. result = self.normalize_ret(ret)
  1176. self.assertEqual(expected_result_simple_no_state_module, result)
  1177. def test_infinite_recursion_sls_prereq(self):
  1178. ret = self.run_function('state.sls', mods='requisites.prereq_sls_infinite_recursion')
  1179. self.assertSaltTrueReturn(ret)
  1180. def test_requisites_use(self):
  1181. '''
  1182. Call sls file containing several use_in and use.
  1183. '''
  1184. # TODO issue #8235 & #8774 some examples are still commented in the test file
  1185. ret = self.run_function('state.sls', mods='requisites.use')
  1186. self.assertReturnNonEmptySaltType(ret)
  1187. for item, descr in six.iteritems(ret):
  1188. self.assertEqual(descr['comment'], 'onlyif condition is false')
  1189. # TODO: issue #8802 : use recursions undetected
  1190. # issue is closed as use does not actually inherit requisites
  1191. # if chain-use is added after #8774 resolution theses tests would maybe become useful
  1192. #ret = self.run_function('state.sls', mods='requisites.use_recursion')
  1193. #self.assertEqual(ret, [
  1194. # 'A recursive requisite was found, SLS "requisites.use_recursion"'
  1195. # + ' ID "B" ID "A"'
  1196. #])
  1197. #ret = self.run_function('state.sls', mods='requisites.use_recursion2')
  1198. #self.assertEqual(ret, [
  1199. # 'A recursive requisite was found, SLS "requisites.use_recursion2"'
  1200. # + ' ID "C" ID "A"'
  1201. #])
  1202. #ret = self.run_function('state.sls', mods='requisites.use_auto_recursion')
  1203. #self.assertEqual(ret, [
  1204. # 'A recursive requisite was found, SLS "requisites.use_recursion"'
  1205. # + ' ID "A" ID "A"'
  1206. #])
  1207. def test_requisites_use_no_state_module(self):
  1208. '''
  1209. Call sls file containing several use_in and use.
  1210. '''
  1211. ret = self.run_function('state.sls', mods='requisites.use_no_state_module')
  1212. self.assertReturnNonEmptySaltType(ret)
  1213. for item, descr in six.iteritems(ret):
  1214. self.assertEqual(descr['comment'], 'onlyif condition is false')
  1215. def test_get_file_from_env_in_top_match(self):
  1216. tgt = os.path.join(TMP, 'prod-cheese-file')
  1217. try:
  1218. ret = self.run_function(
  1219. 'state.highstate', minion_tgt='sub_minion'
  1220. )
  1221. self.assertSaltTrueReturn(ret)
  1222. self.assertTrue(os.path.isfile(tgt))
  1223. with salt.utils.files.fopen(tgt, 'r') as cheese:
  1224. data = salt.utils.stringutils.to_unicode(cheese.read())
  1225. self.assertIn('Gromit', data)
  1226. self.assertIn('Comte', data)
  1227. finally:
  1228. if os.path.islink(tgt):
  1229. os.unlink(tgt)
  1230. # onchanges tests
  1231. def test_onchanges_requisite(self):
  1232. '''
  1233. Tests a simple state using the onchanges requisite
  1234. '''
  1235. # Only run the state once and keep the return data
  1236. state_run = self.run_function('state.sls', mods='requisites.onchanges_simple')
  1237. # First, test the result of the state run when changes are expected to happen
  1238. test_data = state_run['cmd_|-test_changing_state_|-echo "Success!"_|-run']['comment']
  1239. expected_result = 'Command "echo "Success!"" run'
  1240. self.assertIn(expected_result, test_data)
  1241. # Then, test the result of the state run when changes are not expected to happen
  1242. test_data = state_run['cmd_|-test_non_changing_state_|-echo "Should not run"_|-run']['comment']
  1243. expected_result = 'State was not run because none of the onchanges reqs changed'
  1244. self.assertIn(expected_result, test_data)
  1245. def test_onchanges_requisite_multiple(self):
  1246. '''
  1247. Tests a simple state using the onchanges requisite
  1248. '''
  1249. # Only run the state once and keep the return data
  1250. state_run = self.run_function('state.sls',
  1251. mods='requisites.onchanges_multiple')
  1252. # First, test the result of the state run when two changes are expected to happen
  1253. test_data = state_run['cmd_|-test_two_changing_states_|-echo "Success!"_|-run']['comment']
  1254. expected_result = 'Command "echo "Success!"" run'
  1255. self.assertIn(expected_result, test_data)
  1256. # Then, test the result of the state run when two changes are not expected to happen
  1257. test_data = state_run['cmd_|-test_two_non_changing_states_|-echo "Should not run"_|-run']['comment']
  1258. expected_result = 'State was not run because none of the onchanges reqs changed'
  1259. self.assertIn(expected_result, test_data)
  1260. # Finally, test the result of the state run when only one of the onchanges requisites changes.
  1261. test_data = state_run['cmd_|-test_one_changing_state_|-echo "Success!"_|-run']['comment']
  1262. expected_result = 'Command "echo "Success!"" run'
  1263. self.assertIn(expected_result, test_data)
  1264. def test_onchanges_in_requisite(self):
  1265. '''
  1266. Tests a simple state using the onchanges_in requisite
  1267. '''
  1268. # Only run the state once and keep the return data
  1269. state_run = self.run_function('state.sls', mods='requisites.onchanges_in_simple')
  1270. # First, test the result of the state run of when changes are expected to happen
  1271. test_data = state_run['cmd_|-test_changes_expected_|-echo "Success!"_|-run']['comment']
  1272. expected_result = 'Command "echo "Success!"" run'
  1273. self.assertIn(expected_result, test_data)
  1274. # Then, test the result of the state run when changes are not expected to happen
  1275. test_data = state_run['cmd_|-test_changes_not_expected_|-echo "Should not run"_|-run']['comment']
  1276. expected_result = 'State was not run because none of the onchanges reqs changed'
  1277. self.assertIn(expected_result, test_data)
  1278. def test_onchanges_requisite_no_state_module(self):
  1279. '''
  1280. Tests a simple state using the onchanges requisite without state modules
  1281. '''
  1282. # Only run the state once and keep the return data
  1283. state_run = self.run_function('state.sls', mods='requisites.onchanges_simple_no_state_module')
  1284. test_data = state_run['cmd_|-test_changing_state_|-echo "Success!"_|-run']['comment']
  1285. expected_result = 'Command "echo "Success!"" run'
  1286. self.assertIn(expected_result, test_data)
  1287. def test_onchanges_requisite_with_duration(self):
  1288. '''
  1289. Tests a simple state using the onchanges requisite
  1290. the state will not run but results will include duration
  1291. '''
  1292. # Only run the state once and keep the return data
  1293. state_run = self.run_function('state.sls', mods='requisites.onchanges_simple')
  1294. # Then, test the result of the state run when changes are not expected to happen
  1295. # and ensure duration is included in the results
  1296. test_data = state_run['cmd_|-test_non_changing_state_|-echo "Should not run"_|-run']
  1297. self.assertIn('duration', test_data)
  1298. # onfail tests
  1299. def test_onfail_requisite(self):
  1300. '''
  1301. Tests a simple state using the onfail requisite
  1302. '''
  1303. # Only run the state once and keep the return data
  1304. state_run = self.run_function('state.sls', mods='requisites.onfail_simple')
  1305. # First, test the result of the state run when a failure is expected to happen
  1306. test_data = state_run['cmd_|-test_failing_state_|-echo "Success!"_|-run']['comment']
  1307. expected_result = 'Command "echo "Success!"" run'
  1308. self.assertIn(expected_result, test_data)
  1309. # Then, test the result of the state run when a failure is not expected to happen
  1310. test_data = state_run['cmd_|-test_non_failing_state_|-echo "Should not run"_|-run']['comment']
  1311. expected_result = 'State was not run because onfail req did not change'
  1312. self.assertIn(expected_result, test_data)
  1313. def test_multiple_onfail_requisite(self):
  1314. '''
  1315. test to ensure state is run even if only one
  1316. of the onfails fails. This is a test for the issue:
  1317. https://github.com/saltstack/salt/issues/22370
  1318. '''
  1319. state_run = self.run_function('state.sls',
  1320. mods='requisites.onfail_multiple',
  1321. timeout=self.TIMEOUT)
  1322. retcode = state_run['cmd_|-c_|-echo itworked_|-run']['changes']['retcode']
  1323. self.assertEqual(retcode, 0)
  1324. stdout = state_run['cmd_|-c_|-echo itworked_|-run']['changes']['stdout']
  1325. self.assertEqual(stdout, 'itworked')
  1326. def test_onfail_in_requisite(self):
  1327. '''
  1328. Tests a simple state using the onfail_in requisite
  1329. '''
  1330. # Only run the state once and keep the return data
  1331. state_run = self.run_function('state.sls', mods='requisites.onfail_in_simple')
  1332. # First, test the result of the state run when a failure is expected to happen
  1333. test_data = state_run['cmd_|-test_failing_state_|-echo "Success!"_|-run']['comment']
  1334. expected_result = 'Command "echo "Success!"" run'
  1335. self.assertIn(expected_result, test_data)
  1336. # Then, test the result of the state run when a failure is not expected to happen
  1337. test_data = state_run['cmd_|-test_non_failing_state_|-echo "Should not run"_|-run']['comment']
  1338. expected_result = 'State was not run because onfail req did not change'
  1339. self.assertIn(expected_result, test_data)
  1340. def test_onfail_requisite_no_state_module(self):
  1341. '''
  1342. Tests a simple state using the onfail requisite
  1343. '''
  1344. # Only run the state once and keep the return data
  1345. state_run = self.run_function('state.sls', mods='requisites.onfail_simple_no_state_module')
  1346. # First, test the result of the state run when a failure is expected to happen
  1347. test_data = state_run['cmd_|-test_failing_state_|-echo "Success!"_|-run']['comment']
  1348. expected_result = 'Command "echo "Success!"" run'
  1349. self.assertIn(expected_result, test_data)
  1350. # Then, test the result of the state run when a failure is not expected to happen
  1351. test_data = state_run['cmd_|-test_non_failing_state_|-echo "Should not run"_|-run']['comment']
  1352. expected_result = 'State was not run because onfail req did not change'
  1353. self.assertIn(expected_result, test_data)
  1354. def test_onfail_requisite_with_duration(self):
  1355. '''
  1356. Tests a simple state using the onfail requisite
  1357. '''
  1358. # Only run the state once and keep the return data
  1359. state_run = self.run_function('state.sls', mods='requisites.onfail_simple')
  1360. # Then, test the result of the state run when a failure is not expected to happen
  1361. test_data = state_run['cmd_|-test_non_failing_state_|-echo "Should not run"_|-run']
  1362. self.assertIn('duration', test_data)
  1363. def test_multiple_onfail_requisite_with_required(self):
  1364. '''
  1365. test to ensure multiple states are run
  1366. when specified as onfails for a single state.
  1367. This is a test for the issue:
  1368. https://github.com/saltstack/salt/issues/46552
  1369. '''
  1370. state_run = self.run_function('state.sls', mods='requisites.onfail_multiple_required')
  1371. retcode = state_run['cmd_|-b_|-echo b_|-run']['changes']['retcode']
  1372. self.assertEqual(retcode, 0)
  1373. retcode = state_run['cmd_|-c_|-echo c_|-run']['changes']['retcode']
  1374. self.assertEqual(retcode, 0)
  1375. retcode = state_run['cmd_|-d_|-echo d_|-run']['changes']['retcode']
  1376. self.assertEqual(retcode, 0)
  1377. stdout = state_run['cmd_|-b_|-echo b_|-run']['changes']['stdout']
  1378. self.assertEqual(stdout, 'b')
  1379. stdout = state_run['cmd_|-c_|-echo c_|-run']['changes']['stdout']
  1380. self.assertEqual(stdout, 'c')
  1381. stdout = state_run['cmd_|-d_|-echo d_|-run']['changes']['stdout']
  1382. self.assertEqual(stdout, 'd')
  1383. def test_multiple_onfail_requisite_with_required_no_run(self):
  1384. '''
  1385. test to ensure multiple states are not run
  1386. when specified as onfails for a single state
  1387. which fails.
  1388. This is a test for the issue:
  1389. https://github.com/saltstack/salt/issues/46552
  1390. '''
  1391. state_run = self.run_function('state.sls', mods='requisites.onfail_multiple_required_no_run')
  1392. expected = 'State was not run because onfail req did not change'
  1393. stdout = state_run['cmd_|-b_|-echo b_|-run']['comment']
  1394. self.assertEqual(stdout, expected)
  1395. stdout = state_run['cmd_|-c_|-echo c_|-run']['comment']
  1396. self.assertEqual(stdout, expected)
  1397. stdout = state_run['cmd_|-d_|-echo d_|-run']['comment']
  1398. self.assertEqual(stdout, expected)
  1399. # listen tests
  1400. def test_listen_requisite(self):
  1401. '''
  1402. Tests a simple state using the listen requisite
  1403. '''
  1404. # Only run the state once and keep the return data
  1405. state_run = self.run_function('state.sls', mods='requisites.listen_simple')
  1406. # First, test the result of the state run when a listener is expected to trigger
  1407. listener_state = 'cmd_|-listener_test_listening_change_state_|-echo "Listening State"_|-mod_watch'
  1408. self.assertIn(listener_state, state_run)
  1409. # Then, test the result of the state run when a listener should not trigger
  1410. absent_state = 'cmd_|-listener_test_listening_non_changing_state_|-echo "Only run once"_|-mod_watch'
  1411. self.assertNotIn(absent_state, state_run)
  1412. def test_listen_in_requisite(self):
  1413. '''
  1414. Tests a simple state using the listen_in requisite
  1415. '''
  1416. # Only run the state once and keep the return data
  1417. state_run = self.run_function('state.sls', mods='requisites.listen_in_simple')
  1418. # First, test the result of the state run when a listener is expected to trigger
  1419. listener_state = 'cmd_|-listener_test_listening_change_state_|-echo "Listening State"_|-mod_watch'
  1420. self.assertIn(listener_state, state_run)
  1421. # Then, test the result of the state run when a listener should not trigger
  1422. absent_state = 'cmd_|-listener_test_listening_non_changing_state_|-echo "Only run once"_|-mod_watch'
  1423. self.assertNotIn(absent_state, state_run)
  1424. def test_listen_in_requisite_resolution(self):
  1425. '''
  1426. Verify listen_in requisite lookups use ID declaration to check for changes
  1427. '''
  1428. # Only run the state once and keep the return data
  1429. state_run = self.run_function('state.sls', mods='requisites.listen_in_simple')
  1430. # Test the result of the state run when a listener is expected to trigger
  1431. listener_state = 'cmd_|-listener_test_listen_in_resolution_|-echo "Successful listen_in resolution"_|-mod_watch'
  1432. self.assertIn(listener_state, state_run)
  1433. def test_listen_requisite_resolution(self):
  1434. '''
  1435. Verify listen requisite lookups use ID declaration to check for changes
  1436. '''
  1437. # Only run the state once and keep the return data
  1438. state_run = self.run_function('state.sls', mods='requisites.listen_simple')
  1439. # Both listeners are expected to trigger
  1440. listener_state = 'cmd_|-listener_test_listening_resolution_one_|-echo "Successful listen resolution"_|-mod_watch'
  1441. self.assertIn(listener_state, state_run)
  1442. listener_state = 'cmd_|-listener_test_listening_resolution_two_|-echo "Successful listen resolution"_|-mod_watch'
  1443. self.assertIn(listener_state, state_run)
  1444. def test_listen_requisite_no_state_module(self):
  1445. '''
  1446. Tests a simple state using the listen requisite
  1447. '''
  1448. # Only run the state once and keep the return data
  1449. state_run = self.run_function('state.sls', mods='requisites.listen_simple_no_state_module')
  1450. # First, test the result of the state run when a listener is expected to trigger
  1451. listener_state = 'cmd_|-listener_test_listening_change_state_|-echo "Listening State"_|-mod_watch'
  1452. self.assertIn(listener_state, state_run)
  1453. # Then, test the result of the state run when a listener should not trigger
  1454. absent_state = 'cmd_|-listener_test_listening_non_changing_state_|-echo "Only run once"_|-mod_watch'
  1455. self.assertNotIn(absent_state, state_run)
  1456. def test_listen_in_requisite_resolution_names(self):
  1457. '''
  1458. Verify listen_in requisite lookups use ID declaration to check for changes
  1459. and resolves magic names state variable
  1460. '''
  1461. # Only run the state once and keep the return data
  1462. state_run = self.run_function('state.sls', mods='requisites.listen_in_names')
  1463. self.assertIn('test_|-listener_service_|-nginx_|-mod_watch', state_run)
  1464. self.assertIn('test_|-listener_service_|-crond_|-mod_watch', state_run)
  1465. def test_listen_requisite_resolution_names(self):
  1466. '''
  1467. Verify listen requisite lookups use ID declaration to check for changes
  1468. and resolves magic names state variable
  1469. '''
  1470. # Only run the state once and keep the return data
  1471. state_run = self.run_function('state.sls',
  1472. mods='requisites.listen_names',
  1473. timeout=self.TIMEOUT)
  1474. self.assertIn('test_|-listener_service_|-nginx_|-mod_watch', state_run)
  1475. self.assertIn('test_|-listener_service_|-crond_|-mod_watch', state_run)
  1476. def test_issue_30820_requisite_in_match_by_name(self):
  1477. '''
  1478. This tests the case where a requisite_in matches by name instead of ID
  1479. See https://github.com/saltstack/salt/issues/30820 for more info
  1480. '''
  1481. state_run = self.run_function(
  1482. 'state.sls',
  1483. mods='requisites.requisite_in_match_by_name'
  1484. )
  1485. bar_state = 'cmd_|-bar state_|-echo bar_|-wait'
  1486. self.assertIn(bar_state, state_run)
  1487. self.assertEqual(state_run[bar_state]['comment'],
  1488. 'Command "echo bar" run')
  1489. def test_retry_option_defaults(self):
  1490. '''
  1491. test the retry option on a simple state with defaults
  1492. ensure comment is as expected
  1493. ensure state duration is greater than default retry_interval (30 seconds)
  1494. '''
  1495. state_run = self.run_function(
  1496. 'state.sls',
  1497. mods='retry.retry_defaults'
  1498. )
  1499. retry_state = 'file_|-file_test_|-/path/to/a/non-existent/file.txt_|-exists'
  1500. expected_comment = ('Attempt 1: Returned a result of "False", with the following '
  1501. 'comment: "Specified path /path/to/a/non-existent/file.txt does not exist"\n'
  1502. 'Specified path /path/to/a/non-existent/file.txt does not exist')
  1503. self.assertEqual(state_run[retry_state]['comment'], expected_comment)
  1504. self.assertTrue(state_run[retry_state]['duration'] > 30)
  1505. self.assertEqual(state_run[retry_state]['result'], False)
  1506. def test_retry_option_custom(self):
  1507. '''
  1508. test the retry option on a simple state with custom retry values
  1509. ensure comment is as expected
  1510. ensure state duration is greater than custom defined interval * (retries - 1)
  1511. '''
  1512. state_run = self.run_function(
  1513. 'state.sls',
  1514. mods='retry.retry_custom'
  1515. )
  1516. retry_state = 'file_|-file_test_|-/path/to/a/non-existent/file.txt_|-exists'
  1517. expected_comment = ('Attempt 1: Returned a result of "False", with the following '
  1518. 'comment: "Specified path /path/to/a/non-existent/file.txt does not exist"\n'
  1519. 'Attempt 2: Returned a result of "False", with the following comment: "Specified'
  1520. ' path /path/to/a/non-existent/file.txt does not exist"\nAttempt 3: Returned'
  1521. ' a result of "False", with the following comment: "Specified path'
  1522. ' /path/to/a/non-existent/file.txt does not exist"\nAttempt 4: Returned a'
  1523. ' result of "False", with the following comment: "Specified path'
  1524. ' /path/to/a/non-existent/file.txt does not exist"\nSpecified path'
  1525. ' /path/to/a/non-existent/file.txt does not exist')
  1526. self.assertEqual(state_run[retry_state]['comment'], expected_comment)
  1527. self.assertTrue(state_run[retry_state]['duration'] > 40)
  1528. self.assertEqual(state_run[retry_state]['result'], False)
  1529. def test_retry_option_success(self):
  1530. '''
  1531. test a state with the retry option that should return True immedietly (i.e. no retries)
  1532. '''
  1533. testfile = os.path.join(TMP, 'retry_file')
  1534. state_run = self.run_function(
  1535. 'state.sls',
  1536. mods='retry.retry_success'
  1537. )
  1538. os.unlink(testfile)
  1539. retry_state = 'file_|-file_test_|-{0}_|-exists'.format(testfile)
  1540. self.assertNotIn('Attempt', state_run[retry_state]['comment'])
  1541. def run_create(self):
  1542. '''
  1543. helper function to wait 30 seconds and then create the temp retry file
  1544. '''
  1545. testfile = os.path.join(TMP, 'retry_file')
  1546. time.sleep(30)
  1547. with salt.utils.files.fopen(testfile, 'a'):
  1548. pass
  1549. @flaky
  1550. def test_retry_option_eventual_success(self):
  1551. '''
  1552. test a state with the retry option that should return True after at least 4 retry attmempt
  1553. but never run 15 attempts
  1554. '''
  1555. testfile = os.path.join(TMP, 'retry_file')
  1556. create_thread = threading.Thread(target=self.run_create)
  1557. create_thread.start()
  1558. state_run = self.run_function(
  1559. 'state.sls',
  1560. mods='retry.retry_success2'
  1561. )
  1562. retry_state = 'file_|-file_test_|-{0}_|-exists'.format(testfile)
  1563. self.assertIn('Attempt 1:', state_run[retry_state]['comment'])
  1564. self.assertIn('Attempt 2:', state_run[retry_state]['comment'])
  1565. self.assertIn('Attempt 3:', state_run[retry_state]['comment'])
  1566. self.assertIn('Attempt 4:', state_run[retry_state]['comment'])
  1567. self.assertNotIn('Attempt 15:', state_run[retry_state]['comment'])
  1568. self.assertEqual(state_run[retry_state]['result'], True)
  1569. def test_issue_38683_require_order_failhard_combination(self):
  1570. '''
  1571. This tests the case where require, order, and failhard are all used together in a state definition.
  1572. Previously, the order option, which used in tandem with require and failhard, would cause the state
  1573. compiler to stacktrace. This exposed a logic error in the ``check_failhard`` function of the state
  1574. compiler. With the logic error resolved, this test should now pass.
  1575. See https://github.com/saltstack/salt/issues/38683 for more information.
  1576. '''
  1577. state_run = self.run_function(
  1578. 'state.sls',
  1579. mods='requisites.require_order_failhard_combo'
  1580. )
  1581. state_id = 'test_|-b_|-b_|-fail_with_changes'
  1582. self.assertIn(state_id, state_run)
  1583. self.assertEqual(state_run[state_id]['comment'], 'Failure!')
  1584. self.assertFalse(state_run[state_id]['result'])
  1585. def test_issue_46762_prereqs_on_a_state_with_unfulfilled_requirements(self):
  1586. '''
  1587. This tests the case where state C requires state A, which fails.
  1588. State C is a pre-required state for State B.
  1589. Since state A fails, state C will not run because the requisite failed,
  1590. therefore state B will not run because state C failed to run.
  1591. See https://github.com/saltstack/salt/issues/46762 for
  1592. more information.
  1593. '''
  1594. state_run = self.run_function(
  1595. 'state.sls',
  1596. mods='issue-46762'
  1597. )
  1598. state_id = 'test_|-a_|-a_|-fail_without_changes'
  1599. self.assertIn(state_id, state_run)
  1600. self.assertEqual(state_run[state_id]['comment'],
  1601. 'Failure!')
  1602. self.assertFalse(state_run[state_id]['result'])
  1603. state_id = 'test_|-b_|-b_|-nop'
  1604. self.assertIn(state_id, state_run)
  1605. self.assertEqual(state_run[state_id]['comment'],
  1606. 'One or more requisite failed: issue-46762.c')
  1607. self.assertFalse(state_run[state_id]['result'])
  1608. state_id = 'test_|-c_|-c_|-nop'
  1609. self.assertIn(state_id, state_run)
  1610. self.assertEqual(state_run[state_id]['comment'],
  1611. 'One or more requisite failed: issue-46762.a')
  1612. self.assertFalse(state_run[state_id]['result'])
  1613. def test_state_nonbase_environment(self):
  1614. '''
  1615. test state.sls with saltenv using a nonbase environment
  1616. with a salt source
  1617. '''
  1618. filename = os.path.join(TMP, 'nonbase_env')
  1619. try:
  1620. ret = self.run_function(
  1621. 'state.sls',
  1622. mods='non-base-env',
  1623. saltenv='prod'
  1624. )
  1625. ret = ret[next(iter(ret))]
  1626. assert ret['result']
  1627. assert ret['comment'] == 'File {0} updated'.format(filename)
  1628. assert os.path.isfile(filename)
  1629. finally:
  1630. try:
  1631. os.remove(filename)
  1632. except OSError:
  1633. pass
  1634. @skipIf(sys.platform.startswith('win'), 'Skipped until parallel states can be fixed on Windows')
  1635. def test_parallel_state_with_long_tag(self):
  1636. '''
  1637. This tests the case where the state being executed has a long ID dec or
  1638. name and states are being run in parallel. The filenames used for the
  1639. parallel state cache were previously based on the tag for each chunk,
  1640. and longer ID decs or name params can cause the cache file to be longer
  1641. than the operating system's max file name length. To counter this we
  1642. instead generate a SHA1 hash of the chunk's tag to use as the cache
  1643. filename. This test will ensure that long tags don't cause caching
  1644. failures.
  1645. See https://github.com/saltstack/salt/issues/49738 for more info.
  1646. '''
  1647. short_command = 'helloworld'
  1648. long_command = short_command * 25
  1649. ret = self.run_function(
  1650. 'state.sls',
  1651. mods='issue-49738',
  1652. pillar={'short_command': short_command,
  1653. 'long_command': long_command}
  1654. )
  1655. comments = sorted([x['comment'] for x in six.itervalues(ret)])
  1656. expected = sorted(['Command "{0}" run'.format(x)
  1657. for x in (short_command, long_command)])
  1658. assert comments == expected, '{0} != {1}'.format(comments, expected)
  1659. def _add_runtime_pillar(self, pillar):
  1660. '''
  1661. helper class to add pillar data at runtime
  1662. '''
  1663. import salt.utils.yaml
  1664. with salt.utils.files.fopen(os.path.join(TMP_PILLAR_TREE,
  1665. 'pillar.sls'), 'w') as fp:
  1666. salt.utils.yaml.safe_dump(pillar, fp)
  1667. with salt.utils.files.fopen(os.path.join(TMP_PILLAR_TREE, 'top.sls'), 'w') as fp:
  1668. fp.write(textwrap.dedent('''\
  1669. base:
  1670. '*':
  1671. - pillar
  1672. '''))
  1673. self.run_function('saltutil.refresh_pillar')
  1674. self.run_function('test.sleep', [5])
  1675. def test_state_sls_id_test(self):
  1676. '''
  1677. test state.sls_id when test is set
  1678. to true in pillar data
  1679. '''
  1680. self._add_runtime_pillar(pillar={'test': True})
  1681. testfile = os.path.join(TMP, 'testfile')
  1682. comment = 'The file {0} is set to be changed\nNote: No changes made, actual changes may\nbe different due to other states.'.format(testfile)
  1683. ret = self.run_function('state.sls', ['core'])
  1684. for key, val in ret.items():
  1685. self.assertEqual(val['comment'], comment)
  1686. self.assertEqual(val['changes'], {'newfile': testfile})
  1687. def test_state_sls_id_test_state_test_post_run(self):
  1688. '''
  1689. test state.sls_id when test is set to
  1690. true post the state already being run previously
  1691. '''
  1692. file_name = os.path.join(TMP, 'testfile')
  1693. ret = self.run_function('state.sls', ['core'])
  1694. for key, val in ret.items():
  1695. self.assertEqual(val['comment'],
  1696. 'File {0} updated'.format(file_name))
  1697. self.assertEqual(val['changes']['diff'], 'New file')
  1698. self._add_runtime_pillar(pillar={'test': True})
  1699. ret = self.run_function('state.sls', ['core'])
  1700. for key, val in ret.items():
  1701. self.assertEqual(
  1702. val['comment'],
  1703. 'The file {0} is in the correct state'.format(file_name))
  1704. self.assertEqual(val['changes'], {})
  1705. def test_state_sls_id_test_true(self):
  1706. '''
  1707. test state.sls_id when test=True is passed as arg
  1708. '''
  1709. file_name = os.path.join(TMP, 'testfile')
  1710. ret = self.run_function('state.sls', ['core'], test=True)
  1711. for key, val in ret.items():
  1712. self.assertEqual(
  1713. val['comment'],
  1714. 'The file {0} is set to be changed\nNote: No changes made, actual changes may\nbe different due to other states.'.format(file_name))
  1715. self.assertEqual(val['changes'], {'newfile': file_name})
  1716. def test_state_sls_id_test_true_post_run(self):
  1717. '''
  1718. test state.sls_id when test is set to true as an
  1719. arg post the state already being run previously
  1720. '''
  1721. file_name = os.path.join(TMP, 'testfile')
  1722. ret = self.run_function('state.sls', ['core'])
  1723. for key, val in ret.items():
  1724. self.assertEqual(val['comment'],
  1725. 'File {0} updated'.format(file_name))
  1726. self.assertEqual(val['changes']['diff'], 'New file')
  1727. ret = self.run_function('state.sls', ['core'], test=True)
  1728. for key, val in ret.items():
  1729. self.assertEqual(
  1730. val['comment'],
  1731. 'The file {0} is in the correct state'.format(file_name))
  1732. self.assertEqual(val['changes'], {})
  1733. def test_state_sls_id_test_false_pillar_true(self):
  1734. '''
  1735. test state.sls_id when test is set to false as an
  1736. arg and minion_state_test is set to True. Should
  1737. return test=False.
  1738. '''
  1739. file_name = os.path.join(TMP, 'testfile')
  1740. self._add_runtime_pillar(pillar={'test': True})
  1741. ret = self.run_function('state.sls', ['core'], test=False)
  1742. for key, val in ret.items():
  1743. self.assertEqual(val['comment'],
  1744. 'File {0} updated'.format(file_name))
  1745. self.assertEqual(val['changes']['diff'], 'New file')
  1746. def test_issue_30161_unless_and_onlyif_together(self):
  1747. '''
  1748. test cmd.run using multiple unless options where the first cmd in the
  1749. list will pass, but the second will fail. This tests the fix for issue
  1750. #35384. (The fix is in PR #35545.)
  1751. '''
  1752. sls = self.run_function('state.sls', mods='issue-30161')
  1753. self.assertSaltTrueReturn(sls)
  1754. # We must assert against the comment here to make sure the comment reads that the
  1755. # command "echo "hello"" was run. This ensures that we made it to the last unless
  1756. # command in the state. If the comment reads "unless condition is true", or similar,
  1757. # then the unless state run bailed out after the first unless command succeeded,
  1758. # which is the bug we're regression testing for.
  1759. _expected = {'file_|-unless_false_onlyif_false_|-{0}{1}test.txt_|-managed'.format(TMP, os.path.sep):
  1760. {'comment': 'onlyif condition is false\nunless condition is false',
  1761. 'name': '{0}{1}test.txt'.format(TMP, os.path.sep),
  1762. 'skip_watch': True,
  1763. 'changes': {},
  1764. 'result': True},
  1765. 'file_|-unless_false_onlyif_true_|-{0}{1}test.txt_|-managed'.format(TMP, os.path.sep):
  1766. {'comment': 'Empty file',
  1767. 'pchanges': {},
  1768. 'name': '{0}{1}test.txt'.format(TMP, os.path.sep),
  1769. 'start_time': '18:10:20.341753',
  1770. 'result': True,
  1771. 'changes': {'new': 'file {0}{1}test.txt created'.format(TMP, os.path.sep)}},
  1772. 'file_|-unless_true_onlyif_false_|-{0}{1}test.txt_|-managed'.format(TMP, os.path.sep):
  1773. {'comment': 'onlyif condition is false\nunless condition is true',
  1774. 'name': '{0}{1}test.txt'.format(TMP, os.path.sep),
  1775. 'start_time': '18:10:22.936446',
  1776. 'skip_watch': True,
  1777. 'changes': {},
  1778. 'result': True},
  1779. 'file_|-unless_true_onlyif_true_|-{0}{1}test.txt_|-managed'.format(TMP, os.path.sep):
  1780. {'comment': 'onlyif condition is true\nunless condition is true',
  1781. 'name': '{0}{1}test.txt'.format(TMP, os.path.sep),
  1782. 'skip_watch': True,
  1783. 'changes': {},
  1784. 'result': True}}
  1785. for id in _expected:
  1786. self.assertEqual(sls[id]['comment'], _expected[id]['comment'])
  1787. def test_state_sls_unicode_characters(self):
  1788. '''
  1789. test state.sls when state file contains non-ascii characters
  1790. '''
  1791. ret = self.run_function('state.sls', ['issue-46672'])
  1792. log.debug('== ret %s ==', type(ret))
  1793. _expected = "cmd_|-echo1_|-echo 'This is Æ test!'_|-run"
  1794. self.assertIn(_expected, ret)
  1795. def test_state_sls_unicode_characters_cmd_output(self):
  1796. '''
  1797. test the output from running and echo command with non-ascii
  1798. characters.
  1799. '''
  1800. ret = self.run_function('state.sls', ['issue-46672-a'])
  1801. key = list(ret.keys())[0]
  1802. log.debug('== ret %s ==', type(ret))
  1803. _expected = 'This is Æ test!'
  1804. if salt.utils.platform.is_windows():
  1805. # Windows cmd.exe will mangle the output using cmd's codepage.
  1806. if six.PY2:
  1807. _expected = "'This is A+ test!'"
  1808. else:
  1809. _expected = "'This is ’ test!'"
  1810. self.assertEqual(_expected, ret[key]['changes']['stdout'])
  1811. def tearDown(self):
  1812. nonbase_file = os.path.join(TMP, 'nonbase_env')
  1813. if os.path.isfile(nonbase_file):
  1814. os.remove(nonbase_file)
  1815. # remove old pillar data
  1816. for filename in os.listdir(TMP_PILLAR_TREE):
  1817. os.remove(os.path.join(TMP_PILLAR_TREE, filename))
  1818. self.run_function('saltutil.refresh_pillar')
  1819. self.run_function('test.sleep', [5])
  1820. # remove testfile added in core.sls state file
  1821. state_file = os.path.join(TMP, 'testfile')
  1822. if os.path.isfile(state_file):
  1823. os.remove(state_file)
  1824. # remove testfile added in issue-30161.sls state file
  1825. state_file = os.path.join(TMP, 'test.txt')
  1826. if os.path.isfile(state_file):
  1827. os.remove(state_file)
  1828. def test_state_sls_integer_name(self):
  1829. '''
  1830. This tests the case where the state file is named
  1831. only with integers
  1832. '''
  1833. state_run = self.run_function(
  1834. 'state.sls',
  1835. mods='12345'
  1836. )
  1837. state_id = 'test_|-always-passes_|-always-passes_|-succeed_without_changes'
  1838. self.assertIn(state_id, state_run)
  1839. self.assertEqual(state_run[state_id]['comment'],
  1840. 'Success!')
  1841. self.assertTrue(state_run[state_id]['result'])