cli_installer_tests.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. # TODO: Temporarily disabled due to importing old code into openshift-ansible
  2. # repo. We will work on these over time.
  3. # pylint: disable=bad-continuation,missing-docstring,no-self-use,invalid-name
  4. import copy
  5. import os
  6. import ConfigParser
  7. import yaml
  8. import ooinstall.cli_installer as cli
  9. from click.testing import CliRunner
  10. from test.oo_config_tests import OOInstallFixture
  11. from mock import patch
  12. MOCK_FACTS = {
  13. '10.0.0.1': {
  14. 'common': {
  15. 'ip': '10.0.0.1',
  16. 'public_ip': '10.0.0.1',
  17. 'hostname': 'master-private.example.com',
  18. 'public_hostname': 'master.example.com'
  19. }
  20. },
  21. '10.0.0.2': {
  22. 'common': {
  23. 'ip': '10.0.0.2',
  24. 'public_ip': '10.0.0.2',
  25. 'hostname': 'node1-private.example.com',
  26. 'public_hostname': 'node1.example.com'
  27. }
  28. },
  29. '10.0.0.3': {
  30. 'common': {
  31. 'ip': '10.0.0.3',
  32. 'public_ip': '10.0.0.3',
  33. 'hostname': 'node2-private.example.com',
  34. 'public_hostname': 'node2.example.com'
  35. }
  36. },
  37. }
  38. # Substitute in a product name before use:
  39. SAMPLE_CONFIG = """
  40. variant: %s
  41. ansible_ssh_user: root
  42. hosts:
  43. - connect_to: master-private.example.com
  44. ip: 10.0.0.1
  45. hostname: master-private.example.com
  46. public_ip: 24.222.0.1
  47. public_hostname: master.example.com
  48. master: true
  49. node: true
  50. - connect_to: node1-private.example.com
  51. ip: 10.0.0.2
  52. hostname: node1-private.example.com
  53. public_ip: 24.222.0.2
  54. public_hostname: node1.example.com
  55. node: true
  56. - connect_to: node2-private.example.com
  57. ip: 10.0.0.3
  58. hostname: node2-private.example.com
  59. public_ip: 24.222.0.3
  60. public_hostname: node2.example.com
  61. node: true
  62. """
  63. class OOCliFixture(OOInstallFixture):
  64. def setUp(self):
  65. OOInstallFixture.setUp(self)
  66. self.runner = CliRunner()
  67. # Add any arguments you would like to test here, the defaults ensure
  68. # we only do unattended invocations here, and using temporary files/dirs.
  69. self.cli_args = ["-a", self.work_dir]
  70. def run_cli(self):
  71. return self.runner.invoke(cli.cli, self.cli_args)
  72. def assert_result(self, result, exit_code):
  73. if result.exception is not None or result.exit_code != exit_code:
  74. print "Unexpected result from CLI execution"
  75. print "Exit code: %s" % result.exit_code
  76. print "Exception: %s" % result.exception
  77. print result.exc_info
  78. import traceback
  79. traceback.print_exception(*result.exc_info)
  80. print "Output:\n%s" % result.output
  81. self.fail("Exception during CLI execution")
  82. def _read_yaml(self, config_file_path):
  83. f = open(config_file_path, 'r')
  84. config = yaml.safe_load(f.read())
  85. f.close()
  86. return config
  87. class UnattendedCliTests(OOCliFixture):
  88. def setUp(self):
  89. OOCliFixture.setUp(self)
  90. self.cli_args.append("-u")
  91. @patch('ooinstall.openshift_ansible.run_main_playbook')
  92. @patch('ooinstall.openshift_ansible.load_system_facts')
  93. def test_cfg_full_run(self, load_facts_mock, run_playbook_mock):
  94. load_facts_mock.return_value = (MOCK_FACTS, 0)
  95. run_playbook_mock.return_value = 0
  96. config_file = self.write_config(os.path.join(self.work_dir,
  97. 'ooinstall.conf'), SAMPLE_CONFIG % 'openshift-enterprise')
  98. self.cli_args.extend(["-c", config_file, "install"])
  99. result = self.runner.invoke(cli.cli, self.cli_args)
  100. self.assert_result(result, 0)
  101. load_facts_args = load_facts_mock.call_args[0]
  102. self.assertEquals(os.path.join(self.work_dir, ".ansible/hosts"),
  103. load_facts_args[0])
  104. self.assertEquals(os.path.join(self.work_dir,
  105. "playbooks/byo/openshift_facts.yml"), load_facts_args[1])
  106. env_vars = load_facts_args[2]
  107. self.assertEquals(os.path.join(self.work_dir,
  108. '.ansible/callback_facts.yaml'),
  109. env_vars['OO_INSTALL_CALLBACK_FACTS_YAML'])
  110. self.assertEqual('/tmp/ansible.log', env_vars['ANSIBLE_LOG_PATH'])
  111. self.assertTrue('ANSIBLE_CONFIG' not in env_vars)
  112. # Make sure we ran on the expected masters and nodes:
  113. hosts = run_playbook_mock.call_args[0][0]
  114. hosts_to_run_on = run_playbook_mock.call_args[0][1]
  115. self.assertEquals(3, len(hosts))
  116. self.assertEquals(3, len(hosts_to_run_on))
  117. @patch('ooinstall.openshift_ansible.run_main_playbook')
  118. @patch('ooinstall.openshift_ansible.load_system_facts')
  119. def test_inventory_write(self, load_facts_mock, run_playbook_mock):
  120. # Add an ssh user so we can verify it makes it to the inventory file:
  121. merged_config = "%s\n%s" % (SAMPLE_CONFIG % 'openshift-enterprise',
  122. "ansible_ssh_user: bob")
  123. load_facts_mock.return_value = (MOCK_FACTS, 0)
  124. run_playbook_mock.return_value = 0
  125. config_file = self.write_config(os.path.join(self.work_dir,
  126. 'ooinstall.conf'), merged_config)
  127. self.cli_args.extend(["-c", config_file, "install"])
  128. result = self.runner.invoke(cli.cli, self.cli_args)
  129. self.assert_result(result, 0)
  130. # Check the inventory file looks as we would expect:
  131. inventory = ConfigParser.ConfigParser(allow_no_value=True)
  132. inventory.read(os.path.join(self.work_dir, '.ansible/hosts'))
  133. self.assertEquals('bob',
  134. inventory.get('OSEv3:vars', 'ansible_ssh_user'))
  135. self.assertEquals('openshift-enterprise',
  136. inventory.get('OSEv3:vars', 'deployment_type'))
  137. # Check the masters:
  138. self.assertEquals(1, len(inventory.items('masters')))
  139. self.assertEquals(3, len(inventory.items('nodes')))
  140. for item in inventory.items('masters'):
  141. # ansible host lines do NOT parse nicely:
  142. master_line = item[0]
  143. if item[1] is not None:
  144. master_line = "%s=%s" % (master_line, item[1])
  145. self.assertTrue('openshift_ip' in master_line)
  146. self.assertTrue('openshift_public_ip' in master_line)
  147. self.assertTrue('openshift_hostname' in master_line)
  148. self.assertTrue('openshift_public_hostname' in master_line)
  149. @patch('ooinstall.openshift_ansible.run_main_playbook')
  150. @patch('ooinstall.openshift_ansible.load_system_facts')
  151. def test_variant_version_latest_assumed(self, load_facts_mock,
  152. run_playbook_mock):
  153. load_facts_mock.return_value = (MOCK_FACTS, 0)
  154. run_playbook_mock.return_value = 0
  155. config_file = self.write_config(os.path.join(self.work_dir,
  156. 'ooinstall.conf'), SAMPLE_CONFIG % 'openshift-enterprise')
  157. self.cli_args.extend(["-c", config_file, "install"])
  158. result = self.runner.invoke(cli.cli, self.cli_args)
  159. self.assert_result(result, 0)
  160. written_config = self._read_yaml(config_file)
  161. self.assertEquals('openshift-enterprise', written_config['variant'])
  162. # We didn't specify a version so the latest should have been assumed,
  163. # and written to disk:
  164. self.assertEquals('3.1', written_config['variant_version'])
  165. # Make sure the correct value was passed to ansible:
  166. inventory = ConfigParser.ConfigParser(allow_no_value=True)
  167. inventory.read(os.path.join(self.work_dir, '.ansible/hosts'))
  168. self.assertEquals('openshift-enterprise',
  169. inventory.get('OSEv3:vars', 'deployment_type'))
  170. @patch('ooinstall.openshift_ansible.run_main_playbook')
  171. @patch('ooinstall.openshift_ansible.load_system_facts')
  172. def test_variant_version_preserved(self, load_facts_mock,
  173. run_playbook_mock):
  174. load_facts_mock.return_value = (MOCK_FACTS, 0)
  175. run_playbook_mock.return_value = 0
  176. config = SAMPLE_CONFIG % 'openshift-enterprise'
  177. config = '%s\n%s' % (config, 'variant_version: 3.0')
  178. config_file = self.write_config(os.path.join(self.work_dir,
  179. 'ooinstall.conf'), config)
  180. self.cli_args.extend(["-c", config_file, "install"])
  181. result = self.runner.invoke(cli.cli, self.cli_args)
  182. self.assert_result(result, 0)
  183. written_config = self._read_yaml(config_file)
  184. self.assertEquals('openshift-enterprise', written_config['variant'])
  185. # Make sure our older version was preserved:
  186. # and written to disk:
  187. self.assertEquals('3.0', written_config['variant_version'])
  188. inventory = ConfigParser.ConfigParser(allow_no_value=True)
  189. inventory.read(os.path.join(self.work_dir, '.ansible/hosts'))
  190. self.assertEquals('enterprise',
  191. inventory.get('OSEv3:vars', 'deployment_type'))
  192. @patch('ooinstall.openshift_ansible.run_ansible')
  193. @patch('ooinstall.openshift_ansible.load_system_facts')
  194. def test_no_ansible_config_specified(self, load_facts_mock, run_ansible_mock):
  195. load_facts_mock.return_value = (MOCK_FACTS, 0)
  196. run_ansible_mock.return_value = 0
  197. config = SAMPLE_CONFIG % 'openshift-enterprise'
  198. self._ansible_config_test(load_facts_mock, run_ansible_mock,
  199. config, None, None)
  200. @patch('ooinstall.openshift_ansible.run_ansible')
  201. @patch('ooinstall.openshift_ansible.load_system_facts')
  202. def test_ansible_config_specified_cli(self, load_facts_mock, run_ansible_mock):
  203. load_facts_mock.return_value = (MOCK_FACTS, 0)
  204. run_ansible_mock.return_value = 0
  205. config = SAMPLE_CONFIG % 'openshift-enterprise'
  206. ansible_config = os.path.join(self.work_dir, 'ansible.cfg')
  207. self._ansible_config_test(load_facts_mock, run_ansible_mock,
  208. config, ansible_config, ansible_config)
  209. @patch('ooinstall.openshift_ansible.run_ansible')
  210. @patch('ooinstall.openshift_ansible.load_system_facts')
  211. def test_ansible_config_specified_in_installer_config(self,
  212. load_facts_mock, run_ansible_mock):
  213. load_facts_mock.return_value = (MOCK_FACTS, 0)
  214. run_ansible_mock.return_value = 0
  215. ansible_config = os.path.join(self.work_dir, 'ansible.cfg')
  216. config = SAMPLE_CONFIG % 'openshift-enterprise'
  217. config = "%s\nansible_config: %s" % (config, ansible_config)
  218. self._ansible_config_test(load_facts_mock, run_ansible_mock,
  219. config, None, ansible_config)
  220. #pylint: disable=too-many-arguments
  221. # This method allows for drastically simpler tests to write, and the args
  222. # are all useful.
  223. def _ansible_config_test(self, load_facts_mock, run_ansible_mock,
  224. installer_config, ansible_config_cli=None, expected_result=None):
  225. """
  226. Utility method for testing the ways you can specify the ansible config.
  227. """
  228. load_facts_mock.return_value = (MOCK_FACTS, 0)
  229. run_ansible_mock.return_value = 0
  230. config_file = self.write_config(os.path.join(self.work_dir,
  231. 'ooinstall.conf'), installer_config)
  232. self.cli_args.extend(["-c", config_file])
  233. if ansible_config_cli:
  234. self.cli_args.extend(["--ansible-config", ansible_config_cli])
  235. self.cli_args.append("install")
  236. result = self.runner.invoke(cli.cli, self.cli_args)
  237. self.assert_result(result, 0)
  238. # Test the env vars for facts playbook:
  239. facts_env_vars = load_facts_mock.call_args[0][2]
  240. if expected_result:
  241. self.assertEquals(expected_result, facts_env_vars['ANSIBLE_CONFIG'])
  242. else:
  243. self.assertFalse('ANSIBLE_CONFIG' in facts_env_vars)
  244. # Test the env vars for main playbook:
  245. env_vars = run_ansible_mock.call_args[0][2]
  246. if expected_result:
  247. self.assertEquals(expected_result, env_vars['ANSIBLE_CONFIG'])
  248. else:
  249. self.assertFalse('ANSIBLE_CONFIG' in env_vars)
  250. class AttendedCliTests(OOCliFixture):
  251. def setUp(self):
  252. OOCliFixture.setUp(self)
  253. # Doesn't exist but keeps us from reading the local users config:
  254. self.config_file = os.path.join(self.work_dir, 'config.yml')
  255. self.cli_args.extend(["-c", self.config_file])
  256. #pylint: disable=too-many-arguments
  257. def _build_input(self, ssh_user=None, hosts=None, variant_num=None,
  258. add_nodes=None, confirm_facts=None):
  259. """
  260. Builds a CLI input string with newline characters to simulate
  261. the full run.
  262. This gives us only one place to update when the input prompts change.
  263. """
  264. inputs = [
  265. 'y', # let's proceed
  266. ]
  267. if ssh_user:
  268. inputs.append(ssh_user)
  269. if hosts:
  270. i = 0
  271. for (host, is_master) in hosts:
  272. inputs.append(host)
  273. inputs.append('y' if is_master else 'n')
  274. inputs.append('rpm')
  275. if i < len(hosts) - 1:
  276. inputs.append('y') # Add more hosts
  277. else:
  278. inputs.append('n') # Done adding hosts
  279. i += 1
  280. if variant_num:
  281. inputs.append(str(variant_num)) # Choose variant + version
  282. # TODO: support option 2, fresh install
  283. if add_nodes:
  284. inputs.append('1') # Add more nodes
  285. i = 0
  286. for (host, is_master) in add_nodes:
  287. inputs.append(host)
  288. inputs.append('y' if is_master else 'n')
  289. inputs.append('rpm')
  290. if i < len(add_nodes) - 1:
  291. inputs.append('y') # Add more hosts
  292. else:
  293. inputs.append('n') # Done adding hosts
  294. i += 1
  295. inputs.extend([
  296. confirm_facts,
  297. 'y', # lets do this
  298. ])
  299. return '\n'.join(inputs)
  300. def _verify_load_facts(self, load_facts_mock):
  301. """ Check that we ran load facts with expected inputs. """
  302. load_facts_args = load_facts_mock.call_args[0]
  303. self.assertEquals(os.path.join(self.work_dir, ".ansible/hosts"),
  304. load_facts_args[0])
  305. self.assertEquals(os.path.join(self.work_dir,
  306. "playbooks/byo/openshift_facts.yml"), load_facts_args[1])
  307. env_vars = load_facts_args[2]
  308. self.assertEquals(os.path.join(self.work_dir,
  309. '.ansible/callback_facts.yaml'),
  310. env_vars['OO_INSTALL_CALLBACK_FACTS_YAML'])
  311. self.assertEqual('/tmp/ansible.log', env_vars['ANSIBLE_LOG_PATH'])
  312. def _verify_run_playbook(self, run_playbook_mock, exp_hosts_len, exp_hosts_to_run_on_len):
  313. """ Check that we ran playbook with expected inputs. """
  314. hosts = run_playbook_mock.call_args[0][0]
  315. hosts_to_run_on = run_playbook_mock.call_args[0][1]
  316. self.assertEquals(exp_hosts_len, len(hosts))
  317. self.assertEquals(exp_hosts_to_run_on_len, len(hosts_to_run_on))
  318. def _verify_config_hosts(self, written_config, host_count):
  319. self.assertEquals(host_count, len(written_config['hosts']))
  320. for h in written_config['hosts']:
  321. self.assertTrue(h['node'])
  322. self.assertTrue('ip' in h)
  323. self.assertTrue('hostname' in h)
  324. self.assertTrue('public_ip' in h)
  325. self.assertTrue('public_hostname' in h)
  326. @patch('ooinstall.openshift_ansible.run_main_playbook')
  327. @patch('ooinstall.openshift_ansible.load_system_facts')
  328. def test_full_run(self, load_facts_mock, run_playbook_mock):
  329. load_facts_mock.return_value = (MOCK_FACTS, 0)
  330. run_playbook_mock.return_value = 0
  331. cli_input = self._build_input(hosts=[
  332. ('10.0.0.1', True),
  333. ('10.0.0.2', False),
  334. ('10.0.0.3', False)],
  335. ssh_user='root',
  336. variant_num=1,
  337. confirm_facts='y')
  338. self.cli_args.append("install")
  339. result = self.runner.invoke(cli.cli, self.cli_args,
  340. input=cli_input)
  341. self.assert_result(result, 0)
  342. self._verify_load_facts(load_facts_mock)
  343. self._verify_run_playbook(run_playbook_mock, 3, 3)
  344. written_config = self._read_yaml(self.config_file)
  345. self._verify_config_hosts(written_config, 3)
  346. @patch('ooinstall.openshift_ansible.run_main_playbook')
  347. @patch('ooinstall.openshift_ansible.load_system_facts')
  348. def test_add_nodes(self, load_facts_mock, run_playbook_mock):
  349. # Modify the mock facts to return a version indicating OpenShift
  350. # is already installed on our master, and the first node.
  351. mock_facts = copy.deepcopy(MOCK_FACTS)
  352. mock_facts['10.0.0.1']['common']['version'] = "3.0.0"
  353. mock_facts['10.0.0.2']['common']['version'] = "3.0.0"
  354. load_facts_mock.return_value = (mock_facts, 0)
  355. run_playbook_mock.return_value = 0
  356. cli_input = self._build_input(hosts=[
  357. ('10.0.0.1', True),
  358. ('10.0.0.2', False),
  359. ],
  360. add_nodes=[('10.0.0.3', False)],
  361. ssh_user='root',
  362. variant_num=1,
  363. confirm_facts='y')
  364. self.cli_args.append("install")
  365. result = self.runner.invoke(cli.cli,
  366. self.cli_args,
  367. input=cli_input)
  368. self.assert_result(result, 0)
  369. self._verify_load_facts(load_facts_mock)
  370. self._verify_run_playbook(run_playbook_mock, 3, 2)
  371. written_config = self._read_yaml(self.config_file)
  372. self._verify_config_hosts(written_config, 3)
  373. @patch('ooinstall.openshift_ansible.run_main_playbook')
  374. @patch('ooinstall.openshift_ansible.load_system_facts')
  375. def test_fresh_install_with_config(self, load_facts_mock, run_playbook_mock):
  376. load_facts_mock.return_value = (MOCK_FACTS, 0)
  377. run_playbook_mock.return_value = 0
  378. config_file = self.write_config(os.path.join(self.work_dir,
  379. 'ooinstall.conf'),
  380. SAMPLE_CONFIG % 'openshift-enterprise')
  381. cli_input = self._build_input(confirm_facts='y')
  382. self.cli_args.extend(["-c", config_file])
  383. self.cli_args.append("install")
  384. result = self.runner.invoke(cli.cli,
  385. self.cli_args,
  386. input=cli_input)
  387. self.assert_result(result, 0)
  388. self._verify_load_facts(load_facts_mock)
  389. self._verify_run_playbook(run_playbook_mock, 3, 3)
  390. written_config = self._read_yaml(config_file)
  391. self._verify_config_hosts(written_config, 3)
  392. # TODO: test with config file, attended add node
  393. # TODO: test with config file, attended new node already in config file
  394. # TODO: test with config file, attended new node already in config file, plus manually added nodes
  395. # TODO: test with config file, attended reject facts