From 931e5f4f6bdb32ed95f7655eaee3e625f9b0b3e5 Mon Sep 17 00:00:00 2001 From: pvizeli Date: Mon, 3 Apr 2017 17:00:56 +0200 Subject: [PATCH] Update api / use local config.json for resinos --- hassio_api/hassio/api/homeassistant.py | 15 +++++-- hassio_api/hassio/api/host.py | 6 ++- hassio_api/hassio/api/supervisor.py | 16 +++++--- hassio_api/hassio/api/util.py | 25 +++++++++++- hassio_api/hassio/bootstrap.py | 4 +- hassio_api/hassio/config.py | 40 +++++++++++++++++-- hassio_api/hassio/const.py | 16 ++------ hassio_api/hassio/core.py | 17 ++++---- hassio_api/hassio/dock/__init__.py | 2 + hassio_api/hassio/host_controll.py | 2 +- hassio_api/hassio/tools.py | 4 +- .../update-resin-supervisor | 16 +++++--- 12 files changed, 112 insertions(+), 51 deletions(-) diff --git a/hassio_api/hassio/api/homeassistant.py b/hassio_api/hassio/api/homeassistant.py index 627de3a4e..aafcc9379 100644 --- a/hassio_api/hassio/api/homeassistant.py +++ b/hassio_api/hassio/api/homeassistant.py @@ -3,7 +3,7 @@ import logging from aiohttp.web_exceptions import HTTPServiceUnavailable -from .util import api_return_ok +from .util import api_process, json_loads from ..const import ATTR_VERSION _LOGGER = logging.getLogger(__name__) @@ -18,12 +18,19 @@ class APIHomeAssistant(object): self.loop = loop self.dock_hass = dock_hass + @api_process async def info(self, request): """Return host information.""" - return api_return_ok({ + info = { ATTR_VERSION: self.dock_hass.version, - }) + } + return info + + @api_process async def update(self, request): """Update host OS.""" - raise HTTPServiceUnavailable() + body = await request.json(loads=json_loads) + version = body.get(ATTR_VERSION, self.config.current_homeassistant) + + return await self.dock_hass.update(version): diff --git a/hassio_api/hassio/api/host.py b/hassio_api/hassio/api/host.py index 4b509e75d..3ceadb997 100644 --- a/hassio_api/hassio/api/host.py +++ b/hassio_api/hassio/api/host.py @@ -44,5 +44,7 @@ class APIHost(object): @api_process_hostcontroll async def update(self, request): """Update host OS.""" - body = await request.json() or {} - return await self.host_controll.host_update(body.get(ATTR_VERSION)) + body = await request.json(loads=json_loads) + version = body.get(ATTR_VERSION) + + return await self.host_controll.host_update(version=version) diff --git a/hassio_api/hassio/api/supervisor.py b/hassio_api/hassio/api/supervisor.py index 0cfef1276..9118787bf 100644 --- a/hassio_api/hassio/api/supervisor.py +++ b/hassio_api/hassio/api/supervisor.py @@ -1,7 +1,7 @@ """Init file for HassIO supervisor rest api.""" import logging -from .util import api_return_ok, api_process_hostcontroll +from .util import api_process, api_process_hostcontroll, json_loads from ..const import ATTR_VERSION, HASSIO_VERSION _LOGGER = logging.getLogger(__name__) @@ -16,15 +16,19 @@ class APISupervisor(object): self.loop = loop self.host_controll = host_controll + @api_process async def info(self, request): """Return host information.""" - return api_return_ok({ + info = { ATTR_VERSION: HASSIO_VERSION, - }) + } + + return info @api_process_hostcontroll async def update(self, request): """Update host OS.""" - body = await request.json() or {} - return await self.host_controll.supervisor_update( - body.get(ATTR_VERSION)) + body = await request.json(loads=json_loads) + version = body.get(ATTR_VERSION, self.config.current_hassio) + + return await self.host_controll.supervisor_update(version=version) diff --git a/hassio_api/hassio/api/util.py b/hassio_api/hassio/api/util.py index 8526437c4..cd9dc4f56 100644 --- a/hassio_api/hassio/api/util.py +++ b/hassio_api/hassio/api/util.py @@ -1,4 +1,5 @@ """Init file for HassIO util for rest api.""" +import json import logging from aiohttp import web @@ -10,6 +11,28 @@ from ..const import ( _LOGGER = logging.getLogger(__name__) +def json_loads(data): + """Extract json from string with support for '' and None.""" + try: + return json.loads(data) + except json.JSONDecodeError: + return {} + + +def api_process(method): + """Wrap function with true/false calls to rest api.""" + async def wrap_api(api, *args, **kwargs): + """Return api information.""" + answer = await method(api, *args, **kwargs) + if isinstance(answer, dict): + return api_return_ok(data=answer) + elif answer: + return api_return_ok() + return api_return_error() + + return wrap_api + + def api_process_hostcontroll(method): """Wrap HostControll calls to rest api.""" async def wrap_hostcontroll(api, *args, **kwargs): @@ -19,7 +42,7 @@ def api_process_hostcontroll(method): answer = await method(api, *args, **kwargs) if isinstance(answer, dict): - return web.json_response(answer) + return api_return_ok(data=answer) elif answer is None: return api_not_supported() elif answer: diff --git a/hassio_api/hassio/bootstrap.py b/hassio_api/hassio/bootstrap.py index 47a0f3301..d4ce6844e 100644 --- a/hassio_api/hassio/bootstrap.py +++ b/hassio_api/hassio/bootstrap.py @@ -11,9 +11,9 @@ from .config import CoreConfig _LOGGER = logging.getLogger(__name__) -def initialize_system_data(): +def initialize_system_data(websession): """Setup default config and create folders.""" - config = CoreConfig() + config = CoreConfig(websession) # homeassistant config folder if not os.path.isdir(config.path_config): diff --git a/hassio_api/hassio/config.py b/hassio_api/hassio/config.py index 372e86ff7..a159148a5 100644 --- a/hassio_api/hassio/config.py +++ b/hassio_api/hassio/config.py @@ -3,18 +3,26 @@ import json import logging import os -from .const import ( - FILE_HASSIO_CONFIG, HOMEASSISTANT_TAG, HOMEASSISTANT_IMAGE, - HOMEASSISTANT_SSL, HOMEASSISTANT_CONFIG, HASSIO_SHARE) +from .const import FILE_HASSIO_CONFIG, HASSIO_SHARE +from .tools import fetch_current_versions _LOGGER = logging.getLogger(__name__) +HOMEASSISTANT_CONFIG = "{}/homeassistant_config" +HOMEASSISTANT_SSL = "{}/homeassistant_ssl" +HOMEASSISTANT_IMAGE = 'homeassistant_image' +HOMEASSISTANT_TAG = 'homeassistant_tag' +HOMEASSISTANT_CURRENT = 'homeassistant_current' + +HASSIO_CURRENT = 'hassio_current' + class CoreConfig(object): """Hold all config data.""" - def __init__(self, config_file=FILE_HASSIO_CONFIG): + def __init__(self, websession, config_file=FILE_HASSIO_CONFIG): """Initialize config object.""" + self.websession = websession self._filename = config_file self._data = {} @@ -40,6 +48,20 @@ class CoreConfig(object): except OSError: _LOGGER.exception("Can't store config in %s", self._filename) + async def fetch_update_infos(): + """Read current versions from web.""" + avilable_updates = await fetch_current_versions(self.websession) + + if avilable_updates: + self._data.update({ + HOMEASSISTANT_CURRENT: current.get('homeassistant_tag'), + HASSIO_CURRENT: current.get('hassio_tag'), + }) + self.save() + return True + + return False + @property def homeassistant_image(self): """Return docker homeassistant repository.""" @@ -56,6 +78,16 @@ class CoreConfig(object): self._data[HOMEASSISTANT_TAG] = value self.save() + @property + def current_homeassistant(self): + """Actual version of homeassistant.""" + return self._data.get(HOMEASSISTANT_CURRENT) + + @property + def current_hassio(self): + """Actual version of hassio.""" + return self._data.get(HASSIO_CURRENT) + @property def path_config_docker(self): """Return config path extern for docker.""" diff --git a/hassio_api/hassio/const.py b/hassio_api/hassio/const.py index 573a3f645..163eeffee 100644 --- a/hassio_api/hassio/const.py +++ b/hassio_api/hassio/const.py @@ -6,24 +6,14 @@ URL_HASSIO_VERSION = \ URL_ADDONS_REPO = 'https://github.com/pvizeli/hassio-addons' -FILE_HOST_CONFIG = '/boot/config.json' -FILE_HOST_NETWORK = '/boot/network' -FILE_HASSIO_ADDONS = '/data/addons.json' -FILE_HASSIO_CONFIG = '/data/config.json' +HASSIO_SHARE = "/data" -HASSIO_SHARE = '/data' +FILE_HASSIO_ADDONS = "{}/addons.json".format(HASSIO_SHARE) +FILE_HASSIO_CONFIG = "{}/config.json".format(HASSIO_SHARE) SOCKET_DOCKER = "/var/run/docker.sock" SOCKET_HC = "/var/run/hassio-hc.sock" -HOMEASSISTANT_CONFIG = "{}/homeassistant_config" -HOMEASSISTANT_SSL = "{}/homeassistant_ssl" - -HTTP_PORT = 9123 - -HOMEASSISTANT_IMAGE = 'homeassistant_image' -HOMEASSISTANT_TAG = 'homeassistant_tag' - JSON_RESULT = 'result' JSON_DATA = 'data' JSON_MESSAGE = 'message' diff --git a/hassio_api/hassio/core.py b/hassio_api/hassio/core.py index dccf06fca..8b2de4b30 100644 --- a/hassio_api/hassio/core.py +++ b/hassio_api/hassio/core.py @@ -21,8 +21,8 @@ class HassIO(object): def __init__(self, loop): """Initialize hassio object.""" self.loop = loop - self.config = bootstrap.initialize_system_data() self.websession = aiohttp.ClientSession(loop=self.loop) + self.config = bootstrap.initialize_system_data(self.websession) self.api = RestAPI(self.config, self.loop) self.dock = docker.DockerClient( base_url="unix:/{}".format(SOCKET_DOCKER), version='auto') @@ -79,18 +79,17 @@ class HassIO(object): async def _setup_homeassistant(self): """Install a homeassistant docker container.""" - current = None while True: # read homeassistant tag and install it - current = await tools.fetch_current_versions(self.websession) - if current and HOMEASSISTANT_TAG in current: - resp = await self.homeassistant.install( - current[HOMEASSISTANT_TAG]) - if resp: - break + if not self.config.current_homeassistant: + await self.config.fetch_update_infos(): + + tag = self.config.current_homeassistant + if tag and await self.homeassistant.install(tag): + break _LOGGER.warning("Error on setup HomeAssistant. Retry in 60.") await asyncio.sleep(60, loop=self.loop) # store version - self.config.homeassistant_tag = current[HOMEASSISTANT_TAG] + self.config.homeassistant_tag = self.config.current_homeassistant _LOGGER.info("HomeAssistant docker now exists.") diff --git a/hassio_api/hassio/dock/__init__.py b/hassio_api/hassio/dock/__init__.py index abb067bb6..89312d887 100644 --- a/hassio_api/hassio/dock/__init__.py +++ b/hassio_api/hassio/dock/__init__.py @@ -151,6 +151,8 @@ class DockerBase(object): if self._install(tag): try: self.dock.images.remove(image=old_image, force=True) + return True except docker.errors.DockerException as err: _LOGGER.warning( "Can't remove old image %s -> %s.", old_image, err) + return False diff --git a/hassio_api/hassio/host_controll.py b/hassio_api/hassio/host_controll.py index 81c64b1b0..b7c710d77 100644 --- a/hassio_api/hassio/host_controll.py +++ b/hassio_api/hassio/host_controll.py @@ -62,7 +62,7 @@ class HostControll(object): else: try: return json.loads(response) - except ValueError: + except json.JSONDecodeError: _LOGGER.warning("Json parse error from HostControll.") except asyncio.TimeoutError: diff --git a/hassio_api/hassio/tools.py b/hassio_api/hassio/tools.py index 95e351ef3..e12f4ca96 100644 --- a/hassio_api/hassio/tools.py +++ b/hassio_api/hassio/tools.py @@ -1,7 +1,6 @@ """Tools file for HassIO.""" import asyncio import logging -import json import re import socket @@ -23,8 +22,7 @@ async def fetch_current_versions(websession): try: with async_timeout.timeout(10, loop=websession.loop): async with websession.get(URL_HASSIO_VERSION) as request: - data = await request.text() - return json.loads(data) + return await request.json(content_type=None) except (ValueError, aiohttp.ClientError, asyncio.TimeoutError) as err: _LOGGER.warning("Can't fetch versions from github! %s", err) diff --git a/meta-hassio/recipes-containers/docker-disk/docker-resin-supervisor-disk/update-resin-supervisor b/meta-hassio/recipes-containers/docker-disk/docker-resin-supervisor-disk/update-resin-supervisor index 373193c38..207d025b2 100644 --- a/meta-hassio/recipes-containers/docker-disk/docker-resin-supervisor-disk/update-resin-supervisor +++ b/meta-hassio/recipes-containers/docker-disk/docker-resin-supervisor-disk/update-resin-supervisor @@ -1,6 +1,8 @@ #!/bin/bash set -o pipefail +SUPERVISOR_CONFIG="/resin-data/config.json" + # Help function function update-resin-supervisor-help { cat << EOF @@ -70,15 +72,12 @@ function error_handler { trap 'error_handler $LINENO' ERR -if tag=$(curl --silent $ENDPOINT | jq -e -r '.hassio_tag'); then +if [ -f $SUPERVISOR_CONFIG ]; then + tag=$(jq --raw-output ".hassio_current // empty" $SUPERVISOR_CONFIG) image_name=$SUPERVISOR_IMAGE - - # Check that we didn't somehow get an empty tag version. - if [ -z $tag ] || [ -z $image_name ]; then - error_handler $LINENO "no tag received" - fi fi +# override from params if [ ! -z $UPDATER_SUPERVISOR_TAG ]; then tag=$UPDATER_SUPERVISOR_TAG fi @@ -86,6 +85,11 @@ if [ ! -z $UPDATER_SUPERVISOR_IMAGE ]; then image_name=$UPDATER_SUPERVISOR_IMAGE fi +# Check that we didn't somehow get an empty tag version. +if [ -z $tag ] || [ -z $image_name ]; then + error_handler $LINENO "no tag received" +fi + # Get image id of tag. This will be non-empty only in case it's already downloaded. echo "Getting image id..." imageid=$(docker inspect -f '{{.Id}}' "$image_name:$tag") || imageid=""