Add check_config API (#5898)
* Add check_config API * Add config panel to default config * Add tests * Lint * lint
This commit is contained in:
parent
dc6a28a8b2
commit
dab6d011ca
7 changed files with 107 additions and 28 deletions
|
@ -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))
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
31
homeassistant/components/config/core.py
Normal file
31
homeassistant/components/config/core.py
Normal 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,
|
||||
})
|
|
@ -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'))
|
||||
|
|
39
tests/components/config/test_core.py
Normal file
39
tests/components/config/test_core.py
Normal 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'
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue