conftest.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. import shutil
  2. import textwrap
  3. import time
  4. import attr
  5. import pytest
  6. @attr.s
  7. class BlackoutPillar:
  8. pillar_state_tree = attr.ib(repr=False)
  9. salt_cli = attr.ib(repr=False)
  10. top_file = attr.ib(init=False)
  11. minion_1_id = attr.ib(repr=False)
  12. minion_1_pillar = attr.ib(init=False)
  13. minion_2_id = attr.ib(repr=False)
  14. minion_2_pillar = attr.ib(init=False)
  15. in_blackout = attr.ib(default=False)
  16. def __attrs_post_init__(self):
  17. self.top_file = self.pillar_state_tree / "top.sls"
  18. top_file_contents = textwrap.dedent(
  19. """\
  20. base:
  21. {}:
  22. - minion-1-pillar
  23. {}:
  24. - minion-2-pillar
  25. """.format(
  26. self.minion_1_id, self.minion_2_id
  27. )
  28. )
  29. self.top_file.write_text(top_file_contents)
  30. self.minion_1_pillar = self.pillar_state_tree / "minion-1-pillar.sls"
  31. self.minion_2_pillar = self.pillar_state_tree / "minion-2-pillar.sls"
  32. self.minion_1_pillar.write_text("minion_blackout: false")
  33. self.minion_2_pillar.write_text("minion_blackout: false")
  34. self.refresh_pillar()
  35. def enter_blackout(self, pillar_contents=None):
  36. if pillar_contents is None:
  37. pillar_contents = "minion_blackout: false"
  38. if pillar_contents.startswith("\n"):
  39. pillar_contents = pillar_contents[1:]
  40. pillar_contents = textwrap.dedent(pillar_contents)
  41. self.minion_1_pillar.write_text(pillar_contents)
  42. self.refresh_pillar(exiting_blackout=False)
  43. self.in_blackout = True
  44. return self.__enter__()
  45. def exit_blackout(self):
  46. if self.in_blackout:
  47. self.minion_1_pillar.write_text("minion_blackout: false")
  48. self.refresh_pillar(exiting_blackout=True)
  49. self.in_blackout = False
  50. def refresh_pillar(self, timeout=60, sleep=0.5, exiting_blackout=None):
  51. ret = self.salt_cli.run("saltutil.refresh_pillar", wait=True, minion_tgt="*")
  52. assert ret.exitcode == 0
  53. assert self.minion_1_id in ret.json
  54. assert self.minion_2_id in ret.json
  55. stop_at = time.time() + timeout
  56. while True:
  57. if time.time() > stop_at:
  58. if exiting_blackout is True:
  59. pytest.fail(
  60. "Minion did not exit blackout mode after {} seconds".format(
  61. timeout
  62. )
  63. )
  64. elif exiting_blackout is False:
  65. pytest.fail(
  66. "Minion did not enter blackout mode after {} seconds".format(
  67. timeout
  68. )
  69. )
  70. else:
  71. pytest.fail(
  72. "Minion did not refresh pillar after {} seconds".format(timeout)
  73. )
  74. time.sleep(sleep)
  75. ret = self.salt_cli.run("pillar.get", "minion_blackout", minion_tgt="*")
  76. if not ret.json:
  77. # Something is wrong here. Try again
  78. continue
  79. assert self.minion_1_id in ret.json
  80. assert self.minion_2_id in ret.json
  81. if ret.json[self.minion_1_id] == "" or ret.json[self.minion_2_id] == "":
  82. # Pillar not found
  83. continue
  84. # Minion 2 must NEVER enter blackout
  85. assert ret.json[self.minion_2_id] is False
  86. if exiting_blackout is True and ret.json[self.minion_1_id] is not False:
  87. continue
  88. elif (
  89. exiting_blackout is False
  90. and "Minion in blackout mode" not in ret.json[self.minion_1_id]
  91. ):
  92. continue
  93. # We got the pillar we're after, break out of the loop
  94. break
  95. def __enter__(self):
  96. return self
  97. def __exit__(self, *args):
  98. self.exit_blackout()
  99. @pytest.fixture(scope="package")
  100. def pillar_state_tree(tmp_path_factory):
  101. _pillar_state_tree = tmp_path_factory.mktemp("pillar")
  102. try:
  103. yield _pillar_state_tree
  104. finally:
  105. shutil.rmtree(str(_pillar_state_tree), ignore_errors=True)
  106. @pytest.fixture(scope="package")
  107. def salt_master(salt_factories, pillar_state_tree):
  108. config_defaults = {
  109. "pillar_roots": {"base": [str(pillar_state_tree)]},
  110. "open_mode": True,
  111. }
  112. factory = salt_factories.get_salt_master_daemon(
  113. "blackout-master",
  114. config_defaults=config_defaults,
  115. extra_cli_arguments_after_first_start_failure=["--log-level=debug"],
  116. )
  117. with factory.started():
  118. yield factory
  119. @pytest.fixture(scope="package")
  120. def salt_minion_1(salt_master):
  121. factory = salt_master.get_salt_minion_daemon(
  122. "blackout-minion-1", config_defaults={"open_mode": True}
  123. )
  124. with factory.started():
  125. yield factory
  126. @pytest.fixture(scope="package")
  127. def salt_minion_2(salt_master):
  128. factory = salt_master.get_salt_minion_daemon(
  129. "blackout-minion-2", config_defaults={"open_mode": True}
  130. )
  131. with factory.started():
  132. yield factory
  133. @pytest.fixture(scope="package")
  134. def salt_cli(salt_master):
  135. return salt_master.get_salt_cli()
  136. @pytest.fixture(scope="package")
  137. def blackout_pillar(salt_cli, pillar_state_tree, salt_minion_1, salt_minion_2):
  138. return BlackoutPillar(
  139. pillar_state_tree=pillar_state_tree,
  140. salt_cli=salt_cli,
  141. minion_1_id=salt_minion_1.id,
  142. minion_2_id=salt_minion_2.id,
  143. )
  144. @pytest.fixture
  145. def blackout(blackout_pillar):
  146. try:
  147. yield blackout_pillar
  148. finally:
  149. blackout_pillar.exit_blackout()