Merge pull request #745 from home-assistant/dev

Release 134
This commit is contained in:
Pascal Vizeli 2018-10-08 00:20:20 +02:00 committed by GitHub
commit ff80ccce64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 43 additions and 27 deletions

View File

@ -355,7 +355,7 @@ class Addon(CoreSysAttributes):
@property @property
def privileged(self): def privileged(self):
"""Return list of privilege.""" """Return list of privilege."""
return self._mesh.get(ATTR_PRIVILEGED) return self._mesh.get(ATTR_PRIVILEGED, [])
@property @property
def apparmor(self): def apparmor(self):

View File

@ -7,7 +7,7 @@ import re
from ..const import ( from ..const import (
SECURITY_DISABLE, SECURITY_PROFILE, PRIVILEGED_NET_ADMIN, SECURITY_DISABLE, SECURITY_PROFILE, PRIVILEGED_NET_ADMIN,
PRIVILEGED_SYS_ADMIN, PRIVILEGED_SYS_RAWIO, PRIVILEGED_SYS_PTRACE, PRIVILEGED_SYS_ADMIN, PRIVILEGED_SYS_RAWIO, PRIVILEGED_SYS_PTRACE,
ROLE_ADMIN, ROLE_MANAGER) PRIVILEGED_DAC_READ_SEARCH, ROLE_ADMIN, ROLE_MANAGER)
RE_SHA1 = re.compile(r"[a-f0-9]{8}") RE_SHA1 = re.compile(r"[a-f0-9]{8}")
@ -29,8 +29,10 @@ def rating_security(addon):
rating += 1 rating += 1
# Privileged options # Privileged options
if addon.privileged in (PRIVILEGED_NET_ADMIN, PRIVILEGED_SYS_ADMIN, if any(privilege in addon.privileged
PRIVILEGED_SYS_RAWIO, PRIVILEGED_SYS_PTRACE): for privilege in (PRIVILEGED_NET_ADMIN, PRIVILEGED_SYS_ADMIN,
PRIVILEGED_SYS_RAWIO, PRIVILEGED_SYS_PTRACE,
PRIVILEGED_DAC_READ_SEARCH)):
rating += -1 rating += -1
# API Hass.io role # API Hass.io role

View File

