test_client.py 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. """
  2. :codeauthor: Mike Place <mp@saltstack.com>
  3. """
  4. import salt.utils.platform
  5. from salt import client
  6. from salt.exceptions import (
  7. EauthAuthenticationError,
  8. SaltClientError,
  9. SaltInvocationError,
  10. SaltReqTimeoutError,
  11. )
  12. from tests.support.helpers import slowTest
  13. from tests.support.mixins import SaltClientTestCaseMixin
  14. from tests.support.mock import MagicMock, patch
  15. from tests.support.unit import TestCase, skipIf
  16. class LocalClientTestCase(TestCase, SaltClientTestCaseMixin):
  17. def test_job_result_return_success(self):
  18. """
  19. Should return the `expected_return`, since there is a job with the right jid.
  20. """
  21. minions = ()
  22. jid = "0815"
  23. raw_return = {"id": "fake-id", "jid": jid, "data": "", "return": "fake-return"}
  24. expected_return = {"fake-id": {"ret": "fake-return"}}
  25. local_client = client.LocalClient(mopts=self.get_temp_config("master"))
  26. local_client.event.get_event = MagicMock(return_value=raw_return)
  27. local_client.returners = MagicMock()
  28. ret = local_client.get_event_iter_returns(jid, minions)
  29. val = next(ret)
  30. self.assertEqual(val, expected_return)
  31. def test_job_result_return_failure(self):
  32. """
  33. We are _not_ getting a job return, because the jid is different. Instead we should
  34. get a StopIteration exception.
  35. """
  36. minions = ()
  37. jid = "0815"
  38. raw_return = {
  39. "id": "fake-id",
  40. "jid": "0816",
  41. "data": "",
  42. "return": "fake-return",
  43. }
  44. local_client = client.LocalClient(mopts=self.get_temp_config("master"))
  45. local_client.event.get_event = MagicMock()
  46. local_client.event.get_event.side_effect = [raw_return, None]
  47. local_client.returners = MagicMock()
  48. ret = local_client.get_event_iter_returns(jid, minions)
  49. with self.assertRaises(StopIteration):
  50. next(ret)
  51. def test_create_local_client(self):
  52. local_client = client.LocalClient(mopts=self.get_temp_config("master"))
  53. self.assertIsInstance(
  54. local_client,
  55. client.LocalClient,
  56. "LocalClient did not create a LocalClient instance",
  57. )
  58. def test_check_pub_data(self):
  59. just_minions = {"minions": ["m1", "m2"]}
  60. jid_no_minions = {"jid": "1234", "minions": []}
  61. valid_pub_data = {"minions": ["m1", "m2"], "jid": "1234"}
  62. self.assertRaises(EauthAuthenticationError, self.client._check_pub_data, "")
  63. self.assertDictEqual(
  64. {},
  65. self.client._check_pub_data(just_minions),
  66. "Did not handle lack of jid correctly",
  67. )
  68. self.assertDictEqual(
  69. {},
  70. self.client._check_pub_data({"jid": "0"}),
  71. "Passing JID of zero is not handled gracefully",
  72. )
  73. with patch.dict(self.client.opts, {}):
  74. self.client._check_pub_data(jid_no_minions)
  75. self.assertDictEqual(
  76. valid_pub_data, self.client._check_pub_data(valid_pub_data)
  77. )
  78. def test_cmd_subset(self):
  79. with patch(
  80. "salt.client.LocalClient.cmd",
  81. return_value={
  82. "minion1": ["first.func", "second.func"],
  83. "minion2": ["first.func", "second.func"],
  84. },
  85. ):
  86. with patch("salt.client.LocalClient.cmd_cli") as cmd_cli_mock:
  87. self.client.cmd_subset("*", "first.func", subset=1, cli=True)
  88. try:
  89. cmd_cli_mock.assert_called_with(
  90. ["minion2"],
  91. "first.func",
  92. (),
  93. progress=False,
  94. kwarg=None,
  95. tgt_type="list",
  96. full_return=False,
  97. ret="",
  98. )
  99. except AssertionError:
  100. cmd_cli_mock.assert_called_with(
  101. ["minion1"],
  102. "first.func",
  103. (),
  104. progress=False,
  105. kwarg=None,
  106. tgt_type="list",
  107. full_return=False,
  108. ret="",
  109. )
  110. self.client.cmd_subset("*", "first.func", subset=10, cli=True)
  111. try:
  112. cmd_cli_mock.assert_called_with(
  113. ["minion2", "minion1"],
  114. "first.func",
  115. (),
  116. progress=False,
  117. kwarg=None,
  118. tgt_type="list",
  119. full_return=False,
  120. ret="",
  121. )
  122. except AssertionError:
  123. cmd_cli_mock.assert_called_with(
  124. ["minion1", "minion2"],
  125. "first.func",
  126. (),
  127. progress=False,
  128. kwarg=None,
  129. tgt_type="list",
  130. full_return=False,
  131. ret="",
  132. )
  133. ret = self.client.cmd_subset(
  134. "*", "first.func", subset=1, cli=True, full_return=True
  135. )
  136. try:
  137. cmd_cli_mock.assert_called_with(
  138. ["minion2"],
  139. "first.func",
  140. (),
  141. progress=False,
  142. kwarg=None,
  143. tgt_type="list",
  144. full_return=True,
  145. ret="",
  146. )
  147. except AssertionError:
  148. cmd_cli_mock.assert_called_with(
  149. ["minion1"],
  150. "first.func",
  151. (),
  152. progress=False,
  153. kwarg=None,
  154. tgt_type="list",
  155. full_return=True,
  156. ret="",
  157. )
  158. @skipIf(salt.utils.platform.is_windows(), "Not supported on Windows")
  159. def test_pub(self):
  160. """
  161. Tests that the client cleanly returns when the publisher is not running
  162. Note: Requires ZeroMQ's IPC transport which is not supported on windows.
  163. """
  164. if self.get_config("minion")["transport"] != "zeromq":
  165. self.skipTest("This test only works with ZeroMQ")
  166. # Make sure we cleanly return if the publisher isn't running
  167. with patch("os.path.exists", return_value=False):
  168. self.assertRaises(
  169. SaltClientError, lambda: self.client.pub("*", "test.ping")
  170. )
  171. # Check nodegroups behavior
  172. with patch("os.path.exists", return_value=True):
  173. with patch.dict(
  174. self.client.opts,
  175. {
  176. "nodegroups": {
  177. "group1": "L@foo.domain.com,bar.domain.com,baz.domain.com or bl*.domain.com"
  178. }
  179. },
  180. ):
  181. # Do we raise an exception if the nodegroup can't be matched?
  182. self.assertRaises(
  183. SaltInvocationError,
  184. self.client.pub,
  185. "non_existent_group",
  186. "test.ping",
  187. tgt_type="nodegroup",
  188. )
  189. @skipIf(not salt.utils.platform.is_windows(), "Windows only test")
  190. @slowTest
  191. def test_pub_win32(self):
  192. """
  193. Tests that the client raises a timeout error when using ZeroMQ's TCP
  194. transport and publisher is not running.
  195. Note: Requires ZeroMQ's TCP transport, this is only the default on Windows.
  196. """
  197. if self.get_config("minion")["transport"] != "zeromq":
  198. self.skipTest("This test only works with ZeroMQ")
  199. # Make sure we cleanly return if the publisher isn't running
  200. with patch("os.path.exists", return_value=False):
  201. self.assertRaises(
  202. SaltReqTimeoutError, lambda: self.client.pub("*", "test.ping")
  203. )
  204. # Check nodegroups behavior
  205. with patch("os.path.exists", return_value=True):
  206. with patch.dict(
  207. self.client.opts,
  208. {
  209. "nodegroups": {
  210. "group1": "L@foo.domain.com,bar.domain.com,baz.domain.com or bl*.domain.com"
  211. }
  212. },
  213. ):
  214. # Do we raise an exception if the nodegroup can't be matched?
  215. self.assertRaises(
  216. SaltInvocationError,
  217. self.client.pub,
  218. "non_existent_group",
  219. "test.ping",
  220. tgt_type="nodegroup",
  221. )