Add check_config API (#5898)

* Add check_config API

* Add config panel to default config

* Add tests

* Lint

* lint
This commit is contained in:
Paulus Schoutsen 2017-02-12 11:31:46 -08:00 committed by GitHub
parent dc6a28a8b2
commit dab6d011ca
7 changed files with 107 additions and 28 deletions

View file

@ -156,10 +156,18 @@ def async_setup(hass, config):
return
try:
yield from conf_util.async_check_ha_config_file(hass)
errors = yield from conf_util.async_check_ha_config_file(hass)
except HomeAssistantError:
return
if errors:
notif = get_component('persistent_notification')
_LOGGER.error(errors)
notif.async_create(
hass, "Config error. See dev-info panel for details.",
"Config validating", "{0}.check_config".format(ha.DOMAIN))
return
if call.service == SERVICE_HOMEASSISTANT_RESTART:
hass.async_add_job(hass.async_stop(RESTART_EXIT_CODE))

View file

@ -6,6 +6,7 @@ from homeassistant.components.frontend import register_built_in_panel
DOMAIN = 'config'
DEPENDENCIES = ['http']
SECTIONS = ('core', 'hassbian')
@asyncio.coroutine
@ -13,7 +14,7 @@ def async_setup(hass, config):
"""Setup the hassbian component."""
register_built_in_panel(hass, 'config', 'Configuration', 'mdi:settings')
for panel_name in ('hassbian',):
for panel_name in SECTIONS:
panel = yield from async_prepare_setup_platform(hass, config, DOMAIN,
panel_name)

View file

@ -0,0 +1,31 @@
"""Component to interact with Hassbian tools."""
import asyncio
from homeassistant.components.http import HomeAssistantView
from homeassistant.config import async_check_ha_config_file
@asyncio.coroutine
def async_setup(hass):
"""Setup the hassbian config."""
hass.http.register_view(CheckConfigView)
return True
class CheckConfigView(HomeAssistantView):
"""Hassbian packages endpoint."""
url = '/api/config/core/check_config'
name = 'api:config:core:check_config'
@asyncio.coroutine
def post(self, request):
"""Validate config and return results."""
errors = yield from async_check_ha_config_file(request.app['hass'])
state = 'invalid' if errors else 'valid'
return self.json({
"result": state,
"errors": errors,
})

View file

@ -53,6 +53,9 @@ introduction:
# Enables the frontend
frontend:
# Enables configuration UI
config:
http:
# Uncomment this to add a password (recommended!)
# api_password: PASSWORD
@ -463,22 +466,15 @@ def async_check_ha_config_file(hass):
This method is a coroutine.
"""
import homeassistant.components.persistent_notification as pn
proc = yield from asyncio.create_subprocess_exec(
sys.argv[0],
'--script',
'check_config',
stdout=asyncio.subprocess.PIPE)
sys.executable, '-m', 'homeassistant', '--script',
'check_config', '--config', hass.config.config_dir,
stdout=asyncio.subprocess.PIPE, loop=hass.loop)
# Wait for the subprocess exit
(stdout_data, dummy) = yield from proc.communicate()
stdout_data, dummy = yield from proc.communicate()
result = yield from proc.wait()
if result:
content = re.sub(r'\033\[[^m]*m', '', str(stdout_data, 'utf-8'))
# Put error cleaned from color codes in the error log so it
# will be visible at the UI.
_LOGGER.error(content)
pn.async_create(
hass, "Config error. See dev-info panel for details.",
"Config validating", "{0}.check_config".format(CONF_CORE))
raise HomeAssistantError("Invalid config")
if not result:
return None
return re.sub(r'\033\[[^m]*m', '', str(stdout_data, 'utf-8'))

View file

@ -0,0 +1,39 @@
"""Test hassbian config."""
import asyncio
from unittest.mock import patch
from homeassistant.bootstrap import async_setup_component
from homeassistant.components import config
from homeassistant.components.config.core import CheckConfigView
from tests.common import mock_http_component_app, mock_coro
@asyncio.coroutine
def test_validate_config_ok(hass, test_client):
"""Test getting suites."""
app = mock_http_component_app(hass)
with patch.object(config, 'SECTIONS', ['core']):
yield from async_setup_component(hass, 'config', {})
hass.http.views[CheckConfigView.name].register(app.router)
client = yield from test_client(app)
with patch(
'homeassistant.components.config.core.async_check_ha_config_file',
return_value=mock_coro(None)()):
resp = yield from client.post('/api/config/core/check_config')
assert resp.status == 200
result = yield from resp.json()
assert result['result'] == 'valid'
assert result['errors'] is None
with patch(
'homeassistant.components.config.core.async_check_ha_config_file',
return_value=mock_coro('beer')()):
resp = yield from client.post('/api/config/core/check_config')
assert resp.status == 200
result = yield from resp.json()
assert result['result'] == 'invalid'
assert result['errors'] == 'beer'

View file

@ -4,6 +4,7 @@ import os
from unittest.mock import patch
from homeassistant.bootstrap import async_setup_component
from homeassistant.components import config
from homeassistant.components.config.hassbian import (
HassbianSuitesView, HassbianSuiteInstallView)
from tests.common import (
@ -13,7 +14,8 @@ from tests.common import (
def test_setup_check_env_prevents_load(hass, loop):
"""Test it does not set up hassbian if environment var not present."""
mock_http_component(hass)
with patch.dict(os.environ, clear=True):
with patch.dict(os.environ, clear=True), \
patch.object(config, 'SECTIONS', ['hassbian']):
loop.run_until_complete(async_setup_component(hass, 'config', {}))
assert 'config' in hass.config.components
assert HassbianSuitesView.name not in hass.http.views
@ -23,7 +25,8 @@ def test_setup_check_env_prevents_load(hass, loop):
def test_setup_check_env_works(hass, loop):
"""Test it sets up hassbian if environment var present."""
mock_http_component(hass)
with patch.dict(os.environ, {'FORCE_HASSBIAN': '1'}):
with patch.dict(os.environ, {'FORCE_HASSBIAN': '1'}), \
patch.object(config, 'SECTIONS', ['hassbian']):
loop.run_until_complete(async_setup_component(hass, 'config', {}))
assert 'config' in hass.config.components
assert HassbianSuitesView.name in hass.http.views
@ -35,7 +38,8 @@ def test_get_suites(hass, test_client):
"""Test getting suites."""
app = mock_http_component_app(hass)
with patch.dict(os.environ, {'FORCE_HASSBIAN': '1'}):
with patch.dict(os.environ, {'FORCE_HASSBIAN': '1'}), \
patch.object(config, 'SECTIONS', ['hassbian']):
yield from async_setup_component(hass, 'config', {})
hass.http.views[HassbianSuitesView.name].register(app.router)
@ -56,7 +60,8 @@ def test_install_suite(hass, test_client):
"""Test getting suites."""
app = mock_http_component_app(hass)
with patch.dict(os.environ, {'FORCE_HASSBIAN': '1'}):
with patch.dict(os.environ, {'FORCE_HASSBIAN': '1'}), \
patch.object(config, 'SECTIONS', ['hassbian']):
yield from async_setup_component(hass, 'config', {})
hass.http.views[HassbianSuiteInstallView.name].register(app.router)

View file

@ -396,16 +396,15 @@ class TestConfig(unittest.TestCase):
process_mock = mock.MagicMock()
attrs = {
'communicate.return_value':
mock_generator((r'\033[hellom'.encode('utf-8'), 'error')),
mock_generator(('\033[34mhello'.encode('utf-8'), 'error')),
'wait.return_value': mock_generator(1)}
process_mock.configure_mock(**attrs)
mock_create.return_value = mock_generator(process_mock)
with self.assertRaises(HomeAssistantError):
run_coroutine_threadsafe(
config_util.async_check_ha_config_file(self.hass),
self.hass.loop
).result()
assert run_coroutine_threadsafe(
config_util.async_check_ha_config_file(self.hass),
self.hass.loop
).result() == 'hello'
# pylint: disable=redefined-outer-name