Use tmp folder / fix log bug / add executor (#83)

* Use tmp folder / fix log bug / add executor

* Update __main__.py

* Update .travis.yml

* Add autoupdate on startup.

* Fix bug

* Move selfupdate code part into start
This commit is contained in:
Pascal Vizeli 2017-06-28 16:22:44 +02:00 committed by GitHub
parent 40343089b5
commit d5eb66bc0d
11 changed files with 86 additions and 57 deletions

View File

@ -2,7 +2,7 @@ sudo: false
matrix:
fast_finish: true
include:
- python: "3.5"
- python: "3.6"
cache:
directories:

View File

@ -1,5 +1,6 @@
"""Main file for HassIO."""
import asyncio
from concurrent.futures import ThreadPoolExecutor
import logging
import sys
@ -17,7 +18,14 @@ if __name__ == "__main__":
exit(1)
loop = asyncio.get_event_loop()
hassio = core.HassIO(loop)
executor = ThreadPoolExecutor(thread_name_prefix="SyncWorker")
loop.set_default_executor(executor)
_LOGGER.info("Initialize Hassio setup")
config = bootstrap.initialize_system_data()
hassio = core.HassIO(loop, config)
bootstrap.migrate_system_env(config)
_LOGGER.info("Run Hassio setup")
loop.run_until_complete(hassio.setup())
@ -26,7 +34,11 @@ if __name__ == "__main__":
loop.call_soon_threadsafe(loop.create_task, hassio.start())
loop.call_soon_threadsafe(bootstrap.reg_signal, loop, hassio)
_LOGGER.info("Run Hassio loop")
loop.run_forever()
_LOGGER.info("Cleanup system")
executor.shutdown(wait=False)
loop.close()
_LOGGER.info("Close Hassio")

View File

@ -96,6 +96,7 @@ class Addon(object):
**self.data.system[self._id][ATTR_OPTIONS],
**self.data.user[self._id][ATTR_OPTIONS],
}
return self.data.cache[self._id][ATTR_OPTIONS]
@options.setter
def options(self, value):

View File

@ -79,10 +79,10 @@ class APIAddons(object):
return True
@api_process
async def install(self, request, check_installed=False):
async def install(self, request):
"""Install addon."""
body = await api_validate(SCHEMA_VERSION, request)
addon = self._extract_addon(request)
addon = self._extract_addon(request, check_installed=False)
version = body.get(ATTR_VERSION)
return await asyncio.shield(

View File

@ -2,6 +2,7 @@
import logging
import os
import signal
from pathlib import Path
from colorlog import ColoredFormatter
@ -11,9 +12,9 @@ from .config import CoreConfig
_LOGGER = logging.getLogger(__name__)
def initialize_system_data(websession):
def initialize_system_data():
"""Setup default config and create folders."""
config = CoreConfig(websession)
config = CoreConfig()
# homeassistant config folder
if not config.path_config.is_dir():
@ -42,10 +43,10 @@ def initialize_system_data(websession):
config.path_addons_git)
config.path_addons_git.mkdir(parents=True)
if not config.path_addons_build.is_dir():
_LOGGER.info("Create Home-Assistant addon build folder %s",
config.path_addons_build)
config.path_addons_build.mkdir(parents=True)
# hassio tmp folder
if not config.path_tmp.is_dir():
_LOGGER.info("Create hassio temp folder %s", config.path_tmp)
config.path_tmp.mkdir(parents=True)
# hassio backup folder
if not config.path_backup.is_dir():
@ -60,6 +61,18 @@ def initialize_system_data(websession):
return config
def migrate_system_env(config):
"""Cleanup some stuff after update."""
# hass.io 0.37 -> 0.38
old_build = Path(config.path_hassio, "addons/build")
if old_build.is_dir():
try:
old_build.rmdir()
except OSError:
_LOGGER.warning("Can't cleanup old addons build dir.")
def initialize_logging():
"""Setup the logging."""
logging.basicConfig(level=logging.INFO)

View File

