Use lock on homeassistant level

This commit is contained in:
Pascal Vizeli 2018-03-13 23:09:53 +01:00
parent 1efdcd4691
commit c2918d4519
6 changed files with 99 additions and 93 deletions

View File

@ -6,11 +6,11 @@ import docker
import requests
from .interface import DockerInterface
from .utils import docker_process
from ..addons.build import AddonBuild
from ..const import (
MAP_CONFIG, MAP_SSL, MAP_ADDONS, MAP_BACKUP, MAP_SHARE, ENV_TOKEN,
ENV_TIME)
from ..utils import process_lock
_LOGGER = logging.getLogger(__name__)
@ -285,7 +285,7 @@ class DockerAddon(DockerInterface):
_LOGGER.info("Build %s:%s done", self.image, tag)
return True
@docker_process
@process_lock
def export_image(self, path):
"""Export current images into a tar file."""
return self._loop.run_in_executor(None, self._export_image, path)
@ -313,7 +313,7 @@ class DockerAddon(DockerInterface):
_LOGGER.info("Export image %s done", self.image)
return True
@docker_process
@process_lock
def import_image(self, path, tag):
"""Import a tar file as image."""
return self._loop.run_in_executor(None, self._import_image, path, tag)
@ -338,7 +338,7 @@ class DockerAddon(DockerInterface):
self._cleanup()
return True
@docker_process
@process_lock
def write_stdin(self, data):
"""Write to add-on stdin."""
return self._loop.run_in_executor(None, self._write_stdin, data)

View File

