From 8e043a01c19c22253f3d05faa9123016916dff78 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 21 May 2020 12:06:27 +0200 Subject: [PATCH] Add Docker info & check (#1736) * Add Docker info & check * Add checks * Fix logging * fix lint --- API.md | 1 + supervisor/api/info.py | 2 ++ supervisor/const.py | 1 + supervisor/core.py | 17 +++++++++++++-- supervisor/docker/__init__.py | 39 +++++++++++++++++++++++++++++++++++ 5 files changed, 58 insertions(+), 2 deletions(-) diff --git a/API.md b/API.md index 46b3da236..c641dfea6 100644 --- a/API.md +++ b/API.md @@ -789,6 +789,7 @@ return: "supervisor": "version", "homeassistant": "version", "hassos": "null|version", + "docker": "version", "hostname": "name", "machine": "type", "arch": "arch", diff --git a/supervisor/api/info.py b/supervisor/api/info.py index 7c2914dc5..f8d56aae8 100644 --- a/supervisor/api/info.py +++ b/supervisor/api/info.py @@ -7,6 +7,7 @@ from aiohttp import web from ..const import ( ATTR_ARCH, ATTR_CHANNEL, + ATTR_DOCKER, ATTR_HASSOS, ATTR_HOMEASSISTANT, ATTR_HOSTNAME, @@ -32,6 +33,7 @@ class APIInfo(CoreSysAttributes): ATTR_SUPERVISOR: self.sys_supervisor.version, ATTR_HOMEASSISTANT: self.sys_homeassistant.version, ATTR_HASSOS: self.sys_hassos.version, + ATTR_DOCKER: self.sys_docker.info.version, ATTR_HOSTNAME: self.sys_host.info.hostname, ATTR_MACHINE: self.sys_machine, ATTR_ARCH: self.sys_arch.default, diff --git a/supervisor/const.py b/supervisor/const.py index 82343affa..9bdb8aa71 100644 --- a/supervisor/const.py +++ b/supervisor/const.py @@ -76,6 +76,7 @@ ENV_SUPERVISOR_MACHINE = "SUPERVISOR_MACHINE" REQUEST_FROM = "HASSIO_FROM" +ATTR_DOCKER = "docker" ATTR_SUPERVISOR = "supervisor" ATTR_MACHINE = "machine" ATTR_MULTICAST = "multicast" diff --git a/supervisor/core.py b/supervisor/core.py index db2928f5b..5e0cf18f7 100644 --- a/supervisor/core.py +++ b/supervisor/core.py @@ -31,16 +31,29 @@ class Core(CoreSysAttributes): """Connect Supervisor container.""" await self.sys_supervisor.load() - # Check if system is healthy + # If a update is failed? if self.sys_dev: self.sys_config.version = self.sys_supervisor.version elif ( self.sys_config.version and self.sys_config.version != self.sys_supervisor.version ): + self.healthy = False + _LOGGER.critical("Update of Supervisor fails!") + + # If local docker is supported? + if not self.sys_docker.info.supported_version: self.healthy = False _LOGGER.critical( - "System running in a unhealthy state. Please update you OS!" + "Docker version %s is not supported by Supervisor!", + self.sys_docker.info.version, + ) + self.sys_docker.info.check_requirements() + + # Check if system is healthy + if not self.healthy: + _LOGGER.critical( + "System running in a unhealthy state. Please update you OS or software!" ) async def setup(self): diff --git a/supervisor/docker/__init__.py b/supervisor/docker/__init__.py index a517dd187..713359fb3 100644 --- a/supervisor/docker/__init__.py +++ b/supervisor/docker/__init__.py @@ -6,6 +6,7 @@ from typing import Any, Dict, Optional import attr import docker +from packaging import version as pkg_version from ..const import SOCKET_DOCKER, DNS_SUFFIX from ..exceptions import DockerAPIError @@ -13,6 +14,8 @@ from .network import DockerNetwork _LOGGER: logging.Logger = logging.getLogger(__name__) +MIN_SUPPORTED_DOCKER = "19.03.0" + @attr.s(frozen=True) class CommandReturn: @@ -22,6 +25,36 @@ class CommandReturn: output: bytes = attr.ib() +@attr.s(frozen=True) +class DockerInfo: + """Return docker information.""" + + version: str = attr.ib() + storage: str = attr.ib() + logging: str = attr.ib() + + @staticmethod + def new(data: Dict[str, Any]): + """Create a object from docker info.""" + return DockerInfo(data["ServerVersion"], data["Driver"], data["LoggingDriver"]) + + @property + def supported_version(self) -> bool: + """Return true, if docker version is supported.""" + version_local = pkg_version.parse(self.version) + version_min = pkg_version.parse(MIN_SUPPORTED_DOCKER) + + return version_local >= version_min + + def check_requirements(self) -> None: + """Show wrong configurations.""" + if self.storage != "overlay2": + _LOGGER.error("Docker storage driver %s is not supported!", self.storage) + + if self.logging != "journald": + _LOGGER.error("Docker logging driver %s is not supported!", self.logging) + + class DockerAPI: """Docker Supervisor wrapper. @@ -34,6 +67,7 @@ class DockerAPI: base_url="unix:/{}".format(str(SOCKET_DOCKER)), version="auto", timeout=900 ) self.network: DockerNetwork = DockerNetwork(self.docker) + self._info: DockerInfo = DockerInfo.new(self.docker.info()) @property def images(self) -> docker.models.images.ImageCollection: @@ -50,6 +84,11 @@ class DockerAPI: """Return API containers.""" return self.docker.api + @property + def info(self) -> DockerInfo: + """Return local docker info.""" + return self._info + def run( self, image: str,