test_client.py 8.7 KB

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