test_master.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754
  1. import time
  2. import salt.config
  3. import salt.master
  4. from tests.support.helpers import slowTest
  5. from tests.support.mixins import AdaptedConfigurationTestCaseMixin
  6. from tests.support.mock import MagicMock, patch
  7. from tests.support.unit import TestCase
  8. class TransportMethodsTest(TestCase):
  9. def test_transport_methods(self):
  10. class Foo(salt.master.TransportMethods):
  11. expose_methods = ["bar"]
  12. def bar(self):
  13. pass
  14. def bang(self):
  15. pass
  16. foo = Foo()
  17. assert foo.get_method("bar") is not None
  18. assert foo.get_method("bang") is None
  19. def test_aes_funcs_white(self):
  20. """
  21. Validate methods exposed on AESFuncs exist and are callable
  22. """
  23. opts = salt.config.master_config(None)
  24. aes_funcs = salt.master.AESFuncs(opts)
  25. for name in aes_funcs.expose_methods:
  26. func = getattr(aes_funcs, name, None)
  27. assert callable(func)
  28. def test_aes_funcs_black(self):
  29. """
  30. Validate methods on AESFuncs that should not be called remotely
  31. """
  32. opts = salt.config.master_config(None)
  33. aes_funcs = salt.master.AESFuncs(opts)
  34. # Any callable that should not explicitly be allowed should be added
  35. # here.
  36. blacklist_methods = [
  37. "_AESFuncs__setup_fileserver",
  38. "_AESFuncs__verify_load",
  39. "_AESFuncs__verify_minion",
  40. "_AESFuncs__verify_minion_publish",
  41. "__class__",
  42. "__delattr__",
  43. "__dir__",
  44. "__eq__",
  45. "__format__",
  46. "__ge__",
  47. "__getattribute__",
  48. "__gt__",
  49. "__hash__",
  50. "__init__",
  51. "__init_subclass__",
  52. "__le__",
  53. "__lt__",
  54. "__ne__",
  55. "__new__",
  56. "__reduce__",
  57. "__reduce_ex__",
  58. "__repr__",
  59. "__setattr__",
  60. "__sizeof__",
  61. "__str__",
  62. "__subclasshook__",
  63. "get_method",
  64. "run_func",
  65. ]
  66. for name in dir(aes_funcs):
  67. if name in aes_funcs.expose_methods:
  68. continue
  69. if not callable(getattr(aes_funcs, name)):
  70. continue
  71. assert name in blacklist_methods, name
  72. def test_clear_funcs_white(self):
  73. """
  74. Validate methods exposed on ClearFuncs exist and are callable
  75. """
  76. opts = salt.config.master_config(None)
  77. clear_funcs = salt.master.ClearFuncs(opts, {})
  78. for name in clear_funcs.expose_methods:
  79. func = getattr(clear_funcs, name, None)
  80. assert callable(func)
  81. def test_clear_funcs_black(self):
  82. """
  83. Validate methods on ClearFuncs that should not be called remotely
  84. """
  85. opts = salt.config.master_config(None)
  86. clear_funcs = salt.master.ClearFuncs(opts, {})
  87. blacklist_methods = [
  88. "__class__",
  89. "__delattr__",
  90. "__dir__",
  91. "__eq__",
  92. "__format__",
  93. "__ge__",
  94. "__getattribute__",
  95. "__gt__",
  96. "__hash__",
  97. "__init__",
  98. "__init_subclass__",
  99. "__le__",
  100. "__lt__",
  101. "__ne__",
  102. "__new__",
  103. "__reduce__",
  104. "__reduce_ex__",
  105. "__repr__",
  106. "__setattr__",
  107. "__sizeof__",
  108. "__str__",
  109. "__subclasshook__",
  110. "_prep_auth_info",
  111. "_prep_jid",
  112. "_prep_pub",
  113. "_send_pub",
  114. "_send_ssh_pub",
  115. "get_method",
  116. ]
  117. for name in dir(clear_funcs):
  118. if name in clear_funcs.expose_methods:
  119. continue
  120. if not callable(getattr(clear_funcs, name)):
  121. continue
  122. assert name in blacklist_methods, name
  123. class ClearFuncsTestCase(TestCase):
  124. """
  125. TestCase for salt.master.ClearFuncs class
  126. """
  127. @classmethod
  128. def setUpClass(cls):
  129. opts = salt.config.master_config(None)
  130. cls.clear_funcs = salt.master.ClearFuncs(opts, {})
  131. @classmethod
  132. def tearDownClass(cls):
  133. del cls.clear_funcs
  134. def test_get_method(self):
  135. assert getattr(self.clear_funcs, "_send_pub", None) is not None
  136. assert self.clear_funcs.get_method("_send_pub") is None
  137. # runner tests
  138. @slowTest
  139. def test_runner_token_not_authenticated(self):
  140. """
  141. Asserts that a TokenAuthenticationError is returned when the token can't authenticate.
  142. """
  143. mock_ret = {
  144. "error": {
  145. "name": "TokenAuthenticationError",
  146. "message": 'Authentication failure of type "token" occurred.',
  147. }
  148. }
  149. ret = self.clear_funcs.runner({"token": "asdfasdfasdfasdf"})
  150. self.assertDictEqual(mock_ret, ret)
  151. @slowTest
  152. def test_runner_token_authorization_error(self):
  153. """
  154. Asserts that a TokenAuthenticationError is returned when the token authenticates, but is
  155. not authorized.
  156. """
  157. token = "asdfasdfasdfasdf"
  158. clear_load = {"token": token, "fun": "test.arg"}
  159. mock_token = {"token": token, "eauth": "foo", "name": "test"}
  160. mock_ret = {
  161. "error": {
  162. "name": "TokenAuthenticationError",
  163. "message": 'Authentication failure of type "token" occurred '
  164. "for user test.",
  165. }
  166. }
  167. with patch(
  168. "salt.auth.LoadAuth.authenticate_token", MagicMock(return_value=mock_token)
  169. ), patch("salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=[])):
  170. ret = self.clear_funcs.runner(clear_load)
  171. self.assertDictEqual(mock_ret, ret)
  172. @slowTest
  173. def test_runner_token_salt_invocation_error(self):
  174. """
  175. Asserts that a SaltInvocationError is returned when the token authenticates, but the
  176. command is malformed.
  177. """
  178. token = "asdfasdfasdfasdf"
  179. clear_load = {"token": token, "fun": "badtestarg"}
  180. mock_token = {"token": token, "eauth": "foo", "name": "test"}
  181. mock_ret = {
  182. "error": {
  183. "name": "SaltInvocationError",
  184. "message": "A command invocation error occurred: Check syntax.",
  185. }
  186. }
  187. with patch(
  188. "salt.auth.LoadAuth.authenticate_token", MagicMock(return_value=mock_token)
  189. ), patch(
  190. "salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=["testing"])
  191. ):
  192. ret = self.clear_funcs.runner(clear_load)
  193. self.assertDictEqual(mock_ret, ret)
  194. @slowTest
  195. def test_runner_eauth_not_authenticated(self):
  196. """
  197. Asserts that an EauthAuthenticationError is returned when the user can't authenticate.
  198. """
  199. mock_ret = {
  200. "error": {
  201. "name": "EauthAuthenticationError",
  202. "message": 'Authentication failure of type "eauth" occurred for '
  203. "user UNKNOWN.",
  204. }
  205. }
  206. ret = self.clear_funcs.runner({"eauth": "foo"})
  207. self.assertDictEqual(mock_ret, ret)
  208. @slowTest
  209. def test_runner_eauth_authorization_error(self):
  210. """
  211. Asserts that an EauthAuthenticationError is returned when the user authenticates, but is
  212. not authorized.
  213. """
  214. clear_load = {"eauth": "foo", "username": "test", "fun": "test.arg"}
  215. mock_ret = {
  216. "error": {
  217. "name": "EauthAuthenticationError",
  218. "message": 'Authentication failure of type "eauth" occurred for '
  219. "user test.",
  220. }
  221. }
  222. with patch(
  223. "salt.auth.LoadAuth.authenticate_eauth", MagicMock(return_value=True)
  224. ), patch("salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=[])):
  225. ret = self.clear_funcs.runner(clear_load)
  226. self.assertDictEqual(mock_ret, ret)
  227. @slowTest
  228. def test_runner_eauth_salt_invocation_error(self):
  229. """
  230. Asserts that an EauthAuthenticationError is returned when the user authenticates, but the
  231. command is malformed.
  232. """
  233. clear_load = {"eauth": "foo", "username": "test", "fun": "bad.test.arg.func"}
  234. mock_ret = {
  235. "error": {
  236. "name": "SaltInvocationError",
  237. "message": "A command invocation error occurred: Check syntax.",
  238. }
  239. }
  240. with patch(
  241. "salt.auth.LoadAuth.authenticate_eauth", MagicMock(return_value=True)
  242. ), patch(
  243. "salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=["testing"])
  244. ):
  245. ret = self.clear_funcs.runner(clear_load)
  246. self.assertDictEqual(mock_ret, ret)
  247. @slowTest
  248. def test_runner_user_not_authenticated(self):
  249. """
  250. Asserts that an UserAuthenticationError is returned when the user can't authenticate.
  251. """
  252. mock_ret = {
  253. "error": {
  254. "name": "UserAuthenticationError",
  255. "message": 'Authentication failure of type "user" occurred',
  256. }
  257. }
  258. ret = self.clear_funcs.runner({})
  259. self.assertDictEqual(mock_ret, ret)
  260. # wheel tests
  261. @slowTest
  262. def test_wheel_token_not_authenticated(self):
  263. """
  264. Asserts that a TokenAuthenticationError is returned when the token can't authenticate.
  265. """
  266. mock_ret = {
  267. "error": {
  268. "name": "TokenAuthenticationError",
  269. "message": 'Authentication failure of type "token" occurred.',
  270. }
  271. }
  272. ret = self.clear_funcs.wheel({"token": "asdfasdfasdfasdf"})
  273. self.assertDictEqual(mock_ret, ret)
  274. @slowTest
  275. def test_wheel_token_authorization_error(self):
  276. """
  277. Asserts that a TokenAuthenticationError is returned when the token authenticates, but is
  278. not authorized.
  279. """
  280. token = "asdfasdfasdfasdf"
  281. clear_load = {"token": token, "fun": "test.arg"}
  282. mock_token = {"token": token, "eauth": "foo", "name": "test"}
  283. mock_ret = {
  284. "error": {
  285. "name": "TokenAuthenticationError",
  286. "message": 'Authentication failure of type "token" occurred '
  287. "for user test.",
  288. }
  289. }
  290. with patch(
  291. "salt.auth.LoadAuth.authenticate_token", MagicMock(return_value=mock_token)
  292. ), patch("salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=[])):
  293. ret = self.clear_funcs.wheel(clear_load)
  294. self.assertDictEqual(mock_ret, ret)
  295. @slowTest
  296. def test_wheel_token_salt_invocation_error(self):
  297. """
  298. Asserts that a SaltInvocationError is returned when the token authenticates, but the
  299. command is malformed.
  300. """
  301. token = "asdfasdfasdfasdf"
  302. clear_load = {"token": token, "fun": "badtestarg"}
  303. mock_token = {"token": token, "eauth": "foo", "name": "test"}
  304. mock_ret = {
  305. "error": {
  306. "name": "SaltInvocationError",
  307. "message": "A command invocation error occurred: Check syntax.",
  308. }
  309. }
  310. with patch(
  311. "salt.auth.LoadAuth.authenticate_token", MagicMock(return_value=mock_token)
  312. ), patch(
  313. "salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=["testing"])
  314. ):
  315. ret = self.clear_funcs.wheel(clear_load)
  316. self.assertDictEqual(mock_ret, ret)
  317. @slowTest
  318. def test_wheel_eauth_not_authenticated(self):
  319. """
  320. Asserts that an EauthAuthenticationError is returned when the user can't authenticate.
  321. """
  322. mock_ret = {
  323. "error": {
  324. "name": "EauthAuthenticationError",
  325. "message": 'Authentication failure of type "eauth" occurred for '
  326. "user UNKNOWN.",
  327. }
  328. }
  329. ret = self.clear_funcs.wheel({"eauth": "foo"})
  330. self.assertDictEqual(mock_ret, ret)
  331. @slowTest
  332. def test_wheel_eauth_authorization_error(self):
  333. """
  334. Asserts that an EauthAuthenticationError is returned when the user authenticates, but is
  335. not authorized.
  336. """
  337. clear_load = {"eauth": "foo", "username": "test", "fun": "test.arg"}
  338. mock_ret = {
  339. "error": {
  340. "name": "EauthAuthenticationError",
  341. "message": 'Authentication failure of type "eauth" occurred for '
  342. "user test.",
  343. }
  344. }
  345. with patch(
  346. "salt.auth.LoadAuth.authenticate_eauth", MagicMock(return_value=True)
  347. ), patch("salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=[])):
  348. ret = self.clear_funcs.wheel(clear_load)
  349. self.assertDictEqual(mock_ret, ret)
  350. @slowTest
  351. def test_wheel_eauth_salt_invocation_error(self):
  352. """
  353. Asserts that an EauthAuthenticationError is returned when the user authenticates, but the
  354. command is malformed.
  355. """
  356. clear_load = {"eauth": "foo", "username": "test", "fun": "bad.test.arg.func"}
  357. mock_ret = {
  358. "error": {
  359. "name": "SaltInvocationError",
  360. "message": "A command invocation error occurred: Check syntax.",
  361. }
  362. }
  363. with patch(
  364. "salt.auth.LoadAuth.authenticate_eauth", MagicMock(return_value=True)
  365. ), patch(
  366. "salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=["testing"])
  367. ):
  368. ret = self.clear_funcs.wheel(clear_load)
  369. self.assertDictEqual(mock_ret, ret)
  370. @slowTest
  371. def test_wheel_user_not_authenticated(self):
  372. """
  373. Asserts that an UserAuthenticationError is returned when the user can't authenticate.
  374. """
  375. mock_ret = {
  376. "error": {
  377. "name": "UserAuthenticationError",
  378. "message": 'Authentication failure of type "user" occurred',
  379. }
  380. }
  381. ret = self.clear_funcs.wheel({})
  382. self.assertDictEqual(mock_ret, ret)
  383. # publish tests
  384. @slowTest
  385. def test_publish_user_is_blacklisted(self):
  386. """
  387. Asserts that an AuthorizationError is returned when the user has been blacklisted.
  388. """
  389. mock_ret = {
  390. "error": {
  391. "name": "AuthorizationError",
  392. "message": "Authorization error occurred.",
  393. }
  394. }
  395. with patch(
  396. "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=True)
  397. ):
  398. self.assertEqual(
  399. mock_ret, self.clear_funcs.publish({"user": "foo", "fun": "test.arg"})
  400. )
  401. @slowTest
  402. def test_publish_cmd_blacklisted(self):
  403. """
  404. Asserts that an AuthorizationError is returned when the command has been blacklisted.
  405. """
  406. mock_ret = {
  407. "error": {
  408. "name": "AuthorizationError",
  409. "message": "Authorization error occurred.",
  410. }
  411. }
  412. with patch(
  413. "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False)
  414. ), patch(
  415. "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=True)
  416. ):
  417. self.assertEqual(
  418. mock_ret, self.clear_funcs.publish({"user": "foo", "fun": "test.arg"})
  419. )
  420. @slowTest
  421. def test_publish_token_not_authenticated(self):
  422. """
  423. Asserts that an AuthenticationError is returned when the token can't authenticate.
  424. """
  425. mock_ret = {
  426. "error": {
  427. "name": "AuthenticationError",
  428. "message": "Authentication error occurred.",
  429. }
  430. }
  431. load = {
  432. "user": "foo",
  433. "fun": "test.arg",
  434. "tgt": "test_minion",
  435. "kwargs": {"token": "asdfasdfasdfasdf"},
  436. }
  437. with patch(
  438. "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False)
  439. ), patch(
  440. "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=False)
  441. ):
  442. self.assertEqual(mock_ret, self.clear_funcs.publish(load))
  443. @slowTest
  444. def test_publish_token_authorization_error(self):
  445. """
  446. Asserts that an AuthorizationError is returned when the token authenticates, but is not
  447. authorized.
  448. """
  449. token = "asdfasdfasdfasdf"
  450. load = {
  451. "user": "foo",
  452. "fun": "test.arg",
  453. "tgt": "test_minion",
  454. "arg": "bar",
  455. "kwargs": {"token": token},
  456. }
  457. mock_token = {"token": token, "eauth": "foo", "name": "test"}
  458. mock_ret = {
  459. "error": {
  460. "name": "AuthorizationError",
  461. "message": "Authorization error occurred.",
  462. }
  463. }
  464. with patch(
  465. "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False)
  466. ), patch(
  467. "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=False)
  468. ), patch(
  469. "salt.auth.LoadAuth.authenticate_token", MagicMock(return_value=mock_token)
  470. ), patch(
  471. "salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=[])
  472. ):
  473. self.assertEqual(mock_ret, self.clear_funcs.publish(load))
  474. @slowTest
  475. def test_publish_eauth_not_authenticated(self):
  476. """
  477. Asserts that an AuthenticationError is returned when the user can't authenticate.
  478. """
  479. load = {
  480. "user": "test",
  481. "fun": "test.arg",
  482. "tgt": "test_minion",
  483. "kwargs": {"eauth": "foo"},
  484. }
  485. mock_ret = {
  486. "error": {
  487. "name": "AuthenticationError",
  488. "message": "Authentication error occurred.",
  489. }
  490. }
  491. with patch(
  492. "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False)
  493. ), patch(
  494. "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=False)
  495. ):
  496. self.assertEqual(mock_ret, self.clear_funcs.publish(load))
  497. @slowTest
  498. def test_publish_eauth_authorization_error(self):
  499. """
  500. Asserts that an AuthorizationError is returned when the user authenticates, but is not
  501. authorized.
  502. """
  503. load = {
  504. "user": "test",
  505. "fun": "test.arg",
  506. "tgt": "test_minion",
  507. "kwargs": {"eauth": "foo"},
  508. "arg": "bar",
  509. }
  510. mock_ret = {
  511. "error": {
  512. "name": "AuthorizationError",
  513. "message": "Authorization error occurred.",
  514. }
  515. }
  516. with patch(
  517. "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False)
  518. ), patch(
  519. "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=False)
  520. ), patch(
  521. "salt.auth.LoadAuth.authenticate_eauth", MagicMock(return_value=True)
  522. ), patch(
  523. "salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=[])
  524. ):
  525. self.assertEqual(mock_ret, self.clear_funcs.publish(load))
  526. @slowTest
  527. def test_publish_user_not_authenticated(self):
  528. """
  529. Asserts that an AuthenticationError is returned when the user can't authenticate.
  530. """
  531. load = {"user": "test", "fun": "test.arg", "tgt": "test_minion"}
  532. mock_ret = {
  533. "error": {
  534. "name": "AuthenticationError",
  535. "message": "Authentication error occurred.",
  536. }
  537. }
  538. with patch(
  539. "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False)
  540. ), patch(
  541. "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=False)
  542. ):
  543. self.assertEqual(mock_ret, self.clear_funcs.publish(load))
  544. @slowTest
  545. def test_publish_user_authenticated_missing_auth_list(self):
  546. """
  547. Asserts that an AuthenticationError is returned when the user has an effective user id and is
  548. authenticated, but the auth_list is empty.
  549. """
  550. load = {
  551. "user": "test",
  552. "fun": "test.arg",
  553. "tgt": "test_minion",
  554. "kwargs": {"user": "test"},
  555. "arg": "foo",
  556. }
  557. mock_ret = {
  558. "error": {
  559. "name": "AuthenticationError",
  560. "message": "Authentication error occurred.",
  561. }
  562. }
  563. with patch(
  564. "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False)
  565. ), patch(
  566. "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=False)
  567. ), patch(
  568. "salt.auth.LoadAuth.authenticate_key",
  569. MagicMock(return_value="fake-user-key"),
  570. ), patch(
  571. "salt.utils.master.get_values_of_matching_keys", MagicMock(return_value=[])
  572. ):
  573. self.assertEqual(mock_ret, self.clear_funcs.publish(load))
  574. @slowTest
  575. def test_publish_user_authorization_error(self):
  576. """
  577. Asserts that an AuthorizationError is returned when the user authenticates, but is not
  578. authorized.
  579. """
  580. load = {
  581. "user": "test",
  582. "fun": "test.arg",
  583. "tgt": "test_minion",
  584. "kwargs": {"user": "test"},
  585. "arg": "foo",
  586. }
  587. mock_ret = {
  588. "error": {
  589. "name": "AuthorizationError",
  590. "message": "Authorization error occurred.",
  591. }
  592. }
  593. with patch(
  594. "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False)
  595. ), patch(
  596. "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=False)
  597. ), patch(
  598. "salt.auth.LoadAuth.authenticate_key",
  599. MagicMock(return_value="fake-user-key"),
  600. ), patch(
  601. "salt.utils.master.get_values_of_matching_keys",
  602. MagicMock(return_value=["test"]),
  603. ), patch(
  604. "salt.utils.minions.CkMinions.auth_check", MagicMock(return_value=False)
  605. ):
  606. self.assertEqual(mock_ret, self.clear_funcs.publish(load))
  607. class MaintenanceTestCase(TestCase, AdaptedConfigurationTestCaseMixin):
  608. """
  609. TestCase for salt.master.Maintenance class
  610. """
  611. def setUp(self):
  612. opts = self.get_temp_config("master", git_pillar_update_interval=180)
  613. self.main_class = salt.master.Maintenance(opts)
  614. self.main_class._after_fork_methods = self.main_class._finalize_methods = []
  615. def tearDown(self):
  616. del self.main_class
  617. def test_run_func(self):
  618. """
  619. Test the run function inside Maintenance class.
  620. """
  621. class MockTime:
  622. def __init__(self, max_duration):
  623. self._start_time = time.time()
  624. self._current_duration = 0
  625. self._max_duration = max_duration
  626. self._calls = []
  627. def time(self):
  628. return self._start_time + self._current_duration
  629. def sleep(self, secs):
  630. self._calls += [secs]
  631. self._current_duration += secs
  632. if self._current_duration >= self._max_duration:
  633. raise RuntimeError("Time passes")
  634. mocked_time = MockTime(60 * 4)
  635. class MockTimedFunc:
  636. def __init__(self):
  637. self.call_times = []
  638. def __call__(self, *args, **kwargs):
  639. self.call_times += [mocked_time._current_duration]
  640. mocked__post_fork_init = MockTimedFunc()
  641. mocked_clean_old_jobs = MockTimedFunc()
  642. mocked_clean_expired_tokens = MockTimedFunc()
  643. mocked_clean_pub_auth = MockTimedFunc()
  644. mocked_handle_git_pillar = MockTimedFunc()
  645. mocked_handle_schedule = MockTimedFunc()
  646. mocked_handle_key_cache = MockTimedFunc()
  647. mocked_handle_presence = MockTimedFunc()
  648. mocked_handle_key_rotate = MockTimedFunc()
  649. mocked_check_max_open_files = MockTimedFunc()
  650. with patch("salt.master.time", mocked_time), patch(
  651. "salt.utils.process", autospec=True
  652. ), patch(
  653. "salt.master.Maintenance._post_fork_init", mocked__post_fork_init
  654. ), patch(
  655. "salt.daemons.masterapi.clean_old_jobs", mocked_clean_old_jobs
  656. ), patch(
  657. "salt.daemons.masterapi.clean_expired_tokens", mocked_clean_expired_tokens
  658. ), patch(
  659. "salt.daemons.masterapi.clean_pub_auth", mocked_clean_pub_auth
  660. ), patch(
  661. "salt.master.Maintenance.handle_git_pillar", mocked_handle_git_pillar
  662. ), patch(
  663. "salt.master.Maintenance.handle_schedule", mocked_handle_schedule
  664. ), patch(
  665. "salt.master.Maintenance.handle_key_cache", mocked_handle_key_cache
  666. ), patch(
  667. "salt.master.Maintenance.handle_presence", mocked_handle_presence
  668. ), patch(
  669. "salt.master.Maintenance.handle_key_rotate", mocked_handle_key_rotate
  670. ), patch(
  671. "salt.utils.verify.check_max_open_files", mocked_check_max_open_files
  672. ):
  673. try:
  674. self.main_class.run()
  675. except RuntimeError as exc:
  676. self.assertEqual(str(exc), "Time passes")
  677. self.assertEqual(mocked_time._calls, [60] * 4)
  678. self.assertEqual(mocked__post_fork_init.call_times, [0])
  679. self.assertEqual(mocked_clean_old_jobs.call_times, [60, 120, 180])
  680. self.assertEqual(mocked_clean_expired_tokens.call_times, [60, 120, 180])
  681. self.assertEqual(mocked_clean_pub_auth.call_times, [60, 120, 180])
  682. self.assertEqual(mocked_handle_git_pillar.call_times, [0, 180])
  683. self.assertEqual(mocked_handle_schedule.call_times, [0, 60, 120, 180])
  684. self.assertEqual(mocked_handle_key_cache.call_times, [0, 60, 120, 180])
  685. self.assertEqual(mocked_handle_presence.call_times, [0, 60, 120, 180])
  686. self.assertEqual(mocked_handle_key_rotate.call_times, [0, 60, 120, 180])
  687. self.assertEqual(mocked_check_max_open_files.call_times, [0, 60, 120, 180])