test_client.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. import logging
  2. import os
  3. import time
  4. import pytest
  5. import salt.config
  6. import salt.netapi
  7. from salt.exceptions import EauthAuthenticationError
  8. from tests.support.case import SSHCase
  9. from tests.support.helpers import SaveRequestsPostHandler, Webserver, slowTest
  10. from tests.support.mixins import AdaptedConfigurationTestCaseMixin
  11. from tests.support.mock import patch
  12. from tests.support.runtests import RUNTIME_VARS
  13. from tests.support.unit import TestCase, skipIf
  14. log = logging.getLogger(__name__)
  15. @pytest.mark.usefixtures("salt_master", "salt_sub_minion")
  16. class NetapiClientTest(TestCase):
  17. eauth_creds = {
  18. "username": "saltdev_auto",
  19. "password": "saltdev",
  20. "eauth": "auto",
  21. }
  22. def setUp(self):
  23. """
  24. Set up a NetapiClient instance
  25. """
  26. opts = AdaptedConfigurationTestCaseMixin.get_config("client_config").copy()
  27. self.netapi = salt.netapi.NetapiClient(opts)
  28. def tearDown(self):
  29. del self.netapi
  30. @slowTest
  31. def test_local(self):
  32. low = {"client": "local", "tgt": "*", "fun": "test.ping", "timeout": 300}
  33. low.update(self.eauth_creds)
  34. ret = self.netapi.run(low)
  35. # If --proxy is set, it will cause an extra minion_id to be in the
  36. # response. Since there's not a great way to know if the test
  37. # runner's proxy minion is running, and we're not testing proxy
  38. # minions here anyway, just remove it from the response.
  39. ret.pop("proxytest", None)
  40. self.assertEqual(ret, {"minion": True, "sub_minion": True})
  41. @slowTest
  42. def test_local_batch(self):
  43. low = {"client": "local_batch", "tgt": "*", "fun": "test.ping", "timeout": 300}
  44. low.update(self.eauth_creds)
  45. ret = self.netapi.run(low)
  46. rets = []
  47. for _ret in ret:
  48. rets.append(_ret)
  49. self.assertIn({"sub_minion": True}, rets)
  50. self.assertIn({"minion": True}, rets)
  51. def test_local_async(self):
  52. low = {"client": "local_async", "tgt": "*", "fun": "test.ping"}
  53. low.update(self.eauth_creds)
  54. ret = self.netapi.run(low)
  55. # Remove all the volatile values before doing the compare.
  56. self.assertIn("jid", ret)
  57. ret.pop("jid", None)
  58. ret["minions"] = sorted(ret["minions"])
  59. try:
  60. # If --proxy is set, it will cause an extra minion_id to be in the
  61. # response. Since there's not a great way to know if the test
  62. # runner's proxy minion is running, and we're not testing proxy
  63. # minions here anyway, just remove it from the response.
  64. ret["minions"].remove("proxytest")
  65. except ValueError:
  66. pass
  67. self.assertEqual(ret, {"minions": sorted(["minion", "sub_minion"])})
  68. def test_local_unauthenticated(self):
  69. low = {"client": "local", "tgt": "*", "fun": "test.ping"}
  70. with self.assertRaises(EauthAuthenticationError) as excinfo:
  71. ret = self.netapi.run(low)
  72. @slowTest
  73. def test_wheel(self):
  74. low = {"client": "wheel", "fun": "key.list_all"}
  75. low.update(self.eauth_creds)
  76. ret = self.netapi.run(low)
  77. # Remove all the volatile values before doing the compare.
  78. self.assertIn("tag", ret)
  79. ret.pop("tag")
  80. data = ret.get("data", {})
  81. self.assertIn("jid", data)
  82. data.pop("jid", None)
  83. self.assertIn("tag", data)
  84. data.pop("tag", None)
  85. ret.pop("_stamp", None)
  86. data.pop("_stamp", None)
  87. self.maxDiff = None
  88. self.assertTrue(
  89. {"master.pem", "master.pub"}.issubset(set(ret["data"]["return"]["local"]))
  90. )
  91. @slowTest
  92. def test_wheel_async(self):
  93. # Give this test a little breathing room
  94. time.sleep(3)
  95. low = {"client": "wheel_async", "fun": "key.list_all"}
  96. low.update(self.eauth_creds)
  97. ret = self.netapi.run(low)
  98. self.assertIn("jid", ret)
  99. self.assertIn("tag", ret)
  100. def test_wheel_unauthenticated(self):
  101. low = {"client": "wheel", "tgt": "*", "fun": "test.ping"}
  102. with self.assertRaises(EauthAuthenticationError) as excinfo:
  103. ret = self.netapi.run(low)
  104. @skipIf(True, "This is not testing anything. Skipping for now.")
  105. def test_runner(self):
  106. # TODO: fix race condition in init of event-- right now the event class
  107. # will finish init even if the underlying zmq socket hasn't connected yet
  108. # this is problematic for the runnerclient's master_call method if the
  109. # runner is quick
  110. # low = {'client': 'runner', 'fun': 'cache.grains'}
  111. low = {"client": "runner", "fun": "test.sleep", "arg": [2]}
  112. low.update(self.eauth_creds)
  113. ret = self.netapi.run(low)
  114. @skipIf(True, "This is not testing anything. Skipping for now.")
  115. def test_runner_async(self):
  116. low = {"client": "runner", "fun": "cache.grains"}
  117. low.update(self.eauth_creds)
  118. ret = self.netapi.run(low)
  119. def test_runner_unauthenticated(self):
  120. low = {"client": "runner", "tgt": "*", "fun": "test.ping"}
  121. with self.assertRaises(EauthAuthenticationError) as excinfo:
  122. ret = self.netapi.run(low)
  123. @pytest.mark.requires_sshd_server
  124. class NetapiSSHClientTest(SSHCase):
  125. eauth_creds = {
  126. "username": "saltdev_auto",
  127. "password": "saltdev",
  128. "eauth": "auto",
  129. }
  130. def setUp(self):
  131. """
  132. Set up a NetapiClient instance
  133. """
  134. opts = AdaptedConfigurationTestCaseMixin.get_config("client_config").copy()
  135. self.netapi = salt.netapi.NetapiClient(opts)
  136. self.priv_file = os.path.join(RUNTIME_VARS.TMP_SSH_CONF_DIR, "client_key")
  137. self.rosters = os.path.join(RUNTIME_VARS.TMP_CONF_DIR)
  138. # Initialize salt-ssh
  139. self.run_function("test.ping")
  140. def tearDown(self):
  141. del self.netapi
  142. @classmethod
  143. def setUpClass(cls):
  144. cls.post_webserver = Webserver(handler=SaveRequestsPostHandler)
  145. cls.post_webserver.start()
  146. cls.post_web_root = cls.post_webserver.web_root
  147. cls.post_web_handler = cls.post_webserver.handler
  148. @classmethod
  149. def tearDownClass(cls):
  150. cls.post_webserver.stop()
  151. del cls.post_webserver
  152. @slowTest
  153. def test_ssh(self):
  154. low = {
  155. "client": "ssh",
  156. "tgt": "localhost",
  157. "fun": "test.ping",
  158. "ignore_host_keys": True,
  159. "roster_file": "roster",
  160. "rosters": [self.rosters],
  161. "ssh_priv": self.priv_file,
  162. }
  163. low.update(self.eauth_creds)
  164. ret = self.netapi.run(low)
  165. self.assertIn("localhost", ret)
  166. self.assertIn("return", ret["localhost"])
  167. self.assertEqual(ret["localhost"]["return"], True)
  168. self.assertEqual(ret["localhost"]["id"], "localhost")
  169. self.assertEqual(ret["localhost"]["fun"], "test.ping")
  170. @slowTest
  171. def test_ssh_unauthenticated(self):
  172. low = {"client": "ssh", "tgt": "localhost", "fun": "test.ping"}
  173. with self.assertRaises(EauthAuthenticationError) as excinfo:
  174. ret = self.netapi.run(low)
  175. @slowTest
  176. def test_ssh_unauthenticated_raw_shell_curl(self):
  177. fun = "-o ProxyCommand curl {}".format(self.post_web_root)
  178. low = {"client": "ssh", "tgt": "localhost", "fun": fun, "raw_shell": True}
  179. ret = None
  180. with self.assertRaises(EauthAuthenticationError) as excinfo:
  181. ret = self.netapi.run(low)
  182. self.assertEqual(self.post_web_handler.received_requests, [])
  183. self.assertEqual(ret, None)
  184. @slowTest
  185. def test_ssh_unauthenticated_raw_shell_touch(self):
  186. badfile = os.path.join(RUNTIME_VARS.TMP, "badfile.txt")
  187. fun = "-o ProxyCommand touch {}".format(badfile)
  188. low = {"client": "ssh", "tgt": "localhost", "fun": fun, "raw_shell": True}
  189. ret = None
  190. with self.assertRaises(EauthAuthenticationError) as excinfo:
  191. ret = self.netapi.run(low)
  192. self.assertEqual(ret, None)
  193. self.assertFalse(os.path.exists("badfile.txt"))
  194. @slowTest
  195. def test_ssh_authenticated_raw_shell_disabled(self):
  196. badfile = os.path.join(RUNTIME_VARS.TMP, "badfile.txt")
  197. fun = "-o ProxyCommand touch {}".format(badfile)
  198. low = {"client": "ssh", "tgt": "localhost", "fun": fun, "raw_shell": True}
  199. low.update(self.eauth_creds)
  200. ret = None
  201. with patch.dict(self.netapi.opts, {"netapi_allow_raw_shell": False}):
  202. with self.assertRaises(EauthAuthenticationError) as excinfo:
  203. ret = self.netapi.run(low)
  204. self.assertEqual(ret, None)
  205. self.assertFalse(os.path.exists("badfile.txt"))