processes.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. """
  2. :copyright: Copyright 2017 by the SaltStack Team, see AUTHORS for more details.
  3. :license: Apache 2.0, see LICENSE for more details.
  4. tests.support.processes
  5. ~~~~~~~~~~~~~~~~~~~~~~~
  6. Process handling utilities
  7. """
  8. import logging
  9. from saltfactories.utils.processes import ( # pylint: disable=unused-import
  10. collect_child_processes,
  11. terminate_process,
  12. terminate_process_list,
  13. )
  14. from tests.support.cli_scripts import ScriptPathMixin, get_script_path
  15. from tests.support.runtests import RUNTIME_VARS
  16. try:
  17. from pytestsalt.fixtures.daemons import Salt as PytestSalt
  18. from pytestsalt.fixtures.daemons import SaltCall as PytestSaltCall
  19. from pytestsalt.fixtures.daemons import SaltKey as PytestSaltKey
  20. from pytestsalt.fixtures.daemons import SaltMaster as PytestSaltMaster
  21. from pytestsalt.fixtures.daemons import SaltMinion as PytestSaltMinion
  22. from pytestsalt.fixtures.daemons import SaltProxy as PytestSaltProxy
  23. from pytestsalt.fixtures.daemons import SaltRun as PytestSaltRun
  24. from pytestsalt.fixtures.daemons import SaltSyndic as PytestSaltSyndic
  25. except ImportError:
  26. # If this happens, we are running under pytest which uninstalls pytest-salt due to impatabilites
  27. # These imports won't actually work but these classes are only used when running under runtests,
  28. # so, we're just making sure we also don't hit NameError's
  29. from tests.support.saltfactories_compat import SaltCallCLI as PytestSaltCall
  30. from tests.support.saltfactories_compat import SaltCLI as PytestSalt
  31. from tests.support.saltfactories_compat import SaltKeyCLI as PytestSaltKey
  32. from tests.support.saltfactories_compat import SaltMaster as PytestSaltMaster
  33. from tests.support.saltfactories_compat import SaltMinion as PytestSaltMinion
  34. from tests.support.saltfactories_compat import SaltProxyMinion as PytestSaltProxy
  35. from tests.support.saltfactories_compat import SaltRunCLI as PytestSaltRun
  36. from tests.support.saltfactories_compat import SaltSyndic as PytestSaltSyndic
  37. log = logging.getLogger(__name__)
  38. class GetSaltRunFixtureMixin(ScriptPathMixin):
  39. """
  40. Override this classes `get_salt_run_fixture` because we're still not running under pytest
  41. """
  42. def get_salt_run_fixture(self):
  43. pass
  44. class Salt(ScriptPathMixin, PytestSalt):
  45. """
  46. Class which runs salt-call commands
  47. """
  48. def __init__(self, *args, **kwargs):
  49. super().__init__(None, *args, **kwargs)
  50. class SaltCall(ScriptPathMixin, PytestSaltCall):
  51. """
  52. Class which runs salt-call commands
  53. """
  54. def __init__(self, *args, **kwargs):
  55. super().__init__(None, *args, **kwargs)
  56. class SaltKey(ScriptPathMixin, PytestSaltKey):
  57. """
  58. Class which runs salt-key commands
  59. """
  60. def __init__(self, *args, **kwargs):
  61. super().__init__(None, *args, **kwargs)
  62. class SaltRun(ScriptPathMixin, PytestSaltRun):
  63. """
  64. Class which runs salt-run commands
  65. """
  66. def __init__(self, *args, **kwargs):
  67. super().__init__(None, *args, **kwargs)
  68. class SaltProxy(GetSaltRunFixtureMixin, PytestSaltProxy):
  69. """
  70. Class which runs the salt-proxy daemon
  71. """
  72. class SaltMinion(GetSaltRunFixtureMixin, PytestSaltMinion):
  73. """
  74. Class which runs the salt-minion daemon
  75. """
  76. class SaltMaster(GetSaltRunFixtureMixin, PytestSaltMaster):
  77. """
  78. Class which runs the salt-master daemon
  79. """
  80. class SaltSyndic(GetSaltRunFixtureMixin, PytestSaltSyndic):
  81. """
  82. Class which runs the salt-syndic daemon
  83. """
  84. def start_daemon(
  85. daemon_name=None,
  86. daemon_id=None,
  87. daemon_log_prefix=None,
  88. daemon_cli_script_name=None,
  89. daemon_config=None,
  90. daemon_config_dir=None,
  91. daemon_class=None,
  92. bin_dir_path=None,
  93. fail_hard=False,
  94. start_timeout=10,
  95. slow_stop=False,
  96. environ=None,
  97. cwd=None,
  98. event_listener_config_dir=None,
  99. ):
  100. """
  101. Returns a running salt daemon
  102. """
  103. # Old config name
  104. daemon_config["pytest_port"] = daemon_config["runtests_conn_check_port"]
  105. # New config name
  106. daemon_config["pytest_engine_port"] = daemon_config["runtests_conn_check_port"]
  107. request = None
  108. if fail_hard:
  109. fail_method = RuntimeError
  110. else:
  111. fail_method = RuntimeWarning
  112. log.info("[%s] Starting pytest %s(%s)", daemon_name, daemon_log_prefix, daemon_id)
  113. attempts = 0
  114. process = None
  115. get_script_path(RUNTIME_VARS.TMP_SCRIPT_DIR, daemon_cli_script_name)
  116. while attempts <= 3: # pylint: disable=too-many-nested-blocks
  117. attempts += 1
  118. try:
  119. process = daemon_class(
  120. request=request,
  121. config=daemon_config,
  122. config_dir=daemon_config_dir,
  123. bin_dir_path=bin_dir_path,
  124. log_prefix=daemon_log_prefix,
  125. cli_script_name=daemon_cli_script_name,
  126. slow_stop=slow_stop,
  127. environ=environ,
  128. cwd=cwd,
  129. event_listener_config_dir=event_listener_config_dir,
  130. )
  131. except TypeError:
  132. process = daemon_class(
  133. request=request,
  134. config=daemon_config,
  135. config_dir=daemon_config_dir,
  136. bin_dir_path=bin_dir_path,
  137. log_prefix=daemon_log_prefix,
  138. cli_script_name=daemon_cli_script_name,
  139. slow_stop=slow_stop,
  140. environ=environ,
  141. cwd=cwd,
  142. )
  143. process.start()
  144. if process.is_alive():
  145. try:
  146. connectable = process.wait_until_running(timeout=start_timeout)
  147. if connectable is False:
  148. connectable = process.wait_until_running(timeout=start_timeout / 2)
  149. if connectable is False:
  150. process.terminate()
  151. if attempts >= 3:
  152. fail_method(
  153. "The pytest {}({}) has failed to confirm running status "
  154. "after {} attempts".format(
  155. daemon_name, daemon_id, attempts
  156. )
  157. )
  158. continue
  159. except Exception as exc: # pylint: disable=broad-except
  160. log.exception("[%s] %s", daemon_log_prefix, exc, exc_info=True)
  161. terminate_process(process.pid, kill_children=True, slow_stop=slow_stop)
  162. if attempts >= 3:
  163. raise fail_method(str(exc))
  164. continue
  165. log.info(
  166. "[%s] The pytest %s(%s) is running and accepting commands "
  167. "after %d attempts",
  168. daemon_log_prefix,
  169. daemon_name,
  170. daemon_id,
  171. attempts,
  172. )
  173. break
  174. else:
  175. terminate_process(process.pid, kill_children=True, slow_stop=slow_stop)
  176. continue
  177. else:
  178. if process is not None:
  179. terminate_process(process.pid, kill_children=True, slow_stop=slow_stop)
  180. raise fail_method(
  181. "The pytest {}({}) has failed to start after {} attempts".format(
  182. daemon_name, daemon_id, attempts - 1
  183. )
  184. )
  185. return process