cli_installer_tests.py 18 KB

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