1# -*- coding: utf-8 -*- 2# Copyright: (c) 2017, Ansible Project 3# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 4 5# Make coding more python3-ish 6from __future__ import (absolute_import, division, print_function) 7__metaclass__ = type 8 9import os 10import os.path 11import pytest 12 13from ansible.config.manager import ConfigManager, Setting, ensure_type, resolve_path, get_config_type 14from ansible.errors import AnsibleOptionsError, AnsibleError 15from ansible.module_utils.six import integer_types, string_types 16from ansible.parsing.yaml.objects import AnsibleVaultEncryptedUnicode 17 18curdir = os.path.dirname(__file__) 19cfg_file = os.path.join(curdir, 'test.cfg') 20cfg_file2 = os.path.join(curdir, 'test2.cfg') 21 22expected_ini = {'CONFIG_FILE': Setting(name='CONFIG_FILE', value=cfg_file, origin='', type='string'), 23 'config_entry': Setting(name='config_entry', value=u'fromini', origin=cfg_file, type='string'), 24 'config_entry_bool': Setting(name='config_entry_bool', value=False, origin=cfg_file, type='bool'), 25 'config_entry_list': Setting(name='config_entry_list', value=['fromini'], origin=cfg_file, type='list'), 26 'config_entry_deprecated': Setting(name='config_entry_deprecated', value=u'fromini', origin=cfg_file, type='string'), 27 'config_entry_multi': Setting(name='config_entry_multi', value=u'morefromini', origin=cfg_file, type='string'), 28 'config_entry_multi_deprecated': Setting(name='config_entry_multi_deprecated', value=u'morefromini', origin=cfg_file, type='string'), 29 'config_entry_multi_deprecated_source': Setting(name='config_entry_multi_deprecated_source', value=u'morefromini', 30 origin=cfg_file, type='string')} 31 32ensure_test_data = [ 33 ('a,b', 'list', list), 34 (['a', 'b'], 'list', list), 35 ('y', 'bool', bool), 36 ('yes', 'bool', bool), 37 ('on', 'bool', bool), 38 ('1', 'bool', bool), 39 ('true', 'bool', bool), 40 ('t', 'bool', bool), 41 (1, 'bool', bool), 42 (1.0, 'bool', bool), 43 (True, 'bool', bool), 44 ('n', 'bool', bool), 45 ('no', 'bool', bool), 46 ('off', 'bool', bool), 47 ('0', 'bool', bool), 48 ('false', 'bool', bool), 49 ('f', 'bool', bool), 50 (0, 'bool', bool), 51 (0.0, 'bool', bool), 52 (False, 'bool', bool), 53 ('10', 'int', integer_types), 54 (20, 'int', integer_types), 55 ('0.10', 'float', float), 56 (0.2, 'float', float), 57 ('/tmp/test.yml', 'pathspec', list), 58 ('/tmp/test.yml,/home/test2.yml', 'pathlist', list), 59 ('a', 'str', string_types), 60 ('a', 'string', string_types), 61 ('Café', 'string', string_types), 62 ('', 'string', string_types), 63 ('None', 'none', type(None)) 64] 65 66 67class TestConfigManager: 68 @classmethod 69 def setup_class(cls): 70 cls.manager = ConfigManager(cfg_file, os.path.join(curdir, 'test.yml')) 71 72 @classmethod 73 def teardown_class(cls): 74 cls.manager = None 75 76 def test_initial_load(self): 77 assert self.manager.data._global_settings == expected_ini 78 79 @pytest.mark.parametrize("value, expected_type, python_type", ensure_test_data) 80 def test_ensure_type(self, value, expected_type, python_type): 81 assert isinstance(ensure_type(value, expected_type), python_type) 82 83 def test_resolve_path(self): 84 assert os.path.join(curdir, 'test.yml') == resolve_path('./test.yml', cfg_file) 85 86 def test_resolve_path_cwd(self): 87 assert os.path.join(os.getcwd(), 'test.yml') == resolve_path('{{CWD}}/test.yml') 88 assert os.path.join(os.getcwd(), 'test.yml') == resolve_path('./test.yml') 89 90 def test_value_and_origin_from_ini(self): 91 assert self.manager.get_config_value_and_origin('config_entry') == ('fromini', cfg_file) 92 93 def test_value_from_ini(self): 94 assert self.manager.get_config_value('config_entry') == 'fromini' 95 96 def test_value_and_origin_from_alt_ini(self): 97 assert self.manager.get_config_value_and_origin('config_entry', cfile=cfg_file2) == ('fromini2', cfg_file2) 98 99 def test_value_from_alt_ini(self): 100 assert self.manager.get_config_value('config_entry', cfile=cfg_file2) == 'fromini2' 101 102 def test_config_types(self): 103 assert get_config_type('/tmp/ansible.ini') == 'ini' 104 assert get_config_type('/tmp/ansible.cfg') == 'ini' 105 assert get_config_type('/tmp/ansible.yaml') == 'yaml' 106 assert get_config_type('/tmp/ansible.yml') == 'yaml' 107 108 def test_config_types_negative(self): 109 with pytest.raises(AnsibleOptionsError) as exec_info: 110 get_config_type('/tmp/ansible.txt') 111 assert "Unsupported configuration file extension for" in str(exec_info.value) 112 113 def test_read_config_yaml_file(self): 114 assert isinstance(self.manager._read_config_yaml_file(os.path.join(curdir, 'test.yml')), dict) 115 116 def test_read_config_yaml_file_negative(self): 117 with pytest.raises(AnsibleError) as exec_info: 118 self.manager._read_config_yaml_file(os.path.join(curdir, 'test_non_existent.yml')) 119 120 assert "Missing base YAML definition file (bad install?)" in str(exec_info.value) 121 122 def test_entry_as_vault_var(self): 123 class MockVault: 124 125 def decrypt(self, value): 126 return value 127 128 vault_var = AnsibleVaultEncryptedUnicode(b"vault text") 129 vault_var.vault = MockVault() 130 131 actual_value, actual_origin = self.manager._loop_entries({'name': vault_var}, [{'name': 'name'}]) 132 assert actual_value == "vault text" 133 assert actual_origin == "name" 134 135 @pytest.mark.parametrize("value_type", ("str", "string", None)) 136 def test_ensure_type_with_vaulted_str(self, value_type): 137 class MockVault: 138 def decrypt(self, value): 139 return value 140 141 vault_var = AnsibleVaultEncryptedUnicode(b"vault text") 142 vault_var.vault = MockVault() 143 144 actual_value = ensure_type(vault_var, value_type) 145 assert actual_value == "vault text" 146