Update api / use local config.json for resinos

This commit is contained in:
pvizeli 2017-04-03 17:00:56 +02:00
parent 9a390f6fc9
commit 931e5f4f6b
12 changed files with 112 additions and 51 deletions

View File

@ -3,7 +3,7 @@ import logging
from aiohttp.web_exceptions import HTTPServiceUnavailable from aiohttp.web_exceptions import HTTPServiceUnavailable
from .util import api_return_ok from .util import api_process, json_loads
from ..const import ATTR_VERSION from ..const import ATTR_VERSION
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -18,12 +18,19 @@ class APIHomeAssistant(object):
self.loop = loop self.loop = loop
self.dock_hass = dock_hass self.dock_hass = dock_hass
@api_process
async def info(self, request): async def info(self, request):
"""Return host information.""" """Return host information."""
return api_return_ok({ info = {
ATTR_VERSION: self.dock_hass.version, ATTR_VERSION: self.dock_hass.version,
}) }
return info
@api_process
async def update(self, request): async def update(self, request):
"""Update host OS.""" """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):

View File

@ -44,5 +44,7 @@ class APIHost(object):
@api_process_hostcontroll @api_process_hostcontroll
async def update(self, request): async def update(self, request):
"""Update host OS.""" """Update host OS."""
body = await request.json() or {} body = await request.json(loads=json_loads)
return await self.host_controll.host_update(body.get(ATTR_VERSION)) version = body.get(ATTR_VERSION)
return await self.host_controll.host_update(version=version)

View File

@ -1,7 +1,7 @@
"""Init file for HassIO supervisor rest api.""" """Init file for HassIO supervisor rest api."""
import logging 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 from ..const import ATTR_VERSION, HASSIO_VERSION
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -16,15 +16,19 @@ class APISupervisor(object):
self.loop = loop self.loop = loop
self.host_controll = host_controll self.host_controll = host_controll
@api_process
async def info(self, request): async def info(self, request):
"""Return host information.""" """Return host information."""
return api_return_ok({ info = {
ATTR_VERSION: HASSIO_VERSION, ATTR_VERSION: HASSIO_VERSION,
}) }
return info
@api_process_hostcontroll @api_process_hostcontroll
async def update(self, request): async def update(self, request):
"""Update host OS.""" """Update host OS."""
body = await request.json() or {} body = await request.json(loads=json_loads)
return await self.host_controll.supervisor_update( version = body.get(ATTR_VERSION, self.config.current_hassio)
body.get(ATTR_VERSION))
return await self.host_controll.supervisor_update(version=version)

View File

@ -1,4 +1,5 @@
"""Init file for HassIO util for rest api.""" """Init file for HassIO util for rest api."""
import json
import logging import logging
from aiohttp import web from aiohttp import web
@ -10,6 +11,28 @@ from ..const import (
_LOGGER = logging.getLogger(__name__) _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): def api_process_hostcontroll(method):
"""Wrap HostControll calls to rest api.""" """Wrap HostControll calls to rest api."""
async def wrap_hostcontroll(api, *args, **kwargs): async def wrap_hostcontroll(api, *args, **kwargs):
@ -19,7 +42,7 @@ def api_process_hostcontroll(method):
answer = await method(api, *args, **kwargs) answer = await method(api, *args, **kwargs)
if isinstance(answer, dict): if isinstance(answer, dict):
return web.json_response(answer) return api_return_ok(data=answer)
elif answer is None: elif answer is None:
return api_not_supported() return api_not_supported()
elif answer: elif answer:

View File

@ -11,9 +11,9 @@ from .config import CoreConfig
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
def initialize_system_data(): def initialize_system_data(websession):
"""Setup default config and create folders.""" """Setup default config and create folders."""
config = CoreConfig() config = CoreConfig(websession)
# homeassistant config folder # homeassistant config folder
if not os.path.isdir(config.path_config): if not os.path.isdir(config.path_config):

View File

@ -3,18 +3,26 @@ import json
import logging import logging
import os import os
from .const import ( from .const import FILE_HASSIO_CONFIG, HASSIO_SHARE
FILE_HASSIO_CONFIG, HOMEASSISTANT_TAG, HOMEASSISTANT_IMAGE, from .tools import fetch_current_versions
HOMEASSISTANT_SSL, HOMEASSISTANT_CONFIG, HASSIO_SHARE)
_LOGGER = logging.getLogger(__name__) _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): class CoreConfig(object):
"""Hold all config data.""" """Hold all config data."""
def __init__(self, config_file=FILE_HASSIO_CONFIG): def __init__(self, websession, config_file=FILE_HASSIO_CONFIG):
"""Initialize config object.""" """Initialize config object."""
self.websession = websession
self._filename = config_file self._filename = config_file
self._data = {} self._data = {}
@ -40,6 +48,20 @@ class CoreConfig(object):
except OSError: except OSError:
_LOGGER.exception("Can't store config in %s", self._filename) _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 @property
def homeassistant_image(self): def homeassistant_image(self):
"""Return docker homeassistant repository.""" """Return docker homeassistant repository."""
@ -56,6 +78,16 @@ class CoreConfig(object):
self._data[HOMEASSISTANT_TAG] = value self._data[HOMEASSISTANT_TAG] = value
self.save() 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 @property
def path_config_docker(self): def path_config_docker(self):
"""Return config path extern for docker.""" """Return config path extern for docker."""

View File

@ -6,24 +6,14 @@ URL_HASSIO_VERSION = \
URL_ADDONS_REPO = 'https://github.com/pvizeli/hassio-addons' URL_ADDONS_REPO = 'https://github.com/pvizeli/hassio-addons'
FILE_HOST_CONFIG = '/boot/config.json' HASSIO_SHARE = "/data"
FILE_HOST_NETWORK = '/boot/network'
FILE_HASSIO_ADDONS = '/data/addons.json'
FILE_HASSIO_CONFIG = '/data/config.json'
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_DOCKER = "/var/run/docker.sock"
SOCKET_HC = "/var/run/hassio-hc.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_RESULT = 'result'
JSON_DATA = 'data' JSON_DATA = 'data'
JSON_MESSAGE = 'message' JSON_MESSAGE = 'message'

View File

@ -21,8 +21,8 @@ class HassIO(object):
def __init__(self, loop): def __init__(self, loop):
"""Initialize hassio object.""" """Initialize hassio object."""
self.loop = loop self.loop = loop
self.config = bootstrap.initialize_system_data()
self.websession = aiohttp.ClientSession(loop=self.loop) self.websession = aiohttp.ClientSession(loop=self.loop)
self.config = bootstrap.initialize_system_data(self.websession)
self.api = RestAPI(self.config, self.loop) self.api = RestAPI(self.config, self.loop)
self.dock = docker.DockerClient( self.dock = docker.DockerClient(
base_url="unix:/{}".format(SOCKET_DOCKER), version='auto') base_url="unix:/{}".format(SOCKET_DOCKER), version='auto')
@ -79,18 +79,17 @@ class HassIO(object):
async def _setup_homeassistant(self): async def _setup_homeassistant(self):
"""Install a homeassistant docker container.""" """Install a homeassistant docker container."""
current = None
while True: while True:
# read homeassistant tag and install it # read homeassistant tag and install it
current = await tools.fetch_current_versions(self.websession) if not self.config.current_homeassistant:
if current and HOMEASSISTANT_TAG in current: await self.config.fetch_update_infos():
resp = await self.homeassistant.install(
current[HOMEASSISTANT_TAG]) tag = self.config.current_homeassistant
if resp: if tag and await self.homeassistant.install(tag):
break break
_LOGGER.warning("Error on setup HomeAssistant. Retry in 60.") _LOGGER.warning("Error on setup HomeAssistant. Retry in 60.")
await asyncio.sleep(60, loop=self.loop) await asyncio.sleep(60, loop=self.loop)
# store version # store version
self.config.homeassistant_tag = current[HOMEASSISTANT_TAG] self.config.homeassistant_tag = self.config.current_homeassistant
_LOGGER.info("HomeAssistant docker now exists.") _LOGGER.info("HomeAssistant docker now exists.")

View File

@ -151,6 +151,8 @@ class DockerBase(object):
if self._install(tag): if self._install(tag):
try: try:
self.dock.images.remove(image=old_image, force=True) self.dock.images.remove(image=old_image, force=True)
return True
except docker.errors.DockerException as err: except docker.errors.DockerException as err:
_LOGGER.warning( _LOGGER.warning(
"Can't remove old image %s -> %s.", old_image, err) "Can't remove old image %s -> %s.", old_image, err)
return False

View File

@ -62,7 +62,7 @@ class HostControll(object):
else: else:
try: try:
return json.loads(response) return json.loads(response)
except ValueError: except json.JSONDecodeError:
_LOGGER.warning("Json parse error from HostControll.") _LOGGER.warning("Json parse error from HostControll.")
except asyncio.TimeoutError: except asyncio.TimeoutError:

View File

@ -1,7 +1,6 @@
"""Tools file for HassIO.""" """Tools file for HassIO."""
import asyncio import asyncio
import logging import logging
import json
import re import re
import socket import socket
@ -23,8 +22,7 @@ async def fetch_current_versions(websession):
try: try:
with async_timeout.timeout(10, loop=websession.loop): with async_timeout.timeout(10, loop=websession.loop):
async with websession.get(URL_HASSIO_VERSION) as request: async with websession.get(URL_HASSIO_VERSION) as request:
data = await request.text() return await request.json(content_type=None)
return json.loads(data)
except (ValueError, aiohttp.ClientError, asyncio.TimeoutError) as err: except (ValueError, aiohttp.ClientError, asyncio.TimeoutError) as err:
_LOGGER.warning("Can't fetch versions from github! %s", err) _LOGGER.warning("Can't fetch versions from github! %s", err)

View File

@ -1,6 +1,8 @@
#!/bin/bash #!/bin/bash
set -o pipefail set -o pipefail
SUPERVISOR_CONFIG="/resin-data/config.json"
# Help function # Help function
function update-resin-supervisor-help { function update-resin-supervisor-help {
cat << EOF cat << EOF
@ -70,15 +72,12 @@ function error_handler {
trap 'error_handler $LINENO' ERR 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 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 fi
# override from params
if [ ! -z $UPDATER_SUPERVISOR_TAG ]; then if [ ! -z $UPDATER_SUPERVISOR_TAG ]; then
tag=$UPDATER_SUPERVISOR_TAG tag=$UPDATER_SUPERVISOR_TAG
fi fi
@ -86,6 +85,11 @@ if [ ! -z $UPDATER_SUPERVISOR_IMAGE ]; then
image_name=$UPDATER_SUPERVISOR_IMAGE image_name=$UPDATER_SUPERVISOR_IMAGE
fi 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. # Get image id of tag. This will be non-empty only in case it's already downloaded.
echo "Getting image id..." echo "Getting image id..."
imageid=$(docker inspect -f '{{.Id}}' "$image_name:$tag") || imageid="" imageid=$(docker inspect -f '{{.Id}}' "$image_name:$tag") || imageid=""