cli_installer_tests.py 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040
  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. MOCK_FACTS_QUICKHA = {
  39. '10.0.0.1': {
  40. 'common': {
  41. 'ip': '10.0.0.1',
  42. 'public_ip': '10.0.0.1',
  43. 'hostname': 'master-private.example.com',
  44. 'public_hostname': 'master.example.com'
  45. }
  46. },
  47. '10.0.0.2': {
  48. 'common': {
  49. 'ip': '10.0.0.2',
  50. 'public_ip': '10.0.0.2',
  51. 'hostname': 'node1-private.example.com',
  52. 'public_hostname': 'node1.example.com'
  53. }
  54. },
  55. '10.0.0.3': {
  56. 'common': {
  57. 'ip': '10.0.0.3',
  58. 'public_ip': '10.0.0.3',
  59. 'hostname': 'node2-private.example.com',
  60. 'public_hostname': 'node2.example.com'
  61. }
  62. },
  63. '10.0.0.4': {
  64. 'common': {
  65. 'ip': '10.0.0.4',
  66. 'public_ip': '10.0.0.4',
  67. 'hostname': 'proxy-private.example.com',
  68. 'public_hostname': 'proxy.example.com'
  69. }
  70. },
  71. }
  72. # Substitute in a product name before use:
  73. SAMPLE_CONFIG = """
  74. variant: %s
  75. ansible_ssh_user: root
  76. hosts:
  77. - connect_to: 10.0.0.1
  78. ip: 10.0.0.1
  79. hostname: master-private.example.com
  80. public_ip: 24.222.0.1
  81. public_hostname: master.example.com
  82. master: true
  83. node: true
  84. - connect_to: 10.0.0.2
  85. ip: 10.0.0.2
  86. hostname: node1-private.example.com
  87. public_ip: 24.222.0.2
  88. public_hostname: node1.example.com
  89. node: true
  90. - connect_to: 10.0.0.3
  91. ip: 10.0.0.3
  92. hostname: node2-private.example.com
  93. public_ip: 24.222.0.3
  94. public_hostname: node2.example.com
  95. node: true
  96. """
  97. # Missing connect_to on some hosts:
  98. BAD_CONFIG = """
  99. variant: %s
  100. ansible_ssh_user: root
  101. hosts:
  102. - connect_to: 10.0.0.1
  103. ip: 10.0.0.1
  104. hostname: master-private.example.com
  105. public_ip: 24.222.0.1
  106. public_hostname: master.example.com
  107. master: true
  108. node: true
  109. - ip: 10.0.0.2
  110. hostname: node1-private.example.com
  111. public_ip: 24.222.0.2
  112. public_hostname: node1.example.com
  113. node: true
  114. - connect_to: 10.0.0.3
  115. ip: 10.0.0.3
  116. hostname: node2-private.example.com
  117. public_ip: 24.222.0.3
  118. public_hostname: node2.example.com
  119. node: true
  120. """
  121. QUICKHA_CONFIG = """
  122. variant: %s
  123. ansible_ssh_user: root
  124. hosts:
  125. - connect_to: 10.0.0.1
  126. ip: 10.0.0.1
  127. hostname: master-private.example.com
  128. public_ip: 24.222.0.1
  129. public_hostname: master.example.com
  130. master: true
  131. node: true
  132. - connect_to: 10.0.0.2
  133. ip: 10.0.0.2
  134. hostname: node1-private.example.com
  135. public_ip: 24.222.0.2
  136. public_hostname: node1.example.com
  137. master: true
  138. node: true
  139. - connect_to: 10.0.0.3
  140. ip: 10.0.0.3
  141. hostname: node2-private.example.com
  142. public_ip: 24.222.0.3
  143. public_hostname: node2.example.com
  144. node: true
  145. - connect_to: 10.0.0.4
  146. ip: 10.0.0.4
  147. hostname: proxy-private.example.com
  148. public_ip: 24.222.0.4
  149. public_hostname: proxy.example.com
  150. master_lb: true
  151. """
  152. QUICKHA_CONFIG_REUSED_LB = """
  153. variant: %s
  154. ansible_ssh_user: root
  155. hosts:
  156. - connect_to: 10.0.0.1
  157. ip: 10.0.0.1
  158. hostname: master-private.example.com
  159. public_ip: 24.222.0.1
  160. public_hostname: master.example.com
  161. master: true
  162. node: true
  163. - connect_to: 10.0.0.2
  164. ip: 10.0.0.2
  165. hostname: node1-private.example.com
  166. public_ip: 24.222.0.2
  167. public_hostname: node1.example.com
  168. master: true
  169. node: true
  170. master_lb: true
  171. - connect_to: 10.0.0.3
  172. ip: 10.0.0.3
  173. hostname: node2-private.example.com
  174. public_ip: 24.222.0.3
  175. public_hostname: node2.example.com
  176. node: true
  177. """
  178. QUICKHA_CONFIG_NO_LB = """
  179. variant: %s
  180. ansible_ssh_user: root
  181. hosts:
  182. - connect_to: 10.0.0.1
  183. ip: 10.0.0.1
  184. hostname: master-private.example.com
  185. public_ip: 24.222.0.1
  186. public_hostname: master.example.com
  187. master: true
  188. node: true
  189. - connect_to: 10.0.0.2
  190. ip: 10.0.0.2
  191. hostname: node1-private.example.com
  192. public_ip: 24.222.0.2
  193. public_hostname: node1.example.com
  194. master: true
  195. node: true
  196. - connect_to: 10.0.0.3
  197. ip: 10.0.0.3
  198. hostname: node2-private.example.com
  199. public_ip: 24.222.0.3
  200. public_hostname: node2.example.com
  201. node: true
  202. """
  203. class OOCliFixture(OOInstallFixture):
  204. def setUp(self):
  205. OOInstallFixture.setUp(self)
  206. self.runner = CliRunner()
  207. # Add any arguments you would like to test here, the defaults ensure
  208. # we only do unattended invocations here, and using temporary files/dirs.
  209. self.cli_args = ["-a", self.work_dir]
  210. def run_cli(self):
  211. return self.runner.invoke(cli.cli, self.cli_args)
  212. def assert_result(self, result, exit_code):
  213. if result.exception is not None or result.exit_code != exit_code:
  214. print "Unexpected result from CLI execution"
  215. print "Exit code: %s" % result.exit_code
  216. print "Exception: %s" % result.exception
  217. print result.exc_info
  218. import traceback
  219. traceback.print_exception(*result.exc_info)
  220. print "Output:\n%s" % result.output
  221. self.fail("Exception during CLI execution")
  222. def _read_yaml(self, config_file_path):
  223. f = open(config_file_path, 'r')
  224. config = yaml.safe_load(f.read())
  225. f.close()
  226. return config
  227. def _verify_load_facts(self, load_facts_mock):
  228. """ Check that we ran load facts with expected inputs. """
  229. load_facts_args = load_facts_mock.call_args[0]
  230. self.assertEquals(os.path.join(self.work_dir, ".ansible/hosts"),
  231. load_facts_args[0])
  232. self.assertEquals(os.path.join(self.work_dir,
  233. "playbooks/byo/openshift_facts.yml"), load_facts_args[1])
  234. env_vars = load_facts_args[2]
  235. self.assertEquals(os.path.join(self.work_dir,
  236. '.ansible/callback_facts.yaml'),
  237. env_vars['OO_INSTALL_CALLBACK_FACTS_YAML'])
  238. self.assertEqual('/tmp/ansible.log', env_vars['ANSIBLE_LOG_PATH'])
  239. def _verify_run_playbook(self, run_playbook_mock, exp_hosts_len, exp_hosts_to_run_on_len):
  240. """ Check that we ran playbook with expected inputs. """
  241. hosts = run_playbook_mock.call_args[0][0]
  242. hosts_to_run_on = run_playbook_mock.call_args[0][1]
  243. self.assertEquals(exp_hosts_len, len(hosts))
  244. self.assertEquals(exp_hosts_to_run_on_len, len(hosts_to_run_on))
  245. def _verify_config_hosts(self, written_config, host_count):
  246. self.assertEquals(host_count, len(written_config['hosts']))
  247. for h in written_config['hosts']:
  248. self.assertTrue('hostname' in h)
  249. self.assertTrue('public_hostname' in h)
  250. if 'preconfigured' not in h:
  251. self.assertTrue(h['node'])
  252. self.assertTrue('ip' in h)
  253. self.assertTrue('public_ip' in h)
  254. #pylint: disable=too-many-arguments
  255. def _verify_get_hosts_to_run_on(self, mock_facts, load_facts_mock,
  256. run_playbook_mock, cli_input,
  257. exp_hosts_len=None, exp_hosts_to_run_on_len=None,
  258. force=None):
  259. """
  260. Tests cli_installer.py:get_hosts_to_run_on. That method has quite a
  261. few subtle branches in the logic. The goal with this method is simply
  262. to handle all the messy stuff here and allow the main test cases to be
  263. easily read. The basic idea is to modify mock_facts to return a
  264. version indicating OpenShift is already installed on particular hosts.
  265. """
  266. load_facts_mock.return_value = (mock_facts, 0)
  267. run_playbook_mock.return_value = 0
  268. if cli_input:
  269. self.cli_args.append("install")
  270. result = self.runner.invoke(cli.cli,
  271. self.cli_args,
  272. input=cli_input)
  273. else:
  274. config_file = self.write_config(os.path.join(self.work_dir,
  275. 'ooinstall.conf'), SAMPLE_CONFIG % 'openshift-enterprise')
  276. self.cli_args.extend(["-c", config_file, "install"])
  277. if force:
  278. self.cli_args.append("--force")
  279. result = self.runner.invoke(cli.cli, self.cli_args)
  280. written_config = self._read_yaml(config_file)
  281. self._verify_config_hosts(written_config, exp_hosts_len)
  282. self.assert_result(result, 0)
  283. self._verify_load_facts(load_facts_mock)
  284. self._verify_run_playbook(run_playbook_mock, exp_hosts_len, exp_hosts_to_run_on_len)
  285. # Make sure we ran on the expected masters and nodes:
  286. hosts = run_playbook_mock.call_args[0][0]
  287. hosts_to_run_on = run_playbook_mock.call_args[0][1]
  288. self.assertEquals(exp_hosts_len, len(hosts))
  289. self.assertEquals(exp_hosts_to_run_on_len, len(hosts_to_run_on))
  290. class UnattendedCliTests(OOCliFixture):
  291. def setUp(self):
  292. OOCliFixture.setUp(self)
  293. self.cli_args.append("-u")
  294. # unattended with config file and all installed hosts (without --force)
  295. @patch('ooinstall.openshift_ansible.run_main_playbook')
  296. @patch('ooinstall.openshift_ansible.load_system_facts')
  297. def test_get_hosts_to_run_on1(self, load_facts_mock, run_playbook_mock):
  298. mock_facts = copy.deepcopy(MOCK_FACTS)
  299. mock_facts['10.0.0.1']['common']['version'] = "3.0.0"
  300. mock_facts['10.0.0.2']['common']['version'] = "3.0.0"
  301. mock_facts['10.0.0.3']['common']['version'] = "3.0.0"
  302. load_facts_mock.return_value = (mock_facts, 0)
  303. run_playbook_mock.return_value = 0
  304. config_file = self.write_config(os.path.join(self.work_dir,
  305. 'ooinstall.conf'), SAMPLE_CONFIG % 'openshift-enterprise')
  306. self.cli_args.extend(["-c", config_file, "install"])
  307. result = self.runner.invoke(cli.cli, self.cli_args)
  308. if result.exception is None or result.exit_code != 1:
  309. print "Exit code: %s" % result.exit_code
  310. self.fail("Unexpected CLI return")
  311. # unattended with config file and all installed hosts (with --force)
  312. @patch('ooinstall.openshift_ansible.run_main_playbook')
  313. @patch('ooinstall.openshift_ansible.load_system_facts')
  314. def test_get_hosts_to_run_on2(self, load_facts_mock, run_playbook_mock):
  315. mock_facts = copy.deepcopy(MOCK_FACTS)
  316. mock_facts['10.0.0.1']['common']['version'] = "3.0.0"
  317. mock_facts['10.0.0.2']['common']['version'] = "3.0.0"
  318. mock_facts['10.0.0.3']['common']['version'] = "3.0.0"
  319. self._verify_get_hosts_to_run_on(mock_facts, load_facts_mock, run_playbook_mock,
  320. cli_input=None,
  321. exp_hosts_len=3,
  322. exp_hosts_to_run_on_len=3,
  323. force=True)
  324. # unattended with config file and no installed hosts (without --force)
  325. @patch('ooinstall.openshift_ansible.run_main_playbook')
  326. @patch('ooinstall.openshift_ansible.load_system_facts')
  327. def test_get_hosts_to_run_on3(self, load_facts_mock, run_playbook_mock):
  328. load_facts_mock.return_value = (MOCK_FACTS, 0)
  329. run_playbook_mock.return_value = 0
  330. self._verify_get_hosts_to_run_on(MOCK_FACTS, load_facts_mock, run_playbook_mock,
  331. cli_input=None,
  332. exp_hosts_len=3,
  333. exp_hosts_to_run_on_len=3,
  334. force=False)
  335. # unattended with config file and no installed hosts (with --force)
  336. @patch('ooinstall.openshift_ansible.run_main_playbook')
  337. @patch('ooinstall.openshift_ansible.load_system_facts')
  338. def test_get_hosts_to_run_on4(self, load_facts_mock, run_playbook_mock):
  339. load_facts_mock.return_value = (MOCK_FACTS, 0)
  340. run_playbook_mock.return_value = 0
  341. self._verify_get_hosts_to_run_on(MOCK_FACTS, load_facts_mock, run_playbook_mock,
  342. cli_input=None,
  343. exp_hosts_len=3,
  344. exp_hosts_to_run_on_len=3,
  345. force=True)
  346. # unattended with config file and some installed some uninstalled hosts (without --force)
  347. @patch('ooinstall.openshift_ansible.run_main_playbook')
  348. @patch('ooinstall.openshift_ansible.load_system_facts')
  349. def test_get_hosts_to_run_on5(self, load_facts_mock, run_playbook_mock):
  350. mock_facts = copy.deepcopy(MOCK_FACTS)
  351. mock_facts['10.0.0.1']['common']['version'] = "3.0.0"
  352. mock_facts['10.0.0.2']['common']['version'] = "3.0.0"
  353. self._verify_get_hosts_to_run_on(mock_facts, load_facts_mock, run_playbook_mock,
  354. cli_input=None,
  355. exp_hosts_len=3,
  356. exp_hosts_to_run_on_len=2,
  357. force=False)
  358. # unattended with config file and some installed some uninstalled hosts (with --force)
  359. @patch('ooinstall.openshift_ansible.run_main_playbook')
  360. @patch('ooinstall.openshift_ansible.load_system_facts')
  361. def test_get_hosts_to_run_on6(self, load_facts_mock, run_playbook_mock):
  362. mock_facts = copy.deepcopy(MOCK_FACTS)
  363. mock_facts['10.0.0.1']['common']['version'] = "3.0.0"
  364. mock_facts['10.0.0.2']['common']['version'] = "3.0.0"
  365. self._verify_get_hosts_to_run_on(mock_facts, load_facts_mock, run_playbook_mock,
  366. cli_input=None,
  367. exp_hosts_len=3,
  368. exp_hosts_to_run_on_len=3,
  369. force=True)
  370. @patch('ooinstall.openshift_ansible.run_main_playbook')
  371. @patch('ooinstall.openshift_ansible.load_system_facts')
  372. def test_cfg_full_run(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'), SAMPLE_CONFIG % 'openshift-enterprise')
  377. self.cli_args.extend(["-c", config_file, "install"])
  378. result = self.runner.invoke(cli.cli, self.cli_args)
  379. self.assert_result(result, 0)
  380. load_facts_args = load_facts_mock.call_args[0]
  381. self.assertEquals(os.path.join(self.work_dir, ".ansible/hosts"),
  382. load_facts_args[0])
  383. self.assertEquals(os.path.join(self.work_dir,
  384. "playbooks/byo/openshift_facts.yml"), load_facts_args[1])
  385. env_vars = load_facts_args[2]
  386. self.assertEquals(os.path.join(self.work_dir,
  387. '.ansible/callback_facts.yaml'),
  388. env_vars['OO_INSTALL_CALLBACK_FACTS_YAML'])
  389. self.assertEqual('/tmp/ansible.log', env_vars['ANSIBLE_LOG_PATH'])
  390. # If user running test has rpm installed, this might be set to default:
  391. self.assertTrue('ANSIBLE_CONFIG' not in env_vars or
  392. env_vars['ANSIBLE_CONFIG'] == cli.DEFAULT_ANSIBLE_CONFIG)
  393. # Make sure we ran on the expected masters and nodes:
  394. hosts = run_playbook_mock.call_args[0][0]
  395. hosts_to_run_on = run_playbook_mock.call_args[0][1]
  396. self.assertEquals(3, len(hosts))
  397. self.assertEquals(3, len(hosts_to_run_on))
  398. @patch('ooinstall.openshift_ansible.run_main_playbook')
  399. @patch('ooinstall.openshift_ansible.load_system_facts')
  400. def test_inventory_write(self, load_facts_mock, run_playbook_mock):
  401. # Add an ssh user so we can verify it makes it to the inventory file:
  402. merged_config = "%s\n%s" % (SAMPLE_CONFIG % 'openshift-enterprise',
  403. "ansible_ssh_user: bob")
  404. load_facts_mock.return_value = (MOCK_FACTS, 0)
  405. run_playbook_mock.return_value = 0
  406. config_file = self.write_config(os.path.join(self.work_dir,
  407. 'ooinstall.conf'), merged_config)
  408. self.cli_args.extend(["-c", config_file, "install"])
  409. result = self.runner.invoke(cli.cli, self.cli_args)
  410. self.assert_result(result, 0)
  411. # Check the inventory file looks as we would expect:
  412. inventory = ConfigParser.ConfigParser(allow_no_value=True)
  413. inventory.read(os.path.join(self.work_dir, '.ansible/hosts'))
  414. self.assertEquals('bob',
  415. inventory.get('OSEv3:vars', 'ansible_ssh_user'))
  416. self.assertEquals('openshift-enterprise',
  417. inventory.get('OSEv3:vars', 'deployment_type'))
  418. # Check the masters:
  419. self.assertEquals(1, len(inventory.items('masters')))
  420. self.assertEquals(3, len(inventory.items('nodes')))
  421. for item in inventory.items('masters'):
  422. # ansible host lines do NOT parse nicely:
  423. master_line = item[0]
  424. if item[1] is not None:
  425. master_line = "%s=%s" % (master_line, item[1])
  426. self.assertTrue('openshift_ip' in master_line)
  427. self.assertTrue('openshift_public_ip' in master_line)
  428. self.assertTrue('openshift_hostname' in master_line)
  429. self.assertTrue('openshift_public_hostname' in master_line)
  430. @patch('ooinstall.openshift_ansible.run_main_playbook')
  431. @patch('ooinstall.openshift_ansible.load_system_facts')
  432. def test_variant_version_latest_assumed(self, load_facts_mock,
  433. run_playbook_mock):
  434. load_facts_mock.return_value = (MOCK_FACTS, 0)
  435. run_playbook_mock.return_value = 0
  436. config_file = self.write_config(os.path.join(self.work_dir,
  437. 'ooinstall.conf'), SAMPLE_CONFIG % 'openshift-enterprise')
  438. self.cli_args.extend(["-c", config_file, "install"])
  439. result = self.runner.invoke(cli.cli, self.cli_args)
  440. self.assert_result(result, 0)
  441. written_config = self._read_yaml(config_file)
  442. self.assertEquals('openshift-enterprise', written_config['variant'])
  443. # We didn't specify a version so the latest should have been assumed,
  444. # and written to disk:
  445. self.assertEquals('3.1', written_config['variant_version'])
  446. # Make sure the correct value was passed to ansible:
  447. inventory = ConfigParser.ConfigParser(allow_no_value=True)
  448. inventory.read(os.path.join(self.work_dir, '.ansible/hosts'))
  449. self.assertEquals('openshift-enterprise',
  450. inventory.get('OSEv3:vars', 'deployment_type'))
  451. @patch('ooinstall.openshift_ansible.run_main_playbook')
  452. @patch('ooinstall.openshift_ansible.load_system_facts')
  453. def test_variant_version_preserved(self, load_facts_mock,
  454. run_playbook_mock):
  455. load_facts_mock.return_value = (MOCK_FACTS, 0)
  456. run_playbook_mock.return_value = 0
  457. config = SAMPLE_CONFIG % 'openshift-enterprise'
  458. config = '%s\n%s' % (config, 'variant_version: 3.0')
  459. config_file = self.write_config(os.path.join(self.work_dir,
  460. 'ooinstall.conf'), config)
  461. self.cli_args.extend(["-c", config_file, "install"])
  462. result = self.runner.invoke(cli.cli, self.cli_args)
  463. self.assert_result(result, 0)
  464. written_config = self._read_yaml(config_file)
  465. self.assertEquals('openshift-enterprise', written_config['variant'])
  466. # Make sure our older version was preserved:
  467. # and written to disk:
  468. self.assertEquals('3.0', written_config['variant_version'])
  469. inventory = ConfigParser.ConfigParser(allow_no_value=True)
  470. inventory.read(os.path.join(self.work_dir, '.ansible/hosts'))
  471. self.assertEquals('enterprise',
  472. inventory.get('OSEv3:vars', 'deployment_type'))
  473. @patch('ooinstall.openshift_ansible.run_ansible')
  474. @patch('ooinstall.openshift_ansible.load_system_facts')
  475. def test_no_ansible_config_specified(self, load_facts_mock, run_ansible_mock):
  476. load_facts_mock.return_value = (MOCK_FACTS, 0)
  477. run_ansible_mock.return_value = 0
  478. config = SAMPLE_CONFIG % 'openshift-enterprise'
  479. self._ansible_config_test(load_facts_mock, run_ansible_mock,
  480. config, None, None)
  481. @patch('ooinstall.openshift_ansible.run_ansible')
  482. @patch('ooinstall.openshift_ansible.load_system_facts')
  483. def test_ansible_config_specified_cli(self, load_facts_mock, run_ansible_mock):
  484. load_facts_mock.return_value = (MOCK_FACTS, 0)
  485. run_ansible_mock.return_value = 0
  486. config = SAMPLE_CONFIG % 'openshift-enterprise'
  487. ansible_config = os.path.join(self.work_dir, 'ansible.cfg')
  488. self._ansible_config_test(load_facts_mock, run_ansible_mock,
  489. config, ansible_config, ansible_config)
  490. @patch('ooinstall.openshift_ansible.run_ansible')
  491. @patch('ooinstall.openshift_ansible.load_system_facts')
  492. def test_ansible_config_specified_in_installer_config(self,
  493. load_facts_mock, run_ansible_mock):
  494. load_facts_mock.return_value = (MOCK_FACTS, 0)
  495. run_ansible_mock.return_value = 0
  496. ansible_config = os.path.join(self.work_dir, 'ansible.cfg')
  497. config = SAMPLE_CONFIG % 'openshift-enterprise'
  498. config = "%s\nansible_config: %s" % (config, ansible_config)
  499. self._ansible_config_test(load_facts_mock, run_ansible_mock,
  500. config, None, ansible_config)
  501. #pylint: disable=too-many-arguments
  502. # This method allows for drastically simpler tests to write, and the args
  503. # are all useful.
  504. def _ansible_config_test(self, load_facts_mock, run_ansible_mock,
  505. installer_config, ansible_config_cli=None, expected_result=None):
  506. """
  507. Utility method for testing the ways you can specify the ansible config.
  508. """
  509. load_facts_mock.return_value = (MOCK_FACTS, 0)
  510. run_ansible_mock.return_value = 0
  511. config_file = self.write_config(os.path.join(self.work_dir,
  512. 'ooinstall.conf'), installer_config)
  513. self.cli_args.extend(["-c", config_file])
  514. if ansible_config_cli:
  515. self.cli_args.extend(["--ansible-config", ansible_config_cli])
  516. self.cli_args.append("install")
  517. result = self.runner.invoke(cli.cli, self.cli_args)
  518. self.assert_result(result, 0)
  519. # Test the env vars for facts playbook:
  520. facts_env_vars = load_facts_mock.call_args[0][2]
  521. if expected_result:
  522. self.assertEquals(expected_result, facts_env_vars['ANSIBLE_CONFIG'])
  523. else:
  524. # If user running test has rpm installed, this might be set to default:
  525. self.assertTrue('ANSIBLE_CONFIG' not in facts_env_vars or
  526. facts_env_vars['ANSIBLE_CONFIG'] == cli.DEFAULT_ANSIBLE_CONFIG)
  527. # Test the env vars for main playbook:
  528. env_vars = run_ansible_mock.call_args[0][2]
  529. if expected_result:
  530. self.assertEquals(expected_result, env_vars['ANSIBLE_CONFIG'])
  531. else:
  532. # If user running test has rpm installed, this might be set to default:
  533. self.assertTrue('ANSIBLE_CONFIG' not in env_vars or
  534. env_vars['ANSIBLE_CONFIG'] == cli.DEFAULT_ANSIBLE_CONFIG)
  535. # unattended with bad config file and no installed hosts (without --force)
  536. @patch('ooinstall.openshift_ansible.run_main_playbook')
  537. @patch('ooinstall.openshift_ansible.load_system_facts')
  538. def test_bad_config(self, load_facts_mock, run_playbook_mock):
  539. load_facts_mock.return_value = (MOCK_FACTS, 0)
  540. run_playbook_mock.return_value = 0
  541. config_file = self.write_config(os.path.join(self.work_dir,
  542. 'ooinstall.conf'), BAD_CONFIG % 'openshift-enterprise')
  543. self.cli_args.extend(["-c", config_file, "install"])
  544. result = self.runner.invoke(cli.cli, self.cli_args)
  545. self.assertEquals(1, result.exit_code)
  546. self.assertTrue("You must specify either an ip or hostname"
  547. in result.output)
  548. #unattended with two masters, one node, and haproxy
  549. @patch('ooinstall.openshift_ansible.run_main_playbook')
  550. @patch('ooinstall.openshift_ansible.load_system_facts')
  551. def test_quick_ha_full_run(self, load_facts_mock, run_playbook_mock):
  552. load_facts_mock.return_value = (MOCK_FACTS_QUICKHA, 0)
  553. run_playbook_mock.return_value = 0
  554. config_file = self.write_config(os.path.join(self.work_dir,
  555. 'ooinstall.conf'), QUICKHA_CONFIG % 'openshift-enterprise')
  556. self.cli_args.extend(["-c", config_file, "install"])
  557. result = self.runner.invoke(cli.cli, self.cli_args)
  558. self.assert_result(result, 0)
  559. load_facts_args = load_facts_mock.call_args[0]
  560. self.assertEquals(os.path.join(self.work_dir, ".ansible/hosts"),
  561. load_facts_args[0])
  562. self.assertEquals(os.path.join(self.work_dir,
  563. "playbooks/byo/openshift_facts.yml"), load_facts_args[1])
  564. env_vars = load_facts_args[2]
  565. self.assertEquals(os.path.join(self.work_dir,
  566. '.ansible/callback_facts.yaml'),
  567. env_vars['OO_INSTALL_CALLBACK_FACTS_YAML'])
  568. self.assertEqual('/tmp/ansible.log', env_vars['ANSIBLE_LOG_PATH'])
  569. # If user running test has rpm installed, this might be set to default:
  570. self.assertTrue('ANSIBLE_CONFIG' not in env_vars or
  571. env_vars['ANSIBLE_CONFIG'] == cli.DEFAULT_ANSIBLE_CONFIG)
  572. # Make sure we ran on the expected masters and nodes:
  573. hosts = run_playbook_mock.call_args[0][0]
  574. hosts_to_run_on = run_playbook_mock.call_args[0][1]
  575. self.assertEquals(4, len(hosts))
  576. self.assertEquals(4, len(hosts_to_run_on))
  577. #unattended with two masters, one node, but no load balancer specified:
  578. @patch('ooinstall.openshift_ansible.run_main_playbook')
  579. @patch('ooinstall.openshift_ansible.load_system_facts')
  580. def test_quick_ha_no_lb(self, load_facts_mock, run_playbook_mock):
  581. load_facts_mock.return_value = (MOCK_FACTS_QUICKHA, 0)
  582. run_playbook_mock.return_value = 0
  583. config_file = self.write_config(os.path.join(self.work_dir,
  584. 'ooinstall.conf'), QUICKHA_CONFIG_NO_LB % 'openshift-enterprise')
  585. self.cli_args.extend(["-c", config_file, "install"])
  586. result = self.runner.invoke(cli.cli, self.cli_args)
  587. # We consider this a valid outcome but lets make sure the warning
  588. # was displayed:
  589. self.assert_result(result, 0)
  590. self.assertTrue('No master load balancer specified in config' in result.output)
  591. load_facts_args = load_facts_mock.call_args[0]
  592. self.assertEquals(os.path.join(self.work_dir, ".ansible/hosts"),
  593. load_facts_args[0])
  594. self.assertEquals(os.path.join(self.work_dir,
  595. "playbooks/byo/openshift_facts.yml"), load_facts_args[1])
  596. env_vars = load_facts_args[2]
  597. self.assertEquals(os.path.join(self.work_dir,
  598. '.ansible/callback_facts.yaml'),
  599. env_vars['OO_INSTALL_CALLBACK_FACTS_YAML'])
  600. self.assertEqual('/tmp/ansible.log', env_vars['ANSIBLE_LOG_PATH'])
  601. # If user running test has rpm installed, this might be set to default:
  602. self.assertTrue('ANSIBLE_CONFIG' not in env_vars or
  603. env_vars['ANSIBLE_CONFIG'] == cli.DEFAULT_ANSIBLE_CONFIG)
  604. # Make sure we ran on the expected masters and nodes:
  605. hosts = run_playbook_mock.call_args[0][0]
  606. hosts_to_run_on = run_playbook_mock.call_args[0][1]
  607. self.assertEquals(3, len(hosts))
  608. self.assertEquals(3, len(hosts_to_run_on))
  609. #unattended with two masters, one node, and one of the masters reused as load balancer:
  610. @patch('ooinstall.openshift_ansible.run_main_playbook')
  611. @patch('ooinstall.openshift_ansible.load_system_facts')
  612. def test_quick_ha_reused_lb(self, load_facts_mock, run_playbook_mock):
  613. load_facts_mock.return_value = (MOCK_FACTS_QUICKHA, 0)
  614. run_playbook_mock.return_value = 0
  615. config_file = self.write_config(os.path.join(self.work_dir,
  616. 'ooinstall.conf'), QUICKHA_CONFIG_REUSED_LB % 'openshift-enterprise')
  617. self.cli_args.extend(["-c", config_file, "install"])
  618. result = self.runner.invoke(cli.cli, self.cli_args)
  619. # This is not a valid configuration:
  620. self.assert_result(result, 0)
  621. class AttendedCliTests(OOCliFixture):
  622. def setUp(self):
  623. OOCliFixture.setUp(self)
  624. # Doesn't exist but keeps us from reading the local users config:
  625. self.config_file = os.path.join(self.work_dir, 'config.yml')
  626. self.cli_args.extend(["-c", self.config_file])
  627. #pylint: disable=too-many-arguments,too-many-branches
  628. def _build_input(self, ssh_user=None, hosts=None, variant_num=None,
  629. add_nodes=None, confirm_facts=None, scheduleable_masters_ok=None,
  630. master_lb=None):
  631. """
  632. Builds a CLI input string with newline characters to simulate
  633. the full run.
  634. This gives us only one place to update when the input prompts change.
  635. """
  636. inputs = [
  637. 'y', # let's proceed
  638. ]
  639. if ssh_user:
  640. inputs.append(ssh_user)
  641. if variant_num:
  642. inputs.append(str(variant_num)) # Choose variant + version
  643. num_masters = 0
  644. if hosts:
  645. i = 0
  646. min_masters_for_ha = 3
  647. for (host, is_master) in hosts:
  648. inputs.append(host)
  649. if is_master:
  650. inputs.append('y')
  651. num_masters += 1
  652. else:
  653. inputs.append('n')
  654. #inputs.append('rpm')
  655. if i < len(hosts) - 1:
  656. if num_masters <= 1 or num_masters >= min_masters_for_ha:
  657. inputs.append('y') # Add more hosts
  658. else:
  659. inputs.append('n') # Done adding hosts
  660. i += 1
  661. # You can pass a single master_lb or a list if you intend for one to get rejected:
  662. if master_lb:
  663. if type(master_lb[0]) is list or type(master_lb[0]) is tuple:
  664. inputs.extend(master_lb[0])
  665. else:
  666. inputs.append(master_lb[0])
  667. inputs.append('y' if master_lb[1] else 'n')
  668. # TODO: support option 2, fresh install
  669. if add_nodes:
  670. if scheduleable_masters_ok:
  671. inputs.append('y')
  672. inputs.append('1') # Add more nodes
  673. i = 0
  674. for (host, is_master) in add_nodes:
  675. inputs.append(host)
  676. #inputs.append('rpm')
  677. if i < len(add_nodes) - 1:
  678. inputs.append('y') # Add more hosts
  679. else:
  680. inputs.append('n') # Done adding hosts
  681. i += 1
  682. if add_nodes is None:
  683. total_hosts = hosts
  684. else:
  685. total_hosts = hosts + add_nodes
  686. if total_hosts is not None and num_masters == len(total_hosts):
  687. inputs.append('y')
  688. inputs.extend([
  689. confirm_facts,
  690. 'y', # lets do this
  691. ])
  692. return '\n'.join(inputs)
  693. @patch('ooinstall.openshift_ansible.run_main_playbook')
  694. @patch('ooinstall.openshift_ansible.load_system_facts')
  695. def test_full_run(self, load_facts_mock, run_playbook_mock):
  696. load_facts_mock.return_value = (MOCK_FACTS, 0)
  697. run_playbook_mock.return_value = 0
  698. cli_input = self._build_input(hosts=[
  699. ('10.0.0.1', True),
  700. ('10.0.0.2', False),
  701. ('10.0.0.3', False)],
  702. ssh_user='root',
  703. variant_num=1,
  704. confirm_facts='y')
  705. self.cli_args.append("install")
  706. result = self.runner.invoke(cli.cli, self.cli_args,
  707. input=cli_input)
  708. self.assert_result(result, 0)
  709. self._verify_load_facts(load_facts_mock)
  710. self._verify_run_playbook(run_playbook_mock, 3, 3)
  711. written_config = self._read_yaml(self.config_file)
  712. self._verify_config_hosts(written_config, 3)
  713. inventory = ConfigParser.ConfigParser(allow_no_value=True)
  714. inventory.read(os.path.join(self.work_dir, '.ansible/hosts'))
  715. self.assertEquals('False',
  716. inventory.get('nodes', '10.0.0.1 openshift_schedulable'))
  717. self.assertEquals(None,
  718. inventory.get('nodes', '10.0.0.2'))
  719. self.assertEquals(None,
  720. inventory.get('nodes', '10.0.0.3'))
  721. # interactive with config file and some installed some uninstalled hosts
  722. @patch('ooinstall.openshift_ansible.run_main_playbook')
  723. @patch('ooinstall.openshift_ansible.load_system_facts')
  724. def test_add_nodes(self, load_facts_mock, run_playbook_mock):
  725. # Modify the mock facts to return a version indicating OpenShift
  726. # is already installed on our master, and the first node.
  727. mock_facts = copy.deepcopy(MOCK_FACTS)
  728. mock_facts['10.0.0.1']['common']['version'] = "3.0.0"
  729. mock_facts['10.0.0.2']['common']['version'] = "3.0.0"
  730. load_facts_mock.return_value = (mock_facts, 0)
  731. run_playbook_mock.return_value = 0
  732. cli_input = self._build_input(hosts=[
  733. ('10.0.0.1', True),
  734. ('10.0.0.2', False),
  735. ],
  736. add_nodes=[('10.0.0.3', False)],
  737. ssh_user='root',
  738. variant_num=1,
  739. confirm_facts='y')
  740. self.cli_args.append("install")
  741. result = self.runner.invoke(cli.cli,
  742. self.cli_args,
  743. input=cli_input)
  744. self.assert_result(result, 0)
  745. self._verify_load_facts(load_facts_mock)
  746. self._verify_run_playbook(run_playbook_mock, 3, 2)
  747. written_config = self._read_yaml(self.config_file)
  748. self._verify_config_hosts(written_config, 3)
  749. @patch('ooinstall.openshift_ansible.run_main_playbook')
  750. @patch('ooinstall.openshift_ansible.load_system_facts')
  751. def test_fresh_install_with_config(self, load_facts_mock, run_playbook_mock):
  752. load_facts_mock.return_value = (MOCK_FACTS, 0)
  753. run_playbook_mock.return_value = 0
  754. config_file = self.write_config(os.path.join(self.work_dir,
  755. 'ooinstall.conf'),
  756. SAMPLE_CONFIG % 'openshift-enterprise')
  757. cli_input = self._build_input(confirm_facts='y')
  758. self.cli_args.extend(["-c", config_file])
  759. self.cli_args.append("install")
  760. result = self.runner.invoke(cli.cli,
  761. self.cli_args,
  762. input=cli_input)
  763. self.assert_result(result, 0)
  764. self._verify_load_facts(load_facts_mock)
  765. self._verify_run_playbook(run_playbook_mock, 3, 3)
  766. written_config = self._read_yaml(config_file)
  767. self._verify_config_hosts(written_config, 3)
  768. #interactive with config file and all installed hosts
  769. @patch('ooinstall.openshift_ansible.run_main_playbook')
  770. @patch('ooinstall.openshift_ansible.load_system_facts')
  771. def test_get_hosts_to_run_on(self, load_facts_mock, run_playbook_mock):
  772. mock_facts = copy.deepcopy(MOCK_FACTS)
  773. mock_facts['10.0.0.1']['common']['version'] = "3.0.0"
  774. mock_facts['10.0.0.2']['common']['version'] = "3.0.0"
  775. cli_input = self._build_input(hosts=[
  776. ('10.0.0.1', True),
  777. ],
  778. add_nodes=[('10.0.0.2', False)],
  779. ssh_user='root',
  780. variant_num=1,
  781. scheduleable_masters_ok=True,
  782. confirm_facts='y')
  783. self._verify_get_hosts_to_run_on(mock_facts, load_facts_mock,
  784. run_playbook_mock,
  785. cli_input,
  786. exp_hosts_len=2,
  787. exp_hosts_to_run_on_len=2,
  788. force=False)
  789. #interactive multimaster: one more node than master
  790. @patch('ooinstall.openshift_ansible.run_main_playbook')
  791. @patch('ooinstall.openshift_ansible.load_system_facts')
  792. def test_ha_dedicated_node(self, load_facts_mock, run_playbook_mock):
  793. load_facts_mock.return_value = (MOCK_FACTS_QUICKHA, 0)
  794. run_playbook_mock.return_value = 0
  795. cli_input = self._build_input(hosts=[
  796. ('10.0.0.1', True),
  797. ('10.0.0.2', True),
  798. ('10.0.0.3', False),
  799. ('10.0.0.4', True)],
  800. ssh_user='root',
  801. variant_num=1,
  802. confirm_facts='y',
  803. master_lb=('10.0.0.5', False))
  804. self.cli_args.append("install")
  805. result = self.runner.invoke(cli.cli, self.cli_args,
  806. input=cli_input)
  807. self.assert_result(result, 0)
  808. self._verify_load_facts(load_facts_mock)
  809. self._verify_run_playbook(run_playbook_mock, 5, 5)
  810. written_config = self._read_yaml(self.config_file)
  811. self._verify_config_hosts(written_config, 5)
  812. inventory = ConfigParser.ConfigParser(allow_no_value=True)
  813. inventory.read(os.path.join(self.work_dir, '.ansible/hosts'))
  814. self.assertEquals('False',
  815. inventory.get('nodes', '10.0.0.1 openshift_schedulable'))
  816. self.assertEquals('False',
  817. inventory.get('nodes', '10.0.0.2 openshift_schedulable'))
  818. self.assertEquals(None,
  819. inventory.get('nodes', '10.0.0.3'))
  820. self.assertEquals('False',
  821. inventory.get('nodes', '10.0.0.4 openshift_schedulable'))
  822. #interactive multimaster: identical masters and nodes
  823. @patch('ooinstall.openshift_ansible.run_main_playbook')
  824. @patch('ooinstall.openshift_ansible.load_system_facts')
  825. def test_ha_no_dedicated_nodes(self, load_facts_mock, run_playbook_mock):
  826. load_facts_mock.return_value = (MOCK_FACTS_QUICKHA, 0)
  827. run_playbook_mock.return_value = 0
  828. cli_input = self._build_input(hosts=[
  829. ('10.0.0.1', True),
  830. ('10.0.0.2', True),
  831. ('10.0.0.3', True)],
  832. ssh_user='root',
  833. variant_num=1,
  834. confirm_facts='y',
  835. master_lb=('10.0.0.5', False))
  836. self.cli_args.append("install")
  837. result = self.runner.invoke(cli.cli, self.cli_args,
  838. input=cli_input)
  839. self.assert_result(result, 0)
  840. self._verify_load_facts(load_facts_mock)
  841. self._verify_run_playbook(run_playbook_mock, 4, 4)
  842. written_config = self._read_yaml(self.config_file)
  843. self._verify_config_hosts(written_config, 4)
  844. inventory = ConfigParser.ConfigParser(allow_no_value=True)
  845. inventory.read(os.path.join(self.work_dir, '.ansible/hosts'))
  846. self.assertEquals('True',
  847. inventory.get('nodes', '10.0.0.1 openshift_schedulable'))
  848. self.assertEquals('True',
  849. inventory.get('nodes', '10.0.0.2 openshift_schedulable'))
  850. self.assertEquals('True',
  851. inventory.get('nodes', '10.0.0.3 openshift_schedulable'))
  852. #interactive multimaster: attempting to use a master as the load balancer should fail:
  853. @patch('ooinstall.openshift_ansible.run_main_playbook')
  854. @patch('ooinstall.openshift_ansible.load_system_facts')
  855. def test_ha_reuse_master_as_lb(self, load_facts_mock, run_playbook_mock):
  856. load_facts_mock.return_value = (MOCK_FACTS_QUICKHA, 0)
  857. run_playbook_mock.return_value = 0
  858. cli_input = self._build_input(hosts=[
  859. ('10.0.0.1', True),
  860. ('10.0.0.2', True),
  861. ('10.0.0.3', False),
  862. ('10.0.0.4', True)],
  863. ssh_user='root',
  864. variant_num=1,
  865. confirm_facts='y',
  866. master_lb=(['10.0.0.2', '10.0.0.5'], False))
  867. self.cli_args.append("install")
  868. result = self.runner.invoke(cli.cli, self.cli_args,
  869. input=cli_input)
  870. self.assert_result(result, 0)
  871. #interactive all-in-one
  872. @patch('ooinstall.openshift_ansible.run_main_playbook')
  873. @patch('ooinstall.openshift_ansible.load_system_facts')
  874. def test_all_in_one(self, load_facts_mock, run_playbook_mock):
  875. load_facts_mock.return_value = (MOCK_FACTS, 0)
  876. run_playbook_mock.return_value = 0
  877. cli_input = self._build_input(hosts=[
  878. ('10.0.0.1', True)],
  879. ssh_user='root',
  880. variant_num=1,
  881. confirm_facts='y')
  882. self.cli_args.append("install")
  883. result = self.runner.invoke(cli.cli, self.cli_args,
  884. input=cli_input)
  885. self.assert_result(result, 0)
  886. self._verify_load_facts(load_facts_mock)
  887. self._verify_run_playbook(run_playbook_mock, 1, 1)
  888. written_config = self._read_yaml(self.config_file)
  889. self._verify_config_hosts(written_config, 1)
  890. inventory = ConfigParser.ConfigParser(allow_no_value=True)
  891. inventory.read(os.path.join(self.work_dir, '.ansible/hosts'))
  892. self.assertEquals('True',
  893. inventory.get('nodes', '10.0.0.1 openshift_schedulable'))
  894. # TODO: test with config file, attended add node
  895. # TODO: test with config file, attended new node already in config file
  896. # TODO: test with config file, attended new node already in config file, plus manually added nodes
  897. # TODO: test with config file, attended reject facts