processes.py 7.0 KB

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