Protect docker / Use Longtime poll

This commit is contained in:
pvizeli 2017-04-04 09:43:14 +02:00
parent 49fe7f2da4
commit bea52bce63
3 changed files with 72 additions and 26 deletions

View File

@ -31,4 +31,7 @@ class APIHomeAssistant(object):
body = await request.json(loads=json_loads) body = await request.json(loads=json_loads)
version = body.get(ATTR_VERSION, self.config.current_homeassistant) version = body.get(ATTR_VERSION, self.config.current_homeassistant)
return await self.dock_hass.update(version) if self.dock_hass.in_progress:
raise RuntimeError("Other task is in progress!")
return await asyncio.shield(self.dock_hass.update(version))

View File

@ -23,7 +23,11 @@ def api_process(method):
"""Wrap function with true/false calls to rest api.""" """Wrap function with true/false calls to rest api."""
async def wrap_api(api, *args, **kwargs): async def wrap_api(api, *args, **kwargs):
"""Return api information.""" """Return api information."""
answer = await method(api, *args, **kwargs) try:
answer = await method(api, *args, **kwargs)
except RuntimeError as err:
return api_return_error(message=str(err))
if isinstance(answer, dict): if isinstance(answer, dict):
return api_return_ok(data=answer) return api_return_ok(data=answer)
elif answer: elif answer:

View File

@ -1,4 +1,5 @@
"""Init file for HassIO docker object.""" """Init file for HassIO docker object."""
import asyncio
from contextlib import suppress from contextlib import suppress
import logging import logging
@ -20,18 +21,26 @@ class DockerBase(object):
self.image = image self.image = image
self.container = None self.container = None
self.version = None self.version = None
self._lock = asyncio.Lock(loop=loop)
@property @property
def docker_name(self): def docker_name(self):
"""Return name of docker container.""" """Return name of docker container."""
return None return None
def install(self, tag): @property
"""Pull docker image. def in_progress(self):
"""Return True if a task is in progress."""
return self._lock.locked
Return a Future. async def install(self, tag):
""" """Pull docker image."""
return self.loop.run_in_executor(None, self._install, tag) if self._lock.locked:
_LOGGER.error("Can't excute install while a task is in progress")
return False
async with self._lock:
return await self.loop.run_in_executor(None, self._install, tag)
def _install(self, tag): def _install(self, tag):
"""Pull docker image. """Pull docker image.
@ -44,6 +53,8 @@ class DockerBase(object):
image.tag(self.image, tag='latest') image.tag(self.image, tag='latest')
self.version = get_version_from_env(image.attrs['Config']['Env']) self.version = get_version_from_env(image.attrs['Config']['Env'])
_LOGGER.info("Tag image %s with version %s as latest.",
self.image, self.version)
except docker.errors.APIError as err: except docker.errors.APIError as err:
_LOGGER.error("Can't install %s:%s -> %s.", self.image, tag, err) _LOGGER.error("Can't install %s:%s -> %s.", self.image, tag, err)
return False return False
@ -72,12 +83,14 @@ class DockerBase(object):
self.container.reload() self.container.reload()
return self.container.status == 'running' return self.container.status == 'running'
def attach(self): async def attach(self):
"""Attach to running docker container. """Attach to running docker container."""
if self._lock.locked:
_LOGGER.error("Can't excute stop while a task is in progress")
return False
Return a Future. async with self._lock:
""" return await self.loop.run_in_executor(None, self._attach)
return self.loop.run_in_executor(None, self._attach)
def _attach(self): def _attach(self):
"""Attach to running docker container. """Attach to running docker container.
@ -89,16 +102,25 @@ class DockerBase(object):
self.image = self.container.attrs['Config']['Image'] self.image = self.container.attrs['Config']['Image']
self.version = get_version_from_env( self.version = get_version_from_env(
self.container.attrs['Config']['Env']) self.container.attrs['Config']['Env'])
_LOGGER.info("Attach to image %s with version %s.",
self.image, self.version)
except (docker.errors.DockerException, KeyError): except (docker.errors.DockerException, KeyError):
_LOGGER.fatal( _LOGGER.fatal(
"Can't attach to %s docker container!", self.docker_name) "Can't attach to %s docker container!", self.docker_name)
return False
def run(self): return True
"""Run docker image.
Return a Future. async def run(self):
""" """Run docker image."""
return self.loop.run_in_executor(None, self._run) if self._lock.locked:
_LOGGER.error("Can't excute run while a task is in progress")
return False
async with self._lock:
_LOGGER.info("Run docker image %s.",
self.image)
return await self.loop.run_in_executor(None, self._run)
def _run(self): def _run(self):
"""Run docker image. """Run docker image.
@ -107,12 +129,15 @@ class DockerBase(object):
""" """
raise NotImplementedError() raise NotImplementedError()
def stop(self): async def stop(self):
"""Stop/remove docker container. """Stop/remove docker container."""
if self._lock.locked:
_LOGGER.error("Can't excute stop while a task is in progress")
return False
Return a Future. async with self._lock:
""" await self.loop.run_in_executor(None, self._stop)
return self.loop.run_in_executor(None, self._stop) return True
def _stop(self): def _stop(self):
"""Stop/remove and remove docker container. """Stop/remove and remove docker container.
@ -132,12 +157,17 @@ class DockerBase(object):
self.container = None self.container = None
def update(self, tag): async def update(self, tag):
"""Update a docker image. """Update a docker image.
Return a Future. Return a Future.
""" """
return self.loop.run_in_executor(None, self._update, tag) if self._lock.locked:
_LOGGER.error("Can't excute update while a task is in progress")
return False
async with self._lock:
return await self.loop.run_in_executor(None, self._update, tag)
def _update(self, tag): def _update(self, tag):
"""Update a docker image. """Update a docker image.
@ -145,13 +175,22 @@ class DockerBase(object):
Need run inside executor. Need run inside executor.
""" """
old_image = "{}:{}".format(self.image, self.version) old_image = "{}:{}".format(self.image, self.version)
old_run = self._is_running()
_LOGGER.info("Update docker %s with {}:{}.", self.image, tag)
# update docker image
if self._install(tag): if self._install(tag):
_LOGGER.info("Cleanup old %s docker.", old_image)
self._stop() self._stop()
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 # restore
if old_run:
self._run()
return True
return False