From e61e7f41f2a2a97d87c8f2cb6fae0ff897dbec12 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 3 Oct 2017 17:37:48 +0200 Subject: [PATCH 1/8] Pump version to 0.69 --- hassio/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hassio/const.py b/hassio/const.py index e055ea3f0..c4de0006f 100644 --- a/hassio/const.py +++ b/hassio/const.py @@ -2,7 +2,7 @@ from pathlib import Path from ipaddress import ip_network -HASSIO_VERSION = '0.68' +HASSIO_VERSION = '0.69' URL_HASSIO_VERSION = ('https://raw.githubusercontent.com/home-assistant/' 'hassio/{}/version.json') From 361969aca2d1ad9ebfd0aa23bd7ce57b6581e897 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Sun, 8 Oct 2017 09:43:07 +0200 Subject: [PATCH 2/8] Update Home-Assistant to version 0.55 --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index 7acd047b2..494a66f46 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "hassio": "0.68", - "homeassistant": "0.54", + "homeassistant": "0.55", "resinos": "1.1", "resinhup": "0.3", "generic": "0.3", From 38f96d7ddd16eedf4abb18605d6d03bb6baf4e59 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Mon, 9 Oct 2017 10:09:43 +0200 Subject: [PATCH 3/8] Remove unknown options from input (#213) * Update validate.py * Update validate.py * Cleanup unneeded code --- hassio/addons/validate.py | 76 ++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/hassio/addons/validate.py b/hassio/addons/validate.py index f44f9571e..10c8ecb4d 100644 --- a/hassio/addons/validate.py +++ b/hassio/addons/validate.py @@ -1,4 +1,5 @@ """Validate addons options schema.""" +import logging import re import voluptuous as vol @@ -17,6 +18,8 @@ from ..const import ( ATTR_ARGS, ATTR_GPIO, ATTR_HOMEASSISTANT_API, ATTR_STDIN) from ..validate import NETWORK_PORT, DOCKER_PORTS, ALSA_CHANNEL +_LOGGER = logging.getLogger(__name__) + RE_VOLUME = re.compile(r"^(config|ssl|addons|backup|share)(?::(rw|:ro))?$") @@ -176,8 +179,10 @@ def validate_options(raw_schema): # read options for key, value in struct.items(): + # Ignore unknown options / remove from list if key not in raw_schema: - raise vol.Invalid("Unknown options {}.".format(key)) + _LOGGER.warning("Unknown options %s", key) + continue typ = raw_schema[key] try: @@ -202,42 +207,38 @@ def validate_options(raw_schema): # pylint: disable=no-value-for-parameter def _single_validate(typ, value, key): """Validate a single element.""" - try: - # if required argument - if value is None: - raise vol.Invalid("Missing required option '{}'.".format(key)) + # if required argument + if value is None: + raise vol.Invalid("Missing required option '{}'.".format(key)) - # parse extend data from type - match = RE_SCHEMA_ELEMENT.match(typ) + # parse extend data from type + match = RE_SCHEMA_ELEMENT.match(typ) - # prepare range - range_args = {} - for group_name in ('i_min', 'i_max', 'f_min', 'f_max'): - group_value = match.group(group_name) - if group_value: - range_args[group_name[2:]] = float(group_value) + # prepare range + range_args = {} + for group_name in ('i_min', 'i_max', 'f_min', 'f_max'): + group_value = match.group(group_name) + if group_value: + range_args[group_name[2:]] = float(group_value) - if typ.startswith(V_STR): - return str(value) - elif typ.startswith(V_INT): - return vol.All(vol.Coerce(int), vol.Range(**range_args))(value) - elif typ.startswith(V_FLOAT): - return vol.All(vol.Coerce(float), vol.Range(**range_args))(value) - elif typ.startswith(V_BOOL): - return vol.Boolean()(value) - elif typ.startswith(V_EMAIL): - return vol.Email()(value) - elif typ.startswith(V_URL): - return vol.Url()(value) - elif typ.startswith(V_PORT): - return NETWORK_PORT(value) - elif typ.startswith(V_MATCH): - return vol.Match(match.group('match'))(str(value)) + if typ.startswith(V_STR): + return str(value) + elif typ.startswith(V_INT): + return vol.All(vol.Coerce(int), vol.Range(**range_args))(value) + elif typ.startswith(V_FLOAT): + return vol.All(vol.Coerce(float), vol.Range(**range_args))(value) + elif typ.startswith(V_BOOL): + return vol.Boolean()(value) + elif typ.startswith(V_EMAIL): + return vol.Email()(value) + elif typ.startswith(V_URL): + return vol.Url()(value) + elif typ.startswith(V_PORT): + return NETWORK_PORT(value) + elif typ.startswith(V_MATCH): + return vol.Match(match.group('match'))(str(value)) - raise vol.Invalid("Fatal error for {} type {}".format(key, typ)) - except ValueError: - raise vol.Invalid( - "Type {} error for '{}' on {}.".format(typ, value, key)) from None + raise vol.Invalid("Fatal error for {} type {}".format(key, typ)) def _nested_validate_list(typ, data_list, key): @@ -249,9 +250,10 @@ def _nested_validate_list(typ, data_list, key): if isinstance(typ, dict): c_options = {} for c_key, c_value in element.items(): + # Ignore unknown options / remove from list if c_key not in typ: - raise vol.Invalid( - "Unknown nested options {}".format(c_key)) + _LOGGER.warning("Unknown options %s", c_key) + continue c_options[c_key] = _single_validate(typ[c_key], c_value, c_key) options.append(c_options) @@ -267,8 +269,10 @@ def _nested_validate_dict(typ, data_dict, key): options = {} for c_key, c_value in data_dict.items(): + # Ignore unknown options / remove from list if c_key not in typ: - raise vol.Invalid("Unknow nested dict options {}".format(c_key)) + _LOGGER.warning("Unknown options %s", c_key) + continue options[c_key] = _single_validate(typ[c_key], c_value, c_key) From fe04b7ec59541740661cda962a76eeb46185a35a Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Mon, 9 Oct 2017 10:48:17 +0200 Subject: [PATCH 4/8] Remove dedicated API calls (#212) * Update addons.py * Update API.md * Update addon.py * Update addon.py * Update addons.py --- API.md | 16 ---------------- hassio/addons/addon.py | 18 ++++++++---------- hassio/api/addons.py | 21 +++++++-------------- 3 files changed, 15 insertions(+), 40 deletions(-) diff --git a/API.md b/API.md index c1395573b..352c087eb 100644 --- a/API.md +++ b/API.md @@ -430,26 +430,10 @@ For reset custom network/audio settings, set it `null`. - POST `/addons/{addon}/install` -Optional: - -```json -{ - "version": "VERSION" -} -``` - - POST `/addons/{addon}/uninstall` - POST `/addons/{addon}/update` -Optional: - -```json -{ - "version": "VERSION" -} -``` - - GET `/addons/{addon}/logs` Output is the raw Docker log. diff --git a/hassio/addons/addon.py b/hassio/addons/addon.py index 3c13ecd8b..42aac8290 100644 --- a/hassio/addons/addon.py +++ b/hassio/addons/addon.py @@ -447,7 +447,7 @@ class Addon(object): return False return True - async def install(self, version=None): + async def install(self): """Install a addon.""" if self.config.arch not in self.supported_arch: _LOGGER.error( @@ -463,11 +463,10 @@ class Addon(object): "Create Home-Assistant addon data folder %s", self.path_data) self.path_data.mkdir() - version = version or self.last_version - if not await self.docker.install(version): + if not await self.docker.install(self.last_version): return False - self._set_install(version) + self._set_install(self.last_version) return True @check_installed @@ -510,19 +509,18 @@ class Addon(object): return self.docker.stop() @check_installed - async def update(self, version=None): + async def update(self): """Update addon.""" - version = version or self.last_version last_state = await self.state() - if version == self.version_installed: + if self.last_version == self.version_installed: _LOGGER.warning( - "Addon %s is already installed in %s", self._id, version) + "No update available for Addon %s", self._id) return False - if not await self.docker.update(version): + if not await self.docker.update(self.last_version): return False - self._set_update(version) + self._set_update(self.last_version) # restore state if last_state == STATE_STARTED: diff --git a/hassio/api/addons.py b/hassio/api/addons.py index 24fb7b77a..dd8aad01c 100644 --- a/hassio/api/addons.py +++ b/hassio/api/addons.py @@ -166,14 +166,10 @@ class APIAddons(object): return True @api_process - async def install(self, request): + def install(self, request): """Install addon.""" - body = await api_validate(SCHEMA_VERSION, request) addon = self._extract_addon(request, check_installed=False) - version = body.get(ATTR_VERSION, addon.last_version) - - return await asyncio.shield( - addon.install(version=version), loop=self.loop) + return asyncio.shield(addon.install(), loop=self.loop) @api_process def uninstall(self, request): @@ -202,17 +198,14 @@ class APIAddons(object): return asyncio.shield(addon.stop(), loop=self.loop) @api_process - async def update(self, request): + def update(self, request): """Update addon.""" - body = await api_validate(SCHEMA_VERSION, request) addon = self._extract_addon(request) - version = body.get(ATTR_VERSION, addon.last_version) - if version == addon.version_installed: - raise RuntimeError("Version %s is already in use", version) + if addon.last_version == addon.version_installed: + raise RuntimeError("No update available!") - return await asyncio.shield( - addon.update(version=version), loop=self.loop) + return asyncio.shield(addon.update(), loop=self.loop) @api_process def restart(self, request): @@ -253,4 +246,4 @@ class APIAddons(object): raise RuntimeError("STDIN not supported by addons") data = await request.read() - return asyncio.shield(addon.write_stdin(data), loop=self.loop) + return await asyncio.shield(addon.write_stdin(data), loop=self.loop) From 68566ee9e1fb4e781dd9276655d627ecc1145d18 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Mon, 9 Oct 2017 13:21:21 +0200 Subject: [PATCH 5/8] Update homeassistant.py (#215) --- hassio/homeassistant.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hassio/homeassistant.py b/hassio/homeassistant.py index 90ab43d6c..3e7037614 100644 --- a/hassio/homeassistant.py +++ b/hassio/homeassistant.py @@ -95,7 +95,7 @@ class HomeAssistant(JsonConfig): def watchdog(self, value): """Return True if the watchdog should protect Home-Assistant.""" self._data[ATTR_WATCHDOG] = value - self._data.save() + self.save() @property def version(self): From 2df4f80aa5b48ed03935faaf4f70b03c8c33dd2a Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Mon, 9 Oct 2017 13:30:15 +0200 Subject: [PATCH 6/8] More log output (#214) * Update snapshot.py * Update __init__.py * Update snapshot.py * Update snapshot.py * fix lint --- hassio/snapshots/__init__.py | 4 ++++ hassio/snapshots/snapshot.py | 8 ++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/hassio/snapshots/__init__.py b/hassio/snapshots/__init__.py index c865becdd..1597d3c7c 100644 --- a/hassio/snapshots/__init__.py +++ b/hassio/snapshots/__init__.py @@ -197,6 +197,8 @@ class SnapshotsManager(object): await snapshot.restore_folders() # start homeassistant restore + _LOGGER.info("Full-Restore %s restore Home-Assistant", + snapshot.slug) snapshot.restore_homeassistant(self.homeassistant) task_hass = self.loop.create_task( self.homeassistant.update(snapshot.homeassistant_version)) @@ -279,6 +281,8 @@ class SnapshotsManager(object): await snapshot.restore_folders(folders) if homeassistant: + _LOGGER.info("Partial-Restore %s restore Home-Assistant", + snapshot.slug) snapshot.restore_homeassistant(self.homeassistant) tasks.append(self.homeassistant.update( snapshot.homeassistant_version)) diff --git a/hassio/snapshots/snapshot.py b/hassio/snapshots/snapshot.py index cc006aad6..d810a2e24 100644 --- a/hassio/snapshots/snapshot.py +++ b/hassio/snapshots/snapshot.py @@ -261,7 +261,8 @@ class Snapshot(object): """Async context to close a snapshot.""" # exists snapshot or exception on build if self.tar_file.is_file() or exception_type is not None: - return self._tmp.cleanup() + self._tmp.cleanup() + return # validate data try: @@ -283,7 +284,6 @@ class Snapshot(object): _LOGGER.error("Can't write snapshot.json") self._tmp.cleanup() - self._tmp = None async def import_addon(self, addon): """Add a addon into snapshot.""" @@ -323,9 +323,11 @@ class Snapshot(object): origin_dir = Path(self.config.path_hassio, name) try: + _LOGGER.info("Snapshot folder %s", name) with tarfile.open(snapshot_tar, "w:gz", compresslevel=1) as tar_file: tar_file.add(origin_dir, arcname=".") + _LOGGER.info("Snapshot folder %s done", name) self._data[ATTR_FOLDERS].append(name) except tarfile.TarError as err: @@ -352,8 +354,10 @@ class Snapshot(object): remove_folder(origin_dir) try: + _LOGGER.info("Restore folder %s", name) with tarfile.open(snapshot_tar, "r:gz") as tar_file: tar_file.extractall(path=origin_dir) + _LOGGER.info("Restore folder %s done", name) except tarfile.TarError as err: _LOGGER.warning("Can't restore folder %s -> %s", name, err) From 8a11e6c845257424d77bdd3512e67727666d4695 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Mon, 9 Oct 2017 14:08:29 +0200 Subject: [PATCH 7/8] Check if a option is missing inside nested lists (#216) * Update validate.py * fix lint --- hassio/addons/validate.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hassio/addons/validate.py b/hassio/addons/validate.py index 10c8ecb4d..3f02c4738 100644 --- a/hassio/addons/validate.py +++ b/hassio/addons/validate.py @@ -256,6 +256,13 @@ def _nested_validate_list(typ, data_list, key): continue c_options[c_key] = _single_validate(typ[c_key], c_value, c_key) + + # check if all options are exists + missing = set(typ) - set(c_options) + if missing: + raise vol.Invalid( + "Missing {} options inside nested list".format(missing)) + options.append(c_options) # normal list else: From 31cc1dce82889d02232d5d8b8c8c5e848136d466 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Mon, 9 Oct 2017 15:15:30 +0200 Subject: [PATCH 8/8] Update Hass.io to version 0.69 --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index 494a66f46..e98a5f140 100644 --- a/version.json +++ b/version.json @@ -1,5 +1,5 @@ { - "hassio": "0.68", + "hassio": "0.69", "homeassistant": "0.55", "resinos": "1.1", "resinhup": "0.3",