From 530f17d50242b0fbcd3a9011bc018364eedbe77a Mon Sep 17 00:00:00 2001 From: pvizeli Date: Mon, 10 Apr 2017 11:32:19 +0200 Subject: [PATCH] Add support for addon config. --- hassio/addons/__init__.py | 20 ++++++++++++- hassio/addons/config.py | 62 +++++++++++++++++++++++++++++++++++++++ hassio/addons/git.py | 13 ++++++-- hassio/api/util.py | 2 +- hassio/config.py | 31 +++++++++++++------- hassio/const.py | 18 ++++++++++++ 6 files changed, 131 insertions(+), 15 deletions(-) create mode 100644 hassio/addons/config.py diff --git a/hassio/addons/__init__.py b/hassio/addons/__init__.py index 62cf1c8ce..26b58fdb5 100644 --- a/hassio/addons/__init__.py +++ b/hassio/addons/__init__.py @@ -1,13 +1,31 @@ """Init file for HassIO addons.""" import logging +from .config import AddonsConfig +from .git import AddonsRepo + _LOGGER = logging.getLogger(__name__) -class AddonManager(object): +class AddonsManager(object): """Manage addons inside HassIO.""" def __init__(self, config, loop): """Initialize docker base wrapper.""" self.config = config self.loop = loop + self.repo = AddonsRepo(config, loop) + self.addons = AddonsConfig(config) + self.dockers = {} + + async def prepare(self): + """Startup addon management.""" + # load addon repository + if await self.repo.load(): + self.addons.read_addons_repo() + + async def relaod_addons(self): + """Update addons from repo and reload list.""" + if not await self.repo.pull(): + return + self.addons.read_addons_repo() diff --git a/hassio/addons/config.py b/hassio/addons/config.py new file mode 100644 index 000000000..c4cd1c74f --- /dev/null +++ b/hassio/addons/config.py @@ -0,0 +1,62 @@ +"""Init file for HassIO addons.""" +import logging +import glob +import json + +import voluptuous as vol +from voluptuous.humanize import humanize_error + +from ..const import ( + FILE_HASSIO_ADDONS, ATTR_NAME, ATTR_VERSION, ATTR_SLUG, ATTR_DESCRIPTON, + ATTR_STARTUP, ATTR_BOOT, ATTR_MAP_SSL, ATTR_MAP_CONFIG, ATTR_MAP_DATA, + ATTR_OPTIONS, STARTUP_ONCE, STARTUP_AFTER, STARTUP_BEFORE, BOOT_START, + BOOT_STOP, BOOT_MANUAL) + +_LOGGER = logging.getLogger(__name__) + +ADDONS_REPO_PATTERN = "{}/*/config.json" + +# pylint: disable=no-value-for-parameter +SCHEMA_ADDON_CONFIG = vol.Schema({ + vol.Required(ATTR_NAME): vol.Coerce(str), + vol.Required(ATTR_VERSION): vol.Coerce(str), + vol.Required(ATTR_SLUG): vol.Coerce(str), + vol.Required(ATTR_DESCRIPTON): vol.Coerce(str), + vol.Required(ATTR_STARTUP): + vol.In([STARTUP_BEFORE, STARTUP_AFTER, STARTUP_ONCE]), + vol.Required(ATTR_BOOT): + vol.IN([BOOT_START, BOOT_STOP, BOOT_MANUAL]), + vol.Required(ATTR_MAP_CONFIG): vol.Boolean(), + vol.Required(ATTR_MAP_SSL): vol.Boolean(), + vol.Required(ATTR_MAP_DATA): vol.Boolean(), + vol.Required(ATTR_OPTIONS): dict, +}) + + +class AddonsConfig(Config): + """Config for addons inside HassIO.""" + + def __init__(self, config): + """Initialize docker base wrapper.""" + super().__init__(FILE_HASSIO_ADDONS) + self.config + self._addons_data = {} + + def read_addons_repo(self): + """Read data from addons repository.""" + pattern = ADDONS_REPO_PATTERN.format(self.config.path_addons_repo) + + for addon in glob.iglob(pattern): + try: + with open(addon, 'r') as cfile: + addon_config = json.loads(cfile.read()) + + addon_config = SCHEMA_ADDON_CONFIG(addon_config) + self._addons_data[addon_config[ATTR_SLUG]] = addon_config + + except (OSError, KeyError): + _LOGGER.warning("Can't read %s", addon) + + except vol.Invalid as ex: + _LOGGER.warnign("Can't read %s -> %s.", addon, + humanize_error(addon_config, ex)) diff --git a/hassio/addons/git.py b/hassio/addons/git.py index 54980ca0d..a81731fa6 100644 --- a/hassio/addons/git.py +++ b/hassio/addons/git.py @@ -20,7 +20,7 @@ class AddonsRepo(object): self.repo = None self._lock = asyncio.Lock(loop=loop) - async def init(self): + async def load(self): """Init git addon repo.""" if not os.path.isdir(self.config.path_addons_repo): return await self.clone() @@ -32,6 +32,9 @@ class AddonsRepo(object): except (git.InvalidGitRepositoryError, git.NoSuchPathError) as err: _LOGGER.error("Can't load addons repo: %s.", err) + return False + + return True async def clone(self): """Clone git addon repo.""" @@ -43,12 +46,15 @@ class AddonsRepo(object): except (git.InvalidGitRepositoryError, git.NoSuchPathError) as err: _LOGGER.error("Can't clone addons repo: %s.", err) + return False + + return True async def pull(self): """Pull git addon repo.""" if self._lock.locked(): _LOGGER.warning("It is already a task in progress.") - return + return False await with self._lock: try: @@ -57,3 +63,6 @@ class AddonsRepo(object): except (git.InvalidGitRepositoryError, git.NoSuchPathError) as err: _LOGGER.error("Can't pull addons repo: %s.", err) + return False + + return True diff --git a/hassio/api/util.py b/hassio/api/util.py index fd75d5ec2..e435da684 100644 --- a/hassio/api/util.py +++ b/hassio/api/util.py @@ -82,7 +82,7 @@ async def api_validate(schema, request): """Validate request data with schema.""" data = await request.json(loads=json_loads) try: - schema(data) + data = schema(data) except vol.Invalid as ex: raise RuntimeError(humanize_error(data, ex)) from None diff --git a/hassio/config.py b/hassio/config.py index f0ca3e338..d10f497dc 100644 --- a/hassio/config.py +++ b/hassio/config.py @@ -21,12 +21,11 @@ ADDONS_DATA = "{}/addons_data" UPSTREAM_BETA = 'upstream_beta' -class CoreConfig(object): +class Config(object): """Hold all config data.""" - def __init__(self, websession, config_file=FILE_HASSIO_CONFIG): + def __init__(self, config_file): """Initialize config object.""" - self.websession = websession self._filename = config_file self._data = {} @@ -38,14 +37,6 @@ class CoreConfig(object): except OSError: _LOGGER.warning("Can't read %s", self._filename) - # init data - if not self._data: - self._data.update({ - HOMEASSISTANT_IMAGE: os.environ['HOMEASSISTANT_REPOSITORY'], - UPSTREAM_BETA: False, - }) - self.save() - def save(self): """Store data to config file.""" try: @@ -57,6 +48,24 @@ class CoreConfig(object): return True + +class CoreConfig(Config): + """Hold all core config data.""" + + def __init__(self, websession): + """Initialize config object.""" + self.websession = websession + + super().__ini__(FILE_HASSIO_CONFIG) + + # init data + if not self._data: + self._data.update({ + HOMEASSISTANT_IMAGE: os.environ['HOMEASSISTANT_REPOSITORY'], + UPSTREAM_BETA: False, + }) + self.save() + async def fetch_update_infos(self): """Read current versions from web.""" current = await fetch_current_versions( diff --git a/hassio/const.py b/hassio/const.py index 40c97d4f4..99fc13fbb 100644 --- a/hassio/const.py +++ b/hassio/const.py @@ -28,3 +28,21 @@ RESULT_OK = 'ok' ATTR_VERSION = 'version' ATTR_CURRENT = 'current' ATTR_BETA = 'beta' +ATTR_NAME = 'name' +ATTR_SLUG = 'slug' +ATTR_DESCRIPTON = 'description' +ATTR_STARTUP = 'startup' +ATTR_BOOT = 'boot' +ATTR_PORTS = 'ports' +ATTR_MAP_CONFIG = 'map_config' +ATTR_MAP_SSL = 'map_ssl' +ATTR_MAP_DATA = 'map_data' +ATTR_OPTIONS = 'options' +ATTR_INSTALLED = 'installed' + +STARTUP_BEFORE = 'before' +STARTUP_AFTER = 'after' +STARTUP_ONCE = 'once' +BOOT_START = 'start' +BOOT_STOP = 'stop' +BOOT_MANUAL = 'manual'