diff --git a/supervisor/misc/filter.py b/supervisor/misc/filter.py index 10622a171..209d85b7a 100644 --- a/supervisor/misc/filter.py +++ b/supervisor/misc/filter.py @@ -62,6 +62,7 @@ def filter_data(coresys: CoreSys, event: dict, hint: dict) -> dict: "host": coresys.host.info.operating_system, "kernel": coresys.host.info.kernel, "machine": coresys.machine, + "images": coresys.resolution.evaluate.cached_images, }, "versions": { "audio": coresys.plugins.audio.version, diff --git a/supervisor/resolution/evaluate.py b/supervisor/resolution/evaluate.py index 7c14092c0..1947cf819 100644 --- a/supervisor/resolution/evaluate.py +++ b/supervisor/resolution/evaluate.py @@ -1,6 +1,6 @@ """Helpers to evaluate the system.""" import logging -from typing import List +from typing import List, Set from ..coresys import CoreSys, CoreSysAttributes from .const import UnhealthyReason, UnsupportedReason @@ -33,6 +33,8 @@ class ResolutionEvaluation(CoreSysAttributes): """Initialize the evaluation class.""" self.coresys = coresys + self.cached_images: Set[str] = set() + self._container = EvaluateContainer(coresys) self._dbus = EvaluateDbus(coresys) self._docker_configuration = EvaluateDockerConfiguration(coresys) diff --git a/supervisor/resolution/evaluations/container.py b/supervisor/resolution/evaluations/container.py index 2c02139a2..af107ae51 100644 --- a/supervisor/resolution/evaluations/container.py +++ b/supervisor/resolution/evaluations/container.py @@ -7,7 +7,7 @@ from requests import RequestException from ...const import CoreState from ...coresys import CoreSys -from ..const import UnsupportedReason +from ..const import ContextType, IssueType, SuggestionType, UnsupportedReason from .base import EvaluateBase _LOGGER: logging.Logger = logging.getLogger(__name__) @@ -44,12 +44,15 @@ class EvaluateContainer(EvaluateBase): async def evaluate(self) -> None: """Run evaluation.""" + self.sys_resolution.evaluate.cached_images.clear() self._images.clear() + for image in await self.sys_run_in_executor(self._get_images): for tag in image.tags: - image_name = tag.partition(":")[0].split("/")[-1] + self.sys_resolution.evaluate.cached_images.add(tag) # Evalue system + image_name = tag.partition(":")[0].split("/")[-1] if ( any( image_name.startswith(deny_name) @@ -68,5 +71,10 @@ class EvaluateContainer(EvaluateBase): images = self.sys_docker.images.list() except (DockerException, RequestException) as err: _LOGGER.error("Corrupt docker overlayfs detect: %s", err) + self.sys_resolution.create_issue( + IssueType.CORRUPT_DOCKER, + ContextType.SYSTEM, + suggestions=[SuggestionType.EXECUTE_REPAIR], + ) return images diff --git a/tests/misc/test_filter_data.py b/tests/misc/test_filter_data.py index 94eed42da..e8c2dcc78 100644 --- a/tests/misc/test_filter_data.py +++ b/tests/misc/test_filter_data.py @@ -165,3 +165,17 @@ def test_unhealthy_on_report(coresys): assert "issues" in event["contexts"]["resolution"] assert event["contexts"]["resolution"]["unhealthy"][-1] == UnhealthyReason.DOCKER + + +def test_images_report(coresys): + """Attach image to report.""" + + coresys.config.diagnostics = True + coresys.core.state = CoreState.RUNNING + coresys.resolution.evaluate.cached_images.add("my/test:image") + + with patch("shutil.disk_usage", return_value=(42, 42, 2 * (1024.0 ** 3))): + event = filter_data(coresys, SAMPLE_EVENT, {}) + + assert "issues" in event["contexts"]["resolution"] + assert event["contexts"]["host"]["images"] == {"my/test:image"} diff --git a/tests/resolution/evaluation/test_evaluate_container.py b/tests/resolution/evaluation/test_evaluate_container.py index 75d52659d..f75de3afd 100644 --- a/tests/resolution/evaluation/test_evaluate_container.py +++ b/tests/resolution/evaluation/test_evaluate_container.py @@ -48,6 +48,13 @@ async def test_evaluation(coresys: CoreSys): await container() assert container.reason in coresys.resolution.unsupported + assert coresys.resolution.evaluate.cached_images == { + "armhfbuild/watchtower:latest", + "concerco/watchtowerv6:10.0.2", + "containrrr/watchtower:1.1", + "pyouroboros/ouroboros:1.4.3", + } + with patch( "supervisor.resolution.evaluations.container.EvaluateContainer._get_images", return_value=[MagicMock(tags=[])], @@ -55,6 +62,8 @@ async def test_evaluation(coresys: CoreSys): await container() assert container.reason not in coresys.resolution.unsupported + assert coresys.resolution.evaluate.cached_images == set() + async def test_did_run(coresys: CoreSys): """Test that the evaluation ran as expected."""