@ -5,10 +5,10 @@ import logging
import docker
from .utils import docker_process
from .stats import DockerStats
from ..const import LABEL_VERSION, LABEL_ARCH
from ..coresys import CoreSysAttributes
from ..utils import process_lock
_LOGGER = logging.getLogger(__name__)
@ -20,7 +20,7 @@ class DockerInterface(CoreSysAttributes):
"""Initialize docker base wrapper."""
self.coresys = coresys
self._meta = None
self.lock = asyncio.Lock(loop=self._loop)
self.lock = asyncio.Lock(loop=coresys.loop)
@property
def timeout(self):
@ -58,7 +58,7 @@ class DockerInterface(CoreSysAttributes):
"""Return True if a task is in progress."""
return self.lock.locked()
@docker_process
@process_lock
def install(self, tag):
"""Pull docker image."""
return self._loop.run_in_executor(None, self._install, tag)
@ -126,7 +126,7 @@ class DockerInterface(CoreSysAttributes):
return True
@docker_process
@process_lock
def attach(self):
"""Attach to running docker container."""
return self._loop.run_in_executor(None, self._attach)
@ -149,7 +149,7 @@ class DockerInterface(CoreSysAttributes):
return True
@docker_process
@process_lock
def run(self):
"""Run docker image."""
return self._loop.run_in_executor(None, self._run)
@ -161,7 +161,7 @@ class DockerInterface(CoreSysAttributes):
"""
raise NotImplementedError()
@docker_process
@process_lock
def stop(self):
"""Stop/remove docker container."""
return self._loop.run_in_executor(None, self._stop)
@ -187,7 +187,7 @@ class DockerInterface(CoreSysAttributes):
return True
@docker_process
@process_lock
def remove(self):
"""Remove docker images."""
return self._loop.run_in_executor(None, self._remove)
@ -219,7 +219,7 @@ class DockerInterface(CoreSysAttributes):
self._meta = None
return True
@docker_process
@process_lock
def update(self, tag):
"""Update a docker image."""
return self._loop.run_in_executor(None, self._update, tag)
@ -264,7 +264,7 @@ class DockerInterface(CoreSysAttributes):
except docker.errors.DockerException as err:
_LOGGER.warning("Can't grap logs from %s: %s", self.image, err)
@docker_process
@process_lock
def restart(self):
"""Restart docker container."""
return self._loop.run_in_executor(None, self._restart)
@ -289,7 +289,7 @@ class DockerInterface(CoreSysAttributes):
return True
@docker_process
@process_lock
def cleanup(self):
"""Check if old version exists and cleanup."""
return self._loop.run_in_executor(None, self._cleanup)
@ -315,7 +315,7 @@ class DockerInterface(CoreSysAttributes):
return True
@docker_process
@process_lock
def execute_command(self, command):
"""Create a temporary container and run command."""
return self._loop.run_in_executor(None, self._execute_command, command)

View File

@ -1,20 +0,0 @@
"""HassIO docker utilitys."""
import logging
_LOGGER = logging.getLogger(__name__)
# pylint: disable=protected-access
def docker_process(method):
"""Wrap function with only run once."""
async def wrap_api(api, *args, **kwargs):
"""Return api wrapper."""
if api.lock.locked():
_LOGGER.error(
"Can't excute %s while a task is in progress", method.__name__)
return False
async with api.lock:
return await method(api, *args, **kwargs)
return wrap_api

View File

@ -16,7 +16,7 @@ from .const import (
ATTR_WAIT_BOOT, HEADER_HA_ACCESS, CONTENT_TYPE_JSON)
from .coresys import CoreSysAttributes
from .docker.homeassistant import DockerHomeAssistant
from .utils import convert_to_ascii
from .utils import convert_to_ascii, process_lock
from .utils.json import JsonConfig
from .validate import SCHEMA_HASS_CONFIG
@ -35,6 +35,7 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
super().__init__(FILE_HASSIO_HOMEASSISTANT, SCHEMA_HASS_CONFIG)
self.coresys = coresys
self.instance = DockerHomeAssistant(coresys)
self.lock = asyncio.Lock(loop=coresys.loop)
async def load(self):
"""Prepare HomeAssistant object."""
@ -162,6 +163,7 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
"""Return a UUID of this HomeAssistant."""
return self._data[ATTR_UUID]
@process_lock
async def install_landingpage(self):
"""Install a landingpage."""
_LOGGER.info("Setup HomeAssistant landingpage")
@ -172,8 +174,10 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
await asyncio.sleep(60, loop=self._loop)
# Run landingpage after installation
await self.start()
await self.instance.run()
await self._block_till_run()
@process_lock
async def install(self):
"""Install a landingpage."""
_LOGGER.info("Setup HomeAssistant")
@ -191,9 +195,11 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
# finishing
_LOGGER.info("HomeAssistant docker now installed")
if self.boot:
await self.start()
await self.instance.run()
await self._block_till_run()
await self.instance.cleanup()
@process_lock
async def update(self, version=None):
"""Update HomeAssistant version."""
version = version or self.last_version
@ -208,8 +214,10 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
return await self.instance.update(version)
finally:
if running:
await self.start()
await self.instance.run()
await self._block_till_run()
@process_lock
async def start(self):
"""Run HomeAssistant docker."""
if not await self.instance.run():
@ -224,6 +232,7 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
"""
return self.instance.stop()
@process_lock
async def restart(self):
"""Restart HomeAssistant docker."""
if not await self.instance.restart():
@ -262,7 +271,7 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
@property
def in_progress(self):
"""Return True if a task is in progress."""
return self.instance.in_progress
return self.instance.in_progress or self.lock.locked()
async def check_config(self):
"""Run homeassistant config check."""

View File

@ -1,7 +1,9 @@
"""Tools file for HassIO."""
from datetime import datetime
import logging
import re
_LOGGER = logging.getLogger(__name__)
RE_STRING = re.compile(r"\x1b(\[.*?[@-~]|\].*?(\x07|\x1b\\))")
@ -10,6 +12,21 @@ def convert_to_ascii(raw):
return RE_STRING.sub("", raw.decode())
def process_lock(method):
"""Wrap function with only run once."""
async def wrap_api(api, *args, **kwargs):
"""Return api wrapper."""
if api.lock.locked():
_LOGGER.error(
"Can't excute %s while a task is in progress", method.__name__)
return False
async with api.lock:
return await method(api, *args, **kwargs)
return wrap_api
class AsyncThrottle(object):
"""
Decorator that prevents a function from being called more than once every