diff options
Diffstat (limited to 'utils/test/cli_installer_tests.py')
-rw-r--r-- | utils/test/cli_installer_tests.py | 471 |
1 files changed, 471 insertions, 0 deletions
diff --git a/utils/test/cli_installer_tests.py b/utils/test/cli_installer_tests.py new file mode 100644 index 000000000..076fe5dc9 --- /dev/null +++ b/utils/test/cli_installer_tests.py @@ -0,0 +1,471 @@ +# TODO: Temporarily disabled due to importing old code into openshift-ansible +# repo. We will work on these over time. +# pylint: disable=bad-continuation,missing-docstring,no-self-use,invalid-name + +import copy +import os +import ConfigParser +import yaml + +import ooinstall.cli_installer as cli + +from click.testing import CliRunner +from test.oo_config_tests import OOInstallFixture +from mock import patch + + +MOCK_FACTS = { + '10.0.0.1': { + 'common': { + 'ip': '10.0.0.1', + 'public_ip': '10.0.0.1', + 'hostname': 'master-private.example.com', + 'public_hostname': 'master.example.com' + } + }, + '10.0.0.2': { + 'common': { + 'ip': '10.0.0.2', + 'public_ip': '10.0.0.2', + 'hostname': 'node1-private.example.com', + 'public_hostname': 'node1.example.com' + } + }, + '10.0.0.3': { + 'common': { + 'ip': '10.0.0.3', + 'public_ip': '10.0.0.3', + 'hostname': 'node2-private.example.com', + 'public_hostname': 'node2.example.com' + } + }, +} + +# Substitute in a product name before use: +SAMPLE_CONFIG = """ +variant: %s +ansible_ssh_user: root +hosts: + - ip: 10.0.0.1 + hostname: master-private.example.com + public_ip: 24.222.0.1 + public_hostname: master.example.com + master: true + node: true + - ip: 10.0.0.2 + hostname: node1-private.example.com + public_ip: 24.222.0.2 + public_hostname: node1.example.com + node: true + - ip: 10.0.0.3 + hostname: node2-private.example.com + public_ip: 24.222.0.3 + public_hostname: node2.example.com + node: true +""" + + +class OOCliFixture(OOInstallFixture): + + def setUp(self): + OOInstallFixture.setUp(self) + self.runner = CliRunner() + + # Add any arguments you would like to test here, the defaults ensure + # we only do unattended invocations here, and using temporary files/dirs. + self.cli_args = ["-a", self.work_dir] + + def run_cli(self): + return self.runner.invoke(cli.main, self.cli_args) + + def assert_result(self, result, exit_code): + if result.exception is not None or result.exit_code != exit_code: + print "Unexpected result from CLI execution" + print "Exit code: %s" % result.exit_code + print "Exception: %s" % result.exception + print result.exc_info + import traceback + traceback.print_exception(*result.exc_info) + print "Output:\n%s" % result.output + self.fail("Exception during CLI execution") + + def _read_yaml(self, config_file_path): + f = open(config_file_path, 'r') + config = yaml.safe_load(f.read()) + f.close() + return config + + +class UnattendedCliTests(OOCliFixture): + + def setUp(self): + OOCliFixture.setUp(self) + self.cli_args.append("-u") + + @patch('ooinstall.install_transactions.run_main_playbook') + @patch('ooinstall.install_transactions.load_system_facts') + def test_cfg_full_run(self, load_facts_mock, run_playbook_mock): + load_facts_mock.return_value = (MOCK_FACTS, 0) + run_playbook_mock.return_value = 0 + + config_file = self.write_config(os.path.join(self.work_dir, + 'ooinstall.conf'), SAMPLE_CONFIG % 'openshift-enterprise') + + self.cli_args.extend(["-c", config_file]) + result = self.runner.invoke(cli.main, self.cli_args) + self.assert_result(result, 0) + + load_facts_args = load_facts_mock.call_args[0] + self.assertEquals(os.path.join(self.work_dir, ".ansible/hosts"), + load_facts_args[0]) + self.assertEquals(os.path.join(self.work_dir, + "playbooks/byo/openshift_facts.yml"), load_facts_args[1]) + env_vars = load_facts_args[2] + self.assertEquals(os.path.join(self.work_dir, + '.ansible/callback_facts.yaml'), + env_vars['OO_INSTALL_CALLBACK_FACTS_YAML']) + self.assertEqual('/tmp/ansible.log', env_vars['ANSIBLE_LOG_PATH']) + self.assertTrue('ANSIBLE_CONFIG' not in env_vars) + + # Make sure we ran on the expected masters and nodes: + hosts = run_playbook_mock.call_args[0][0] + hosts_to_run_on = run_playbook_mock.call_args[0][1] + self.assertEquals(3, len(hosts)) + self.assertEquals(3, len(hosts_to_run_on)) + + @patch('ooinstall.install_transactions.run_main_playbook') + @patch('ooinstall.install_transactions.load_system_facts') + def test_inventory_write(self, load_facts_mock, run_playbook_mock): + + # Add an ssh user so we can verify it makes it to the inventory file: + merged_config = "%s\n%s" % (SAMPLE_CONFIG % 'openshift-enterprise', + "ansible_ssh_user: bob") + load_facts_mock.return_value = (MOCK_FACTS, 0) + run_playbook_mock.return_value = 0 + + config_file = self.write_config(os.path.join(self.work_dir, + 'ooinstall.conf'), merged_config) + + self.cli_args.extend(["-c", config_file]) + result = self.runner.invoke(cli.main, self.cli_args) + self.assert_result(result, 0) + + # Check the inventory file looks as we would expect: + inventory = ConfigParser.ConfigParser(allow_no_value=True) + inventory.read(os.path.join(self.work_dir, '.ansible/hosts')) + self.assertEquals('bob', + inventory.get('OSEv3:vars', 'ansible_ssh_user')) + self.assertEquals('openshift-enterprise', + inventory.get('OSEv3:vars', 'deployment_type')) + + # Check the masters: + self.assertEquals(1, len(inventory.items('masters'))) + self.assertEquals(3, len(inventory.items('nodes'))) + + for item in inventory.items('masters'): + # ansible host lines do NOT parse nicely: + master_line = item[0] + if item[1] is not None: + master_line = "%s=%s" % (master_line, item[1]) + self.assertTrue('openshift_ip' in master_line) + self.assertTrue('openshift_public_ip' in master_line) + self.assertTrue('openshift_hostname' in master_line) + self.assertTrue('openshift_public_hostname' in master_line) + + @patch('ooinstall.install_transactions.run_main_playbook') + @patch('ooinstall.install_transactions.load_system_facts') + def test_variant_version_latest_assumed(self, load_facts_mock, + run_playbook_mock): + load_facts_mock.return_value = (MOCK_FACTS, 0) + run_playbook_mock.return_value = 0 + + config_file = self.write_config(os.path.join(self.work_dir, + 'ooinstall.conf'), SAMPLE_CONFIG % 'openshift-enterprise') + + self.cli_args.extend(["-c", config_file]) + result = self.runner.invoke(cli.main, self.cli_args) + self.assert_result(result, 0) + + written_config = self._read_yaml(config_file) + + self.assertEquals('openshift-enterprise', written_config['variant']) + # We didn't specify a version so the latest should have been assumed, + # and written to disk: + self.assertEquals('3.1', written_config['variant_version']) + + # Make sure the correct value was passed to ansible: + inventory = ConfigParser.ConfigParser(allow_no_value=True) + inventory.read(os.path.join(self.work_dir, '.ansible/hosts')) + self.assertEquals('openshift-enterprise', + inventory.get('OSEv3:vars', 'deployment_type')) + + @patch('ooinstall.install_transactions.run_main_playbook') + @patch('ooinstall.install_transactions.load_system_facts') + def test_variant_version_preserved(self, load_facts_mock, + run_playbook_mock): + load_facts_mock.return_value = (MOCK_FACTS, 0) + run_playbook_mock.return_value = 0 + + config = SAMPLE_CONFIG % 'openshift-enterprise' + config = '%s\n%s' % (config, 'variant_version: 3.0') + config_file = self.write_config(os.path.join(self.work_dir, + 'ooinstall.conf'), config) + + self.cli_args.extend(["-c", config_file]) + result = self.runner.invoke(cli.main, self.cli_args) + self.assert_result(result, 0) + + written_config = self._read_yaml(config_file) + + self.assertEquals('openshift-enterprise', written_config['variant']) + # Make sure our older version was preserved: + # and written to disk: + self.assertEquals('3.0', written_config['variant_version']) + + inventory = ConfigParser.ConfigParser(allow_no_value=True) + inventory.read(os.path.join(self.work_dir, '.ansible/hosts')) + self.assertEquals('enterprise', + inventory.get('OSEv3:vars', 'deployment_type')) + + @patch('ooinstall.install_transactions.run_ansible') + @patch('ooinstall.install_transactions.load_system_facts') + def test_no_ansible_config_specified(self, load_facts_mock, run_ansible_mock): + load_facts_mock.return_value = (MOCK_FACTS, 0) + run_ansible_mock.return_value = 0 + + config = SAMPLE_CONFIG % 'openshift-enterprise' + + self._ansible_config_test(load_facts_mock, run_ansible_mock, + config, None, None) + + @patch('ooinstall.install_transactions.run_ansible') + @patch('ooinstall.install_transactions.load_system_facts') + def test_ansible_config_specified_cli(self, load_facts_mock, run_ansible_mock): + load_facts_mock.return_value = (MOCK_FACTS, 0) + run_ansible_mock.return_value = 0 + + config = SAMPLE_CONFIG % 'openshift-enterprise' + ansible_config = os.path.join(self.work_dir, 'ansible.cfg') + + self._ansible_config_test(load_facts_mock, run_ansible_mock, + config, ansible_config, ansible_config) + + @patch('ooinstall.install_transactions.run_ansible') + @patch('ooinstall.install_transactions.load_system_facts') + def test_ansible_config_specified_in_installer_config(self, + load_facts_mock, run_ansible_mock): + + load_facts_mock.return_value = (MOCK_FACTS, 0) + run_ansible_mock.return_value = 0 + + ansible_config = os.path.join(self.work_dir, 'ansible.cfg') + config = SAMPLE_CONFIG % 'openshift-enterprise' + config = "%s\nansible_config: %s" % (config, ansible_config) + self._ansible_config_test(load_facts_mock, run_ansible_mock, + config, None, ansible_config) + + #pylint: disable=too-many-arguments + # This method allows for drastically simpler tests to write, and the args + # are all useful. + def _ansible_config_test(self, load_facts_mock, run_ansible_mock, + installer_config, ansible_config_cli=None, expected_result=None): + """ + Utility method for testing the ways you can specify the ansible config. + """ + + load_facts_mock.return_value = (MOCK_FACTS, 0) + run_ansible_mock.return_value = 0 + + config_file = self.write_config(os.path.join(self.work_dir, + 'ooinstall.conf'), installer_config) + + self.cli_args.extend(["-c", config_file]) + if ansible_config_cli: + self.cli_args.extend(["--ansible-config", ansible_config_cli]) + result = self.runner.invoke(cli.main, self.cli_args) + self.assert_result(result, 0) + + # Test the env vars for facts playbook: + facts_env_vars = load_facts_mock.call_args[0][2] + if expected_result: + self.assertEquals(expected_result, facts_env_vars['ANSIBLE_CONFIG']) + else: + self.assertFalse('ANSIBLE_CONFIG' in facts_env_vars) + + # Test the env vars for main playbook: + env_vars = run_ansible_mock.call_args[0][2] + if expected_result: + self.assertEquals(expected_result, env_vars['ANSIBLE_CONFIG']) + else: + self.assertFalse('ANSIBLE_CONFIG' in env_vars) + + +class AttendedCliTests(OOCliFixture): + + def setUp(self): + OOCliFixture.setUp(self) + # Doesn't exist but keeps us from reading the local users config: + self.config_file = os.path.join(self.work_dir, 'config.yml') + self.cli_args.extend(["-c", self.config_file]) + + #pylint: disable=too-many-arguments + def _build_input(self, ssh_user=None, hosts=None, variant_num=None, + add_nodes=None, confirm_facts=None): + """ + Builds a CLI input string with newline characters to simulate + the full run. + This gives us only one place to update when the input prompts change. + """ + + inputs = [ + 'y', # let's proceed + ] + if ssh_user: + inputs.append(ssh_user) + + if hosts: + i = 0 + for (host, is_master) in hosts: + inputs.append(host) + inputs.append('y' if is_master else 'n') + inputs.append('rpm') + if i < len(hosts) - 1: + inputs.append('y') # Add more hosts + else: + inputs.append('n') # Done adding hosts + i += 1 + + if variant_num: + inputs.append(str(variant_num)) # Choose variant + version + + # TODO: support option 2, fresh install + if add_nodes: + inputs.append('1') # Add more nodes + i = 0 + for (host, is_master) in add_nodes: + inputs.append(host) + inputs.append('y' if is_master else 'n') + inputs.append('rpm') + if i < len(add_nodes) - 1: + inputs.append('y') # Add more hosts + else: + inputs.append('n') # Done adding hosts + i += 1 + + inputs.extend([ + confirm_facts, + 'y', # lets do this + ]) + + return '\n'.join(inputs) + + def _verify_load_facts(self, load_facts_mock): + """ Check that we ran load facts with expected inputs. """ + load_facts_args = load_facts_mock.call_args[0] + self.assertEquals(os.path.join(self.work_dir, ".ansible/hosts"), + load_facts_args[0]) + self.assertEquals(os.path.join(self.work_dir, + "playbooks/byo/openshift_facts.yml"), load_facts_args[1]) + env_vars = load_facts_args[2] + self.assertEquals(os.path.join(self.work_dir, + '.ansible/callback_facts.yaml'), + env_vars['OO_INSTALL_CALLBACK_FACTS_YAML']) + self.assertEqual('/tmp/ansible.log', env_vars['ANSIBLE_LOG_PATH']) + + def _verify_run_playbook(self, run_playbook_mock, exp_hosts_len, exp_hosts_to_run_on_len): + """ Check that we ran playbook with expected inputs. """ + hosts = run_playbook_mock.call_args[0][0] + hosts_to_run_on = run_playbook_mock.call_args[0][1] + self.assertEquals(exp_hosts_len, len(hosts)) + self.assertEquals(exp_hosts_to_run_on_len, len(hosts_to_run_on)) + + def _verify_config_hosts(self, written_config, host_count): + self.assertEquals(host_count, len(written_config['hosts'])) + for h in written_config['hosts']: + self.assertTrue(h['node']) + self.assertTrue('ip' in h) + self.assertTrue('hostname' in h) + self.assertTrue('public_ip' in h) + self.assertTrue('public_hostname' in h) + + @patch('ooinstall.install_transactions.run_main_playbook') + @patch('ooinstall.install_transactions.load_system_facts') + def test_full_run(self, load_facts_mock, run_playbook_mock): + load_facts_mock.return_value = (MOCK_FACTS, 0) + run_playbook_mock.return_value = 0 + + cli_input = self._build_input(hosts=[ + ('10.0.0.1', True), + ('10.0.0.2', False), + ('10.0.0.3', False)], + ssh_user='root', + variant_num=1, + confirm_facts='y') + result = self.runner.invoke(cli.main, self.cli_args, + input=cli_input) + self.assert_result(result, 0) + + self._verify_load_facts(load_facts_mock) + self._verify_run_playbook(run_playbook_mock, 3, 3) + + written_config = self._read_yaml(self.config_file) + self._verify_config_hosts(written_config, 3) + + @patch('ooinstall.install_transactions.run_main_playbook') + @patch('ooinstall.install_transactions.load_system_facts') + def test_add_nodes(self, load_facts_mock, run_playbook_mock): + + # Modify the mock facts to return a version indicating OpenShift + # is already installed on our master, and the first node. + mock_facts = copy.deepcopy(MOCK_FACTS) + mock_facts['10.0.0.1']['common']['version'] = "3.0.0" + mock_facts['10.0.0.2']['common']['version'] = "3.0.0" + + load_facts_mock.return_value = (mock_facts, 0) + run_playbook_mock.return_value = 0 + + cli_input = self._build_input(hosts=[ + ('10.0.0.1', True), + ('10.0.0.2', False), + ], + add_nodes=[('10.0.0.3', False)], + ssh_user='root', + variant_num=1, + confirm_facts='y') + result = self.runner.invoke(cli.main, + self.cli_args, + input=cli_input) + self.assert_result(result, 0) + + self._verify_load_facts(load_facts_mock) + self._verify_run_playbook(run_playbook_mock, 3, 2) + + written_config = self._read_yaml(self.config_file) + self._verify_config_hosts(written_config, 3) + + @patch('ooinstall.install_transactions.run_main_playbook') + @patch('ooinstall.install_transactions.load_system_facts') + def test_fresh_install_with_config(self, load_facts_mock, run_playbook_mock): + load_facts_mock.return_value = (MOCK_FACTS, 0) + run_playbook_mock.return_value = 0 + + config_file = self.write_config(os.path.join(self.work_dir, + 'ooinstall.conf'), + SAMPLE_CONFIG % 'openshift-enterprise') + cli_input = self._build_input(confirm_facts='y') + self.cli_args.extend(["-c", config_file]) + result = self.runner.invoke(cli.main, + self.cli_args, + input=cli_input) + self.assert_result(result, 0) + + self._verify_load_facts(load_facts_mock) + self._verify_run_playbook(run_playbook_mock, 3, 3) + + written_config = self._read_yaml(config_file) + self._verify_config_hosts(written_config, 3) + +# TODO: test with config file, attended add node +# TODO: test with config file, attended new node already in config file +# TODO: test with config file, attended new node already in config file, plus manually added nodes +# TODO: test with config file, attended reject facts |