mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-27 02:56:31 +00:00
commit
ff80ccce64
@ -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):
|
||||||
|
@ -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
|
||||||
|
@ -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 = [
|
||||||
|
@ -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))
|
||||||
|
@ -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
|
|
||||||
|
@ -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")$"
|
||||||
),
|
),
|
||||||
|
@ -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'
|
||||||
|
@ -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)
|
||||||
|
@ -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):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user