mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-14 12:46:32 +00:00
Update api / use local config.json for resinos
This commit is contained in:
parent
9a390f6fc9
commit
931e5f4f6b
@ -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):
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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:
|
||||||
|
@ -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):
|
||||||
|
@ -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."""
|
||||||
|
@ -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'
|
||||||
|
@ -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.")
|
||||||
|
@ -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
|
||||||
|
@ -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:
|
||||||
|
@ -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)
|
||||||
|
@ -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=""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user