TestPythonExecutor.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. #!/usr/bin/env python
  2. '''
  3. Licensed to the Apache Software Foundation (ASF) under one
  4. or more contributor license agreements. See the NOTICE file
  5. distributed with this work for additional information
  6. regarding copyright ownership. The ASF licenses this file
  7. to you under the Apache License, Version 2.0 (the
  8. "License"); you may not use this file except in compliance
  9. with the License. You may obtain a copy of the License at
  10. http://www.apache.org/licenses/LICENSE-2.0
  11. Unless required by applicable law or agreed to in writing, software
  12. distributed under the License is distributed on an "AS IS" BASIS,
  13. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. See the License for the specific language governing permissions and
  15. limitations under the License.
  16. '''
  17. import pprint
  18. from unittest import TestCase
  19. import threading
  20. import tempfile
  21. import time
  22. from threading import Thread
  23. from PythonExecutor import PythonExecutor
  24. from AmbariConfig import AmbariConfig
  25. from mock.mock import MagicMock, patch
  26. class TestPythonExecutor(TestCase):
  27. @patch("shell.kill_process_with_children")
  28. def test_watchdog_1(self, kill_process_with_children_mock):
  29. """
  30. Tests whether watchdog works
  31. """
  32. subproc_mock = self.Subprocess_mockup()
  33. executor = PythonExecutor("/tmp", AmbariConfig().getConfig())
  34. _, tmpoutfile = tempfile.mkstemp()
  35. _, tmperrfile = tempfile.mkstemp()
  36. _, tmpstrucout = tempfile.mkstemp()
  37. PYTHON_TIMEOUT_SECONDS = 0.1
  38. kill_process_with_children_mock.side_effect = lambda pid : subproc_mock.terminate()
  39. def launch_python_subprocess_method(command, tmpout, tmperr):
  40. subproc_mock.tmpout = tmpout
  41. subproc_mock.tmperr = tmperr
  42. return subproc_mock
  43. executor.launch_python_subprocess = launch_python_subprocess_method
  44. runShellKillPgrp_method = MagicMock()
  45. runShellKillPgrp_method.side_effect = lambda python : python.terminate()
  46. executor.runShellKillPgrp = runShellKillPgrp_method
  47. subproc_mock.returncode = None
  48. thread = Thread(target = executor.run_file, args = ("fake_puppetFile",
  49. ["arg1", "arg2"], tmpoutfile, tmperrfile, PYTHON_TIMEOUT_SECONDS, tmpstrucout))
  50. thread.start()
  51. time.sleep(0.1)
  52. subproc_mock.finished_event.wait()
  53. self.assertEquals(subproc_mock.was_terminated, True, "Subprocess should be terminated due to timeout")
  54. def test_watchdog_2(self):
  55. """
  56. Tries to catch false positive watchdog invocations
  57. """
  58. subproc_mock = self.Subprocess_mockup()
  59. executor = PythonExecutor("/tmp", AmbariConfig().getConfig())
  60. _, tmpoutfile = tempfile.mkstemp()
  61. _, tmperrfile = tempfile.mkstemp()
  62. _, tmpstrucout = tempfile.mkstemp()
  63. PYTHON_TIMEOUT_SECONDS = 5
  64. def launch_python_subprocess_method(command, tmpout, tmperr):
  65. subproc_mock.tmpout = tmpout
  66. subproc_mock.tmperr = tmperr
  67. return subproc_mock
  68. executor.launch_python_subprocess = launch_python_subprocess_method
  69. runShellKillPgrp_method = MagicMock()
  70. runShellKillPgrp_method.side_effect = lambda python : python.terminate()
  71. executor.runShellKillPgrp = runShellKillPgrp_method
  72. subproc_mock.returncode = 0
  73. thread = Thread(target = executor.run_file, args = ("fake_puppetFile", ["arg1", "arg2"],
  74. tmpoutfile, tmperrfile,
  75. PYTHON_TIMEOUT_SECONDS, tmpstrucout))
  76. thread.start()
  77. time.sleep(0.1)
  78. subproc_mock.should_finish_event.set()
  79. subproc_mock.finished_event.wait()
  80. self.assertEquals(subproc_mock.was_terminated, False, "Subprocess should not be terminated before timeout")
  81. self.assertEquals(subproc_mock.returncode, 0, "Subprocess should not be terminated before timeout")
  82. def test_execution_results(self):
  83. subproc_mock = self.Subprocess_mockup()
  84. executor = PythonExecutor("/tmp", AmbariConfig().getConfig())
  85. _, tmpoutfile = tempfile.mkstemp()
  86. _, tmperrfile = tempfile.mkstemp()
  87. _, tmpstroutfile = tempfile.mkstemp()
  88. PYTHON_TIMEOUT_SECONDS = 5
  89. def launch_python_subprocess_method(command, tmpout, tmperr):
  90. subproc_mock.tmpout = tmpout
  91. subproc_mock.tmperr = tmperr
  92. return subproc_mock
  93. executor.launch_python_subprocess = launch_python_subprocess_method
  94. runShellKillPgrp_method = MagicMock()
  95. runShellKillPgrp_method.side_effect = lambda python : python.terminate()
  96. executor.runShellKillPgrp = runShellKillPgrp_method
  97. subproc_mock.returncode = 0
  98. subproc_mock.should_finish_event.set()
  99. result = executor.run_file("file", ["arg1", "arg2"], tmpoutfile, tmperrfile, PYTHON_TIMEOUT_SECONDS, tmpstroutfile)
  100. self.assertEquals(result, {'exitcode': 0, 'stderr': 'Dummy err', 'stdout': 'Dummy output',
  101. 'structuredOut': {'msg': 'Unable to read structured output from ' + tmpstroutfile}})
  102. def test_is_successfull(self):
  103. executor = PythonExecutor("/tmp", AmbariConfig().getConfig())
  104. executor.python_process_has_been_killed = False
  105. self.assertTrue(executor.isSuccessfull(0))
  106. self.assertFalse(executor.isSuccessfull(1))
  107. executor.python_process_has_been_killed = True
  108. self.assertFalse(executor.isSuccessfull(0))
  109. self.assertFalse(executor.isSuccessfull(1))
  110. def test_python_command(self):
  111. executor = PythonExecutor("/tmp", AmbariConfig().getConfig())
  112. command = executor.python_command("script", ["script_param1"])
  113. self.assertEqual(3, len(command))
  114. self.assertTrue("python" in command[0])
  115. self.assertEquals("script", command[1])
  116. self.assertEquals("script_param1", command[2])
  117. pprint.pprint(command)
  118. class Subprocess_mockup():
  119. """
  120. It's not trivial to use PyMock instead of class here because we need state
  121. and complex logics
  122. """
  123. returncode = 0
  124. started_event = threading.Event()
  125. should_finish_event = threading.Event()
  126. finished_event = threading.Event()
  127. was_terminated = False
  128. tmpout = None
  129. tmperr = None
  130. pid=-1
  131. def communicate(self):
  132. self.started_event.set()
  133. self.tmpout.write("Dummy output")
  134. self.tmpout.flush()
  135. self.tmperr.write("Dummy err")
  136. self.tmperr.flush()
  137. self.should_finish_event.wait()
  138. self.finished_event.set()
  139. pass
  140. def terminate(self):
  141. self.was_terminated = True
  142. self.returncode = 17
  143. self.should_finish_event.set()