From 86a7f11f64a02659b974ededb436f734c3d68aba Mon Sep 17 00:00:00 2001 From: pvizeli Date: Tue, 18 Apr 2017 12:29:43 +0200 Subject: [PATCH] Allow supervisor to update itself. --- API.md | 6 ++---- hassio/__main__.py | 3 +++ hassio/api/__init__.py | 4 ++-- hassio/api/supervisor.py | 13 +++++++------ hassio/const.py | 4 ++++ hassio/core.py | 8 +++++--- hassio/dock/supervisor.py | 33 +++++++++++++++++++++++++++++---- 7 files changed, 52 insertions(+), 19 deletions(-) diff --git a/API.md b/API.md index 7ed976be2..19be4c499 100644 --- a/API.md +++ b/API.md @@ -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: ``` diff --git a/hassio/__main__.py b/hassio/__main__.py index 26b21251c..5f85f872f 100644 --- a/hassio/__main__.py +++ b/hassio/__main__.py @@ -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) diff --git a/hassio/api/__init__.py b/hassio/api/__init__.py index 84ef3573b..681b7131e 100644 --- a/hassio/api/__init__.py +++ b/hassio/api/__init__.py @@ -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) diff --git a/hassio/api/supervisor.py b/hassio/api/supervisor.py index 9ce407d71..6b25f68ff 100644 --- a/hassio/api/supervisor.py +++ b/hassio/api/supervisor.py @@ -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)) diff --git a/hassio/const.py b/hassio/const.py index 1a35682a3..b1af2e5ef 100644 --- a/hassio/const.py +++ b/hassio/const.py @@ -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' diff --git a/hassio/core.py b/hassio/core.py index ea9db2a05..a833ac720 100644 --- a/hassio/core.py +++ b/hassio/core.py @@ -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): diff --git a/hassio/dock/supervisor.py b/hassio/dock/supervisor.py index ed3f1aeed..f12884533 100644 --- a/hassio/dock/supervisor.py +++ b/hassio/dock/supervisor.py @@ -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!")