fixture.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. # pylint: disable=missing-docstring
  2. import os
  3. import yaml
  4. import ooinstall.cli_installer as cli
  5. from test.oo_config_tests import OOInstallFixture
  6. from click.testing import CliRunner
  7. # Substitute in a product name before use:
  8. SAMPLE_CONFIG = """
  9. variant: %s
  10. ansible_ssh_user: root
  11. hosts:
  12. - connect_to: 10.0.0.1
  13. ip: 10.0.0.1
  14. hostname: master-private.example.com
  15. public_ip: 24.222.0.1
  16. public_hostname: master.example.com
  17. master: true
  18. node: true
  19. - connect_to: 10.0.0.2
  20. ip: 10.0.0.2
  21. hostname: node1-private.example.com
  22. public_ip: 24.222.0.2
  23. public_hostname: node1.example.com
  24. node: true
  25. - connect_to: 10.0.0.3
  26. ip: 10.0.0.3
  27. hostname: node2-private.example.com
  28. public_ip: 24.222.0.3
  29. public_hostname: node2.example.com
  30. node: true
  31. """
  32. def read_yaml(config_file_path):
  33. cfg_f = open(config_file_path, 'r')
  34. config = yaml.safe_load(cfg_f.read())
  35. cfg_f.close()
  36. return config
  37. class OOCliFixture(OOInstallFixture):
  38. def setUp(self):
  39. OOInstallFixture.setUp(self)
  40. self.runner = CliRunner()
  41. # Add any arguments you would like to test here, the defaults ensure
  42. # we only do unattended invocations here, and using temporary files/dirs.
  43. self.cli_args = ["-a", self.work_dir]
  44. def run_cli(self):
  45. return self.runner.invoke(cli.cli, self.cli_args)
  46. def assert_result(self, result, exit_code):
  47. if result.exit_code != exit_code:
  48. print "Unexpected result from CLI execution"
  49. print "Exit code: %s" % result.exit_code
  50. print "Exception: %s" % result.exception
  51. print result.exc_info
  52. import traceback
  53. traceback.print_exception(*result.exc_info)
  54. print "Output:\n%s" % result.output
  55. self.fail("Exception during CLI execution")
  56. def _verify_load_facts(self, load_facts_mock):
  57. """ Check that we ran load facts with expected inputs. """
  58. load_facts_args = load_facts_mock.call_args[0]
  59. self.assertEquals(os.path.join(self.work_dir, ".ansible/hosts"),
  60. load_facts_args[0])
  61. self.assertEquals(os.path.join(self.work_dir,
  62. "playbooks/byo/openshift_facts.yml"),
  63. load_facts_args[1])
  64. env_vars = load_facts_args[2]
  65. self.assertEquals(os.path.join(self.work_dir,
  66. '.ansible/callback_facts.yaml'),
  67. env_vars['OO_INSTALL_CALLBACK_FACTS_YAML'])
  68. self.assertEqual('/tmp/ansible.log', env_vars['ANSIBLE_LOG_PATH'])
  69. def _verify_run_playbook(self, run_playbook_mock, exp_hosts_len, exp_hosts_to_run_on_len):
  70. """ Check that we ran playbook with expected inputs. """
  71. hosts = run_playbook_mock.call_args[0][0]
  72. hosts_to_run_on = run_playbook_mock.call_args[0][1]
  73. self.assertEquals(exp_hosts_len, len(hosts))
  74. self.assertEquals(exp_hosts_to_run_on_len, len(hosts_to_run_on))
  75. def _verify_config_hosts(self, written_config, host_count):
  76. self.assertEquals(host_count, len(written_config['hosts']))
  77. for host in written_config['hosts']:
  78. self.assertTrue('hostname' in host)
  79. self.assertTrue('public_hostname' in host)
  80. if 'preconfigured' not in host:
  81. self.assertTrue(host['node'])
  82. self.assertTrue('ip' in host)
  83. self.assertTrue('public_ip' in host)
  84. #pylint: disable=too-many-arguments
  85. def _verify_get_hosts_to_run_on(self, mock_facts, load_facts_mock,
  86. run_playbook_mock, cli_input,
  87. exp_hosts_len=None, exp_hosts_to_run_on_len=None,
  88. force=None):
  89. """
  90. Tests cli_installer.py:get_hosts_to_run_on. That method has quite a
  91. few subtle branches in the logic. The goal with this method is simply
  92. to handle all the messy stuff here and allow the main test cases to be
  93. easily read. The basic idea is to modify mock_facts to return a
  94. version indicating OpenShift is already installed on particular hosts.
  95. """
  96. load_facts_mock.return_value = (mock_facts, 0)
  97. run_playbook_mock.return_value = 0
  98. if cli_input:
  99. self.cli_args.append("install")
  100. result = self.runner.invoke(cli.cli,
  101. self.cli_args,
  102. input=cli_input)
  103. else:
  104. config_file = self.write_config(
  105. os.path.join(self.work_dir,
  106. 'ooinstall.conf'), SAMPLE_CONFIG % 'openshift-enterprise')
  107. self.cli_args.extend(["-c", config_file, "install"])
  108. if force:
  109. self.cli_args.append("--force")
  110. result = self.runner.invoke(cli.cli, self.cli_args)
  111. written_config = read_yaml(config_file)
  112. self._verify_config_hosts(written_config, exp_hosts_len)
  113. self.assert_result(result, 0)
  114. self._verify_load_facts(load_facts_mock)
  115. self._verify_run_playbook(run_playbook_mock, exp_hosts_len, exp_hosts_to_run_on_len)
  116. # Make sure we ran on the expected masters and nodes:
  117. hosts = run_playbook_mock.call_args[0][0]
  118. hosts_to_run_on = run_playbook_mock.call_args[0][1]
  119. self.assertEquals(exp_hosts_len, len(hosts))
  120. self.assertEquals(exp_hosts_to_run_on_len, len(hosts_to_run_on))
  121. #pylint: disable=too-many-arguments,too-many-branches
  122. def build_input(ssh_user=None, hosts=None, variant_num=None,
  123. add_nodes=None, confirm_facts=None, schedulable_masters_ok=None,
  124. master_lb=None):
  125. """
  126. Build an input string simulating a user entering values in an interactive
  127. attended install.
  128. This is intended to give us one place to update when the CLI prompts change.
  129. We should aim to keep this dependent on optional keyword arguments with
  130. sensible defaults to keep things from getting too fragile.
  131. """
  132. inputs = [
  133. 'y', # let's proceed
  134. ]
  135. if ssh_user:
  136. inputs.append(ssh_user)
  137. if variant_num:
  138. inputs.append(str(variant_num)) # Choose variant + version
  139. num_masters = 0
  140. if hosts:
  141. i = 0
  142. for (host, is_master) in hosts:
  143. inputs.append(host)
  144. if is_master:
  145. inputs.append('y')
  146. num_masters += 1
  147. else:
  148. inputs.append('n')
  149. #inputs.append('rpm')
  150. # We should not be prompted to add more hosts if we're currently at
  151. # 2 masters, this is an invalid HA configuration, so this question
  152. # will not be asked, and the user must enter the next host:
  153. if num_masters != 2:
  154. if i < len(hosts) - 1:
  155. if num_masters >= 1:
  156. inputs.append('y') # Add more hosts
  157. else:
  158. inputs.append('n') # Done adding hosts
  159. i += 1
  160. # You can pass a single master_lb or a list if you intend for one to get rejected:
  161. if master_lb:
  162. if isinstance(master_lb[0], list) or isinstance(master_lb[0], tuple):
  163. inputs.extend(master_lb[0])
  164. else:
  165. inputs.append(master_lb[0])
  166. inputs.append('y' if master_lb[1] else 'n')
  167. # TODO: support option 2, fresh install
  168. if add_nodes:
  169. if schedulable_masters_ok:
  170. inputs.append('y')
  171. inputs.append('1') # Add more nodes
  172. i = 0
  173. for (host, is_master) in add_nodes:
  174. inputs.append(host)
  175. #inputs.append('rpm')
  176. if i < len(add_nodes) - 1:
  177. inputs.append('y') # Add more hosts
  178. else:
  179. inputs.append('n') # Done adding hosts
  180. i += 1
  181. if add_nodes is None:
  182. total_hosts = hosts
  183. else:
  184. total_hosts = hosts + add_nodes
  185. if total_hosts is not None and num_masters == len(total_hosts):
  186. inputs.append('y')
  187. inputs.extend([
  188. confirm_facts,
  189. 'y', # lets do this
  190. ])
  191. return '\n'.join(inputs)