@ -8,7 +8,7 @@ from pathlib import Path, PurePath
import voluptuous as vol
from voluptuous.humanize import humanize_error
from .const import FILE_HASSIO_CONFIG, HASSIO_SHARE
from .const import FILE_HASSIO_CONFIG, HASSIO_DATA
from .tools import (
fetch_last_versions, write_json_file, read_json_file, validate_timezone)
@ -27,12 +27,11 @@ ADDONS_CORE = PurePath("addons/core")
ADDONS_LOCAL = PurePath("addons/local")
ADDONS_GIT = PurePath("addons/git")
ADDONS_DATA = PurePath("addons/data")
ADDONS_BUILD = PurePath("addons/build")
ADDONS_CUSTOM_LIST = 'addons_custom_list'
BACKUP_DATA = PurePath("backup")
SHARE_DATA = PurePath("share")
TMP_DATA = PurePath("tmp")
UPSTREAM_BETA = 'upstream_beta'
API_ENDPOINT = 'api_endpoint'
@ -88,9 +87,8 @@ class Config(object):
class CoreConfig(Config):
"""Hold all core config data."""
def __init__(self, websession):
def __init__(self):
"""Initialize config object."""
self.websession = websession
self.arch = None
super().__init__(FILE_HASSIO_CONFIG)
@ -103,10 +101,9 @@ class CoreConfig(Config):
_LOGGER.warning(
"Invalid config %s", humanize_error(self._data, ex))
async def fetch_update_infos(self):
async def fetch_update_infos(self, websession):
"""Read current versions from web."""
last = await fetch_last_versions(
self.websession, beta=self.upstream_beta)
last = await fetch_last_versions(websession, beta=self.upstream_beta)
if last:
self._data.update({
@ -176,6 +173,11 @@ class CoreConfig(Config):
"""Actual version of hassio."""
return self._data.get(HASSIO_LAST)
@property
def path_hassio(self):
"""Return hassio data path."""
return HASSIO_DATA
@property
def path_extern_hassio(self):
"""Return hassio data path extern for docker."""
@ -189,7 +191,7 @@ class CoreConfig(Config):
@property
def path_config(self):
"""Return config path inside supervisor."""
return Path(HASSIO_SHARE, HOMEASSISTANT_CONFIG)
return Path(HASSIO_DATA, HOMEASSISTANT_CONFIG)
@property
def path_extern_ssl(self):
@ -199,22 +201,22 @@ class CoreConfig(Config):
@property
def path_ssl(self):
"""Return SSL path inside supervisor."""
return Path(HASSIO_SHARE, HASSIO_SSL)
return Path(HASSIO_DATA, HASSIO_SSL)
@property
def path_addons_core(self):
"""Return git path for core addons."""
return Path(HASSIO_SHARE, ADDONS_CORE)
return Path(HASSIO_DATA, ADDONS_CORE)
@property
def path_addons_git(self):
"""Return path for git addons."""
return Path(HASSIO_SHARE, ADDONS_GIT)
return Path(HASSIO_DATA, ADDONS_GIT)
@property
def path_addons_local(self):
"""Return path for customs addons."""
return Path(HASSIO_SHARE, ADDONS_LOCAL)
return Path(HASSIO_DATA, ADDONS_LOCAL)
@property
def path_extern_addons_local(self):
@ -224,7 +226,7 @@ class CoreConfig(Config):
@property
def path_addons_data(self):
"""Return root addon data folder."""
return Path(HASSIO_SHARE, ADDONS_DATA)
return Path(HASSIO_DATA, ADDONS_DATA)
@property
def path_extern_addons_data(self):
@ -232,14 +234,14 @@ class CoreConfig(Config):
return PurePath(self.path_extern_hassio, ADDONS_DATA)
@property
def path_addons_build(self):
"""Return root addon build folder."""
return Path(HASSIO_SHARE, ADDONS_BUILD)
def path_tmp(self):
"""Return hass.io temp folder."""
return Path(HASSIO_DATA, TMP_DATA)
@property
def path_backup(self):
"""Return root backup data folder."""
return Path(HASSIO_SHARE, BACKUP_DATA)
return Path(HASSIO_DATA, BACKUP_DATA)
@property
def path_extern_backup(self):
@ -249,7 +251,7 @@ class CoreConfig(Config):
@property
def path_share(self):
"""Return root share data folder."""
return Path(HASSIO_SHARE, SHARE_DATA)
return Path(HASSIO_DATA, SHARE_DATA)
@property
def path_extern_share(self):

View File

@ -10,7 +10,7 @@ URL_HASSIO_VERSION_BETA = ('https://raw.githubusercontent.com/home-assistant/'
URL_HASSIO_ADDONS = 'https://github.com/home-assistant/hassio-addons'
HASSIO_SHARE = Path("/data")
HASSIO_DATA = Path("/data")
RUN_UPDATE_INFO_TASKS = 28800
RUN_UPDATE_SUPERVISOR_TASKS = 29100
@ -20,8 +20,8 @@ RUN_CLEANUP_API_SESSIONS = 900
RESTART_EXIT_CODE = 100
FILE_HASSIO_ADDONS = Path(HASSIO_SHARE, "addons.json")
FILE_HASSIO_CONFIG = Path(HASSIO_SHARE, "config.json")
FILE_HASSIO_ADDONS = Path(HASSIO_DATA, "addons.json")
FILE_HASSIO_CONFIG = Path(HASSIO_DATA, "config.json")
SOCKET_DOCKER = Path("/var/run/docker.sock")
SOCKET_HC = Path("/var/run/hassio-hc.sock")

View File

@ -4,7 +4,6 @@ import logging
import aiohttp
import docker
from . import bootstrap
from .addons import AddonManager
from .api import RestAPI
from .host_control import HostControl
@ -27,28 +26,26 @@ _LOGGER = logging.getLogger(__name__)
class HassIO(object):
"""Main object of hassio."""
def __init__(self, loop):
def __init__(self, loop, config):
"""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)
self.scheduler = Scheduler(self.loop)
self.api = RestAPI(self.config, self.loop)
self.config = config
self.websession = aiohttp.ClientSession(loop=loop)
self.scheduler = Scheduler(loop)
self.api = RestAPI(config, loop)
self.dock = docker.DockerClient(
base_url="unix:/{}".format(str(SOCKET_DOCKER)), version='auto')
# init basic docker container
self.supervisor = DockerSupervisor(
self.config, self.loop, self.dock, self.stop)
self.homeassistant = DockerHomeAssistant(
self.config, self.loop, self.dock)
self.supervisor = DockerSupervisor(config, loop, self.dock, self.stop)
self.homeassistant = DockerHomeAssistant(config, loop, self.dock)
# init HostControl
self.host_control = HostControl(self.loop)
self.host_control = HostControl(loop)
# init addon system
self.addons = AddonManager(self.config, self.loop, self.dock)
self.addons = AddonManager(config, loop, self.dock)
async def setup(self):
"""Setup HassIO orchestration."""
@ -72,8 +69,8 @@ class HassIO(object):
# schedule update info tasks
self.scheduler.register_task(
self.host_control.load, RUN_UPDATE_INFO_TASKS)
# rest api views
self.api.register_host(self.host_control)
self.api.register_network(self.host_control)
@ -89,16 +86,11 @@ class HassIO(object):
api_sessions_cleanup(self.config), RUN_CLEANUP_API_SESSIONS,
now=True)
# schedule update info tasks
self.scheduler.register_task(
self.config.fetch_update_infos, RUN_UPDATE_INFO_TASKS,
now=True)
# first start of supervisor?
if not await self.homeassistant.exists():
_LOGGER.info("No HomeAssistant docker found.")
await homeassistant_setup(
self.config, self.loop, self.homeassistant)
self.config, self.loop, self.homeassistant, self.websession)
else:
await self.homeassistant.attach()
@ -111,7 +103,7 @@ class HassIO(object):
# schedule self update task
self.scheduler.register_task(
hassio_update(self.config, self.supervisor),
hassio_update(self.config, self.supervisor, self.websession),
RUN_UPDATE_SUPERVISOR_TASKS)
# start addon mark as initialize
@ -119,6 +111,14 @@ class HassIO(object):
async def start(self):
"""Start HassIO orchestration."""
# on release channel, try update itself
# on beta channel, only read new versions
if not self.config.upstream_beta:
await self.loop.create_task(
hassio_update(self.config, self.supervisor, self.websession)())
else:
await self.config.fetch_update_infos(self.websession)
# start api
await self.api.start()
_LOGGER.info("Start hassio api on %s", self.config.api_endpoint)

View File

@ -267,7 +267,7 @@ class DockerBase(object):
"""Return docker logs of container."""
if self._lock.locked():
_LOGGER.error("Can't excute logs while a task is in progress")
return False
return b""
async with self._lock:
return await self.loop.run_in_executor(None, self._logs)

View File

@ -145,7 +145,7 @@ class DockerAddon(DockerBase):
Need run inside executor.
"""
build_dir = Path(self.config.path_addons_build, self.addon.slug)
build_dir = Path(self.config.path_tmp, self.addon.slug)
try:
# prepare temporary addon build folder
try:

View File

@ -18,10 +18,11 @@ def api_sessions_cleanup(config):
return _api_sessions_cleanup
def hassio_update(config, supervisor):
def hassio_update(config, supervisor, websession):
"""Create scheduler task for update of supervisor hassio."""
async def _hassio_update():
"""Check and run update of supervisor hassio."""
await config.fetch_update_infos(websession)
if config.last_hassio == supervisor.version:
return
@ -43,12 +44,12 @@ def homeassistant_watchdog(loop, homeassistant):
return _homeassistant_watchdog
async def homeassistant_setup(config, loop, homeassistant):
async def homeassistant_setup(config, loop, homeassistant, websession):
"""Install a homeassistant docker container."""
while True:
# read homeassistant tag and install it
if not config.last_homeassistant:
await config.fetch_update_infos()
await config.fetch_update_infos(websession)
tag = config.last_homeassistant
if tag and await homeassistant.install(tag):