diff --git a/supervisor/resolution/const.py b/supervisor/resolution/const.py index c6ff297c3..4b5247440 100644 --- a/supervisor/resolution/const.py +++ b/supervisor/resolution/const.py @@ -37,6 +37,7 @@ class UnsupportedReason(str, Enum): DNS_SERVER = "dns_server" DOCKER_CONFIGURATION = "docker_configuration" DOCKER_VERSION = "docker_version" + CGROUP_VERSION = "cgroup_version" JOB_CONDITIONS = "job_conditions" LXC = "lxc" NETWORK_MANAGER = "network_manager" diff --git a/supervisor/resolution/evaluations/cgroup.py b/supervisor/resolution/evaluations/cgroup.py new file mode 100644 index 000000000..b6223d199 --- /dev/null +++ b/supervisor/resolution/evaluations/cgroup.py @@ -0,0 +1,54 @@ +"""Evaluation class for CGroup version.""" +import logging + +from ...const import CoreState +from ...coresys import CoreSys +from ..const import UnsupportedReason +from .base import EvaluateBase + +CGROUP_V1_VERSION = "1" +CGROUP_V2_VERSION = "2" + +_LOGGER: logging.Logger = logging.getLogger(__name__) + + +def setup(coresys: CoreSys) -> EvaluateBase: + """Initialize evaluation-setup function.""" + return EvaluateCGroupVersion(coresys) + + +class EvaluateCGroupVersion(EvaluateBase): + """Evaluate Docker configuration.""" + + @property + def reason(self) -> UnsupportedReason: + """Return a UnsupportedReason enum.""" + return UnsupportedReason.CGROUP_VERSION + + @property + def on_failure(self) -> str: + """Return a string that is printed when self.evaluate is False.""" + return "The CGroup version used by Docker is not supported" + + @property + def states(self) -> list[CoreState]: + """Return a list of valid states when this evaluation can run.""" + return [CoreState.SETUP] + + async def evaluate(self): + """Run evaluation.""" + cgroup_version = self.sys_docker.info.cgroup + + expected_version = [CGROUP_V1_VERSION] + if self.coresys.os.available: + expected_version.append(CGROUP_V2_VERSION) + + if cgroup_version not in expected_version: + _LOGGER.warning( + "Docker cgroup version %s is not supported! %s", + cgroup_version, + expected_version, + ) + return True + + return False diff --git a/supervisor/resolution/evaluations/docker_configuration.py b/supervisor/resolution/evaluations/docker_configuration.py index 5a211426e..8c5173580 100644 --- a/supervisor/resolution/evaluations/docker_configuration.py +++ b/supervisor/resolution/evaluations/docker_configuration.py @@ -8,7 +8,6 @@ from .base import EvaluateBase EXPECTED_LOGGING = "journald" EXPECTED_STORAGE = "overlay2" -EXPECTED_CGROUP_VERSION = "1" _LOGGER: logging.Logger = logging.getLogger(__name__) @@ -40,7 +39,6 @@ class EvaluateDockerConfiguration(EvaluateBase): """Run evaluation.""" storage_driver = self.sys_docker.info.storage logging_driver = self.sys_docker.info.logging - cgroup_version = self.sys_docker.info.cgroup if storage_driver != EXPECTED_STORAGE: _LOGGER.warning( @@ -52,13 +50,4 @@ class EvaluateDockerConfiguration(EvaluateBase): "Docker logging driver %s is not supported!", logging_driver ) - if cgroup_version != EXPECTED_CGROUP_VERSION: - _LOGGER.warning( - "Docker cgroup version %s is not supported!", cgroup_version - ) - - return ( - storage_driver != EXPECTED_STORAGE - or logging_driver != EXPECTED_LOGGING - or cgroup_version != EXPECTED_CGROUP_VERSION - ) + return storage_driver != EXPECTED_STORAGE or logging_driver != EXPECTED_LOGGING diff --git a/tests/resolution/evaluation/test_evaluate_cgroup.py b/tests/resolution/evaluation/test_evaluate_cgroup.py new file mode 100644 index 000000000..16ccf1e67 --- /dev/null +++ b/tests/resolution/evaluation/test_evaluate_cgroup.py @@ -0,0 +1,59 @@ +"""Test evaluation base.""" +# pylint: disable=import-error,protected-access +from unittest.mock import patch + +from supervisor.const import CoreState +from supervisor.coresys import CoreSys +from supervisor.resolution.evaluations.cgroup import ( + CGROUP_V1_VERSION, + CGROUP_V2_VERSION, + EvaluateCGroupVersion, +) + + +async def test_evaluation(coresys: CoreSys): + """Test evaluation.""" + cgroup_version = EvaluateCGroupVersion(coresys) + coresys.core.state = CoreState.SETUP + + assert cgroup_version.reason not in coresys.resolution.unsupported + + coresys.docker.info.cgroup = "unsupported" + await cgroup_version() + assert cgroup_version.reason in coresys.resolution.unsupported + coresys.resolution.unsupported.clear() + + coresys.docker.info.cgroup = CGROUP_V2_VERSION + coresys.os._available = False + await cgroup_version() + assert cgroup_version.reason in coresys.resolution.unsupported + coresys.resolution.unsupported.clear() + + coresys.docker.info.cgroup = CGROUP_V1_VERSION + await cgroup_version() + assert cgroup_version.reason not in coresys.resolution.unsupported + + +async def test_did_run(coresys: CoreSys): + """Test that the evaluation ran as expected.""" + cgroup_version = EvaluateCGroupVersion(coresys) + should_run = cgroup_version.states + should_not_run = [state for state in CoreState if state not in should_run] + assert len(should_run) != 0 + assert len(should_not_run) != 0 + + with patch( + "supervisor.resolution.evaluations.cgroup.EvaluateCGroupVersion.evaluate", + return_value=None, + ) as evaluate: + for state in should_run: + coresys.core.state = state + await cgroup_version() + evaluate.assert_called_once() + evaluate.reset_mock() + + for state in should_not_run: + coresys.core.state = state + await cgroup_version() + evaluate.assert_not_called() + evaluate.reset_mock() diff --git a/tests/resolution/evaluation/test_evaluate_docker_configuration.py b/tests/resolution/evaluation/test_evaluate_docker_configuration.py index 3204e0773..31121ba70 100644 --- a/tests/resolution/evaluation/test_evaluate_docker_configuration.py +++ b/tests/resolution/evaluation/test_evaluate_docker_configuration.py @@ -5,7 +5,6 @@ from unittest.mock import patch from supervisor.const import CoreState from supervisor.coresys import CoreSys from supervisor.resolution.evaluations.docker_configuration import ( - EXPECTED_CGROUP_VERSION, EXPECTED_LOGGING, EXPECTED_STORAGE, EvaluateDockerConfiguration, @@ -21,28 +20,18 @@ async def test_evaluation(coresys: CoreSys): coresys.docker.info.storage = "unsupported" coresys.docker.info.logging = EXPECTED_LOGGING - coresys.docker.info.cgroup = EXPECTED_CGROUP_VERSION await docker_configuration() assert docker_configuration.reason in coresys.resolution.unsupported coresys.resolution.unsupported.clear() coresys.docker.info.storage = EXPECTED_STORAGE coresys.docker.info.logging = "unsupported" - coresys.docker.info.cgroup = EXPECTED_CGROUP_VERSION await docker_configuration() assert docker_configuration.reason in coresys.resolution.unsupported coresys.resolution.unsupported.clear() coresys.docker.info.storage = EXPECTED_STORAGE coresys.docker.info.logging = EXPECTED_LOGGING - coresys.docker.info.cgroup = "unsupported" - await docker_configuration() - assert docker_configuration.reason in coresys.resolution.unsupported - coresys.resolution.unsupported.clear() - - coresys.docker.info.storage = EXPECTED_STORAGE - coresys.docker.info.logging = EXPECTED_LOGGING - coresys.docker.info.cgroup = EXPECTED_CGROUP_VERSION await docker_configuration() assert docker_configuration.reason not in coresys.resolution.unsupported