WIP: HassIO allow to access to container logs. (#7271)

* HassIO allow to access to container logs.

* Add unittest & make a fixture for env

* Add unittest to check if no env exists

* Fix lint
This commit is contained in:
Pascal Vizeli 2017-04-24 15:16:28 +02:00 committed by GitHub
parent 575f57a24e
commit ead457f312
2 changed files with 131 additions and 37 deletions

View file

@ -96,9 +96,12 @@ def async_setup(hass, config):
hass.http.register_view(HassIOBaseView(hassio, base))
for base in ('supervisor', 'network'):
hass.http.register_view(HassIOBaseEditView(hassio, base))
for base in ('supervisor', 'homeassistant'):
hass.http.register_view(HassIOBaseLogsView(hassio, base))
# register view for addons
hass.http.register_view(HassIOAddonsView(hassio))
hass.http.register_view(HassIOAddonsLogsView(hassio))
@asyncio.coroutine
def async_service_handler(service):
@ -185,7 +188,7 @@ class HassIO(object):
return False
@asyncio.coroutine
def send_raw(self, cmd, payload=None, timeout=DEFAULT_TIMEOUT):
def send_raw(self, cmd, payload=None, timeout=DEFAULT_TIMEOUT, json=True):
"""Send raw request to API."""
try:
with async_timeout.timeout(timeout, loop=self.loop):
@ -198,8 +201,12 @@ class HassIO(object):
_LOGGER.error("%s return code %d.", cmd, request.status)
return
if json:
return (yield from request.json())
# get raw output
return (yield from request.read())
except asyncio.TimeoutError:
_LOGGER.error("Timeout on api request %s.", cmd)
@ -249,6 +256,28 @@ class HassIOBaseEditView(HassIOBaseView):
return web.json_response(response)
class HassIOBaseLogsView(HomeAssistantView):
"""HassIO view to handle base logs part."""
requires_auth = True
def __init__(self, hassio, base):
"""Initialize a hassio base view."""
self.hassio = hassio
self._url_logs = "/{}/logs".format(base)
self.url = "/api/hassio/logs/{}".format(base)
self.name = "api:hassio:logs:{}".format(base)
@asyncio.coroutine
def get(self, request):
"""Get logs."""
data = yield from self.hassio.send_raw(self._url_logs, json=False)
if not data:
raise HTTPBadGateway()
return web.Response(body=data)
class HassIOAddonsView(HomeAssistantView):
"""HassIO view to handle addons part."""
@ -279,3 +308,24 @@ class HassIOAddonsView(HomeAssistantView):
if not response:
raise HTTPBadGateway()
return web.json_response(response)
class HassIOAddonsLogsView(HomeAssistantView):
"""HassIO view to handle addons logs part."""
requires_auth = True
url = "/api/hassio/logs/addons/{addon}"
name = "api:hassio:logs:addons"
def __init__(self, hassio):
"""Initialize a hassio addon view."""
self.hassio = hassio
@asyncio.coroutine
def get(self, request, addon):
"""Get addon data."""
data = yield from self.hassio.send_raw(
"/addons/{}/logs".format(addon), json=False)
if not data:
raise HTTPBadGateway()
return web.Response(body=data)

View file

@ -1,8 +1,10 @@
"""The tests for the hassio component."""
import asyncio
import os
from unittest.mock import patch
import aiohttp
import pytest
import homeassistant.components.hassio as ho
from homeassistant.setup import setup_component, async_setup_component
@ -11,6 +13,13 @@ from tests.common import (
get_test_home_assistant, assert_setup_component)
@pytest.fixture
def hassio_env():
"""Fixture to inject hassio env."""
with patch.dict(os.environ, {'HASSIO': "127.0.0.1"}) as env_mock:
yield env_mock
class TestHassIOSetup(object):
"""Test the hassio component."""
@ -22,13 +31,11 @@ class TestHassIOSetup(object):
ho.DOMAIN: {},
}
os.environ['HASSIO'] = "127.0.0.1"
def teardown_method(self):
"""Stop everything that was started."""
self.hass.stop()
def test_setup_component(self, aioclient_mock):
def test_setup_component(self, aioclient_mock, hassio_env):
"""Test setup component."""
aioclient_mock.get("http://127.0.0.1/supervisor/ping", json={
'result': 'ok', 'data': {}
@ -36,7 +43,17 @@ class TestHassIOSetup(object):
with assert_setup_component(0, ho.DOMAIN):
setup_component(self.hass, ho.DOMAIN, self.config)
def test_setup_component_test_service(self, aioclient_mock):
def test_setup_component_bad(self, aioclient_mock):
"""Test setup component bad."""
aioclient_mock.get("http://127.0.0.1/supervisor/ping", json={
'result': 'ok', 'data': {}
})
with assert_setup_component(0, ho.DOMAIN):
assert not setup_component(self.hass, ho.DOMAIN, self.config)
assert len(aioclient_mock.mock_calls) == 0
def test_setup_component_test_service(self, aioclient_mock, hassio_env):
"""Test setup component and check if service exits."""
aioclient_mock.get("http://127.0.0.1/supervisor/ping", json={
'result': 'ok', 'data': {}
@ -78,7 +95,6 @@ class TestHassIOComponent(object):
ho.DOMAIN: {},
}
os.environ['HASSIO'] = "127.0.0.1"
self.url = "http://127.0.0.1/{}"
self.error_msg = {
@ -94,7 +110,7 @@ class TestHassIOComponent(object):
"""Stop everything that was started."""
self.hass.stop()
def test_rest_command_timeout(self, aioclient_mock):
def test_rest_command_timeout(self, aioclient_mock, hassio_env):
"""Call a hassio with timeout."""
aioclient_mock.get(
"http://127.0.0.1/supervisor/ping", json=self.ok_msg)
@ -109,7 +125,7 @@ class TestHassIOComponent(object):
assert len(aioclient_mock.mock_calls) == 2
def test_rest_command_aiohttp_error(self, aioclient_mock):
def test_rest_command_aiohttp_error(self, aioclient_mock, hassio_env):
"""Call a hassio with aiohttp exception."""
aioclient_mock.get(
"http://127.0.0.1/supervisor/ping", json=self.ok_msg)
@ -124,7 +140,7 @@ class TestHassIOComponent(object):
assert len(aioclient_mock.mock_calls) == 2
def test_rest_command_http_error(self, aioclient_mock):
def test_rest_command_http_error(self, aioclient_mock, hassio_env):
"""Call a hassio with status code 503."""
aioclient_mock.get(
"http://127.0.0.1/supervisor/ping", json=self.ok_msg)
@ -139,7 +155,7 @@ class TestHassIOComponent(object):
assert len(aioclient_mock.mock_calls) == 2
def test_rest_command_http_error_api(self, aioclient_mock):
def test_rest_command_http_error_api(self, aioclient_mock, hassio_env):
"""Call a hassio with status code 503."""
aioclient_mock.get(
"http://127.0.0.1/supervisor/ping", json=self.ok_msg)
@ -154,7 +170,7 @@ class TestHassIOComponent(object):
assert len(aioclient_mock.mock_calls) == 2
def test_rest_command_http_host_reboot(self, aioclient_mock):
def test_rest_command_http_host_reboot(self, aioclient_mock, hassio_env):
"""Call a hassio for host reboot."""
aioclient_mock.get(
"http://127.0.0.1/supervisor/ping", json=self.ok_msg)
@ -169,7 +185,7 @@ class TestHassIOComponent(object):
assert len(aioclient_mock.mock_calls) == 2
def test_rest_command_http_host_shutdown(self, aioclient_mock):
def test_rest_command_http_host_shutdown(self, aioclient_mock, hassio_env):
"""Call a hassio for host shutdown."""
aioclient_mock.get(
"http://127.0.0.1/supervisor/ping", json=self.ok_msg)
@ -184,7 +200,7 @@ class TestHassIOComponent(object):
assert len(aioclient_mock.mock_calls) == 2
def test_rest_command_http_host_update(self, aioclient_mock):
def test_rest_command_http_host_update(self, aioclient_mock, hassio_env):
"""Call a hassio for host update."""
aioclient_mock.get(
"http://127.0.0.1/supervisor/ping", json=self.ok_msg)
@ -201,7 +217,8 @@ class TestHassIOComponent(object):
assert len(aioclient_mock.mock_calls) == 2
assert aioclient_mock.mock_calls[-1][2]['version'] == '0.4'
def test_rest_command_http_supervisor_update(self, aioclient_mock):
def test_rest_command_http_supervisor_update(self, aioclient_mock,
hassio_env):
"""Call a hassio for supervisor update."""
aioclient_mock.get(
"http://127.0.0.1/supervisor/ping", json=self.ok_msg)
@ -218,7 +235,8 @@ class TestHassIOComponent(object):
assert len(aioclient_mock.mock_calls) == 2
assert aioclient_mock.mock_calls[-1][2]['version'] == '0.4'
def test_rest_command_http_supervisor_reload(self, aioclient_mock):
def test_rest_command_http_supervisor_reload(self, aioclient_mock,
hassio_env):
"""Call a hassio for supervisor reload."""
aioclient_mock.get(
"http://127.0.0.1/supervisor/ping", json=self.ok_msg)
@ -234,7 +252,8 @@ class TestHassIOComponent(object):
assert len(aioclient_mock.mock_calls) == 2
def test_rest_command_http_homeassistant_update(self, aioclient_mock):
def test_rest_command_http_homeassistant_update(self, aioclient_mock,
hassio_env):
"""Call a hassio for homeassistant update."""
aioclient_mock.get(
"http://127.0.0.1/supervisor/ping", json=self.ok_msg)
@ -251,7 +270,7 @@ class TestHassIOComponent(object):
assert len(aioclient_mock.mock_calls) == 2
assert aioclient_mock.mock_calls[-1][2]['version'] == '0.4'
def test_rest_command_http_addon_install(self, aioclient_mock):
def test_rest_command_http_addon_install(self, aioclient_mock, hassio_env):
"""Call a hassio for addon install."""
aioclient_mock.get(
"http://127.0.0.1/supervisor/ping", json=self.ok_msg)
@ -271,7 +290,8 @@ class TestHassIOComponent(object):
assert len(aioclient_mock.mock_calls) == 2
assert aioclient_mock.mock_calls[-1][2]['version'] == '0.4'
def test_rest_command_http_addon_uninstall(self, aioclient_mock):
def test_rest_command_http_addon_uninstall(self, aioclient_mock,
hassio_env):
"""Call a hassio for addon uninstall."""
aioclient_mock.get(
"http://127.0.0.1/supervisor/ping", json=self.ok_msg)
@ -289,7 +309,7 @@ class TestHassIOComponent(object):
assert len(aioclient_mock.mock_calls) == 2
def test_rest_command_http_addon_update(self, aioclient_mock):
def test_rest_command_http_addon_update(self, aioclient_mock, hassio_env):
"""Call a hassio for addon update."""
aioclient_mock.get(
"http://127.0.0.1/supervisor/ping", json=self.ok_msg)
@ -309,7 +329,7 @@ class TestHassIOComponent(object):
assert len(aioclient_mock.mock_calls) == 2
assert aioclient_mock.mock_calls[-1][2]['version'] == '0.4'
def test_rest_command_http_addon_start(self, aioclient_mock):
def test_rest_command_http_addon_start(self, aioclient_mock, hassio_env):
"""Call a hassio for addon start."""
aioclient_mock.get(
"http://127.0.0.1/supervisor/ping", json=self.ok_msg)
@ -327,7 +347,7 @@ class TestHassIOComponent(object):
assert len(aioclient_mock.mock_calls) == 2
def test_rest_command_http_addon_stop(self, aioclient_mock):
def test_rest_command_http_addon_stop(self, aioclient_mock, hassio_env):
"""Call a hassio for addon stop."""
aioclient_mock.get(
"http://127.0.0.1/supervisor/ping", json=self.ok_msg)
@ -347,10 +367,8 @@ class TestHassIOComponent(object):
@asyncio.coroutine
def test_async_hassio_host_view(aioclient_mock, hass, test_client):
def test_async_hassio_host_view(aioclient_mock, hass, test_client, hassio_env):
"""Test that it fetches the given url."""
os.environ['HASSIO'] = "127.0.0.1"
aioclient_mock.get("http://127.0.0.1/supervisor/ping", json={
'result': 'ok', 'data': {}
})
@ -383,10 +401,9 @@ def test_async_hassio_host_view(aioclient_mock, hass, test_client):
@asyncio.coroutine
def test_async_hassio_homeassistant_view(aioclient_mock, hass, test_client):
def test_async_hassio_homeassistant_view(aioclient_mock, hass, test_client,
hassio_env):
"""Test that it fetches the given url."""
os.environ['HASSIO'] = "127.0.0.1"
aioclient_mock.get("http://127.0.0.1/supervisor/ping", json={
'result': 'ok', 'data': {}
})
@ -411,12 +428,21 @@ def test_async_hassio_homeassistant_view(aioclient_mock, hass, test_client):
assert data['version'] == '0.41'
assert data['current'] == '0.41.1'
aioclient_mock.get('http://127.0.0.1/homeassistant/logs',
content=b"That is a test log")
resp = yield from client.get('/api/hassio/logs/homeassistant')
data = yield from resp.read()
assert len(aioclient_mock.mock_calls) == 3
assert resp.status == 200
assert data == b"That is a test log"
@asyncio.coroutine
def test_async_hassio_supervisor_view(aioclient_mock, hass, test_client):
def test_async_hassio_supervisor_view(aioclient_mock, hass, test_client,
hassio_env):
"""Test that it fetches the given url."""
os.environ['HASSIO'] = "127.0.0.1"
aioclient_mock.get("http://127.0.0.1/supervisor/ping", json={
'result': 'ok', 'data': {}
})
@ -457,12 +483,21 @@ def test_async_hassio_supervisor_view(aioclient_mock, hass, test_client):
assert resp.status == 200
assert aioclient_mock.mock_calls[-1][2]['beta']
aioclient_mock.get('http://127.0.0.1/supervisor/logs',
content=b"That is a test log")
resp = yield from client.get('/api/hassio/logs/supervisor')
data = yield from resp.read()
assert len(aioclient_mock.mock_calls) == 4
assert resp.status == 200
assert data == b"That is a test log"
@asyncio.coroutine
def test_async_hassio_network_view(aioclient_mock, hass, test_client):
def test_async_hassio_network_view(aioclient_mock, hass, test_client,
hassio_env):
"""Test that it fetches the given url."""
os.environ['HASSIO'] = "127.0.0.1"
aioclient_mock.get("http://127.0.0.1/supervisor/ping", json={
'result': 'ok', 'data': {}
})
@ -508,10 +543,9 @@ def test_async_hassio_network_view(aioclient_mock, hass, test_client):
@asyncio.coroutine
def test_async_hassio_addon_view(aioclient_mock, hass, test_client):
def test_async_hassio_addon_view(aioclient_mock, hass, test_client,
hassio_env):
"""Test that it fetches the given url."""
os.environ['HASSIO'] = "127.0.0.1"
aioclient_mock.get("http://127.0.0.1/supervisor/ping", json={
'result': 'ok', 'data': {}
})
@ -559,3 +593,13 @@ def test_async_hassio_addon_view(aioclient_mock, hass, test_client):
assert resp.status == 200
assert aioclient_mock.mock_calls[-1][2]['boot'] == 'manual'
assert aioclient_mock.mock_calls[-1][2]['options']['bla']
aioclient_mock.get('http://127.0.0.1/addons/smb_config/logs',
content=b"That is a test log")
resp = yield from client.get('/api/hassio/logs/addons/smb_config')
data = yield from resp.read()
assert len(aioclient_mock.mock_calls) == 4
assert resp.status == 200
assert data == b"That is a test log"