test_state.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839
  1. # -*- coding: utf-8 -*-
  2. """
  3. Tests for the state runner
  4. """
  5. # Import Python Libs
  6. from __future__ import absolute_import, print_function, unicode_literals
  7. import errno
  8. import logging
  9. import os
  10. import shutil
  11. import signal
  12. import tempfile
  13. import textwrap
  14. import threading
  15. import time
  16. # Import Salt Libs
  17. import salt.exceptions
  18. import salt.utils.event
  19. import salt.utils.files
  20. import salt.utils.json
  21. import salt.utils.platform
  22. import salt.utils.stringutils
  23. import salt.utils.yaml
  24. # Import 3rd-party libs
  25. from salt.ext import six
  26. from salt.ext.six.moves import queue
  27. from tests.support.case import ShellCase
  28. from tests.support.helpers import expensiveTest, flaky
  29. from tests.support.mock import MagicMock, patch
  30. # Import Salt Testing Libs
  31. from tests.support.runtests import RUNTIME_VARS
  32. from tests.support.unit import skipIf
  33. log = logging.getLogger(__name__)
  34. @flaky
  35. class StateRunnerTest(ShellCase):
  36. """
  37. Test the state runner.
  38. """
  39. def add_to_queue(self, q, cmd):
  40. """
  41. helper method to add salt-run
  42. return data to a queue
  43. """
  44. ret = self.run_run(cmd)
  45. q.put(ret)
  46. q.task_done()
  47. @skipIf(True, "SLOWTEST skip")
  48. def test_orchestrate_output(self):
  49. """
  50. Ensure the orchestrate runner outputs useful state data.
  51. In Issue #31330, the output only contains ['outputter:', ' highstate'],
  52. and not the full stateful return. This tests ensures we don't regress in that
  53. manner again.
  54. Also test against some sample "good" output that would be included in a correct
  55. orchestrate run.
  56. """
  57. ret_output = self.run_run("state.orchestrate orch.simple")
  58. bad_out = ["outputter:", " highstate"]
  59. good_out = [
  60. " Function: salt.state",
  61. " Result: True",
  62. "Succeeded: 1 (changed=1)",
  63. "Failed: 0",
  64. "Total states run: 1",
  65. ]
  66. # First, check that we don't have the "bad" output that was displaying in
  67. # Issue #31330 where only the highstate outputter was listed
  68. assert bad_out != ret_output
  69. # Now test that some expected good sample output is present in the return.
  70. for item in good_out:
  71. assert item in ret_output
  72. @skipIf(True, "SLOWTEST skip")
  73. def test_orchestrate_nested(self):
  74. """
  75. test salt-run state.orchestrate and failhard with nested orchestration
  76. """
  77. if os.path.exists("/tmp/ewu-2016-12-13"):
  78. os.remove("/tmp/ewu-2016-12-13")
  79. _, code = self.run_run("state.orchestrate nested-orch.outer", with_retcode=True)
  80. assert os.path.exists("/tmp/ewu-2016-12-13") is False
  81. assert code != 0
  82. @skipIf(True, "SLOWTEST skip")
  83. def test_orchestrate_with_mine(self):
  84. """
  85. test salt-run state.orchestrate with mine.get call in sls
  86. """
  87. fail_time = time.time() + 120
  88. self.run_run('mine.update "*"')
  89. exp_ret = "Succeeded: 1 (changed=1)"
  90. while True:
  91. ret = self.run_run("state.orchestrate orch.mine")
  92. try:
  93. assert exp_ret in ret
  94. break
  95. except AssertionError:
  96. if time.time() > fail_time:
  97. self.fail(
  98. '"{0}" was not found in the orchestration call'.format(exp_ret)
  99. )
  100. @skipIf(True, "SLOWTEST skip")
  101. def test_orchestrate_state_and_function_failure(self):
  102. """
  103. Ensure that returns from failed minions are in the changes dict where
  104. they belong, so they can be programatically analyzed.
  105. See https://github.com/saltstack/salt/issues/43204
  106. """
  107. self.run_run("saltutil.sync_modules")
  108. ret = salt.utils.json.loads(
  109. "\n".join(self.run_run("state.orchestrate orch.issue43204 --out=json"))
  110. )
  111. # Drill down to the changes dict
  112. state_ret = ret["data"]["master"]["salt_|-Step01_|-Step01_|-state"]["changes"]
  113. func_ret = ret["data"]["master"][
  114. "salt_|-Step02_|-runtests_helpers.nonzero_retcode_return_false_|-function"
  115. ]["changes"]
  116. # Remove duration and start time from the results, since they would
  117. # vary with each run and that would make it impossible to test.
  118. for item in ("duration", "start_time"):
  119. state_ret["ret"]["minion"][
  120. "test_|-test fail with changes_|-test fail with changes_|-fail_with_changes"
  121. ].pop(item)
  122. self.assertEqual(
  123. state_ret,
  124. {
  125. "out": "highstate",
  126. "ret": {
  127. "minion": {
  128. "test_|-test fail with changes_|-test fail with changes_|-fail_with_changes": {
  129. "__id__": "test fail with changes",
  130. "__run_num__": 0,
  131. "__sls__": "orch.issue43204.fail_with_changes",
  132. "changes": {
  133. "testing": {
  134. "new": "Something pretended to change",
  135. "old": "Unchanged",
  136. }
  137. },
  138. "comment": "Failure!",
  139. "name": "test fail with changes",
  140. "result": False,
  141. }
  142. }
  143. },
  144. },
  145. )
  146. self.assertEqual(func_ret, {"out": "highstate", "ret": {"minion": False}})
  147. @skipIf(True, "SLOWTEST skip")
  148. def test_orchestrate_target_exists(self):
  149. """
  150. test orchestration when target exists
  151. while using multiple states
  152. """
  153. ret = self.run_run("state.orchestrate orch.target-exists")
  154. first = [" ID: core", " Function: salt.state", " Result: True"]
  155. second = [
  156. " ID: test-state",
  157. " Function: salt.state",
  158. " Result: True",
  159. ]
  160. third = [
  161. " ID: cmd.run",
  162. " Function: salt.function",
  163. " Result: True",
  164. ]
  165. ret_out = [first, second, third]
  166. for out in ret_out:
  167. for item in out:
  168. assert item in ret
  169. @skipIf(True, "SLOWTEST skip")
  170. def test_orchestrate_retcode(self):
  171. """
  172. Test orchestration with nonzero retcode set in __context__
  173. """
  174. self.run_run("saltutil.sync_runners")
  175. self.run_run("saltutil.sync_wheel")
  176. ret = "\n".join(self.run_run("state.orchestrate orch.retcode"))
  177. for result in (
  178. " ID: test_runner_success\n"
  179. " Function: salt.runner\n"
  180. " Name: runtests_helpers.success\n"
  181. " Result: True",
  182. " ID: test_runner_failure\n"
  183. " Function: salt.runner\n"
  184. " Name: runtests_helpers.failure\n"
  185. " Result: False",
  186. " ID: test_wheel_success\n"
  187. " Function: salt.wheel\n"
  188. " Name: runtests_helpers.success\n"
  189. " Result: True",
  190. " ID: test_wheel_failure\n"
  191. " Function: salt.wheel\n"
  192. " Name: runtests_helpers.failure\n"
  193. " Result: False",
  194. ):
  195. self.assertIn(result, ret)
  196. @skipIf(True, "SLOWTEST skip")
  197. def test_orchestrate_target_does_not_exist(self):
  198. """
  199. test orchestration when target doesn't exist
  200. while using multiple states
  201. """
  202. ret = self.run_run("state.orchestrate orch.target-does-not-exist")
  203. first = [
  204. "No minions matched the target. No command was sent, no jid was assigned.",
  205. " ID: core",
  206. " Function: salt.state",
  207. " Result: False",
  208. ]
  209. second = [
  210. " ID: test-state",
  211. " Function: salt.state",
  212. " Result: True",
  213. ]
  214. third = [
  215. " ID: cmd.run",
  216. " Function: salt.function",
  217. " Result: True",
  218. ]
  219. ret_out = [first, second, third]
  220. for out in ret_out:
  221. for item in out:
  222. assert item in ret
  223. @skipIf(True, "SLOWTEST skip")
  224. def test_orchestrate_batch_with_failhard_error(self):
  225. """
  226. test orchestration properly stops with failhard and batch.
  227. """
  228. ret = self.run_run("state.orchestrate orch.batch --out=json -l critical")
  229. ret_json = salt.utils.json.loads("\n".join(ret))
  230. retcode = ret_json["retcode"]
  231. result = ret_json["data"]["master"][
  232. "salt_|-call_fail_state_|-call_fail_state_|-state"
  233. ]["result"]
  234. changes = ret_json["data"]["master"][
  235. "salt_|-call_fail_state_|-call_fail_state_|-state"
  236. ]["changes"]
  237. # Looks like there is a platform differences in execution.
  238. # I see empty changes dict in MacOS for some reason. Maybe it's a bug?
  239. if changes:
  240. changes_ret = changes["ret"]
  241. # Debug
  242. print("Retcode: {}".format(retcode))
  243. print("Changes: {}".format(changes))
  244. print("Result: {}".format(result))
  245. assert retcode != 0
  246. assert result is False
  247. if changes:
  248. # The execution should stop after first error, so return dict should contain only one minion
  249. assert len(changes_ret) == 1
  250. @skipIf(True, "SLOWTEST skip")
  251. def test_state_event(self):
  252. """
  253. test to ensure state.event
  254. runner returns correct data
  255. """
  256. q = queue.Queue(maxsize=0)
  257. cmd = "state.event salt/job/*/new count=1"
  258. expect = '"minions": ["minion"]'
  259. server_thread = threading.Thread(target=self.add_to_queue, args=(q, cmd))
  260. server_thread.setDaemon(True)
  261. server_thread.start()
  262. while q.empty():
  263. self.run_salt("minion test.ping --static")
  264. out = q.get()
  265. assert expect in six.text_type(out)
  266. server_thread.join()
  267. @skipIf(True, "SLOWTEST skip")
  268. def test_orchestrate_subset(self):
  269. """
  270. test orchestration state using subset
  271. """
  272. ret = self.run_run("state.orchestrate orch.subset", timeout=500)
  273. def count(thing, listobj):
  274. return sum([obj.strip() == thing for obj in listobj])
  275. assert count("ID: test subset", ret) == 1
  276. assert count("Succeeded: 1", ret) == 1
  277. assert count("Failed: 0", ret) == 1
  278. @skipIf(True, "SLOWTEST skip")
  279. def test_orchestrate_salt_function_return_false_failure(self):
  280. """
  281. Ensure that functions that only return False in the return
  282. are flagged as failed when run as orchestrations.
  283. See https://github.com/saltstack/salt/issues/30367
  284. """
  285. self.run_run("saltutil.sync_modules")
  286. ret = salt.utils.json.loads(
  287. "\n".join(self.run_run("state.orchestrate orch.issue30367 --out=json"))
  288. )
  289. # Drill down to the changes dict
  290. state_result = ret["data"]["master"][
  291. "salt_|-deploy_check_|-test.false_|-function"
  292. ]["result"]
  293. func_ret = ret["data"]["master"]["salt_|-deploy_check_|-test.false_|-function"][
  294. "changes"
  295. ]
  296. assert state_result is False
  297. self.assertEqual(func_ret, {"out": "highstate", "ret": {"minion": False}})
  298. @skipIf(salt.utils.platform.is_windows(), "*NIX-only test")
  299. @flaky
  300. class OrchEventTest(ShellCase):
  301. """
  302. Tests for orchestration events
  303. """
  304. def setUp(self):
  305. self.timeout = 60
  306. self.master_d_dir = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "master.d")
  307. try:
  308. os.makedirs(self.master_d_dir)
  309. except OSError as exc:
  310. if exc.errno != errno.EEXIST:
  311. raise
  312. self.conf = tempfile.NamedTemporaryFile(
  313. mode="w", suffix=".conf", dir=self.master_d_dir, delete=True,
  314. )
  315. self.base_env = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
  316. self.addCleanup(shutil.rmtree, self.base_env)
  317. self.addCleanup(self.conf.close)
  318. for attr in ("timeout", "master_d_dir", "conf", "base_env"):
  319. self.addCleanup(delattr, self, attr)
  320. # Force a reload of the configuration now that our temp config file has
  321. # been removed.
  322. self.addCleanup(self.run_run_plus, "test.arg", __reload_config=True)
  323. def alarm_handler(self, signal, frame):
  324. raise Exception("Timeout of {0} seconds reached".format(self.timeout))
  325. def write_conf(self, data):
  326. """
  327. Dump the config dict to the conf file
  328. """
  329. self.conf.write(salt.utils.yaml.safe_dump(data, default_flow_style=False))
  330. self.conf.flush()
  331. @expensiveTest
  332. def test_jid_in_ret_event(self):
  333. """
  334. Test to confirm that the ret event for the orchestration contains the
  335. jid for the jobs spawned.
  336. """
  337. self.write_conf(
  338. {"fileserver_backend": ["roots"], "file_roots": {"base": [self.base_env]}}
  339. )
  340. state_sls = os.path.join(self.base_env, "test_state.sls")
  341. with salt.utils.files.fopen(state_sls, "w") as fp_:
  342. fp_.write(
  343. salt.utils.stringutils.to_str(
  344. textwrap.dedent(
  345. """
  346. date:
  347. cmd.run
  348. """
  349. )
  350. )
  351. )
  352. orch_sls = os.path.join(self.base_env, "test_orch.sls")
  353. with salt.utils.files.fopen(orch_sls, "w") as fp_:
  354. fp_.write(
  355. salt.utils.stringutils.to_str(
  356. textwrap.dedent(
  357. """
  358. date_cmd:
  359. salt.state:
  360. - tgt: minion
  361. - sls: test_state
  362. ping_minion:
  363. salt.function:
  364. - name: test.ping
  365. - tgt: minion
  366. fileserver.file_list:
  367. salt.runner
  368. config.values:
  369. salt.wheel
  370. """
  371. )
  372. )
  373. )
  374. listener = salt.utils.event.get_event(
  375. "master",
  376. sock_dir=self.master_opts["sock_dir"],
  377. transport=self.master_opts["transport"],
  378. opts=self.master_opts,
  379. )
  380. jid = self.run_run_plus(
  381. "state.orchestrate", "test_orch", __reload_config=True
  382. ).get("jid")
  383. if jid is None:
  384. raise Exception("jid missing from run_run_plus output")
  385. signal.signal(signal.SIGALRM, self.alarm_handler)
  386. signal.alarm(self.timeout)
  387. try:
  388. while True:
  389. event = listener.get_event(full=True)
  390. if event is None:
  391. continue
  392. if event["tag"] == "salt/run/{0}/ret".format(jid):
  393. # Don't wrap this in a try/except. We want to know if the
  394. # data structure is different from what we expect!
  395. ret = event["data"]["return"]["data"]["master"]
  396. for job in ret:
  397. self.assertTrue("__jid__" in ret[job])
  398. break
  399. finally:
  400. del listener
  401. signal.alarm(0)
  402. @expensiveTest
  403. def test_parallel_orchestrations(self):
  404. """
  405. Test to confirm that the parallel state requisite works in orch
  406. we do this by running 10 test.sleep's of 10 seconds, and insure it only takes roughly 10s
  407. """
  408. self.write_conf(
  409. {"fileserver_backend": ["roots"], "file_roots": {"base": [self.base_env]}}
  410. )
  411. orch_sls = os.path.join(self.base_env, "test_par_orch.sls")
  412. with salt.utils.files.fopen(orch_sls, "w") as fp_:
  413. fp_.write(
  414. textwrap.dedent(
  415. """
  416. {% for count in range(1, 20) %}
  417. sleep {{ count }}:
  418. module.run:
  419. - name: test.sleep
  420. - length: 10
  421. - parallel: True
  422. {% endfor %}
  423. sleep 21:
  424. module.run:
  425. - name: test.sleep
  426. - length: 10
  427. - parallel: True
  428. - require:
  429. - module: sleep 1
  430. """
  431. )
  432. )
  433. orch_sls = os.path.join(self.base_env, "test_par_orch.sls")
  434. listener = salt.utils.event.get_event(
  435. "master",
  436. sock_dir=self.master_opts["sock_dir"],
  437. transport=self.master_opts["transport"],
  438. opts=self.master_opts,
  439. )
  440. start_time = time.time()
  441. jid = self.run_run_plus(
  442. "state.orchestrate", "test_par_orch", __reload_config=True
  443. ).get("jid")
  444. if jid is None:
  445. raise Exception("jid missing from run_run_plus output")
  446. signal.signal(signal.SIGALRM, self.alarm_handler)
  447. signal.alarm(self.timeout)
  448. received = False
  449. try:
  450. while True:
  451. event = listener.get_event(full=True)
  452. if event is None:
  453. continue
  454. # if we receive the ret for this job before self.timeout (60),
  455. # the test is implicitly successful; if it were happening in serial it would be
  456. # atleast 110 seconds.
  457. if event["tag"] == "salt/run/{0}/ret".format(jid):
  458. received = True
  459. # Don't wrap this in a try/except. We want to know if the
  460. # data structure is different from what we expect!
  461. ret = event["data"]["return"]["data"]["master"]
  462. for state in ret:
  463. data = ret[state]
  464. # we expect each duration to be greater than 10s
  465. self.assertTrue(data["duration"] > 10000)
  466. break
  467. # self confirm that the total runtime is roughly 30s (left 10s for buffer)
  468. self.assertTrue((time.time() - start_time) < 40)
  469. finally:
  470. self.assertTrue(received)
  471. del listener
  472. signal.alarm(0)
  473. @expensiveTest
  474. def test_orchestration_soft_kill(self):
  475. """
  476. Test to confirm that the parallel state requisite works in orch
  477. we do this by running 10 test.sleep's of 10 seconds, and insure it only takes roughly 10s
  478. """
  479. self.write_conf(
  480. {"fileserver_backend": ["roots"], "file_roots": {"base": [self.base_env]}}
  481. )
  482. orch_sls = os.path.join(self.base_env, "two_stage_orch_kill.sls")
  483. with salt.utils.files.fopen(orch_sls, "w") as fp_:
  484. fp_.write(
  485. textwrap.dedent(
  486. """
  487. stage_one:
  488. test.succeed_without_changes
  489. stage_two:
  490. test.fail_without_changes
  491. """
  492. )
  493. )
  494. listener = salt.utils.event.get_event(
  495. "master",
  496. sock_dir=self.master_opts["sock_dir"],
  497. transport=self.master_opts["transport"],
  498. opts=self.master_opts,
  499. )
  500. mock_jid = "20131219120000000000"
  501. self.run_run("state.soft_kill {0} stage_two".format(mock_jid))
  502. with patch("salt.utils.jid.gen_jid", MagicMock(return_value=mock_jid)):
  503. jid = self.run_run_plus(
  504. "state.orchestrate", "two_stage_orch_kill", __reload_config=True
  505. ).get("jid")
  506. if jid is None:
  507. raise Exception("jid missing from run_run_plus output")
  508. signal.signal(signal.SIGALRM, self.alarm_handler)
  509. signal.alarm(self.timeout)
  510. received = False
  511. try:
  512. while True:
  513. event = listener.get_event(full=True)
  514. if event is None:
  515. continue
  516. # Ensure that stage_two of the state does not run
  517. if event["tag"] == "salt/run/{0}/ret".format(jid):
  518. received = True
  519. # Don't wrap this in a try/except. We want to know if the
  520. # data structure is different from what we expect!
  521. ret = event["data"]["return"]["data"]["master"]
  522. self.assertNotIn(
  523. "test_|-stage_two_|-stage_two_|-fail_without_changes", ret
  524. )
  525. break
  526. finally:
  527. self.assertTrue(received)
  528. del listener
  529. signal.alarm(0)
  530. @skipIf(True, "SLOWTEST skip")
  531. def test_orchestration_with_pillar_dot_items(self):
  532. """
  533. Test to confirm when using a state file that includes other state file, if
  534. one of those state files includes pillar related functions that will not
  535. be pulling from the pillar cache that all the state files are available and
  536. the file_roots has been preserved. See issues #48277 and #46986.
  537. """
  538. self.write_conf(
  539. {"fileserver_backend": ["roots"], "file_roots": {"base": [self.base_env]}}
  540. )
  541. orch_sls = os.path.join(self.base_env, "main.sls")
  542. with salt.utils.files.fopen(orch_sls, "w") as fp_:
  543. fp_.write(
  544. textwrap.dedent(
  545. """
  546. include:
  547. - one
  548. - two
  549. - three
  550. """
  551. )
  552. )
  553. orch_sls = os.path.join(self.base_env, "one.sls")
  554. with salt.utils.files.fopen(orch_sls, "w") as fp_:
  555. fp_.write(
  556. textwrap.dedent(
  557. """
  558. {%- set foo = salt['saltutil.runner']('pillar.show_pillar') %}
  559. placeholder_one:
  560. test.succeed_without_changes
  561. """
  562. )
  563. )
  564. orch_sls = os.path.join(self.base_env, "two.sls")
  565. with salt.utils.files.fopen(orch_sls, "w") as fp_:
  566. fp_.write(
  567. textwrap.dedent(
  568. """
  569. placeholder_two:
  570. test.succeed_without_changes
  571. """
  572. )
  573. )
  574. orch_sls = os.path.join(self.base_env, "three.sls")
  575. with salt.utils.files.fopen(orch_sls, "w") as fp_:
  576. fp_.write(
  577. textwrap.dedent(
  578. """
  579. placeholder_three:
  580. test.succeed_without_changes
  581. """
  582. )
  583. )
  584. orch_sls = os.path.join(self.base_env, "main.sls")
  585. listener = salt.utils.event.get_event(
  586. "master",
  587. sock_dir=self.master_opts["sock_dir"],
  588. transport=self.master_opts["transport"],
  589. opts=self.master_opts,
  590. )
  591. jid = self.run_run_plus("state.orchestrate", "main", __reload_config=True).get(
  592. "jid"
  593. )
  594. if jid is None:
  595. raise salt.exceptions.SaltInvocationError(
  596. "jid missing from run_run_plus output"
  597. )
  598. signal.signal(signal.SIGALRM, self.alarm_handler)
  599. signal.alarm(self.timeout)
  600. received = False
  601. try:
  602. while True:
  603. event = listener.get_event(full=True)
  604. if event is None:
  605. continue
  606. if event.get("tag", "") == "salt/run/{0}/ret".format(jid):
  607. received = True
  608. # Don't wrap this in a try/except. We want to know if the
  609. # data structure is different from what we expect!
  610. ret = event["data"]["return"]["data"]["master"]
  611. for state in ret:
  612. data = ret[state]
  613. # Each state should be successful
  614. self.assertEqual(data["comment"], "Success!")
  615. break
  616. finally:
  617. self.assertTrue(received)
  618. del listener
  619. signal.alarm(0)
  620. @skipIf(True, "SLOWTEST skip")
  621. def test_orchestration_onchanges_and_prereq(self):
  622. """
  623. Test to confirm that the parallel state requisite works in orch
  624. we do this by running 10 test.sleep's of 10 seconds, and insure it only takes roughly 10s
  625. """
  626. self.write_conf(
  627. {"fileserver_backend": ["roots"], "file_roots": {"base": [self.base_env]}}
  628. )
  629. orch_sls = os.path.join(self.base_env, "orch.sls")
  630. with salt.utils.files.fopen(orch_sls, "w") as fp_:
  631. fp_.write(
  632. textwrap.dedent(
  633. """
  634. manage_a_file:
  635. salt.state:
  636. - tgt: minion
  637. - sls:
  638. - orch.req_test
  639. do_onchanges:
  640. salt.function:
  641. - tgt: minion
  642. - name: test.ping
  643. - onchanges:
  644. - salt: manage_a_file
  645. do_prereq:
  646. salt.function:
  647. - tgt: minion
  648. - name: test.ping
  649. - prereq:
  650. - salt: manage_a_file
  651. """
  652. )
  653. )
  654. listener = salt.utils.event.get_event(
  655. "master",
  656. sock_dir=self.master_opts["sock_dir"],
  657. transport=self.master_opts["transport"],
  658. opts=self.master_opts,
  659. )
  660. try:
  661. jid1 = self.run_run_plus(
  662. "state.orchestrate", "orch", test=True, __reload_config=True
  663. ).get("jid")
  664. # Run for real to create the file
  665. self.run_run_plus("state.orchestrate", "orch", __reload_config=True).get(
  666. "jid"
  667. )
  668. # Run again in test mode. Since there were no changes, the
  669. # requisites should not fire.
  670. jid2 = self.run_run_plus(
  671. "state.orchestrate", "orch", test=True, __reload_config=True
  672. ).get("jid")
  673. finally:
  674. try:
  675. os.remove(os.path.join(RUNTIME_VARS.TMP, "orch.req_test"))
  676. except OSError:
  677. pass
  678. assert jid1 is not None
  679. assert jid2 is not None
  680. tags = {"salt/run/{0}/ret".format(x): x for x in (jid1, jid2)}
  681. ret = {}
  682. signal.signal(signal.SIGALRM, self.alarm_handler)
  683. signal.alarm(self.timeout)
  684. try:
  685. while True:
  686. event = listener.get_event(full=True)
  687. if event is None:
  688. continue
  689. if event["tag"] in tags:
  690. ret[tags.pop(event["tag"])] = self.repack_state_returns(
  691. event["data"]["return"]["data"]["master"]
  692. )
  693. if not tags:
  694. # If tags is empty, we've grabbed all the returns we
  695. # wanted, so let's stop listening to the event bus.
  696. break
  697. finally:
  698. del listener
  699. signal.alarm(0)
  700. for sls_id in ("manage_a_file", "do_onchanges", "do_prereq"):
  701. # The first time through, all three states should have a None
  702. # result, while the second time through, they should all have a
  703. # True result.
  704. assert (
  705. ret[jid1][sls_id]["result"] is None
  706. ), "result of {0} ({1}) is not None".format(
  707. sls_id, ret[jid1][sls_id]["result"]
  708. )
  709. assert (
  710. ret[jid2][sls_id]["result"] is True
  711. ), "result of {0} ({1}) is not True".format(
  712. sls_id, ret[jid2][sls_id]["result"]
  713. )
  714. # The file.managed state should have shown changes in the test mode
  715. # return data.
  716. assert ret[jid1]["manage_a_file"]["changes"]
  717. # After the file was created, running again in test mode should have
  718. # shown no changes.
  719. assert not ret[jid2]["manage_a_file"]["changes"], ret[jid2]["manage_a_file"][
  720. "changes"
  721. ]