test_client.py 8.8 KB

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