@ -23,7 +23,7 @@ from ..const import (
ATTR_MACHINE, ATTR_MACHINE,
PRIVILEGED_NET_ADMIN, PRIVILEGED_SYS_ADMIN, PRIVILEGED_SYS_RAWIO, PRIVILEGED_NET_ADMIN, PRIVILEGED_SYS_ADMIN, PRIVILEGED_SYS_RAWIO,
PRIVILEGED_IPC_LOCK, PRIVILEGED_SYS_TIME, PRIVILEGED_SYS_NICE, PRIVILEGED_IPC_LOCK, PRIVILEGED_SYS_TIME, PRIVILEGED_SYS_NICE,
PRIVILEGED_SYS_RESOURCE, PRIVILEGED_SYS_PTRACE, PRIVILEGED_SYS_RESOURCE, PRIVILEGED_SYS_PTRACE, PRIVILEGED_DAC_READ_SEARCH,
ROLE_DEFAULT, ROLE_HOMEASSISTANT, ROLE_MANAGER, ROLE_ADMIN) ROLE_DEFAULT, ROLE_HOMEASSISTANT, ROLE_MANAGER, ROLE_ADMIN)
from ..validate import NETWORK_PORT, DOCKER_PORTS, ALSA_DEVICE, UUID_MATCH from ..validate import NETWORK_PORT, DOCKER_PORTS, ALSA_DEVICE, UUID_MATCH
from ..services.validate import DISCOVERY_SERVICES from ..services.validate import DISCOVERY_SERVICES
@ -61,7 +61,7 @@ ARCH_ALL = [
MACHINE_ALL = [ MACHINE_ALL = [
'intel-nuc', 'qemux86', 'qemux86-64', 'qemuarm', 'qemuarm-64', 'intel-nuc', 'qemux86', 'qemux86-64', 'qemuarm', 'qemuarm-64',
'raspberrypi', 'raspberrypi2', 'raspberrypi3', 'raspberrypi3-64', 'raspberrypi', 'raspberrypi2', 'raspberrypi3', 'raspberrypi3-64',
'odroid-cu2', 'odroid-xu', 'odroid-c2', 'odroid-xu',
] ]
STARTUP_ALL = [ STARTUP_ALL = [
@ -78,6 +78,7 @@ PRIVILEGED_ALL = [
PRIVILEGED_SYS_NICE, PRIVILEGED_SYS_NICE,
PRIVILEGED_SYS_RESOURCE, PRIVILEGED_SYS_RESOURCE,
PRIVILEGED_SYS_PTRACE, PRIVILEGED_SYS_PTRACE,
PRIVILEGED_DAC_READ_SEARCH,
] ]
ROLE_ALL = [ ROLE_ALL = [

View File

@ -24,6 +24,7 @@ from ..const import (
CONTENT_TYPE_PNG, CONTENT_TYPE_BINARY, CONTENT_TYPE_TEXT, REQUEST_FROM) CONTENT_TYPE_PNG, CONTENT_TYPE_BINARY, CONTENT_TYPE_TEXT, REQUEST_FROM)
from ..coresys import CoreSysAttributes from ..coresys import CoreSysAttributes
from ..validate import DOCKER_PORTS, ALSA_DEVICE from ..validate import DOCKER_PORTS, ALSA_DEVICE
from ..exceptions import APIError
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -59,10 +60,10 @@ class APIAddons(CoreSysAttributes):
addon = self.sys_addons.get(addon_slug) addon = self.sys_addons.get(addon_slug)
if not addon: if not addon:
raise RuntimeError("Addon does not exist") raise APIError("Addon does not exist")
if check_installed and not addon.is_installed: if check_installed and not addon.is_installed:
raise RuntimeError("Addon is not installed") raise APIError("Addon is not installed")
return addon return addon
@ -206,7 +207,7 @@ class APIAddons(CoreSysAttributes):
stats = await addon.stats() stats = await addon.stats()
if not stats: if not stats:
raise RuntimeError("No stats available") raise APIError("No stats available")
return { return {
ATTR_CPU_PERCENT: stats.cpu_percent, ATTR_CPU_PERCENT: stats.cpu_percent,
@ -240,7 +241,7 @@ class APIAddons(CoreSysAttributes):
try: try:
addon.schema(options) addon.schema(options)
except vol.Invalid as ex: except vol.Invalid as ex:
raise RuntimeError(humanize_error(options, ex)) from None raise APIError(humanize_error(options, ex)) from None
return asyncio.shield(addon.start()) return asyncio.shield(addon.start())
@ -256,7 +257,7 @@ class APIAddons(CoreSysAttributes):
addon = self._extract_addon(request) addon = self._extract_addon(request)
if addon.last_version == addon.version_installed: if addon.last_version == addon.version_installed:
raise RuntimeError("No update available!") raise APIError("No update available!")
return asyncio.shield(addon.update()) return asyncio.shield(addon.update())
@ -271,7 +272,7 @@ class APIAddons(CoreSysAttributes):
"""Rebuild local build add-on.""" """Rebuild local build add-on."""
addon = self._extract_addon(request) addon = self._extract_addon(request)
if not addon.need_build: if not addon.need_build:
raise RuntimeError("Only local build addons are supported") raise APIError("Only local build addons are supported")
return asyncio.shield(addon.rebuild()) return asyncio.shield(addon.rebuild())
@ -286,7 +287,7 @@ class APIAddons(CoreSysAttributes):
"""Return icon from add-on.""" """Return icon from add-on."""
addon = self._extract_addon(request, check_installed=False) addon = self._extract_addon(request, check_installed=False)
if not addon.with_icon: if not addon.with_icon:
raise RuntimeError("No icon found!") raise APIError("No icon found!")
with addon.path_icon.open('rb') as png: with addon.path_icon.open('rb') as png:
return png.read() return png.read()
@ -296,7 +297,7 @@ class APIAddons(CoreSysAttributes):
"""Return logo from add-on.""" """Return logo from add-on."""
addon = self._extract_addon(request, check_installed=False) addon = self._extract_addon(request, check_installed=False)
if not addon.with_logo: if not addon.with_logo:
raise RuntimeError("No logo found!") raise APIError("No logo found!")
with addon.path_logo.open('rb') as png: with addon.path_logo.open('rb') as png:
return png.read() return png.read()
@ -306,7 +307,7 @@ class APIAddons(CoreSysAttributes):
"""Return changelog from add-on.""" """Return changelog from add-on."""
addon = self._extract_addon(request, check_installed=False) addon = self._extract_addon(request, check_installed=False)
if not addon.with_changelog: if not addon.with_changelog:
raise RuntimeError("No changelog found!") raise APIError("No changelog found!")
with addon.path_changelog.open('r') as changelog: with addon.path_changelog.open('r') as changelog:
return changelog.read() return changelog.read()
@ -316,7 +317,7 @@ class APIAddons(CoreSysAttributes):
"""Write to stdin of add-on.""" """Write to stdin of add-on."""
addon = self._extract_addon(request) addon = self._extract_addon(request)
if not addon.with_stdin: if not addon.with_stdin:
raise RuntimeError("STDIN not supported by add-on") raise APIError("STDIN not supported by add-on")
data = await request.read() data = await request.read()
return await asyncio.shield(addon.write_stdin(data)) return await asyncio.shield(addon.write_stdin(data))

View File

@ -141,5 +141,3 @@ class APIHomeAssistant(CoreSysAttributes):
result = await self.sys_homeassistant.check_config() result = await self.sys_homeassistant.check_config()
if not result.valid: if not result.valid:
raise APIError(result.log) raise APIError(result.log)
return True

View File

@ -59,7 +59,7 @@ ADDONS_ROLE_ACCESS = {
r"|/hardware/.+" r"|/hardware/.+"
r"|/hassos/.+" r"|/hassos/.+"
r"|/supervisor/.+" r"|/supervisor/.+"
r"|/addons/[^/]+/(?!security).+" r"|/addons(?:/[^/]+/(?!security).+)?"
r"|/snapshots.*" r"|/snapshots.*"
r")$" r")$"
), ),

View File

@ -2,7 +2,7 @@
from pathlib import Path from pathlib import Path
from ipaddress import ip_network from ipaddress import ip_network
HASSIO_VERSION = '133' HASSIO_VERSION = '134'
URL_HASSIO_ADDONS = "https://github.com/home-assistant/hassio-addons" URL_HASSIO_ADDONS = "https://github.com/home-assistant/hassio-addons"
URL_HASSIO_VERSION = \ URL_HASSIO_VERSION = \
@ -243,6 +243,7 @@ PRIVILEGED_SYS_TIME = 'SYS_TIME'
PRIVILEGED_SYS_NICE = 'SYS_NICE' PRIVILEGED_SYS_NICE = 'SYS_NICE'
PRIVILEGED_SYS_RESOURCE = 'SYS_RESOURCE' PRIVILEGED_SYS_RESOURCE = 'SYS_RESOURCE'
PRIVILEGED_SYS_PTRACE = 'SYS_PTRACE' PRIVILEGED_SYS_PTRACE = 'SYS_PTRACE'
PRIVILEGED_DAC_READ_SEARCH = 'DAC_READ_SEARCH'
FEATURES_SHUTDOWN = 'shutdown' FEATURES_SHUTDOWN = 'shutdown'
FEATURES_REBOOT = 'reboot' FEATURES_REBOOT = 'reboot'

View File

@ -10,8 +10,12 @@ from ..const import SOCKET_DOCKER
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
# pylint: disable=invalid-name
CommandReturn = attr.make_class('CommandReturn', ['exit_code', 'output']) @attr.s(frozen=True)
class CommandReturn:
"""Return object from command run."""
exit_code = attr.ib()
output = attr.ib()
class DockerAPI: class DockerAPI:
@ -108,8 +112,9 @@ class DockerAPI:
_LOGGER.error("Can't execute command: %s", err) _LOGGER.error("Can't execute command: %s", err)
return CommandReturn(None, b"") return CommandReturn(None, b"")
# cleanup container finally:
with suppress(docker.errors.DockerException): # cleanup container
container.remove(force=True) with suppress(docker.errors.DockerException):
container.remove(force=True)
return CommandReturn(result.get('StatusCode'), output) return CommandReturn(result.get('StatusCode'), output)

View File

@ -31,8 +31,12 @@ _LOGGER = logging.getLogger(__name__)
RE_YAML_ERROR = re.compile(r"homeassistant\.util\.yaml") RE_YAML_ERROR = re.compile(r"homeassistant\.util\.yaml")
# pylint: disable=invalid-name
ConfigResult = attr.make_class('ConfigResult', ['valid', 'log'], frozen=True) @attr.s(frozen=True)
class ConfigResult:
"""Return object from config check."""
valid = attr.ib()
log = attr.ib()
class HomeAssistant(JsonConfig, CoreSysAttributes): class HomeAssistant(JsonConfig, CoreSysAttributes):
@ -357,12 +361,16 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
# if not valid # if not valid
if result.exit_code is None: if result.exit_code is None:
_LOGGER.error("Fatal error on config check!")
raise HomeAssistantError() raise HomeAssistantError()
# parse output # parse output
log = convert_to_ascii(result.output) log = convert_to_ascii(result.output)
if result.exit_code != 0 or RE_YAML_ERROR.search(log): if result.exit_code != 0 or RE_YAML_ERROR.search(log):
_LOGGER.error("Invalid Home Assistant config found!")
return ConfigResult(False, log) return ConfigResult(False, log)
_LOGGER.info("Home Assistant config is valid")
return ConfigResult(True, log) return ConfigResult(True, log)
async def ensure_access_token(self): async def ensure_access_token(self):