Fix Supervisor logs fallback (#5022)

Supervisor logs fallback in get_supervisor_logs didn't work properly
because the exception was caught in api_process_raw instead. This was
not discovered in tests because the side effect raised OSError, which
isn't handled there.

To address that, I split the advanced_logs to two functions, one being a
wrapped API handler, one being plain function returning response without
any additional error handling. The tests now check for both cases of
errors (HassioError and random generic Python error).

Refs #5021
This commit is contained in:
Jan Čermák 2024-04-22 09:42:12 +02:00 committed by GitHub
parent 1246e429c9
commit 18d9d32bca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 25 additions and 5 deletions

View File

@ -401,7 +401,7 @@ class RestAPI(CoreSysAttributes):
async def get_supervisor_logs(*args, **kwargs): async def get_supervisor_logs(*args, **kwargs):
try: try:
return await self._api_host.advanced_logs( return await self._api_host.advanced_logs_handler(
*args, identifier="hassio_supervisor", **kwargs *args, identifier="hassio_supervisor", **kwargs
) )
except Exception as err: # pylint: disable=broad-exception-caught except Exception as err: # pylint: disable=broad-exception-caught

View File

@ -1,4 +1,5 @@
"""Init file for Supervisor host RESTful API.""" """Init file for Supervisor host RESTful API."""
import asyncio import asyncio
from contextlib import suppress from contextlib import suppress
import logging import logging
@ -163,8 +164,7 @@ class APIHost(CoreSysAttributes):
raise APIError() from err raise APIError() from err
return possible_offset return possible_offset
@api_process_raw(CONTENT_TYPE_TEXT, error_type=CONTENT_TYPE_TEXT) async def advanced_logs_handler(
async def advanced_logs(
self, request: web.Request, identifier: str | None = None, follow: bool = False self, request: web.Request, identifier: str | None = None, follow: bool = False
) -> web.StreamResponse: ) -> web.StreamResponse:
"""Return systemd-journald logs.""" """Return systemd-journald logs."""
@ -218,3 +218,10 @@ class APIHost(CoreSysAttributes):
"Connection reset when trying to fetch data from systemd-journald." "Connection reset when trying to fetch data from systemd-journald."
) from ex ) from ex
return response return response
@api_process_raw(CONTENT_TYPE_TEXT, error_type=CONTENT_TYPE_TEXT)
async def advanced_logs(
self, request: web.Request, identifier: str | None = None, follow: bool = False
) -> web.StreamResponse:
"""Return systemd-journald logs. Wrapped as standard API handler."""
return await self.advanced_logs_handler(request, identifier, follow)

View File

@ -6,7 +6,7 @@ from aiohttp.test_utils import TestClient
import pytest import pytest
from supervisor.coresys import CoreSys from supervisor.coresys import CoreSys
from supervisor.exceptions import StoreGitError, StoreNotFound from supervisor.exceptions import HassioError, StoreGitError, StoreNotFound
from supervisor.store.repository import Repository from supervisor.store.repository import Repository
from tests.api import common_test_api_advanced_logs from tests.api import common_test_api_advanced_logs
@ -160,7 +160,7 @@ async def test_api_supervisor_fallback(
api_client: TestClient, journald_logs: MagicMock, docker_logs: MagicMock api_client: TestClient, journald_logs: MagicMock, docker_logs: MagicMock
): ):
"""Check that supervisor logs read from container logs if reading from journald gateway fails badly.""" """Check that supervisor logs read from container logs if reading from journald gateway fails badly."""
journald_logs.side_effect = OSError("Something bad happened!") journald_logs.side_effect = HassioError("Something bad happened!")
with patch("supervisor.api._LOGGER.exception") as logger: with patch("supervisor.api._LOGGER.exception") as logger:
resp = await api_client.get("/supervisor/logs") resp = await api_client.get("/supervisor/logs")
@ -176,6 +176,19 @@ async def test_api_supervisor_fallback(
b"\x1b[36m22-10-11 14:04:23 DEBUG (MainThread) [supervisor.utils.dbus] D-Bus call - org.freedesktop.DBus.Properties.call_get_all on /io/hass/os/AppArmor\x1b[0m", b"\x1b[36m22-10-11 14:04:23 DEBUG (MainThread) [supervisor.utils.dbus] D-Bus call - org.freedesktop.DBus.Properties.call_get_all on /io/hass/os/AppArmor\x1b[0m",
] ]
journald_logs.reset_mock()
# also check generic Python error
journald_logs.side_effect = OSError("Something bad happened!")
with patch("supervisor.api._LOGGER.exception") as logger:
resp = await api_client.get("/supervisor/logs")
logger.assert_called_once_with(
"Failed to get supervisor logs using advanced_logs API"
)
assert resp.status == 200
assert resp.content_type == "text/plain"
async def test_api_supervisor_reload(api_client: TestClient): async def test_api_supervisor_reload(api_client: TestClient):
"""Test supervisor reload.""" """Test supervisor reload."""