# 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: - connect_to: master-private.example.com 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 - connect_to: node1-private.example.com ip: 10.0.0.2 hostname: node1-private.example.com public_ip: 24.222.0.2 public_hostname: node1.example.com node: true - connect_to: node2-private.example.com 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.cli, 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.openshift_ansible.run_main_playbook') @patch('ooinstall.openshift_ansible.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, "install"]) result = self.runner.invoke(cli.cli, 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.openshift_ansible.run_main_playbook') @patch('ooinstall.openshift_ansible.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, "install"]) result = self.runner.invoke(cli.cli, 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.openshift_ansible.run_main_playbook') @patch('ooinstall.openshift_ansible.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, "install"]) result = self.runner.invoke(cli.cli, 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.openshift_ansible.run_main_playbook') @patch('ooinstall.openshift_ansible.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, "install"]) result = self.runner.invoke(cli.cli, 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.openshift_ansible.run_ansible') @patch('ooinstall.openshift_ansible.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.openshift_ansible.run_ansible') @patch('ooinstall.openshift_ansible.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.openshift_ansible.run_ansible') @patch('ooinstall.openshift_ansible.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]) self.cli_args.append("install") result = self.runner.invoke(cli.cli, 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.openshift_ansible.run_main_playbook') @patch('ooinstall.openshift_ansible.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') self.cli_args.append("install") result = self.runner.invoke(cli.cli, 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.openshift_ansible.run_main_playbook') @patch('ooinstall.openshift_ansible.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') self.cli_args.append("install") result = self.runner.invoke(cli.cli, 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.openshift_ansible.run_main_playbook') @patch('ooinstall.openshift_ansible.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]) self.cli_args.append("install") result = self.runner.invoke(cli.cli, 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