Allow supervisor to update itself.

This commit is contained in:
pvizeli 2017-04-18 12:29:43 +02:00
parent 78d1e1d9e7
commit 86a7f11f64
7 changed files with 52 additions and 19 deletions

6
API.md
View File

@ -11,7 +11,6 @@ Communicate over unix socket with a host daemon.
# reboot
# shutdown
# host-update [v]
# supervisor-update [v]
# network info
# network hostname xy
@ -24,9 +23,8 @@ Communicate over unix socket with a host daemon.
level:
- 1: power functions
- 2: supervisor update
- 4: host update
- 8: network functions
- 2: host update
- 4: network functions
Answer:
```

View File

@ -2,6 +2,7 @@
import asyncio
import logging
import signal
import sys
import hassio.bootstrap as bootstrap
import hassio.core as core
@ -33,4 +34,6 @@ if __name__ == "__main__":
loop.run_forever()
loop.close()
_LOGGER.info("Close Hassio")
sys.exit(hassio.exit_code)

View File

@ -41,10 +41,10 @@ class RestAPI(object):
self.webapp.router.add_get('/network/info', api_net.info)
self.webapp.router.add_get('/network/options', api_net.options)
def register_supervisor(self, host_controll, addons):
def register_supervisor(self, supervisor, addons):
"""Register supervisor function."""
api_supervisor = APISupervisor(
self.config, self.loop, host_controll, addons)
self.config, self.loop, supervisor, addons)
self.webapp.router.add_get('/supervisor/ping', api_supervisor.ping)
self.webapp.router.add_get('/supervisor/info', api_supervisor.info)

View File

@ -1,9 +1,10 @@
"""Init file for HassIO supervisor rest api."""
import asyncio
import logging
import voluptuous as vol
from .util import api_process, api_process_hostcontroll, api_validate
from .util import api_process, api_validate
from ..const import (
ATTR_ADDONS, ATTR_VERSION, ATTR_CURRENT, ATTR_BETA, HASSIO_VERSION)
@ -22,11 +23,11 @@ SCHEMA_VERSION = vol.Schema({
class APISupervisor(object):
"""Handle rest api for supervisor functions."""
def __init__(self, config, loop, host_controll, addons):
def __init__(self, config, loop, supervisor, addons):
"""Initialize supervisor rest api part."""
self.config = config
self.loop = loop
self.host_controll = host_controll
self.supervisor = supervisor
self.addons = addons
@api_process
@ -55,13 +56,13 @@ class APISupervisor(object):
return self.config.save()
@api_process_hostcontroll
@api_process
async def update(self, request):
"""Update host OS."""
"""Update supervisor OS."""
body = await api_validate(SCHEMA_VERSION, request)
version = body.get(ATTR_VERSION, self.config.current_hassio)
if version == HASSIO_VERSION:
raise RuntimeError("Version is already in use")
return await self.host_controll.supervisor_update(version=version)
return await asyncio.shield(self.supervisor.update(version))

View File

@ -15,6 +15,8 @@ HASSIO_SHARE = "/data"
RUN_UPDATE_INFO_TASKS = 28800
RUN_RELOAD_ADDONS_TASKS = 28800
RESTART_EXIT_CODE = 100
FILE_HASSIO_ADDONS = "{}/addons.json".format(HASSIO_SHARE)
FILE_HASSIO_CONFIG = "{}/config.json".format(HASSIO_SHARE)
@ -49,7 +51,9 @@ ATTR_IMAGE = 'image'
STARTUP_BEFORE = 'before'
STARTUP_AFTER = 'after'
STARTUP_ONCE = 'once'
BOOT_AUTO = 'auto'
BOOT_MANUAL = 'manual'
STATE_STARTED = 'started'
STATE_STOPPED = 'stopped'

View File

@ -25,6 +25,7 @@ class HassIO(object):
def __init__(self, loop):
"""Initialize hassio object."""
self.exit_code = 0
self.loop = loop
self.websession = aiohttp.ClientSession(loop=self.loop)
self.config = bootstrap.initialize_system_data(self.websession)
@ -35,7 +36,7 @@ class HassIO(object):
# init basic docker container
self.supervisor = DockerSupervisor(
self.config, self.loop, self.dock)
self.config, self.loop, self.dock, self)
self.homeassistant = DockerHomeAssistant(
self.config, self.loop, self.dock)
@ -63,7 +64,7 @@ class HassIO(object):
# rest api views
self.api.register_host(self.host_controll)
self.api.register_network(self.host_controll)
self.api.register_supervisor(self.host_controll, self.addons)
self.api.register_supervisor(self.supervisor, self.addons)
self.api.register_homeassistant(self.homeassistant)
self.api.register_addons(self.addons)
@ -104,11 +105,12 @@ class HassIO(object):
# start addon mark as after
await self.addons.auto_boot(STARTUP_AFTER)
async def stop(self):
async def stop(self, exit_code=0):
"""Stop a running orchestration."""
tasks = [self.websession.close(), self.api.stop()]
await asyncio.wait(tasks, loop=self.loop)
self.exit_code = exit_code
self.loop.stop()
async def _setup_homeassistant(self):

View File

@ -7,11 +7,40 @@ from . import DockerBase
class DockerSupervisor(DockerBase):
"""Docker hassio wrapper for HomeAssistant."""
def __init__(self, config, loop, dock, hassio, image=None):
"""Initialize docker base wrapper."""
super().__init__(config, loop, dock, image=image)
self.hassio = hassio
@property
def docker_name(self):
"""Return name of docker container."""
return os.environ['SUPERVISOR_NAME']
async def update(self, tag):
"""Update a supervisor docker image.
Return a Future.
"""
if self._lock.locked():
_LOGGER.error("Can't excute update while a task is in progress")
return False
async with self._lock:
if await self.loop.run_in_executor(None, self._update, tag):
self.loop.create_task(self.hassio.stop(RESTART_EXIT_CODE))
def _update(self, tag):
"""Update a docker image.
Need run inside executor.
"""
_LOGGER.info("Update supervisor docker to %s:%s", self.image, tag)
# update docker image
return self._install(tag):
async def run(self):
"""Run docker image."""
raise RuntimeError("Not support on supervisor docker container!")
@ -24,10 +53,6 @@ class DockerSupervisor(DockerBase):
"""Stop/remove docker container."""
raise RuntimeError("Not support on supervisor docker container!")
async def update(self, tag):
"""Update docker image."""
raise RuntimeError("Not support on supervisor docker container!")
async def remove(self):
"""Remove docker image."""
raise RuntimeError("Not support on supervisor docker container!")