parent
b199c61c88
commit
17631cd728
3 changed files with 55 additions and 19 deletions
|
@ -21,13 +21,14 @@ _LOGGER = logging.getLogger(__name__)
|
||||||
# pylint: disable=protected-access
|
# pylint: disable=protected-access
|
||||||
MOCKS = {
|
MOCKS = {
|
||||||
'load': ("homeassistant.util.yaml.load_yaml", yaml.load_yaml),
|
'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),
|
'get': ("homeassistant.loader.get_component", loader.get_component),
|
||||||
'secrets': ("homeassistant.util.yaml._secret_yaml", yaml._secret_yaml),
|
'secrets': ("homeassistant.util.yaml._secret_yaml", yaml._secret_yaml),
|
||||||
'except': ("homeassistant.bootstrap.log_exception",
|
'except': ("homeassistant.bootstrap.log_exception",
|
||||||
bootstrap.log_exception)
|
bootstrap.log_exception)
|
||||||
}
|
}
|
||||||
SILENCE = (
|
SILENCE = (
|
||||||
'homeassistant.util.yaml.clear_secret_cache',
|
'homeassistant.bootstrap.clear_secret_cache',
|
||||||
'homeassistant.core._LOGGER.info',
|
'homeassistant.core._LOGGER.info',
|
||||||
'homeassistant.loader._LOGGER.info',
|
'homeassistant.loader._LOGGER.info',
|
||||||
'homeassistant.bootstrap._LOGGER.info',
|
'homeassistant.bootstrap._LOGGER.info',
|
||||||
|
@ -114,7 +115,7 @@ def run(script_args: List) -> int:
|
||||||
if domain_info:
|
if domain_info:
|
||||||
if 'all' in domain_info:
|
if 'all' in domain_info:
|
||||||
print(color('bold_white', 'Successful config (all)'))
|
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 + ':'))
|
print(color(C_HEAD, domain + ':'))
|
||||||
dump_dict(config, indent_count=3)
|
dump_dict(config, indent_count=3)
|
||||||
else:
|
else:
|
||||||
|
@ -207,7 +208,9 @@ def check(config_path):
|
||||||
|
|
||||||
# Patches with local mock functions
|
# Patches with local mock functions
|
||||||
for key, val in MOCKS.items():
|
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)
|
PATCHES[key] = patch(val[0], side_effect=mock_function)
|
||||||
|
|
||||||
# Start all patches
|
# Start all patches
|
||||||
|
|
|
@ -149,9 +149,13 @@ def _env_var_yaml(loader: SafeLineLoader,
|
||||||
|
|
||||||
def _load_secret_yaml(secret_path: str) -> Dict:
|
def _load_secret_yaml(secret_path: str) -> Dict:
|
||||||
"""Load the secrets yaml from path."""
|
"""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:
|
try:
|
||||||
secrets = load_yaml(os.path.join(secret_path, _SECRET_YAML))
|
secrets = load_yaml(secret_path)
|
||||||
if 'logger' in secrets:
|
if 'logger' in secrets:
|
||||||
logger = str(secrets['logger']).lower()
|
logger = str(secrets['logger']).lower()
|
||||||
if logger == 'debug':
|
if logger == 'debug':
|
||||||
|
@ -160,9 +164,10 @@ def _load_secret_yaml(secret_path: str) -> Dict:
|
||||||
_LOGGER.error("secrets.yaml: 'logger: debug' expected,"
|
_LOGGER.error("secrets.yaml: 'logger: debug' expected,"
|
||||||
" but 'logger: %s' found", logger)
|
" but 'logger: %s' found", logger)
|
||||||
del secrets['logger']
|
del secrets['logger']
|
||||||
return secrets
|
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
return {}
|
secrets = {}
|
||||||
|
__SECRET_CACHE[secret_path] = secrets
|
||||||
|
return secrets
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=protected-access
|
# pylint: disable=protected-access
|
||||||
|
@ -171,8 +176,8 @@ def _secret_yaml(loader: SafeLineLoader,
|
||||||
"""Load secrets and embed it into the configuration YAML."""
|
"""Load secrets and embed it into the configuration YAML."""
|
||||||
secret_path = os.path.dirname(loader.name)
|
secret_path = os.path.dirname(loader.name)
|
||||||
while True:
|
while True:
|
||||||
secrets = __SECRET_CACHE.get(secret_path,
|
secrets = _load_secret_yaml(secret_path)
|
||||||
_load_secret_yaml(secret_path))
|
|
||||||
if node.value in secrets:
|
if node.value in secrets:
|
||||||
_LOGGER.debug('Secret %s retrieved from secrets.yaml in '
|
_LOGGER.debug('Secret %s retrieved from secrets.yaml in '
|
||||||
'folder %s', node.value, secret_path)
|
'folder %s', node.value, secret_path)
|
||||||
|
|
|
@ -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
|
def tearDownModule(self): # pylint: disable=invalid-name
|
||||||
"""Clean files."""
|
"""Clean files."""
|
||||||
# .HA_VERSION created during bootstrap's config update
|
# .HA_VERSION created during bootstrap's config update
|
||||||
|
@ -39,12 +54,13 @@ class TestCheckConfig(unittest.TestCase):
|
||||||
}
|
}
|
||||||
with patch_yaml_files(files):
|
with patch_yaml_files(files):
|
||||||
res = check_config.check(get_test_config_dir('light.yaml'))
|
res = check_config.check(get_test_config_dir('light.yaml'))
|
||||||
|
change_yaml_files(res)
|
||||||
self.assertDictEqual({
|
self.assertDictEqual({
|
||||||
'components': {'light': [{'platform': 'hue'}]},
|
'components': {'light': [{'platform': 'hue'}]},
|
||||||
'except': {},
|
'except': {},
|
||||||
'secret_cache': {},
|
'secret_cache': {},
|
||||||
'secrets': {},
|
'secrets': {},
|
||||||
'yaml_files': {}
|
'yaml_files': ['.../light.yaml']
|
||||||
}, res)
|
}, res)
|
||||||
|
|
||||||
def test_config_component_platform_fail_validation(self):
|
def test_config_component_platform_fail_validation(self):
|
||||||
|
@ -54,12 +70,13 @@ class TestCheckConfig(unittest.TestCase):
|
||||||
}
|
}
|
||||||
with patch_yaml_files(files):
|
with patch_yaml_files(files):
|
||||||
res = check_config.check(get_test_config_dir('component.yaml'))
|
res = check_config.check(get_test_config_dir('component.yaml'))
|
||||||
|
change_yaml_files(res)
|
||||||
self.assertDictEqual({
|
self.assertDictEqual({
|
||||||
'components': {},
|
'components': {},
|
||||||
'except': {'http': {'password': 'err123'}},
|
'except': {'http': {'password': 'err123'}},
|
||||||
'secret_cache': {},
|
'secret_cache': {},
|
||||||
'secrets': {},
|
'secrets': {},
|
||||||
'yaml_files': {}
|
'yaml_files': ['.../component.yaml']
|
||||||
}, res)
|
}, res)
|
||||||
|
|
||||||
files = {
|
files = {
|
||||||
|
@ -68,13 +85,14 @@ class TestCheckConfig(unittest.TestCase):
|
||||||
}
|
}
|
||||||
with patch_yaml_files(files):
|
with patch_yaml_files(files):
|
||||||
res = check_config.check(get_test_config_dir('platform.yaml'))
|
res = check_config.check(get_test_config_dir('platform.yaml'))
|
||||||
|
change_yaml_files(res)
|
||||||
self.assertDictEqual({
|
self.assertDictEqual({
|
||||||
'components': {'mqtt': {'keepalive': 60, 'port': 1883,
|
'components': {'mqtt': {'keepalive': 60, 'port': 1883,
|
||||||
'protocol': '3.1.1'}},
|
'protocol': '3.1.1'}},
|
||||||
'except': {'light.mqtt_json': {'platform': 'mqtt_json'}},
|
'except': {'light.mqtt_json': {'platform': 'mqtt_json'}},
|
||||||
'secret_cache': {},
|
'secret_cache': {},
|
||||||
'secrets': {},
|
'secrets': {},
|
||||||
'yaml_files': {}
|
'yaml_files': ['.../platform.yaml']
|
||||||
}, res)
|
}, res)
|
||||||
|
|
||||||
def test_component_platform_not_found(self):
|
def test_component_platform_not_found(self):
|
||||||
|
@ -85,42 +103,52 @@ class TestCheckConfig(unittest.TestCase):
|
||||||
}
|
}
|
||||||
with patch_yaml_files(files):
|
with patch_yaml_files(files):
|
||||||
res = check_config.check(get_test_config_dir('badcomponent.yaml'))
|
res = check_config.check(get_test_config_dir('badcomponent.yaml'))
|
||||||
|
change_yaml_files(res)
|
||||||
self.assertDictEqual({
|
self.assertDictEqual({
|
||||||
'components': {},
|
'components': {},
|
||||||
'except': {check_config.ERROR_STR:
|
'except': {check_config.ERROR_STR:
|
||||||
['Component not found: beer']},
|
['Component not found: beer']},
|
||||||
'secret_cache': {},
|
'secret_cache': {},
|
||||||
'secrets': {},
|
'secrets': {},
|
||||||
'yaml_files': {}
|
'yaml_files': ['.../badcomponent.yaml']
|
||||||
}, res)
|
}, res)
|
||||||
|
|
||||||
res = check_config.check(get_test_config_dir('badplatform.yaml'))
|
res = check_config.check(get_test_config_dir('badplatform.yaml'))
|
||||||
|
change_yaml_files(res)
|
||||||
self.assertDictEqual({
|
self.assertDictEqual({
|
||||||
'components': {},
|
'components': {},
|
||||||
'except': {check_config.ERROR_STR:
|
'except': {check_config.ERROR_STR:
|
||||||
['Platform not found: light.beer']},
|
['Platform not found: light.beer']},
|
||||||
'secret_cache': {},
|
'secret_cache': {},
|
||||||
'secrets': {},
|
'secrets': {},
|
||||||
'yaml_files': {}
|
'yaml_files': ['.../badplatform.yaml']
|
||||||
}, res)
|
}, res)
|
||||||
|
|
||||||
def test_secrets(self):
|
def test_secrets(self):
|
||||||
"""Test secrets config checking method."""
|
"""Test secrets config checking method."""
|
||||||
files = {
|
files = {
|
||||||
'secret.yaml': (BASE_CONFIG +
|
get_test_config_dir('secret.yaml'): (
|
||||||
|
BASE_CONFIG +
|
||||||
'http:\n'
|
'http:\n'
|
||||||
' api_password: !secret http_pw'),
|
' api_password: !secret http_pw'),
|
||||||
'secrets.yaml': ('logger: debug\n'
|
'secrets.yaml': ('logger: debug\n'
|
||||||
'http_pw: abc123'),
|
'http_pw: abc123'),
|
||||||
}
|
}
|
||||||
|
self.maxDiff = None
|
||||||
|
|
||||||
with patch_yaml_files(files):
|
with patch_yaml_files(files):
|
||||||
res = check_config.check(get_test_config_dir('secret.yaml'))
|
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({
|
self.assertDictEqual({
|
||||||
'components': {'http': {'api_password': 'abc123',
|
'components': {'http': {'api_password': 'abc123',
|
||||||
'server_port': 8123}},
|
'server_port': 8123}},
|
||||||
'except': {},
|
'except': {},
|
||||||
'secret_cache': {},
|
'secret_cache': {'secrets.yaml': {'http_pw': 'abc123'}},
|
||||||
'secrets': {'http_pw': 'abc123'},
|
'secrets': {'http_pw': 'abc123'},
|
||||||
'yaml_files': {'secrets.yaml': True}
|
'yaml_files': ['.../secret.yaml', 'secrets.yaml']
|
||||||
}, res)
|
}, res)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue