Fix version merge conflict

This commit is contained in:
pvizeli 2017-10-09 15:25:30 +02:00
commit cbf79f1fab
9 changed files with 75 additions and 81 deletions

16
API.md
View File

@ -430,26 +430,10 @@ For reset custom network/audio settings, set it `null`.
- POST `/addons/{addon}/install`
Optional:
```json
{
"version": "VERSION"
}
```
- POST `/addons/{addon}/uninstall`
- POST `/addons/{addon}/update`
Optional:
```json
{
"version": "VERSION"
}
```
- GET `/addons/{addon}/logs`
Output is the raw Docker log.

View File

@ -447,7 +447,7 @@ class Addon(object):
return False
return True
async def install(self, version=None):
async def install(self):
"""Install a addon."""
if self.config.arch not in self.supported_arch:
_LOGGER.error(
@ -463,11 +463,10 @@ class Addon(object):
"Create Home-Assistant addon data folder %s", self.path_data)
self.path_data.mkdir()
version = version or self.last_version
if not await self.docker.install(version):
if not await self.docker.install(self.last_version):
return False
self._set_install(version)
self._set_install(self.last_version)
return True
@check_installed
@ -510,19 +509,18 @@ class Addon(object):
return self.docker.stop()
@check_installed
async def update(self, version=None):
async def update(self):
"""Update addon."""
version = version or self.last_version
last_state = await self.state()
if version == self.version_installed:
if self.last_version == self.version_installed:
_LOGGER.warning(
"Addon %s is already installed in %s", self._id, version)
"No update available for Addon %s", self._id)
return False
if not await self.docker.update(version):
if not await self.docker.update(self.last_version):
return False
self._set_update(version)
self._set_update(self.last_version)
# restore state
if last_state == STATE_STARTED:

View File

@ -1,4 +1,5 @@
"""Validate addons options schema."""
import logging
import re
import voluptuous as vol
@ -17,6 +18,8 @@ from ..const import (
ATTR_ARGS, ATTR_GPIO, ATTR_HOMEASSISTANT_API, ATTR_STDIN)
from ..validate import NETWORK_PORT, DOCKER_PORTS, ALSA_CHANNEL
_LOGGER = logging.getLogger(__name__)
RE_VOLUME = re.compile(r"^(config|ssl|addons|backup|share)(?::(rw|:ro))?$")
@ -176,8 +179,10 @@ def validate_options(raw_schema):
# read options
for key, value in struct.items():
# Ignore unknown options / remove from list
if key not in raw_schema:
raise vol.Invalid("Unknown options {}.".format(key))
_LOGGER.warning("Unknown options %s", key)
continue
typ = raw_schema[key]
try:
@ -202,42 +207,38 @@ def validate_options(raw_schema):
# pylint: disable=no-value-for-parameter
def _single_validate(typ, value, key):
"""Validate a single element."""
try:
# if required argument
if value is None:
raise vol.Invalid("Missing required option '{}'.".format(key))
# if required argument
if value is None:
raise vol.Invalid("Missing required option '{}'.".format(key))
# parse extend data from type
match = RE_SCHEMA_ELEMENT.match(typ)
# parse extend data from type
match = RE_SCHEMA_ELEMENT.match(typ)
# prepare range
range_args = {}
for group_name in ('i_min', 'i_max', 'f_min', 'f_max'):
group_value = match.group(group_name)
if group_value:
range_args[group_name[2:]] = float(group_value)
# prepare range
range_args = {}
for group_name in ('i_min', 'i_max', 'f_min', 'f_max'):
group_value = match.group(group_name)
if group_value:
range_args[group_name[2:]] = float(group_value)
if typ.startswith(V_STR):
return str(value)
elif typ.startswith(V_INT):
return vol.All(vol.Coerce(int), vol.Range(**range_args))(value)
elif typ.startswith(V_FLOAT):
return vol.All(vol.Coerce(float), vol.Range(**range_args))(value)
elif typ.startswith(V_BOOL):
return vol.Boolean()(value)
elif typ.startswith(V_EMAIL):
return vol.Email()(value)
elif typ.startswith(V_URL):
return vol.Url()(value)
elif typ.startswith(V_PORT):
return NETWORK_PORT(value)
elif typ.startswith(V_MATCH):
return vol.Match(match.group('match'))(str(value))
if typ.startswith(V_STR):
return str(value)
elif typ.startswith(V_INT):
return vol.All(vol.Coerce(int), vol.Range(**range_args))(value)
elif typ.startswith(V_FLOAT):
return vol.All(vol.Coerce(float), vol.Range(**range_args))(value)
elif typ.startswith(V_BOOL):
return vol.Boolean()(value)
elif typ.startswith(V_EMAIL):
return vol.Email()(value)
elif typ.startswith(V_URL):
return vol.Url()(value)
elif typ.startswith(V_PORT):
return NETWORK_PORT(value)
elif typ.startswith(V_MATCH):
return vol.Match(match.group('match'))(str(value))
raise vol.Invalid("Fatal error for {} type {}".format(key, typ))
except ValueError:
raise vol.Invalid(
"Type {} error for '{}' on {}.".format(typ, value, key)) from None
raise vol.Invalid("Fatal error for {} type {}".format(key, typ))
def _nested_validate_list(typ, data_list, key):
@ -249,11 +250,19 @@ def _nested_validate_list(typ, data_list, key):
if isinstance(typ, dict):
c_options = {}
for c_key, c_value in element.items():
# Ignore unknown options / remove from list
if c_key not in typ:
raise vol.Invalid(
"Unknown nested options {}".format(c_key))
_LOGGER.warning("Unknown options %s", c_key)
continue
c_options[c_key] = _single_validate(typ[c_key], c_value, c_key)
# check if all options are exists
missing = set(typ) - set(c_options)
if missing:
raise vol.Invalid(
"Missing {} options inside nested list".format(missing))
options.append(c_options)
# normal list
else:
@ -267,8 +276,10 @@ def _nested_validate_dict(typ, data_dict, key):
options = {}
for c_key, c_value in data_dict.items():
# Ignore unknown options / remove from list
if c_key not in typ:
raise vol.Invalid("Unknow nested dict options {}".format(c_key))
_LOGGER.warning("Unknown options %s", c_key)
continue
options[c_key] = _single_validate(typ[c_key], c_value, c_key)

View File

@ -166,14 +166,10 @@ class APIAddons(object):
return True
@api_process
async def install(self, request):
def install(self, request):
"""Install addon."""
body = await api_validate(SCHEMA_VERSION, request)
addon = self._extract_addon(request, check_installed=False)
version = body.get(ATTR_VERSION, addon.last_version)
return await asyncio.shield(
addon.install(version=version), loop=self.loop)
return asyncio.shield(addon.install(), loop=self.loop)
@api_process
def uninstall(self, request):
@ -202,17 +198,14 @@ class APIAddons(object):
return asyncio.shield(addon.stop(), loop=self.loop)
@api_process
async def update(self, request):
def update(self, request):
"""Update addon."""
body = await api_validate(SCHEMA_VERSION, request)
addon = self._extract_addon(request)
version = body.get(ATTR_VERSION, addon.last_version)
if version == addon.version_installed:
raise RuntimeError("Version %s is already in use", version)
if addon.last_version == addon.version_installed:
raise RuntimeError("No update available!")
return await asyncio.shield(
addon.update(version=version), loop=self.loop)
return asyncio.shield(addon.update(), loop=self.loop)
@api_process
def restart(self, request):
@ -253,4 +246,4 @@ class APIAddons(object):
raise RuntimeError("STDIN not supported by addons")
data = await request.read()
return asyncio.shield(addon.write_stdin(data), loop=self.loop)
return await asyncio.shield(addon.write_stdin(data), loop=self.loop)

View File

@ -2,7 +2,7 @@
from pathlib import Path
from ipaddress import ip_network
HASSIO_VERSION = '0.68'
HASSIO_VERSION = '0.69'
URL_HASSIO_VERSION = ('https://raw.githubusercontent.com/home-assistant/'
'hassio/{}/version.json')

View File

@ -95,7 +95,7 @@ class HomeAssistant(JsonConfig):
def watchdog(self, value):
"""Return True if the watchdog should protect Home-Assistant."""
self._data[ATTR_WATCHDOG] = value
self._data.save()
self.save()
@property
def version(self):

View File

@ -197,6 +197,8 @@ class SnapshotsManager(object):
await snapshot.restore_folders()
# start homeassistant restore
_LOGGER.info("Full-Restore %s restore Home-Assistant",
snapshot.slug)
snapshot.restore_homeassistant(self.homeassistant)
task_hass = self.loop.create_task(
self.homeassistant.update(snapshot.homeassistant_version))
@ -279,6 +281,8 @@ class SnapshotsManager(object):
await snapshot.restore_folders(folders)
if homeassistant:
_LOGGER.info("Partial-Restore %s restore Home-Assistant",
snapshot.slug)
snapshot.restore_homeassistant(self.homeassistant)
tasks.append(self.homeassistant.update(
snapshot.homeassistant_version))

View File

@ -261,7 +261,8 @@ class Snapshot(object):
"""Async context to close a snapshot."""
# exists snapshot or exception on build
if self.tar_file.is_file() or exception_type is not None:
return self._tmp.cleanup()
self._tmp.cleanup()
return
# validate data
try:
@ -283,7 +284,6 @@ class Snapshot(object):
_LOGGER.error("Can't write snapshot.json")
self._tmp.cleanup()
self._tmp = None
async def import_addon(self, addon):
"""Add a addon into snapshot."""
@ -323,9 +323,11 @@ class Snapshot(object):
origin_dir = Path(self.config.path_hassio, name)
try:
_LOGGER.info("Snapshot folder %s", name)
with tarfile.open(snapshot_tar, "w:gz",
compresslevel=1) as tar_file:
tar_file.add(origin_dir, arcname=".")
_LOGGER.info("Snapshot folder %s done", name)
self._data[ATTR_FOLDERS].append(name)
except tarfile.TarError as err:
@ -352,8 +354,10 @@ class Snapshot(object):
remove_folder(origin_dir)
try:
_LOGGER.info("Restore folder %s", name)
with tarfile.open(snapshot_tar, "r:gz") as tar_file:
tar_file.extractall(path=origin_dir)
_LOGGER.info("Restore folder %s done", name)
except tarfile.TarError as err:
_LOGGER.warning("Can't restore folder %s -> %s", name, err)

View File

@ -1,5 +1,5 @@
{
"hassio": "0.68",
"hassio": "0.69",
"homeassistant": "0.55",
"resinos": "1.1",
"resinhup": "0.3",