diff --git a/homeassistant/scripts/check_config.py b/homeassistant/scripts/check_config.py index 47c1e3b3f64..23e9766928e 100644 --- a/homeassistant/scripts/check_config.py +++ b/homeassistant/scripts/check_config.py @@ -21,13 +21,14 @@ _LOGGER = logging.getLogger(__name__) # pylint: disable=protected-access MOCKS = { 'load': ("homeassistant.util.yaml.load_yaml", yaml.load_yaml), + 'load*': ("homeassistant.config.load_yaml", yaml.load_yaml), 'get': ("homeassistant.loader.get_component", loader.get_component), 'secrets': ("homeassistant.util.yaml._secret_yaml", yaml._secret_yaml), 'except': ("homeassistant.bootstrap.log_exception", bootstrap.log_exception) } SILENCE = ( - 'homeassistant.util.yaml.clear_secret_cache', + 'homeassistant.bootstrap.clear_secret_cache', 'homeassistant.core._LOGGER.info', 'homeassistant.loader._LOGGER.info', 'homeassistant.bootstrap._LOGGER.info', @@ -114,7 +115,7 @@ def run(script_args: List) -> int: if domain_info: if 'all' in domain_info: print(color('bold_white', 'Successful config (all)')) - for domain, config in res['components']: + for domain, config in res['components'].items(): print(color(C_HEAD, domain + ':')) dump_dict(config, indent_count=3) else: @@ -207,7 +208,9 @@ def check(config_path): # Patches with local mock functions for key, val in MOCKS.items(): - mock_function = locals()['mock_'+key] + # The * in the key is removed to find the mock_function (side_effect) + # This allows us to use one side_effect to patch multiple locations + mock_function = locals()['mock_' + key.replace('*', '')] PATCHES[key] = patch(val[0], side_effect=mock_function) # Start all patches diff --git a/homeassistant/util/yaml.py b/homeassistant/util/yaml.py index 6696b5434f2..b834ac8048c 100644 --- a/homeassistant/util/yaml.py +++ b/homeassistant/util/yaml.py @@ -149,9 +149,13 @@ def _env_var_yaml(loader: SafeLineLoader, def _load_secret_yaml(secret_path: str) -> Dict: """Load the secrets yaml from path.""" - _LOGGER.debug('Loading %s', os.path.join(secret_path, _SECRET_YAML)) + secret_path = os.path.join(secret_path, _SECRET_YAML) + if secret_path in __SECRET_CACHE: + return __SECRET_CACHE[secret_path] + + _LOGGER.debug('Loading %s', secret_path) try: - secrets = load_yaml(os.path.join(secret_path, _SECRET_YAML)) + secrets = load_yaml(secret_path) if 'logger' in secrets: logger = str(secrets['logger']).lower() if logger == 'debug': @@ -160,9 +164,10 @@ def _load_secret_yaml(secret_path: str) -> Dict: _LOGGER.error("secrets.yaml: 'logger: debug' expected," " but 'logger: %s' found", logger) del secrets['logger'] - return secrets except FileNotFoundError: - return {} + secrets = {} + __SECRET_CACHE[secret_path] = secrets + return secrets # pylint: disable=protected-access @@ -171,8 +176,8 @@ def _secret_yaml(loader: SafeLineLoader, """Load secrets and embed it into the configuration YAML.""" secret_path = os.path.dirname(loader.name) while True: - secrets = __SECRET_CACHE.get(secret_path, - _load_secret_yaml(secret_path)) + secrets = _load_secret_yaml(secret_path) + if node.value in secrets: _LOGGER.debug('Secret %s retrieved from secrets.yaml in ' 'folder %s', node.value, secret_path) diff --git a/tests/scripts/test_check_config.py b/tests/scripts/test_check_config.py index 4cad9504f0c..fbd80760c12 100644 --- a/tests/scripts/test_check_config.py +++ b/tests/scripts/test_check_config.py @@ -20,6 +20,21 @@ BASE_CONFIG = ( ) +def change_yaml_files(check_dict): + """Change the ['yaml_files'] property and remove the config path. + + Also removes other files like service.yaml that gets loaded + """ + root = get_test_config_dir() + keys = check_dict['yaml_files'].keys() + check_dict['yaml_files'] = [] + for key in sorted(keys): + if not key.startswith('/'): + check_dict['yaml_files'].append(key) + if key.startswith(root): + check_dict['yaml_files'].append('...' + key[len(root):]) + + def tearDownModule(self): # pylint: disable=invalid-name """Clean files.""" # .HA_VERSION created during bootstrap's config update @@ -39,12 +54,13 @@ class TestCheckConfig(unittest.TestCase): } with patch_yaml_files(files): res = check_config.check(get_test_config_dir('light.yaml')) + change_yaml_files(res) self.assertDictEqual({ 'components': {'light': [{'platform': 'hue'}]}, 'except': {}, 'secret_cache': {}, 'secrets': {}, - 'yaml_files': {} + 'yaml_files': ['.../light.yaml'] }, res) def test_config_component_platform_fail_validation(self): @@ -54,12 +70,13 @@ class TestCheckConfig(unittest.TestCase): } with patch_yaml_files(files): res = check_config.check(get_test_config_dir('component.yaml')) + change_yaml_files(res) self.assertDictEqual({ 'components': {}, 'except': {'http': {'password': 'err123'}}, 'secret_cache': {}, 'secrets': {}, - 'yaml_files': {} + 'yaml_files': ['.../component.yaml'] }, res) files = { @@ -68,13 +85,14 @@ class TestCheckConfig(unittest.TestCase): } with patch_yaml_files(files): res = check_config.check(get_test_config_dir('platform.yaml')) + change_yaml_files(res) self.assertDictEqual({ 'components': {'mqtt': {'keepalive': 60, 'port': 1883, 'protocol': '3.1.1'}}, 'except': {'light.mqtt_json': {'platform': 'mqtt_json'}}, 'secret_cache': {}, 'secrets': {}, - 'yaml_files': {} + 'yaml_files': ['.../platform.yaml'] }, res) def test_component_platform_not_found(self): @@ -85,42 +103,52 @@ class TestCheckConfig(unittest.TestCase): } with patch_yaml_files(files): res = check_config.check(get_test_config_dir('badcomponent.yaml')) + change_yaml_files(res) self.assertDictEqual({ 'components': {}, 'except': {check_config.ERROR_STR: ['Component not found: beer']}, 'secret_cache': {}, 'secrets': {}, - 'yaml_files': {} + 'yaml_files': ['.../badcomponent.yaml'] }, res) res = check_config.check(get_test_config_dir('badplatform.yaml')) + change_yaml_files(res) self.assertDictEqual({ 'components': {}, 'except': {check_config.ERROR_STR: ['Platform not found: light.beer']}, 'secret_cache': {}, 'secrets': {}, - 'yaml_files': {} + 'yaml_files': ['.../badplatform.yaml'] }, res) def test_secrets(self): """Test secrets config checking method.""" files = { - 'secret.yaml': (BASE_CONFIG + - 'http:\n' - ' api_password: !secret http_pw'), + get_test_config_dir('secret.yaml'): ( + BASE_CONFIG + + 'http:\n' + ' api_password: !secret http_pw'), 'secrets.yaml': ('logger: debug\n' 'http_pw: abc123'), } + self.maxDiff = None with patch_yaml_files(files): res = check_config.check(get_test_config_dir('secret.yaml')) + change_yaml_files(res) + + # convert secrets OrderedDict to dict for assertequal + for key, val in res['secret_cache'].items(): + res['secret_cache'][key] = dict(val) + self.assertDictEqual({ 'components': {'http': {'api_password': 'abc123', 'server_port': 8123}}, 'except': {}, - 'secret_cache': {}, + 'secret_cache': {'secrets.yaml': {'http_pw': 'abc123'}}, 'secrets': {'http_pw': 'abc123'}, - 'yaml_files': {'secrets.yaml': True} + 'yaml_files': ['.../secret.yaml', 'secrets.yaml'] }, res)